Commit 0eadccd7 authored by akling@apple.com's avatar akling@apple.com

Make ImageQualityController per-RenderView.

<https://webkit.org/b/120702>

Reviewed by Anders Carlsson.

Move ImageQualityController to its own files and add a RenderView::imageQualityController()
getter instead of using a global map for all render trees.

This avoids having to unregister every renderer (well, every RenderBoxModelObject) from the
global hash map during render tree teardown.

It also simplifies the live resize optimization a bit since it can now short-circuit if
the RenderView's FrameView is being resized. (Previously there could be any number of
RenderViews present in the map.)

* CMakeLists.txt:
* GNUmakefile.list.am:
* Target.pri:
* WebCore.vcxproj/WebCore.vcxproj:
* WebCore.xcodeproj/project.pbxproj:
* rendering/ImageQualityController.cpp: Added.
* rendering/ImageQualityController.h: Added.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@155077 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent a4be6512
......@@ -2099,6 +2099,7 @@ set(WebCore_SOURCES
rendering/HitTestingTransformState.cpp
rendering/HitTestLocation.cpp
rendering/HitTestResult.cpp
rendering/ImageQualityController.cpp
rendering/InlineBox.cpp
rendering/InlineFlowBox.cpp
rendering/InlineTextBox.cpp
......
2013-09-04 Andreas Kling <akling@apple.com>
Make ImageQualityController per-RenderView.
<https://webkit.org/b/120702>
Reviewed by Anders Carlsson.
Move ImageQualityController to its own files and add a RenderView::imageQualityController()
getter instead of using a global map for all render trees.
This avoids having to unregister every renderer (well, every RenderBoxModelObject) from the
global hash map during render tree teardown.
It also simplifies the live resize optimization a bit since it can now short-circuit if
the RenderView's FrameView is being resized. (Previously there could be any number of
RenderViews present in the map.)
* CMakeLists.txt:
* GNUmakefile.list.am:
* Target.pri:
* WebCore.vcxproj/WebCore.vcxproj:
* WebCore.xcodeproj/project.pbxproj:
* rendering/ImageQualityController.cpp: Added.
* rendering/ImageQualityController.h: Added.
2013-09-04 Roger Fong <roger_fong@apple.com>
Unreviewed Build fix for Windows DebugSuffix configuration.
......@@ -4347,6 +4347,8 @@ webcore_sources += \
Source/WebCore/rendering/HitTestLocation.h \
Source/WebCore/rendering/HitTestResult.cpp \
Source/WebCore/rendering/HitTestResult.h \
Source/WebCore/rendering/ImageQualityController.cpp \
Source/WebCore/rendering/ImageQualityController.h \
Source/WebCore/rendering/InlineBox.cpp \
Source/WebCore/rendering/InlineBox.h \
Source/WebCore/rendering/InlineFlowBox.cpp \
......
......@@ -1146,6 +1146,7 @@ SOURCES += \
rendering/HitTestingTransformState.cpp \
rendering/HitTestLocation.cpp \
rendering/HitTestResult.cpp \
rendering/ImageQualityController.cpp \
rendering/InlineBox.cpp \
rendering/InlineFlowBox.cpp \
rendering/InlineTextBox.cpp \
......@@ -2398,6 +2399,7 @@ HEADERS += \
rendering/HitTestingTransformState.h \
rendering/HitTestLocation.h \
rendering/HitTestResult.h \
rendering/ImageQualityController.h \
rendering/InlineBox.h \
rendering/InlineFlowBox.h \
rendering/InlineTextBox.h \
......
......@@ -9537,6 +9537,20 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Production|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Production|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\rendering\ImageQualityController.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug_WinCairo|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug_WinCairo|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='DebugSuffix|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='DebugSuffix|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release_WinCairo|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release_WinCairo|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Production|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Production|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\rendering\InlineBox.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
......@@ -19959,6 +19973,7 @@
<ClInclude Include="..\rendering\HitTestingTransformState.h" />
<ClInclude Include="..\rendering\HitTestRequest.h" />
<ClInclude Include="..\rendering\HitTestResult.h" />
<ClInclude Include="..\rendering\ImageQualityController.h" />
<ClInclude Include="..\rendering\InlineBox.h" />
<ClInclude Include="..\rendering\InlineFlowBox.h" />
<ClInclude Include="..\rendering\InlineIterator.h" />
......@@ -4625,6 +4625,8 @@
B2FA3E180AB75A6F000E5AC4 /* JSSVGZoomEvent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B2FA3D300AB75A6F000E5AC4 /* JSSVGZoomEvent.cpp */; };
B2FA3E190AB75A6F000E5AC4 /* JSSVGZoomEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = B2FA3D310AB75A6F000E5AC4 /* JSSVGZoomEvent.h */; };
B50F5B810E96CD9900AD71A6 /* WebCoreObjCExtras.mm in Sources */ = {isa = PBXBuildFile; fileRef = B50F5B800E96CD9900AD71A6 /* WebCoreObjCExtras.mm */; settings = {COMPILER_FLAGS = "-Wno-deprecated-declarations"; }; };
B51A2F3F17D7D3AE0072517A /* ImageQualityController.h in Headers */ = {isa = PBXBuildFile; fileRef = B51A2F3E17D7D3A40072517A /* ImageQualityController.h */; };
B51A2F4117D7D5DE0072517A /* ImageQualityController.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B51A2F4017D7D5DA0072517A /* ImageQualityController.cpp */; };
B525A96511CA2340003A23A8 /* JSSQLException.h in Headers */ = {isa = PBXBuildFile; fileRef = B525A96311CA2340003A23A8 /* JSSQLException.h */; };
B525A96611CA2340003A23A8 /* JSSQLException.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B525A96411CA2340003A23A8 /* JSSQLException.cpp */; };
B5320D6B122A24E9002D1440 /* FontPlatformData.h in Headers */ = {isa = PBXBuildFile; fileRef = B5320D69122A24E9002D1440 /* FontPlatformData.h */; settings = {ATTRIBUTES = (Private, ); }; };
......@@ -11431,6 +11433,8 @@
B2FA3D300AB75A6F000E5AC4 /* JSSVGZoomEvent.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = JSSVGZoomEvent.cpp; sourceTree = "<group>"; };
B2FA3D310AB75A6F000E5AC4 /* JSSVGZoomEvent.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = JSSVGZoomEvent.h; sourceTree = "<group>"; };
B50F5B800E96CD9900AD71A6 /* WebCoreObjCExtras.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WebCoreObjCExtras.mm; sourceTree = "<group>"; };
B51A2F3E17D7D3A40072517A /* ImageQualityController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageQualityController.h; sourceTree = "<group>"; };
B51A2F4017D7D5DA0072517A /* ImageQualityController.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ImageQualityController.cpp; sourceTree = "<group>"; };
B525A96311CA2340003A23A8 /* JSSQLException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSSQLException.h; sourceTree = "<group>"; };
B525A96411CA2340003A23A8 /* JSSQLException.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSSQLException.cpp; sourceTree = "<group>"; };
B5320D69122A24E9002D1440 /* FontPlatformData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FontPlatformData.h; sourceTree = "<group>"; };
......@@ -20360,6 +20364,8 @@
930908900AF7EDE40081DF01 /* HitTestRequest.h */,
9307F1D50AF2D59000DBA31A /* HitTestResult.cpp */,
9307F1D60AF2D59000DBA31A /* HitTestResult.h */,
B51A2F4017D7D5DA0072517A /* ImageQualityController.cpp */,
B51A2F3E17D7D3A40072517A /* ImageQualityController.h */,
A8CFF5DF0A155A05000A4234 /* InlineBox.cpp */,
A8CFF5DE0A155A05000A4234 /* InlineBox.h */,
A8CFF5DD0A155A05000A4234 /* InlineFlowBox.cpp */,
......@@ -21611,6 +21617,7 @@
33D0212D131DB37B004091A8 /* CookieStorage.h in Headers */,
9746AF2114F4DDE6003E7A71 /* Coordinates.h in Headers */,
A80E6D040A1989CA007FB8C5 /* Counter.h in Headers */,
B51A2F3F17D7D3AE0072517A /* ImageQualityController.h in Headers */,
BC5EB9790E82069200B25965 /* CounterContent.h in Headers */,
BC5EB9510E82056B00B25965 /* CounterDirectives.h in Headers */,
9392F14C0AD1861B00691BD4 /* CounterNode.h in Headers */,
......@@ -25345,6 +25352,7 @@
85DF812A0AA7787200486AD7 /* DOMHTMLImageElement.mm in Sources */,
85F32AED0AA63B8700FF3184 /* DOMHTMLInputElement.mm in Sources */,
A6148A6812E41D940044A784 /* DOMHTMLKeygenElement.mm in Sources */,
B51A2F4117D7D5DE0072517A /* ImageQualityController.cpp in Sources */,
85BA4CE20AA6861B0088052D /* DOMHTMLLabelElement.mm in Sources */,
85BA4CE40AA6861B0088052D /* DOMHTMLLegendElement.mm in Sources */,
85BA4D120AA688680088052D /* DOMHTMLLIElement.mm in Sources */,
/*
* Copyright (C) 2013 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "ImageQualityController.h"
#include "Frame.h"
#include "GraphicsContext.h"
#include "Page.h"
#include "RenderBoxModelObject.h"
#include "RenderView.h"
namespace WebCore {
static const double cInterpolationCutoff = 800. * 800.;
static const double cLowQualityTimeThreshold = 0.500; // 500 ms
ImageQualityController::ImageQualityController(const RenderView& renderView)
: m_renderView(renderView)
, m_timer(this, &ImageQualityController::highQualityRepaintTimerFired)
, m_animatedResizeIsActive(false)
, m_liveResizeOptimizationIsActive(false)
{
}
void ImageQualityController::removeLayer(RenderBoxModelObject* object, LayerSizeMap* innerMap, const void* layer)
{
if (!innerMap)
return;
innerMap->remove(layer);
if (innerMap->isEmpty())
removeObject(object);
}
void ImageQualityController::set(RenderBoxModelObject* object, LayerSizeMap* innerMap, const void* layer, const LayoutSize& size)
{
if (innerMap)
innerMap->set(layer, size);
else {
LayerSizeMap newInnerMap;
newInnerMap.set(layer, size);
m_objectLayerSizeMap.set(object, newInnerMap);
}
}
void ImageQualityController::removeObject(RenderBoxModelObject* object)
{
m_objectLayerSizeMap.remove(object);
if (m_objectLayerSizeMap.isEmpty()) {
m_animatedResizeIsActive = false;
m_timer.stop();
}
}
void ImageQualityController::highQualityRepaintTimerFired(Timer<ImageQualityController>*)
{
if (m_renderView.documentBeingDestroyed())
return;
if (!m_animatedResizeIsActive && !m_liveResizeOptimizationIsActive)
return;
m_animatedResizeIsActive = false;
// If the FrameView is in live resize, punt the timer and hold back for now.
if (m_renderView.frameView().inLiveResize()) {
restartTimer();
return;
}
for (auto it = m_objectLayerSizeMap.begin(), end = m_objectLayerSizeMap.end(); it != end; ++it)
it->key->repaint();
m_liveResizeOptimizationIsActive = false;
}
void ImageQualityController::restartTimer()
{
m_timer.startOneShot(cLowQualityTimeThreshold);
}
bool ImageQualityController::shouldPaintAtLowQuality(GraphicsContext* context, RenderBoxModelObject* object, Image* image, const void *layer, const LayoutSize& size)
{
// If the image is not a bitmap image, then none of this is relevant and we just paint at high
// quality.
if (!image || !image->isBitmapImage() || context->paintingDisabled())
return false;
switch (object->style()->imageRendering()) {
case ImageRenderingOptimizeSpeed:
case ImageRenderingCrispEdges:
return true;
case ImageRenderingOptimizeQuality:
return false;
case ImageRenderingAuto:
break;
}
// Make sure to use the unzoomed image size, since if a full page zoom is in effect, the image
// is actually being scaled.
IntSize imageSize(image->width(), image->height());
// Look ourselves up in the hashtables.
auto i = m_objectLayerSizeMap.find(object);
LayerSizeMap* innerMap = i != m_objectLayerSizeMap.end() ? &i->value : 0;
LayoutSize oldSize;
bool isFirstResize = true;
if (innerMap) {
LayerSizeMap::iterator j = innerMap->find(layer);
if (j != innerMap->end()) {
isFirstResize = false;
oldSize = j->value;
}
}
// If the containing FrameView is being resized, paint at low quality until resizing is finished.
if (Frame* frame = object->document().frame()) {
bool frameViewIsCurrentlyInLiveResize = frame->view() && frame->view()->inLiveResize();
if (frameViewIsCurrentlyInLiveResize) {
set(object, innerMap, layer, size);
restartTimer();
m_liveResizeOptimizationIsActive = true;
return true;
}
if (m_liveResizeOptimizationIsActive) {
// Live resize has ended, paint in HQ and remove this object from the list.
removeLayer(object, innerMap, layer);
return false;
}
}
const AffineTransform& currentTransform = context->getCTM();
bool contextIsScaled = !currentTransform.isIdentityOrTranslationOrFlipped();
if (!contextIsScaled && size == imageSize) {
// There is no scale in effect. If we had a scale in effect before, we can just remove this object from the list.
removeLayer(object, innerMap, layer);
return false;
}
// There is no need to hash scaled images that always use low quality mode when the page demands it. This is the iChat case.
if (m_renderView.frame().page()->inLowQualityImageInterpolationMode()) {
double totalPixels = static_cast<double>(image->width()) * static_cast<double>(image->height());
if (totalPixels > cInterpolationCutoff)
return true;
}
// If an animated resize is active, paint in low quality and kick the timer ahead.
if (m_animatedResizeIsActive) {
set(object, innerMap, layer, size);
restartTimer();
return true;
}
// If this is the first time resizing this image, or its size is the
// same as the last resize, draw at high res, but record the paint
// size and set the timer.
if (isFirstResize || oldSize == size) {
restartTimer();
set(object, innerMap, layer, size);
return false;
}
// If the timer is no longer active, draw at high quality and don't
// set the timer.
if (!m_timer.isActive()) {
removeLayer(object, innerMap, layer);
return false;
}
// This object has been resized to two different sizes while the timer
// is active, so draw at low quality, set the flag for animated resizes and
// the object to the list for high quality redraw.
set(object, innerMap, layer, size);
m_animatedResizeIsActive = true;
restartTimer();
return true;
}
}
/*
* Copyright (C) 2013 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef ImageQualityController_h
#define ImageQualityController_h
#include "Timer.h"
#include <wtf/HashMap.h>
#include <wtf/PassOwnPtr.h>
namespace WebCore {
class Frame;
class GraphicsContext;
class Image;
class LayoutSize;
class RenderBoxModelObject;
class RenderView;
class ImageQualityController {
WTF_MAKE_NONCOPYABLE(ImageQualityController)
public:
static PassOwnPtr<ImageQualityController> create(const RenderView& renderView) { return adoptPtr(new ImageQualityController(renderView)); }
bool shouldPaintAtLowQuality(GraphicsContext*, RenderBoxModelObject*, Image*, const void* layer, const LayoutSize&);
void rendererWillBeDestroyed(RenderBoxModelObject& renderer) { removeObject(&renderer); }
private:
typedef HashMap<const void*, LayoutSize> LayerSizeMap;
typedef HashMap<RenderBoxModelObject*, LayerSizeMap> ObjectLayerSizeMap;
ImageQualityController(const RenderView&);
void removeLayer(RenderBoxModelObject*, LayerSizeMap* innerMap, const void* layer);
void set(RenderBoxModelObject*, LayerSizeMap* innerMap, const void* layer, const LayoutSize&);
void highQualityRepaintTimerFired(Timer<ImageQualityController>*);
void restartTimer();
void removeObject(RenderBoxModelObject*);
const RenderView& m_renderView;
ObjectLayerSizeMap m_objectLayerSizeMap;
Timer<ImageQualityController> m_timer;
bool m_animatedResizeIsActive;
bool m_liveResizeOptimizationIsActive;
};
} // namespace
#endif
......@@ -3,7 +3,7 @@
* (C) 1999 Antti Koivisto (koivisto@kde.org)
* (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
* (C) 2005, 2006 Samuel Weinig (sam.weinig@gmail.com)
* Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
* Copyright (C) 2005, 2006, 2007, 2008, 2009, 2013 Apple Inc. All rights reserved.
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
......@@ -30,6 +30,7 @@
#include "HTMLFrameOwnerElement.h"
#include "HTMLNames.h"
#include "ImageBuffer.h"
#include "ImageQualityController.h"
#include "Page.h"
#include "Path.h"
#include "RenderBlock.h"
......@@ -54,12 +55,6 @@ namespace WebCore {
using namespace HTMLNames;
static const double cInterpolationCutoff = 800. * 800.;
static const double cLowQualityTimeThreshold = 0.500; // 500 ms
typedef HashMap<const void*, LayoutSize> LayerSizeMap;
typedef HashMap<RenderBoxModelObject*, LayerSizeMap> ObjectLayerSizeMap;
// The HashMap for storing continuation pointers.
// An inline can be split with blocks occuring in between the inline content.
// When this occurs we need a pointer to the next object. We can basically be
......@@ -75,191 +70,6 @@ static ContinuationMap* continuationMap = 0;
typedef HashMap<const RenderBoxModelObject*, RenderTextFragment*> FirstLetterRemainingTextMap;
static FirstLetterRemainingTextMap* firstLetterRemainingTextMap = 0;
class ImageQualityController {
WTF_MAKE_NONCOPYABLE(ImageQualityController); WTF_MAKE_FAST_ALLOCATED;
public:
ImageQualityController();
bool shouldPaintAtLowQuality(GraphicsContext*, RenderBoxModelObject*, Image*, const void* layer, const LayoutSize&);
void removeLayer(RenderBoxModelObject*, LayerSizeMap* innerMap, const void* layer);
void set(RenderBoxModelObject*, LayerSizeMap* innerMap, const void* layer, const LayoutSize&);
void objectDestroyed(RenderBoxModelObject*);
bool isEmpty() { return m_objectLayerSizeMap.isEmpty(); }
private:
void highQualityRepaintTimerFired(Timer<ImageQualityController>*);
void restartTimer();
ObjectLayerSizeMap m_objectLayerSizeMap;
Timer<ImageQualityController> m_timer;
bool m_animatedResizeIsActive;
bool m_liveResizeOptimizationIsActive;
};
ImageQualityController::ImageQualityController()
: m_timer(this, &ImageQualityController::highQualityRepaintTimerFired)
, m_animatedResizeIsActive(false)
, m_liveResizeOptimizationIsActive(false)
{
}
void ImageQualityController::removeLayer(RenderBoxModelObject* object, LayerSizeMap* innerMap, const void* layer)
{
if (innerMap) {
innerMap->remove(layer);
if (innerMap->isEmpty())
objectDestroyed(object);
}
}
void ImageQualityController::set(RenderBoxModelObject* object, LayerSizeMap* innerMap, const void* layer, const LayoutSize& size)
{
if (innerMap)
innerMap->set(layer, size);
else {
LayerSizeMap newInnerMap;
newInnerMap.set(layer, size);
m_objectLayerSizeMap.set(object, newInnerMap);
}
}
void ImageQualityController::objectDestroyed(RenderBoxModelObject* object)
{
m_objectLayerSizeMap.remove(object);
if (m_objectLayerSizeMap.isEmpty()) {
m_animatedResizeIsActive = false;
m_timer.stop();
}
}
void ImageQualityController::highQualityRepaintTimerFired(Timer<ImageQualityController>*)
{
if (!m_animatedResizeIsActive && !m_liveResizeOptimizationIsActive)
return;
m_animatedResizeIsActive = false;
for (ObjectLayerSizeMap::iterator it = m_objectLayerSizeMap.begin(); it != m_objectLayerSizeMap.end(); ++it) {
if (Frame* frame = it->key->document().frame()) {
// If this renderer's containing FrameView is in live resize, punt the timer and hold back for now.
if (frame->view() && frame->view()->inLiveResize()) {
restartTimer();
return;
}
}
it->key->repaint();
}
m_liveResizeOptimizationIsActive = false;
}
void ImageQualityController::restartTimer()
{
m_timer.startOneShot(cLowQualityTimeThreshold);
}
bool ImageQualityController::shouldPaintAtLowQuality(GraphicsContext* context, RenderBoxModelObject* object, Image* image, const void *layer, const LayoutSize& size)
{
// If the image is not a bitmap image, then none of this is relevant and we just paint at high
// quality.
if (!image || !image->isBitmapImage() || context->paintingDisabled())
return false;
switch (object->style()->imageRendering()) {
case ImageRenderingOptimizeSpeed:
case ImageRenderingCrispEdges:
return true;
case ImageRenderingOptimizeQuality:
return false;
case ImageRenderingAuto:
break;
}
// Make sure to use the unzoomed image size, since if a full page zoom is in effect, the image
// is actually being scaled.
IntSize imageSize(image->width(), image->height());
// Look ourselves up in the hashtables.
ObjectLayerSizeMap::iterator i = m_objectLayerSizeMap.find(object);
LayerSizeMap* innerMap = i != m_objectLayerSizeMap.end() ? &i->value : 0;
LayoutSize oldSize;
bool isFirstResize = true;
if (innerMap) {
LayerSizeMap::iterator j = innerMap->find(layer);
if (j != innerMap->end()) {
isFirstResize = false;
oldSize = j->value;
}
}
// If the containing FrameView is being resized, paint at low quality until resizing is finished.
if (Frame* frame = object->document().frame()) {
bool frameViewIsCurrentlyInLiveResize = frame->view() && frame->view()->inLiveResize();
if (frameViewIsCurrentlyInLiveResize) {
set(object, innerMap, layer, size);
restartTimer();
m_liveResizeOptimizationIsActive = true;
return true;
}
if (m_liveResizeOptimizationIsActive) {
// Live resize has ended, paint in HQ and remove this object from the list.
removeLayer(object, innerMap, layer);
return false;
}
}
const AffineTransform& currentTransform = context->getCTM();
bool contextIsScaled = !currentTransform.isIdentityOrTranslationOrFlipped();
if (!contextIsScaled && size == imageSize) {
// There is no scale in effect. If we had a scale in effect before, we can just remove this object from the list.
removeLayer(object, innerMap, layer);
return false;
}
// There is no need to hash scaled images that always use low quality mode when the page demands it. This is the iChat case.
if (object->document().page()->inLowQualityImageInterpolationMode()) {
double totalPixels = static_cast<double>(image->width()) * static_cast<double>(image->height());
if (totalPixels > cInterpolationCutoff)
return true;
}
// If an animated resize is active, paint in low quality and kick the timer ahead.
if (m_animatedResizeIsActive) {
set(object, innerMap, layer, size);
restartTimer();
return true;
}
// If this is the first time resizing this image, or its size is the
// same as the last resize, draw at high res, but record the paint
// size and set the timer.
if (isFirstResize || oldSize == size) {
restartTimer();
set(object, innerMap, layer, size);
return false;
}
// If the timer is no longer active, draw at high quality and don't
// set the timer.
if (!m_timer.isActive()) {
removeLayer(object, innerMap, layer);
return false;
}
// This object has been resized to two different sizes while the timer
// is active, so draw at low quality, set the flag for animated resizes and
// the object to the list for high quality redraw.
set(object, innerMap, layer, size);
m_animatedResizeIsActive = true;
restartTimer();
return true;
}
static ImageQualityController* gImageQualityController = 0;
static ImageQualityController* imageQualityController()
{
if (!gImageQualityController)
gImageQualityController = new ImageQualityController;
return gImageQualityController;
}
void RenderBoxModelObject::setSelectionState(SelectionState state)
{
if (state == SelectionInside && selectionState() != SelectionNone)
......@@ -345,7 +155,7 @@ void RenderBoxModelObject::suspendAnimations(double time)
bool RenderBoxModelObject::shouldPaintAtLowQuality(GraphicsContext* context, Image* image, const void* layer, const LayoutSize& size)