Changeset 34210 in webkit
- Timestamp:
- May 29, 2008 2:05:11 PM (16 years ago)
- Location:
- trunk/WebCore
- Files:
-
- 7 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/WebCore/ChangeLog
r34209 r34210 1 2008-05-29 David Hyatt <hyatt@apple.com> 2 3 Improve the performance of the GUIMark benchmark by 2x in the CoreGraphics code path. 4 5 Whenever a foreground image changes size rapidly, we will now dynamically shift into rendering it 6 using low quality scaling. Once the animation completes, the image will repaint at high quality. 7 Scaled images will still render at high quality by default, only shifting into low quality if 8 the scale factor is rapidly changing. This change raises GUIMark from 21fps to 34fps on my machine. 9 10 Rewrite the Image draw method to avoid the use of throwaway CG subimages. Instead the entire image is 11 always drawn (with the appropriate clip and scale set up to make sure the correct subimage portion shows up 12 in the destination rect). This change raises GUIMark from 34fps to 43fps on my machine. 13 14 Reviewed by Darin 15 16 * platform/graphics/GraphicsContext.cpp: 17 (WebCore::GraphicsContext::drawImage): 18 * platform/graphics/cg/ImageCG.cpp: 19 (WebCore::BitmapImage::draw): 20 * rendering/RenderImage.cpp: 21 (WebCore::RenderImageScaleData::RenderImageScaleData): 22 (WebCore::RenderImageScaleData::~RenderImageScaleData): 23 (WebCore::RenderImageScaleData::size): 24 (WebCore::RenderImageScaleData::time): 25 (WebCore::RenderImageScaleData::useLowQualityScale): 26 (WebCore::RenderImageScaleData::hiqhQualityRepaintTimer): 27 (WebCore::RenderImageScaleData::setSize): 28 (WebCore::RenderImageScaleData::setTime): 29 (WebCore::RenderImageScaleData::setUseLowQualityScale): 30 (WebCore::RenderImageScaleObserver::shouldImagePaintAtLowQuality): 31 (WebCore::RenderImageScaleObserver::imageDestroyed): 32 (WebCore::RenderImageScaleObserver::highQualityRepaintTimerFired): 33 (WebCore::RenderImage::highQualityRepaintTimerFired): 34 (WebCore::RenderImage::~RenderImage): 35 (WebCore::RenderImage::paintReplaced): 36 * rendering/RenderImage.h: 37 1 38 2008-05-29 Justin Garcia <justin.garcia@apple.com> 2 39 -
trunk/WebCore/platform/graphics/BitmapImage.h
r32302 r34210 100 100 ~BitmapImage(); 101 101 102 virtual bool isBitmapImage() const { return true; } 103 102 104 virtual IntSize size() const; 103 105 -
trunk/WebCore/platform/graphics/GraphicsContext.cpp
r32722 r34210 330 330 } 331 331 332 static const int cInterpolationCutoff = 800 * 800;333 334 332 void GraphicsContext::drawImage(Image* image, const FloatRect& dest, const FloatRect& src, CompositeOperator op, bool useLowQualityScale) 335 333 { … … 352 350 th = image->height(); 353 351 354 bool shouldUseLowQualityInterpolation = useLowQualityScale && (tsw != tw || tsh != th) && tsw * tsh > cInterpolationCutoff; 355 if (shouldUseLowQualityInterpolation) { 352 if (useLowQualityScale) { 356 353 save(); 357 354 setUseLowQualityImageInterpolation(true); 358 355 } 359 356 image->draw(this, FloatRect(dest.location(), FloatSize(tw, th)), FloatRect(src.location(), FloatSize(tsw, tsh)), op); 360 if ( shouldUseLowQualityInterpolation)357 if (useLowQualityScale) 361 358 restore(); 362 359 } -
trunk/WebCore/platform/graphics/Image.h
r31830 r34210 80 80 static bool supportsType(const String&); 81 81 82 virtual bool isBitmapImage() const { return false; } 83 82 84 bool isNull() const; 83 85 -
trunk/WebCore/platform/graphics/cg/ImageCG.cpp
r31961 r34210 127 127 } 128 128 129 void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator compositeOp) 130 { 131 CGRect fr = ctxt->roundToDevicePixels(srcRect); 132 CGRect ir = ctxt->roundToDevicePixels(dstRect); 133 129 void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator compositeOp) 130 { 134 131 CGImageRef image = frameAtIndex(m_currentFrame); 135 132 if (!image) // If it's too early we won't have an image yet. … … 137 134 138 135 if (mayFillWithSolidColor()) { 139 fillWithSolidColor(ctxt, ir, solidColor(), compositeOp); 140 return; 141 } 142 143 // Get the height (in adjusted, i.e. scaled, coords) of the portion of the image 144 // that is currently decoded. This could be less that the actual height. 145 CGSize selfSize = size(); // full image size, in pixels 146 float curHeight = CGImageGetHeight(image); // height of loaded portion, in pixels 147 148 CGSize adjustedSize = selfSize; 149 if (curHeight < selfSize.height) { 150 adjustedSize.height *= curHeight / selfSize.height; 151 152 // Is the amount of available bands less than what we need to draw? If so, 153 // we may have to clip 'fr' if it goes outside the available bounds. 154 if (CGRectGetMaxY(fr) > adjustedSize.height) { 155 float frHeight = adjustedSize.height - fr.origin.y; // clip fr to available bounds 156 if (frHeight <= 0) 157 return; // clipped out entirely 158 ir.size.height *= (frHeight / fr.size.height); // scale ir proportionally to fr 159 fr.size.height = frHeight; 160 } 136 fillWithSolidColor(ctxt, destRect, solidColor(), compositeOp); 137 return; 161 138 } 162 139 … … 164 141 ctxt->save(); 165 142 143 // If the source rect is a subportion of the image, then we compute an inflated destination rect that will hold the entire image 144 // and then set a clip to the portion that we want to display. 145 CGSize selfSize = size(); 146 FloatRect adjustedDestRect = destRect; 147 if (srcRect.width() != selfSize.width || srcRect.height() != selfSize.height) { 148 // A subportion of the image is drawing. Adjust the destination rect to 149 // account for this. 150 float xScale = srcRect.width() / destRect.width(); 151 float yScale = srcRect.height() / destRect.height(); 152 153 adjustedDestRect.setLocation(FloatPoint(destRect.x() - srcRect.x() / xScale, destRect.y() - srcRect.y() / yScale)); 154 adjustedDestRect.setSize(FloatSize(size().width() / xScale, size().height() / yScale)); 155 156 CGContextClipToRect(context, destRect); 157 } 158 159 // If the image is only partially loaded, then shrink the destination rect that we're drawing into accordingly. 160 float currHeight = CGImageGetHeight(image); 161 if (currHeight < selfSize.height) 162 adjustedDestRect.setHeight(adjustedDestRect.height() * currHeight / selfSize.height); 163 166 164 // Flip the coords. 167 165 ctxt->setCompositeOperation(compositeOp); 168 CGContextTranslateCTM(context, ir.origin.x, ir.origin.y + ir.size.height);166 CGContextTranslateCTM(context, adjustedDestRect.x(), adjustedDestRect.bottom()); 169 167 CGContextScaleCTM(context, 1, -1); 170 171 // Translated to origin, now draw at 0,0. 172 ir.origin.x = ir.origin.y = 0; 173 174 // If we're drawing a sub portion of the image then create 175 // a image for the sub portion and draw that. 176 // Test using example site at http://www.meyerweb.com/eric/css/edge/complexspiral/demo.html 177 if (fr.size.width != adjustedSize.width || fr.size.height != adjustedSize.height) { 178 // Convert ft to image pixel coords: 179 float xscale = adjustedSize.width / selfSize.width; 180 float yscale = adjustedSize.height / curHeight; // yes, curHeight, not selfSize.height! 181 fr.origin.x /= xscale; 182 fr.origin.y /= yscale; 183 fr.size.width /= xscale; 184 fr.size.height /= yscale; 185 186 image = CGImageCreateWithImageInRect(image, fr); 187 if (image) { 188 CGContextDrawImage(context, ir, image); 189 CFRelease(image); 190 } 191 } else // Draw the whole image. 192 CGContextDrawImage(context, ir, image); 168 adjustedDestRect.setLocation(FloatPoint()); 169 170 // Draw the image. 171 CGContextDrawImage(context, adjustedDestRect, image); 193 172 194 173 ctxt->restore(); -
trunk/WebCore/rendering/RenderImage.cpp
r31981 r34210 37 37 #include "Page.h" 38 38 #include "RenderView.h" 39 #include "SystemTime.h" 39 40 40 41 using namespace std; 41 42 42 43 namespace WebCore { 44 45 static const double cInterpolationCutoff = 800. * 800.; 46 static const double cLowQualityTimeThreshold = 0.050; // 50 ms 47 48 class RenderImageScaleData { 49 public: 50 RenderImageScaleData(RenderImage* image, const IntSize& size, double time, bool lowQualityScale) 51 : m_size(size) 52 , m_time(time) 53 , m_lowQualityScale(lowQualityScale) 54 , m_highQualityRepaintTimer(image, &RenderImage::highQualityRepaintTimerFired) 55 { 56 } 57 58 ~RenderImageScaleData() 59 { 60 m_highQualityRepaintTimer.stop(); 61 } 62 63 const IntSize& size() const { return m_size; } 64 double time() const { return m_time; } 65 bool useLowQualityScale() const { return m_lowQualityScale; } 66 Timer<RenderImage>& hiqhQualityRepaintTimer() { return m_highQualityRepaintTimer; } 67 68 void setSize(const IntSize& s) { m_size = s; } 69 void setTime(double t) { m_time = t; } 70 void setUseLowQualityScale(bool b) 71 { 72 m_highQualityRepaintTimer.stop(); 73 m_lowQualityScale = b; 74 if (b) 75 m_highQualityRepaintTimer.startOneShot(cLowQualityTimeThreshold); 76 } 77 78 private: 79 IntSize m_size; 80 double m_time; 81 bool m_lowQualityScale; 82 Timer<RenderImage> m_highQualityRepaintTimer; 83 }; 84 85 class RenderImageScaleObserver 86 { 87 public: 88 static bool shouldImagePaintAtLowQuality(RenderImage*, const IntSize&); 89 90 static void imageDestroyed(RenderImage* image) 91 { 92 if (gImages) { 93 RenderImageScaleData* data = gImages->take(image); 94 delete data; 95 if (gImages->size() == 0) { 96 delete gImages; 97 gImages = 0; 98 } 99 } 100 } 101 102 static void highQualityRepaintTimerFired(RenderImage* image) 103 { 104 RenderImageScaleObserver::imageDestroyed(image); 105 image->repaint(); 106 } 107 108 static HashMap<RenderImage*, RenderImageScaleData*>* gImages; 109 }; 110 111 bool RenderImageScaleObserver::shouldImagePaintAtLowQuality(RenderImage* image, const IntSize& size) 112 { 113 // If the image is not a bitmap image, then none of this is relevant and we just paint at high 114 // quality. 115 if (!image->image() || !image->image()->isBitmapImage()) 116 return false; 117 118 // Make sure to use the unzoomed image size, since if a full page zoom is in effect, the image 119 // is actually being scaled. 120 IntSize imageSize(image->image()->width(), image->image()->height()); 121 122 // Look ourselves up in the hashtable. 123 RenderImageScaleData* data = 0; 124 if (gImages) 125 data = gImages->get(image); 126 127 if (imageSize == size) { 128 // There is no scale in effect. If we had a scale in effect before, we can just delete this data. 129 if (data) { 130 gImages->remove(image); 131 delete data; 132 } 133 return false; 134 } 135 136 // There is no need to hash scaled images that always use low quality mode when the page demands it. This is the iChat case. 137 if (image->document()->page()->inLowQualityImageInterpolationMode()) { 138 double totalPixels = static_cast<double>(image->image()->width()) * static_cast<double>(image->image()->height()); 139 if (totalPixels > cInterpolationCutoff) 140 return true; 141 } 142 143 // If there is no data yet, we will paint the first scale at high quality and record the paint time in case a second scale happens 144 // very soon. 145 if (!data) { 146 data = new RenderImageScaleData(image, size, currentTime(), false); 147 if (!gImages) 148 gImages = new HashMap<RenderImage*, RenderImageScaleData*>; 149 gImages->set(image, data); 150 return false; 151 } 152 153 // We are scaled, but we painted already at this size, so just keep using whatever mode we last painted with. 154 if (data->size() == size) 155 return data->useLowQualityScale(); 156 157 // We have data and our size just changed. If this change happened quickly, go into low quality mode and then set a repaint 158 // timer to paint in high quality mode. Otherwise it is ok to just paint in high quality mode. 159 double newTime = currentTime(); 160 data->setUseLowQualityScale(newTime - data->time() < cLowQualityTimeThreshold); 161 data->setTime(newTime); 162 data->setSize(size); 163 return data->useLowQualityScale(); 164 } 165 166 HashMap<RenderImage*, RenderImageScaleData*>* RenderImageScaleObserver::gImages = 0; 167 168 void RenderImage::highQualityRepaintTimerFired(Timer<RenderImage>* timer) 169 { 170 RenderImageScaleObserver::highQualityRepaintTimerFired(this); 171 } 43 172 44 173 using namespace HTMLNames; … … 55 184 if (m_cachedImage) 56 185 m_cachedImage->removeClient(this); 186 RenderImageScaleObserver::imageDestroyed(this); 57 187 } 58 188 … … 255 385 #endif 256 386 257 IntRect rect(IntPoint(tx + leftBorder + leftPad, ty + topBorder + topPad), IntSize(cWidth, cHeight)); 258 387 IntSize contentSize(cWidth, cHeight); 388 bool useLowQualityScaling = RenderImageScaleObserver::shouldImagePaintAtLowQuality(this, contentSize); 389 IntRect rect(IntPoint(tx + leftBorder + leftPad, ty + topBorder + topPad), contentSize); 259 390 HTMLImageElement* imageElt = (element() && element()->hasTagName(imgTag)) ? static_cast<HTMLImageElement*>(element()) : 0; 260 391 CompositeOperator compositeOperator = imageElt ? imageElt->compositeOperator() : CompositeSourceOver; 261 context->drawImage(image(cWidth, cHeight), rect, compositeOperator, document()->page()->inLowQualityImageInterpolationMode());392 context->drawImage(image(cWidth, cHeight), rect, compositeOperator, useLowQualityScaling); 262 393 } 263 394 } -
trunk/WebCore/rendering/RenderImage.h
r31981 r34210 69 69 virtual bool hasImage() const { return m_cachedImage; } 70 70 71 void highQualityRepaintTimerFired(Timer<RenderImage>*); 72 71 73 protected: 72 74 virtual Image* image(int w = 0, int h = 0) { return m_cachedImage ? m_cachedImage->image() : nullImage(); } … … 96 98 97 99 static Image* nullImage(); 100 101 friend class RenderImageScaleObserver; 98 102 }; 99 103
Note: See TracChangeset
for help on using the changeset viewer.