Custom CSS cursors do not use -webkit-image-set on retina displays
authortimothy_horton@apple.com <timothy_horton@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 15 Apr 2015 23:21:42 +0000 (23:21 +0000)
committertimothy_horton@apple.com <timothy_horton@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 15 Apr 2015 23:21:42 +0000 (23:21 +0000)
https://bugs.webkit.org/show_bug.cgi?id=120783
.:

Reviewed by Beth Dakin.

Add a manual test for custom CSS cursors on retina displays.

* ManualTests/retina-cursors.html: Added.

Source/WebCore:

<rdar://problem/14921432>

Reviewed by Beth Dakin.

Scale NSCursor images correctly so custom CSS cursors work with
-webkit-image-set on retina displays.

* WebCore.exp.in:
* page/EventHandler.cpp:
(WebCore::EventHandler::selectCursor):
* platform/mac/CursorMac.mm:
(WebCore::createCustomCursor):
(WebCore::Cursor::ensurePlatformCursor):

Source/WebKit2:

Reviewed by Beth Dakin.

Serialize the cursor image scale for SetCursor messages so custom
CSS cursors work with -webkit-image-set on retina displays.

* Shared/WebCoreArgumentCoders.cpp:
(CoreIPC::ArgumentCoder<Cursor>::encode):
(CoreIPC::ArgumentCoder<Cursor>::decode):

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

ChangeLog
ManualTests/retina-cursors.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/page/EventHandler.cpp
Source/WebCore/platform/Cursor.h
Source/WebCore/platform/mac/CursorMac.mm
Source/WebKit2/ChangeLog
Source/WebKit2/Shared/WebCoreArgumentCoders.cpp

index 0deadb7..46824c0 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2015-04-15  Timothy Horton  <timothy_horton@apple.com>
+
+        Custom CSS cursors do not use -webkit-image-set on retina displays
+        https://bugs.webkit.org/show_bug.cgi?id=120783
+
+        Reviewed by Beth Dakin.
+        Patch by Evan Wallace <evan.exe@gmail.com>.
+
+        Add a manual test for custom CSS cursors on retina displays.
+
+        * ManualTests/retina-cursors.html: Added.
+
 2015-04-15  Alex Christensen  <achristensen@webkit.org>
 
         Progress towards CMake on Mac.
diff --git a/ManualTests/retina-cursors.html b/ManualTests/retina-cursors.html
new file mode 100644 (file)
index 0000000..fc00514
--- /dev/null
@@ -0,0 +1,83 @@
+<body>
+This is a test for custom CSS cursors on retina displays.
+Move the mouse over the green pixel in each red square and the left should match the right.
+Note that NSCursor has bugs with precise sub-pixel positioning on retina displays but if you zoom in using the control+scroll shortcut then the cursor will shift to the correct position.
+See https://bugs.webkit.org/show_bug.cgi?id=120783.
+<script>
+
+if (window.devicePixelRatio !== 2) {
+  document.write('<h1>Note: This test only makes sense on a retina display.</h1>');
+}
+
+var demoWidth = 64;
+var demoHeight = 64;
+
+function createCheckerboard(width, height) {
+  var c = document.createElement('canvas').getContext('2d');
+  var imageData = c.createImageData(width, height);
+  var data = imageData.data;
+  c.canvas.width = width;
+  c.canvas.height = height;
+  for (var y = 0; y < height; y++) {
+    for (var x = 0; x < width; x++) {
+      var i = x + y * width << 2;
+      data[i] = data[i + 1] = data[i + 2] = (x ^ y) & 1 ? 255 : 0;
+      data[i + 3] = 255;
+    }
+  }
+  c.putImageData(imageData, 0, 0);
+  return c.canvas;
+}
+
+function createRender(x, y, width, height, scale) {
+  var c = document.createElement('canvas').getContext('2d');
+  var checkerboard = createCheckerboard(width, height);
+  c.canvas.width = demoWidth * 2;
+  c.canvas.height = demoHeight * 2;
+  c.canvas.style.width = demoWidth + 'px';
+  c.canvas.style.height = demoHeight + 'px';
+  c.fillStyle = '#F00'; c.fillRect(0, 0, demoWidth * 2, demoHeight * 2);
+  c.fillStyle = '#D00'; c.fillRect(0, 0, demoWidth, demoHeight);
+  c.fillStyle = '#0F0'; c.fillRect(demoWidth, demoHeight, 2, 2);
+  c.drawImage(checkerboard, demoWidth - 2 * x, demoHeight - 2 * y, checkerboard.width * 2 / scale, checkerboard.height * 2 / scale);
+  return c.canvas;
+}
+
+var number = 0;
+
+function createCursorDemo(x, y, width, height, scale) {
+  var cursor = '-webkit-image-set(url(' + createCheckerboard(width, height).toDataURL() + ') ' + scale + 'x) ' + x + ' ' + y + ', help';
+  document.write('<h3 style="margin-bottom:0;">Test ' + ++number + '</h3>' +
+    '<div style="position:relative;width:' + demoWidth + 'px;height:' + demoHeight + 'px;background:#F00;display:inline-block;margin-right:20px;">' +
+      '<div style="position:absolute;width:' + demoWidth / 2 + 'px;height:' + demoHeight / 2 + 'px;background:#D00;"></div>' +
+      '<div style="position:absolute;left:' + demoWidth / 2 + 'px;top:' + demoHeight / 2 + 'px;width:1px;height:1px;background:#0F0;cursor:' + cursor + ';"></div>' +
+    '</div>');
+  document.body.appendChild(createRender(x, y, width, height, scale));
+}
+
+document.write('<h2>Size tests</h2>');
+createCursorDemo(0, 0, 64, 64, 2);
+createCursorDemo(0, 0, 63, 64, 2);
+createCursorDemo(0, 0, 64, 63, 2);
+createCursorDemo(0, 0, 63, 63, 2);
+createCursorDemo(0, 0, 5, 4, 2);
+createCursorDemo(0, 0, 5, 3, 2);
+createCursorDemo(0, 0, 4, 3, 2);
+createCursorDemo(0, 0, 2, 2, 2);
+createCursorDemo(0, 0, 1, 1, 2);
+
+document.write('<h2>Hot spot tests</h2>');
+createCursorDemo(15, 15, 64, 64, 2);
+createCursorDemo(15, 15, 32, 32, 1);
+createCursorDemo(31, 31, 64, 64, 2);
+createCursorDemo(31, 31, 32, 32, 1);
+createCursorDemo(1, 1, 9, 9, 2);
+createCursorDemo(16, 0, 64, 64, 2);
+createCursorDemo(0, 16, 64, 64, 2);
+createCursorDemo(3, 4, 8, 10, 2);
+createCursorDemo(2, 3, 7, 9, 2);
+createCursorDemo(1, 0, 5, 3, 2);
+createCursorDemo(2, 1, 5, 3, 2);
+
+</script>
+</body>
index 530bc91..6f67418 100644 (file)
@@ -1,3 +1,22 @@
+2015-04-15  Timothy Horton  <timothy_horton@apple.com>
+
+        Custom CSS cursors do not use -webkit-image-set on retina displays
+        https://bugs.webkit.org/show_bug.cgi?id=120783
+        <rdar://problem/14921432>
+
+        Reviewed by Beth Dakin.
+        Patch by Evan Wallace <evan.exe@gmail.com>.
+
+        Scale NSCursor images correctly so custom CSS cursors work with
+        -webkit-image-set on retina displays.
+
+        * WebCore.exp.in:
+        * page/EventHandler.cpp:
+        (WebCore::EventHandler::selectCursor):
+        * platform/mac/CursorMac.mm:
+        (WebCore::createCustomCursor):
+        (WebCore::Cursor::ensurePlatformCursor):
+
 2015-04-15  Alexey Proskuryakov  <ap@apple.com>
 
         No thread safety when passing ThreadableLoaderOptions from a worker thread
index 98a5d39..1f42a91 100644 (file)
@@ -1413,7 +1413,6 @@ OptionalCursor EventHandler::selectCursor(const HitTestResult& result, bool shif
             float scale = styleImage->imageScaleFactor();
             // Get hotspot and convert from logical pixels to physical pixels.
             IntPoint hotSpot = (*cursors)[i].hotSpot();
-            hotSpot.scale(scale, scale);
             FloatSize size = cachedImage->imageForRenderer(renderer)->size();
             if (cachedImage->errorOccurred())
                 continue;
index b47f401..0586f4e 100644 (file)
@@ -146,7 +146,7 @@ namespace WebCore {
 
 #if ENABLE(MOUSE_CURSOR_SCALE)
         // Hot spot is in image pixels.
-        Cursor(Image*, const IntPoint& hotSpot, float imageScaleFactor);
+        WEBCORE_EXPORT Cursor(Image*, const IntPoint& hotSpot, float imageScaleFactor);
 #endif
 
         WEBCORE_EXPORT ~Cursor();
index 27cdf6e..7656423 100644 (file)
@@ -41,16 +41,43 @@ namespace WebCore {
 // Simple NSCursor calls shouldn't need protection,
 // but creating a cursor with a bad image might throw.
 
+#if ENABLE(MOUSE_CURSOR_SCALE)
+static RetainPtr<NSCursor> createCustomCursor(Image* image, const IntPoint& hotSpot, float scale)
+#else
 static RetainPtr<NSCursor> createCustomCursor(Image* image, const IntPoint& hotSpot)
+#endif
 {
     // FIXME: The cursor won't animate.  Not sure if that's a big deal.
     NSImage* nsImage = image->getNSImage();
     if (!nsImage)
         return 0;
     BEGIN_BLOCK_OBJC_EXCEPTIONS;
+
+#if ENABLE(MOUSE_CURSOR_SCALE)
+    NSSize size = NSMakeSize(image->width() / scale, image->height() / scale);
+    NSSize expandedSize = NSMakeSize(ceil(size.width), ceil(size.height));
+
+    // Pad the image with transparent pixels so it has an integer boundary.
+    if (size.width != expandedSize.width || size.height != expandedSize.height) {
+        RetainPtr<NSImage> expandedImage = adoptNS([[NSImage alloc] initWithSize:expandedSize]);
+        NSRect toRect = NSMakeRect(0, expandedSize.height - size.height, size.width, size.height);
+        NSRect fromRect = NSMakeRect(0, 0, image->width(), image->height());
+
+        [expandedImage lockFocus];
+        [nsImage drawInRect:toRect fromRect:fromRect operation:NSCompositeSourceOver fraction:1];
+        [expandedImage unlockFocus];
+
+        return adoptNS([[NSCursor alloc] initWithImage:expandedImage.get() hotSpot:hotSpot]);
+    }
+
+    // Scale the image and its representation to match retina resolution.
+    [nsImage setSize:expandedSize];
+    [[[nsImage representations] objectAtIndex:0] setSize:expandedSize];
+#endif
+
     return adoptNS([[NSCursor alloc] initWithImage:nsImage hotSpot:hotSpot]);
     END_BLOCK_OBJC_EXCEPTIONS;
-    return 0;
+    return nullptr;
 }
 
 void Cursor::ensurePlatformCursor() const
@@ -205,7 +232,11 @@ void Cursor::ensurePlatformCursor() const
         break;
 
     case Cursor::Custom:
+#if ENABLE(MOUSE_CURSOR_SCALE)
+        m_platformCursor = createCustomCursor(m_image.get(), m_hotSpot, m_imageScaleFactor);
+#else
         m_platformCursor = createCustomCursor(m_image.get(), m_hotSpot);
+#endif
         break;
     }
 }
index 5de03fd..35de7fc 100644 (file)
@@ -1,3 +1,18 @@
+2015-04-15  Timothy Horton  <timothy_horton@apple.com>
+
+        Custom CSS cursors do not use -webkit-image-set on retina displays
+        https://bugs.webkit.org/show_bug.cgi?id=120783
+
+        Reviewed by Beth Dakin.
+        Patch by Evan Wallace <evan.exe@gmail.com>.
+
+        Serialize the cursor image scale for SetCursor messages so custom
+        CSS cursors work with -webkit-image-set on retina displays.
+
+        * Shared/WebCoreArgumentCoders.cpp:
+        (CoreIPC::ArgumentCoder<Cursor>::encode):
+        (CoreIPC::ArgumentCoder<Cursor>::decode):
+
 2015-04-15  Alex Christensen  <achristensen@webkit.org>
 
         Progress towards CMake on Mac.
index 966cd8f..f919718 100644 (file)
@@ -766,6 +766,9 @@ void ArgumentCoder<Cursor>::encode(ArgumentEncoder& encoder, const Cursor& curso
     encoder << true;
     encodeImage(encoder, cursor.image());
     encoder << cursor.hotSpot();
+#if ENABLE(MOUSE_CURSOR_SCALE)
+    encoder << cursor.imageScaleFactor();
+#endif
 }
 
 bool ArgumentCoder<Cursor>::decode(ArgumentDecoder& decoder, Cursor& cursor)
@@ -807,7 +810,15 @@ bool ArgumentCoder<Cursor>::decode(ArgumentDecoder& decoder, Cursor& cursor)
     if (!image->rect().contains(hotSpot))
         return false;
 
+#if ENABLE(MOUSE_CURSOR_SCALE)
+    float scale;
+    if (!decoder.decode(scale))
+        return false;
+
+    cursor = Cursor(image.get(), hotSpot, scale);
+#else
     cursor = Cursor(image.get(), hotSpot);
+#endif
     return true;
 }
 #endif