mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-27 12:50:09 +00:00
Bug 888689 - Render SVG cursors correctly on retina displays. r=mstange
This commit is contained in:
parent
0e710fb645
commit
fc7d7c444e
@ -910,7 +910,7 @@ NS_IMETHODIMP nsChildView::SetCursor(imgIContainer* aCursor,
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
||||
|
||||
nsBaseWidget::SetCursor(aCursor, aHotspotX, aHotspotY);
|
||||
return [[nsCursorManager sharedInstance] setCursorWithImage:aCursor hotSpotX:aHotspotX hotSpotY:aHotspotY];
|
||||
return [[nsCursorManager sharedInstance] setCursorWithImage:aCursor hotSpotX:aHotspotX hotSpotY:aHotspotY scaleFactor:BackingScaleFactor()];
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
||||
}
|
||||
|
@ -242,9 +242,10 @@ class nsCocoaUtils
|
||||
@param aImage the image to extract a frame from
|
||||
@param aWhichFrame the frame to extract (see imgIContainer FRAME_*)
|
||||
@param aResult the resulting NSImage
|
||||
@param scaleFactor the desired scale factor of the NSImage (2 for a retina display)
|
||||
@return NS_OK if the conversion worked, NS_ERROR_FAILURE otherwise
|
||||
*/
|
||||
static nsresult CreateNSImageFromImageContainer(imgIContainer *aImage, uint32_t aWhichFrame, NSImage **aResult);
|
||||
static nsresult CreateNSImageFromImageContainer(imgIContainer *aImage, uint32_t aWhichFrame, NSImage **aResult, CGFloat scaleFactor);
|
||||
|
||||
/**
|
||||
* Returns nsAString for aSrc.
|
||||
|
@ -282,7 +282,7 @@ nsresult nsCocoaUtils::CreateCGImageFromSurface(gfxImageSurface *aFrame, CGImage
|
||||
32,
|
||||
stride,
|
||||
colorSpace,
|
||||
kCGBitmapByteOrder32Host | kCGImageAlphaFirst,
|
||||
kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst,
|
||||
dataProvider,
|
||||
NULL,
|
||||
0,
|
||||
@ -296,35 +296,91 @@ nsresult nsCocoaUtils::CreateNSImageFromCGImage(CGImageRef aInputImage, NSImage
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
||||
|
||||
// Be very careful when creating the NSImage that the backing NSImageRep is
|
||||
// exactly 1:1 with the input image. On a retina display, both [NSImage
|
||||
// lockFocus] and [NSImage initWithCGImage:size:] will create an image with a
|
||||
// 2x backing NSImageRep. This prevents NSCursor from recognizing a retina
|
||||
// cursor, which only occurs if pixelsWide and pixelsHigh are exactly 2x the
|
||||
// size of the NSImage.
|
||||
//
|
||||
// For example, if a 32x32 SVG cursor is rendered on a retina display, then
|
||||
// aInputImage will be 64x64. The resulting NSImage will be scaled back down
|
||||
// to 32x32 so it stays the correct size on the screen by changing its size
|
||||
// (resizing a NSImage only scales the image and doesn't resample the data).
|
||||
// If aInputImage is converted using [NSImage initWithCGImage:size:] then the
|
||||
// bitmap will be 128x128 and NSCursor won't recognize a retina cursor, since
|
||||
// it will expect a 64x64 bitmap.
|
||||
|
||||
int32_t width = ::CGImageGetWidth(aInputImage);
|
||||
int32_t height = ::CGImageGetHeight(aInputImage);
|
||||
NSRect imageRect = ::NSMakeRect(0.0, 0.0, width, height);
|
||||
|
||||
// Create a new image to receive the Quartz image data.
|
||||
*aResult = [[NSImage alloc] initWithSize:imageRect.size];
|
||||
NSBitmapImageRep *offscreenRep = [[NSBitmapImageRep alloc]
|
||||
initWithBitmapDataPlanes:NULL
|
||||
pixelsWide:width
|
||||
pixelsHigh:height
|
||||
bitsPerSample:8
|
||||
samplesPerPixel:4
|
||||
hasAlpha:YES
|
||||
isPlanar:NO
|
||||
colorSpaceName:NSDeviceRGBColorSpace
|
||||
bitmapFormat:NSAlphaFirstBitmapFormat
|
||||
bytesPerRow:0
|
||||
bitsPerPixel:0];
|
||||
|
||||
[*aResult lockFocus];
|
||||
NSGraphicsContext *context = [NSGraphicsContext graphicsContextWithBitmapImageRep:offscreenRep];
|
||||
[NSGraphicsContext saveGraphicsState];
|
||||
[NSGraphicsContext setCurrentContext:context];
|
||||
|
||||
// Get the Quartz context and draw.
|
||||
CGContextRef imageContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
|
||||
::CGContextDrawImage(imageContext, *(CGRect*)&imageRect, aInputImage);
|
||||
|
||||
[*aResult unlockFocus];
|
||||
[NSGraphicsContext restoreGraphicsState];
|
||||
|
||||
*aResult = [[NSImage alloc] initWithSize:NSMakeSize(width, height)];
|
||||
[*aResult addRepresentation:offscreenRep];
|
||||
[offscreenRep release];
|
||||
return NS_OK;
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
||||
}
|
||||
|
||||
nsresult nsCocoaUtils::CreateNSImageFromImageContainer(imgIContainer *aImage, uint32_t aWhichFrame, NSImage **aResult)
|
||||
nsresult nsCocoaUtils::CreateNSImageFromImageContainer(imgIContainer *aImage, uint32_t aWhichFrame, NSImage **aResult, CGFloat scaleFactor)
|
||||
{
|
||||
nsRefPtr<gfxASurface> surface;
|
||||
aImage->GetFrame(aWhichFrame,
|
||||
imgIContainer::FLAG_SYNC_DECODE,
|
||||
getter_AddRefs(surface));
|
||||
NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE);
|
||||
nsRefPtr<gfxImageSurface> frame;
|
||||
int32_t width = 0, height = 0;
|
||||
aImage->GetWidth(&width);
|
||||
aImage->GetHeight(&height);
|
||||
|
||||
nsRefPtr<gfxImageSurface> frame(surface->GetAsReadableARGB32ImageSurface());
|
||||
NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
|
||||
// Render a vector image at the correct resolution on a retina display
|
||||
if (aImage->GetType() == imgIContainer::TYPE_VECTOR && scaleFactor != 1.0f) {
|
||||
int scaledWidth = (int)ceilf(width * scaleFactor);
|
||||
int scaledHeight = (int)ceilf(height * scaleFactor);
|
||||
|
||||
frame = new gfxImageSurface(gfxIntSize(scaledWidth, scaledHeight), gfxImageFormatARGB32);
|
||||
NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
|
||||
|
||||
nsRefPtr<gfxContext> context = new gfxContext(frame);
|
||||
NS_ENSURE_TRUE(context, NS_ERROR_FAILURE);
|
||||
|
||||
aImage->Draw(context, GraphicsFilter::FILTER_NEAREST, gfxMatrix(),
|
||||
gfxRect(0.0f, 0.0f, scaledWidth, scaledHeight),
|
||||
nsIntRect(0, 0, width, height),
|
||||
nsIntSize(scaledWidth, scaledHeight),
|
||||
nullptr, aWhichFrame, imgIContainer::FLAG_SYNC_DECODE);
|
||||
}
|
||||
|
||||
else {
|
||||
nsRefPtr<gfxASurface> surface;
|
||||
aImage->GetFrame(aWhichFrame,
|
||||
imgIContainer::FLAG_SYNC_DECODE,
|
||||
getter_AddRefs(surface));
|
||||
NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE);
|
||||
|
||||
frame = surface->GetAsReadableARGB32ImageSurface();
|
||||
NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
CGImageRef imageRef = NULL;
|
||||
nsresult rv = nsCocoaUtils::CreateCGImageFromSurface(frame, &imageRef);
|
||||
@ -337,6 +393,11 @@ nsresult nsCocoaUtils::CreateNSImageFromImageContainer(imgIContainer *aImage, ui
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
::CGImageRelease(imageRef);
|
||||
|
||||
// Ensure the image will be rendered the correct size on a retina display
|
||||
NSSize size = NSMakeSize(width, height);
|
||||
[*aResult setSize:size];
|
||||
[[[*aResult representations] objectAtIndex:0] setSize:size];
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -37,8 +37,9 @@
|
||||
@param aCursorImage the cursor image to use
|
||||
@param aHotSpotX the x coordinate of the cursor's hotspot
|
||||
@param aHotSpotY the y coordinate of the cursor's hotspot
|
||||
@param scaleFactor the scale factor of the target display (2 for a retina display)
|
||||
*/
|
||||
- (nsresult) setCursorWithImage: (imgIContainer*) aCursorImage hotSpotX: (uint32_t) aHotspotX hotSpotY: (uint32_t) aHotspotY;
|
||||
- (nsresult) setCursorWithImage: (imgIContainer*) aCursorImage hotSpotX: (uint32_t) aHotspotX hotSpotY: (uint32_t) aHotspotY scaleFactor: (CGFloat) scaleFactor;
|
||||
|
||||
|
||||
/*! @method sharedInstance
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <math.h>
|
||||
|
||||
static nsCursorManager *gInstance;
|
||||
static CGFloat sCursorScaleFactor = 0.0f;
|
||||
static imgIContainer *sCursorImgContainer = nullptr;
|
||||
static const nsCursor sCustomCursor = eCursorCount;
|
||||
|
||||
@ -240,11 +241,11 @@ static const nsCursor sCustomCursor = eCursorCount;
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
||||
}
|
||||
|
||||
- (nsresult) setCursorWithImage: (imgIContainer*) aCursorImage hotSpotX: (uint32_t) aHotspotX hotSpotY: (uint32_t) aHotspotY
|
||||
- (nsresult) setCursorWithImage: (imgIContainer*) aCursorImage hotSpotX: (uint32_t) aHotspotX hotSpotY: (uint32_t) aHotspotY scaleFactor: (CGFloat) scaleFactor
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
||||
// As the user moves the mouse, this gets called repeatedly with the same aCursorImage
|
||||
if (sCursorImgContainer == aCursorImage && mCurrentMacCursor) {
|
||||
if (sCursorImgContainer == aCursorImage && sCursorScaleFactor == scaleFactor && mCurrentMacCursor) {
|
||||
[self setMacCursor:mCurrentMacCursor];
|
||||
return NS_OK;
|
||||
}
|
||||
@ -259,7 +260,7 @@ static const nsCursor sCustomCursor = eCursorCount;
|
||||
}
|
||||
|
||||
NSImage *cursorImage;
|
||||
nsresult rv = nsCocoaUtils::CreateNSImageFromImageContainer(aCursorImage, imgIContainer::FRAME_FIRST, &cursorImage);
|
||||
nsresult rv = nsCocoaUtils::CreateNSImageFromImageContainer(aCursorImage, imgIContainer::FRAME_FIRST, &cursorImage, scaleFactor);
|
||||
if (NS_FAILED(rv) || !cursorImage) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
@ -274,6 +275,7 @@ static const nsCursor sCustomCursor = eCursorCount;
|
||||
|
||||
NS_IF_RELEASE(sCursorImgContainer);
|
||||
sCursorImgContainer = aCursorImage;
|
||||
sCursorScaleFactor = scaleFactor;
|
||||
NS_ADDREF(sCursorImgContainer);
|
||||
|
||||
return NS_OK;
|
||||
|
Loading…
x
Reference in New Issue
Block a user