Reviewed by Darin.
authorbdakin <bdakin@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 6 Jun 2006 02:27:00 +0000 (02:27 +0000)
committerbdakin <bdakin@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 6 Jun 2006 02:27:00 +0000 (02:27 +0000)
        Fix for <rdar://problem/4567520> Pixel cracks in weather widget at
        1.83 scaling

        To prevent pixel cracks at non-integral scaling factors, before we
        call into CG to draw an image, we have to convert the rect to
        device space, round the origin and size to integers in device
        space, and convert back to user space.

        No test cases added since this only affects non-1.0 resolution
        scale factors.

        * bindings/js/JSCanvasRenderingContext2DCustom.cpp:
        (WebCore::JSCanvasRenderingContext2D::drawImage): drawImage() now
        takes FloatRects.
        * html/CanvasPattern.cpp:
        (WebCore::patternCallback): Call roundToDevicePixels()
        * html/CanvasRenderingContext2D.cpp:
        (WebCore::CanvasRenderingContext2D::drawImage): drawImage() now
        takes FloatRects and call roundToDevicePixels()
        * html/CanvasRenderingContext2D.h: drawImage() now takes
        FloatRects.
        * html/HTMLCanvasElement.cpp:
        (WebCore::HTMLCanvasElement::paint): Call roundToDevicePixels()
        * kcanvas/device/quartz/QuartzSupport.mm:
        (WebCore::debugDumpCGImageToFile): Same as above.
        * platform/GraphicsContext.h:
        * platform/cg/GraphicsContextCG.cpp:
        (WebCore::GraphicsContext::roundToDevicePixels): Takes care of
        converting between coordinate spaces and rounding.
        (WebCore::GraphicsContext::drawLineForText):
        * platform/mac/ImageMac.mm: Call roundToDevicePixels()
        (WebCore::Image::draw): Same as above.
        (WebCore::drawPattern): Same as above.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@14739 268f45cc-cd09-0410-ab3c-d52691b4dbfc

WebCore/ChangeLog
WebCore/bindings/js/JSCanvasRenderingContext2DCustom.cpp
WebCore/html/CanvasPattern.cpp
WebCore/html/CanvasRenderingContext2D.cpp
WebCore/html/CanvasRenderingContext2D.h
WebCore/html/HTMLCanvasElement.cpp
WebCore/kcanvas/device/quartz/QuartzSupport.mm
WebCore/platform/GraphicsContext.h
WebCore/platform/cg/GraphicsContextCG.cpp
WebCore/platform/mac/ImageMac.mm

index d3d64faaaeac3413d4b4b9d826f4bdca01fd7920..d002305ea7c4212a0eee59a7399417b58c023857 100644 (file)
@@ -1,3 +1,41 @@
+2006-06-05  Beth Dakin  <bdakin@apple.com>
+
+        Reviewed by Darin.
+
+        Fix for <rdar://problem/4567520> Pixel cracks in weather widget at 
+        1.83 scaling
+
+        To prevent pixel cracks at non-integral scaling factors, before we 
+        call into CG to draw an image, we have to convert the rect to 
+        device space, round the origin and size to integers in device 
+        space, and convert back to user space. 
+        
+        No test cases added since this only affects non-1.0 resolution 
+        scale factors.
+
+        * bindings/js/JSCanvasRenderingContext2DCustom.cpp:
+        (WebCore::JSCanvasRenderingContext2D::drawImage): drawImage() now 
+        takes FloatRects.
+        * html/CanvasPattern.cpp:
+        (WebCore::patternCallback): Call roundToDevicePixels()
+        * html/CanvasRenderingContext2D.cpp:
+        (WebCore::CanvasRenderingContext2D::drawImage): drawImage() now 
+        takes FloatRects and call roundToDevicePixels()
+        * html/CanvasRenderingContext2D.h: drawImage() now takes 
+        FloatRects.
+        * html/HTMLCanvasElement.cpp:
+        (WebCore::HTMLCanvasElement::paint): Call roundToDevicePixels()
+        * kcanvas/device/quartz/QuartzSupport.mm:
+        (WebCore::debugDumpCGImageToFile): Same as above.
+        * platform/GraphicsContext.h:
+        * platform/cg/GraphicsContextCG.cpp:
+        (WebCore::GraphicsContext::roundToDevicePixels): Takes care of 
+        converting between coordinate spaces and rounding.
+        (WebCore::GraphicsContext::drawLineForText):
+        * platform/mac/ImageMac.mm: Call roundToDevicePixels()
+        (WebCore::Image::draw): Same as above.
+        (WebCore::drawPattern): Same as above.
+
 2006-06-05  Geoffrey Garen  <ggaren@apple.com>
 
         Reviewed by Darin.
index d854b71fd547258d8f171c564749bf74741afa28..d6de007e74f671cd7e3537aa6d3941092a80aff4 100644 (file)
@@ -23,6 +23,7 @@
 #include "CanvasRenderingContext2D.h"
 #include "CanvasStyle.h"
 #include "ExceptionCode.h"
+#include "FloatRect.h"
 #include "HTMLCanvasElement.h"
 #include "HTMLImageElement.h"
 #include "JSCanvasGradient.h"
@@ -196,10 +197,10 @@ JSValue* JSCanvasRenderingContext2D::drawImage(ExecState* exec, const List& args
                 setDOMException(exec, ec);
                 break;
             case 9:
-                context->drawImage(imgElt, args[1]->toNumber(exec), args[2]->toNumber(exec),
-                                   args[3]->toNumber(exec), args[4]->toNumber(exec),
-                                   args[5]->toNumber(exec), args[6]->toNumber(exec),
-                                   args[7]->toNumber(exec), args[8]->toNumber(exec), ec);
+                context->drawImage(imgElt, FloatRect(args[1]->toNumber(exec), args[2]->toNumber(exec),
+                                   args[3]->toNumber(exec), args[4]->toNumber(exec)),
+                                   FloatRect(args[5]->toNumber(exec), args[6]->toNumber(exec),
+                                   args[7]->toNumber(exec), args[8]->toNumber(exec)), ec);
                 setDOMException(exec, ec);
                 break;
             default:
@@ -217,10 +218,10 @@ JSValue* JSCanvasRenderingContext2D::drawImage(ExecState* exec, const List& args
                 setDOMException(exec, ec);
                 break;
             case 9:
-                context->drawImage(canvas, args[1]->toNumber(exec), args[2]->toNumber(exec),
-                                   args[3]->toNumber(exec), args[4]->toNumber(exec),
-                                   args[5]->toNumber(exec), args[6]->toNumber(exec),
-                                   args[7]->toNumber(exec), args[8]->toNumber(exec), ec);
+                context->drawImage(canvas, FloatRect(args[1]->toNumber(exec), args[2]->toNumber(exec),
+                                   args[3]->toNumber(exec), args[4]->toNumber(exec)),
+                                   FloatRect(args[5]->toNumber(exec), args[6]->toNumber(exec),
+                                   args[7]->toNumber(exec), args[8]->toNumber(exec)), ec);
                 setDOMException(exec, ec);
                 break;
             default:
index 382775844785dec4e578d8f1714ee7c3c3064dc6..058fd13801c079b86665fec40daa659d5337e68c 100644 (file)
@@ -104,7 +104,8 @@ static void patternCallback(void* info, CGContextRef context)
 {
     CGImageRef platformImage = static_cast<CanvasPattern*>(info)->platformImage();
     if (platformImage) {
-        CGRect rect = CGRectMake(0, 0, CGImageGetWidth(platformImage), CGImageGetHeight(platformImage));
+        CGRect rect = GraphicsContext(context).roundToDevicePixels(
+            FloatRect(0, 0, CGImageGetWidth(platformImage), CGImageGetHeight(platformImage)));
         CGContextDrawImage(context, rect, platformImage);
         return;
     }
@@ -116,7 +117,7 @@ static void patternCallback(void* info, CGContextRef context)
     if (!image)
         return;
 
-    FloatRect rect = image->rect();
+    FloatRect rect = GraphicsContext(context).roundToDevicePixels(image->rect());
 
     if (image->getCGImageRef()) {
         CGContextDrawImage(context, rect, image->getCGImageRef());
index 3c9f69790c3094eea0ae0fc72aa53fb2bbd5d406..6bda5711d0445c8898a9aa4e791430da4b153551 100644 (file)
@@ -729,34 +729,34 @@ void CanvasRenderingContext2D::drawImage(HTMLImageElement* image,
 {
     ASSERT(image);
     IntSize s = size(image);
-    drawImage(image, 0, 0, s.width(), s.height(), x, y, width, height, ec);
+    drawImage(image, FloatRect(0, 0, s.width(), s.height()), FloatRect(x, y, width, height), ec);
 }
 
-void CanvasRenderingContext2D::drawImage(HTMLImageElement* image,
-    float sx, float sy, float sw, float sh,
-    float dx, float dy, float dw, float dh, ExceptionCode& ec)
+void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, const FloatRect& srcRect, const FloatRect& dstRect,
+    ExceptionCode& ec)
 {
     ASSERT(image);
 
+    GraphicsContext* c = drawingContext();
+    if (!c)
+        return;
+
     ec = 0;
 
     FloatRect imageRect = FloatRect(FloatPoint(), size(image));
-    FloatRect sourceRect = FloatRect(sx, sy, sw, sh);
+    FloatRect sourceRect = c->roundToDevicePixels(srcRect);
+    FloatRect destRect = c->roundToDevicePixels(dstRect);
 
-    if (!(imageRect.contains(sourceRect) && sw > 0 && sh > 0 && dw > 0 && dh > 0)) {
+    if (!(imageRect.contains(sourceRect) && sourceRect.width() > 0 && sourceRect.height() > 0 
+        && destRect.width() > 0 && destRect.height() > 0)) {
         ec = INDEX_SIZE_ERR;
         return;
     }
 
-    GraphicsContext* c = drawingContext();
-    if (!c)
-        return;
-
     CachedImage* cachedImage = image->cachedImage();
     if (!cachedImage)
         return;
 
-    FloatRect destRect = FloatRect(dx, dy, dw, dh);
     willDraw(destRect);
     c->drawImage(cachedImage->image(), destRect, sourceRect, state().m_globalComposite);
 }
@@ -772,18 +772,17 @@ void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas,
     float x, float y, float width, float height, ExceptionCode& ec)
 {
     ASSERT(canvas);
-    drawImage(canvas, 0, 0, canvas->width(), canvas->height(), x, y, width, height, ec);
+    drawImage(canvas, FloatRect(0, 0, canvas->width(), canvas->height()), FloatRect(x, y, width, height), ec);
 }
 
-void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas,
-    float sx, float sy, float sw, float sh,
-    float dx, float dy, float dw, float dh, ExceptionCode& ec)
+void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas, const FloatRect& srcRect,
+    const FloatRect& dstRect, ExceptionCode& ec)
 {
     ASSERT(canvas);
 
     ec = 0;
 
-    if (!(sw > 0 && sh > 0 && dw > 0 && dh > 0)) {
+    if (srcRect.isEmpty() || dstRect.isEmpty()) {
         ec = INDEX_SIZE_ERR;
         return;
     }
@@ -794,26 +793,29 @@ void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas,
     GraphicsContext* c = drawingContext();
     if (!c)
         return;
+        
+    FloatRect sourceRect = c->roundToDevicePixels(srcRect);
+    FloatRect destRect = c->roundToDevicePixels(dstRect);
+        
     // FIXME: Do this through platform-independent GraphicsContext API.
 #if __APPLE__
     CGImageRef platformImage = canvas->createPlatformImage();
     if (!platformImage)
         return;
 
-    FloatRect destRect = FloatRect(dx, dy, dw, dh);
     willDraw(destRect);
 
     float iw = CGImageGetWidth(platformImage);
     float ih = CGImageGetHeight(platformImage);
-    if (sx == 0 && sy == 0 && iw == sw && ih == sh) {
+    if (sourceRect.x() == 0 && sourceRect.y() == 0 && iw == sourceRect.width() && ih == sourceRect.height()) {
         // Fast path, yay!
-        CGContextDrawImage(c->platformContext(), CGRectMake(dx, dy, dw, dh), platformImage);
+        CGContextDrawImage(c->platformContext(), destRect, platformImage);
     } else {
         // Slow path, boo!
         // Create a new bitmap of the appropriate size and then draw that into our context.
 
-        size_t csw = static_cast<size_t>(ceilf(sw));
-        size_t csh = static_cast<size_t>(ceilf(sh));
+        size_t csw = static_cast<size_t>(ceilf(sourceRect.width()));
+        size_t csh = static_cast<size_t>(ceilf(sourceRect.height()));
 
         CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
         size_t bytesPerRow = csw * 4;
@@ -822,13 +824,13 @@ void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas,
         CGContextRef clippedSourceContext = CGBitmapContextCreate(buffer, csw, csh,
             8, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast);
         CGColorSpaceRelease(colorSpace);
-        CGContextTranslateCTM(clippedSourceContext, -sx, -sy);
+        CGContextTranslateCTM(clippedSourceContext, -sourceRect.x(), -sourceRect.y());
         CGContextDrawImage(clippedSourceContext, CGRectMake(0, 0, iw, ih), platformImage);
 
         CGImageRef clippedSourceImage = CGBitmapContextCreateImage(clippedSourceContext);
         CGContextRelease(clippedSourceContext);
 
-        CGContextDrawImage(c->platformContext(), CGRectMake(dx, dy, dw, dh), clippedSourceImage);
+        CGContextDrawImage(c->platformContext(), destRect, clippedSourceImage);
         CGImageRelease(clippedSourceImage);
         
         fastFree(buffer);
index 48590697dce522eca02be70e59dc8c81c455046d..47ea82169196e7a94dbf7d557768d3fae319fc3b 100644 (file)
@@ -143,12 +143,10 @@ namespace WebCore {
 
         void drawImage(HTMLImageElement*, float x, float y);
         void drawImage(HTMLImageElement*, float x, float y, float width, float height, ExceptionCode&);
-        void drawImage(HTMLImageElement*, float sx, float sy, float sw, float sh,
-            float dx, float dy, float dw, float dh, ExceptionCode&);
+        void drawImage(HTMLImageElement*, const FloatRect& srcRect, const FloatRect& dstRect, ExceptionCode&);
         void drawImage(HTMLCanvasElement*, float x, float y);
         void drawImage(HTMLCanvasElement*, float x, float y, float width, float height, ExceptionCode&);
-        void drawImage(HTMLCanvasElement*, float sx, float sy, float sw, float sh,
-            float dx, float dy, float dw, float dh, ExceptionCode&);
+        void drawImage(HTMLCanvasElement*, const FloatRect& srcRect, const FloatRect& dstRect, ExceptionCode&);
 
         void drawImageFromRect(HTMLImageElement*, float sx, float sy, float sw, float sh,
             float dx, float dy, float dw, float dh, const String& compositeOperation);
index 0fee3602edebb2d9d23b73b5d94d7f446d75f98a..e99ad7e5fe6380c125a16a65d51a8c2fc4f403ba 100644 (file)
@@ -138,7 +138,7 @@ void HTMLCanvasElement::paint(GraphicsContext* p, const IntRect& r)
         return;
 #if __APPLE__
     if (CGImageRef image = createPlatformImage()) {
-        CGContextDrawImage(p->platformContext(), r, image);
+        CGContextDrawImage(p->platformContext(), p->roundToDevicePixels(r), image);
         CGImageRelease(image);
     }
 #endif
index e0f5b4dcb92b9bfc97da5c9770da86e83106a427..695a0808b5a47754043713cd3edfb1b8549c3bb3 100644 (file)
@@ -28,6 +28,7 @@
 #if SVG_SUPPORT
 #import "QuartzSupport.h"
 
+#import "GraphicsContext.h"
 #import "KCanvasMatrix.h"
 #import "KCanvasResourcesQuartz.h"
 #import "KRenderingFillPainter.h"
@@ -47,7 +48,8 @@ void debugDumpCGImageToFile(NSString *filename, CGImageRef image, int width, int
     NSImage *fileImage = [[NSImage alloc] initWithSize:NSMakeSize(width, height)];
     [fileImage lockFocus];
     CGContextRef fileImageContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
-    CGContextDrawImage(fileImageContext, CGRectMake(0, 0, width, height), image); 
+    CGContextDrawImage(fileImageContext, GraphicsContext(fileImageContext).roundToDevicePixels(
+        FloatRect(0, 0, width, height)), image); 
     [fileImage unlockFocus];
     NSData *tiff = [fileImage TIFFRepresentation];
     [tiff writeToFile:filename atomically:YES];
index f03a5238ae08a23ce23cccc4f06acfd680ad8d7b..2b004bf7925a70044f95e8ed141d1039a47e38ea 100644 (file)
@@ -116,6 +116,8 @@ namespace WebCore {
         void drawText(const TextRun&, const IntPoint&, const TextStyle& = TextStyle());
         void drawHighlightForText(const TextRun&, const IntPoint&, int h, const TextStyle&, const Color& backgroundColor);
 
+        FloatRect roundToDevicePixels(const FloatRect&);
+        
         void drawLineForText(const IntPoint&, int yOffset, int width, bool printing);
         void drawLineForMisspelling(const IntPoint&, int width);
 
index b1063aa6910d5ddd541b38d37a6fcc4697584005..853c6e3688f67d970867e10ed3f22a8974c94d33 100644 (file)
@@ -636,6 +636,23 @@ void GraphicsContext::translate(const FloatSize& size)
     CGContextTranslateCTM(platformContext(), size.width(), size.height());
 }
 
+FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect)
+{
+    CGRect deviceRect = CGContextConvertRectToDeviceSpace(platformContext(), rect);
+    deviceRect.origin.x = roundf(deviceRect.origin.x);
+    deviceRect.origin.y = roundf(deviceRect.origin.y);
+    deviceRect.size.width = roundf(deviceRect.size.width);
+    deviceRect.size.height = roundf(deviceRect.size.height);
+    
+    // Don't let the height or width round to 0 unless either was originally 0
+    if (deviceRect.size.height == 0 && rect.height() != 0)
+        deviceRect.size.height = 1;
+    if (deviceRect.size.width == 0 && rect.width() != 0)
+        deviceRect.size.width = 1;
+    
+    return CGContextConvertRectToUserSpace(platformContext(), deviceRect);
+}
+
 void GraphicsContext::drawLineForText(const IntPoint& point, int yOffset, int width, bool printing)
 {
     if (paintingDisabled())
@@ -663,14 +680,7 @@ void GraphicsContext::drawLineForText(const IntPoint& point, int yOffset, int wi
             thickness = 1;
 
         // On screen, round all parameters to integer boundaries in device space.
-        CGRect lineRect = CGContextConvertRectToDeviceSpace(platformContext(), CGRectMake(x, y, width, thickness));
-        lineRect.origin.x = roundf(lineRect.origin.x);
-        lineRect.origin.y = roundf(lineRect.origin.y);
-        lineRect.size.width = roundf(lineRect.size.width);
-        lineRect.size.height = roundf(lineRect.size.height);
-        if (lineRect.size.height == 0) // don't let thickness round down to 0 pixels
-            lineRect.size.height = 1;
-        lineRect = CGContextConvertRectToUserSpace(platformContext(), lineRect);
+        CGRect lineRect = roundToDevicePixels(FloatRect(x, y, width, thickness));
         x = lineRect.origin.x;
         y = lineRect.origin.y;
         width = (int)(lineRect.size.width);
index cfa58959547218ff828002f668adee53059a9f95..665563039402556e4dcbb06183267131b495a711 100644 (file)
@@ -204,8 +204,8 @@ void Image::draw(GraphicsContext* ctxt, const FloatRect& dstRect, const FloatRec
     if (!m_source.initialized())
         return;
     
-    CGRect fr = srcRect;
-    CGRect ir = dstRect;
+    CGRect fr = ctxt->roundToDevicePixels(srcRect);
+    CGRect ir = ctxt->roundToDevicePixels(dstRect);
 
     CGImageRef image = frameAtIndex(m_currentFrame);
     if (!image) // If it's too early we won't have an image yet.
@@ -279,7 +279,8 @@ static void drawPattern(void* info, CGContextRef context)
     CGImageRef image = data->frameAtIndex(data->currentFrame());
     float w = CGImageGetWidth(image);
     float h = CGImageGetHeight(image);
-    CGContextDrawImage(context, CGRectMake(0, data->size().height() - h, w, h), image);    
+    CGContextDrawImage(context, GraphicsContext(context).roundToDevicePixels(FloatRect
+        (0, data->size().height() - h, w, h)), image);    
 }
 
 static const CGPatternCallbacks patternCallbacks = { 0, drawPattern, NULL };