Cocoa: QImage -> CGImage conversion cleanup

Move to one qt_mac_toCGImage function that has simple
semantics and properly retains a copy of the QImage
for the lifetime of the CGImage.

Remove the old qt_mac_toCGImage function which had
two problems:

1) It would not retain the QImage data (this was probably
ok for its original use case: creating short-lived
CGImages for the paint engine)

2) It had acquired a somewhat odd **datacopy out
parameter for the cases where you _do_ want to retain
the image data.

This makes the exported image conversion function
from QtMacExtras work: The CGImages it creates will
no longer reference free'd memory once the QImage
is deleted.

Change-Id: I583040d16aefb17fc3d801d6b047a0b2a76c7f74
Reviewed-by: Gabriel de Dietrich <gabriel.dedietrich@digia.com>
This commit is contained in:
Morten Johan Sørvig 2013-12-10 10:02:51 +01:00 committed by The Qt Project
parent 51572d3d8f
commit f2ade01f4c
7 changed files with 42 additions and 113 deletions

View File

@ -122,7 +122,7 @@ bool QCocoaBackingStore::scroll(const QRegion &area, int dx, int dy)
CGImageRef QCocoaBackingStore::getBackingStoreCGImage()
{
if (!m_cgImage)
m_cgImage = qt_mac_toCGImage(m_qImage, false, 0);
m_cgImage = qt_mac_toCGImage(m_qImage);
// Warning: do not retain/release/cache the returned image from
// outside the backingstore since it shares data with a QImage and

View File

@ -68,10 +68,12 @@ void *qt_mac_QStringListToNSMutableArrayVoid(const QStringList &list);
inline NSMutableArray *qt_mac_QStringListToNSMutableArray(const QStringList &qstrlist)
{ return reinterpret_cast<NSMutableArray *>(qt_mac_QStringListToNSMutableArrayVoid(qstrlist)); }
CGImageRef qt_mac_image_to_cgimage(const QImage &image);
NSImage *qt_mac_cgimage_to_nsimage(CGImageRef iamge);
NSImage *qt_mac_create_nsimage(const QPixmap &pm);
NSImage *qt_mac_create_nsimage(const QIcon &icon);
CGImageRef qt_mac_toCGImage(const QImage &qImage);
CGImageRef qt_mac_toCGImageMask(const QImage &qImage);
QImage qt_mac_toQImage(CGImageRef image);
NSSize qt_mac_toNSSize(const QSize &qtSize);
NSRect qt_mac_toNSRect(const QRect &rect);
@ -159,8 +161,6 @@ public:
};
CGContextRef qt_mac_cg_context(QPaintDevice *pdev);
CGImageRef qt_mac_toCGImage(const QImage &qImage, bool isMask, uchar **dataCopy);
QImage qt_mac_toQImage(CGImageRef image);
template<typename T>
T qt_mac_resolveOption(const T &fallback, const QByteArray &environment)

View File

@ -79,24 +79,28 @@ void *qt_mac_QStringListToNSMutableArrayVoid(const QStringList &list)
return result;
}
static void drawImageReleaseData (void *info, const void *, size_t)
static void qt_mac_deleteImage(void *image, const void *, size_t)
{
delete static_cast<QImage *>(info);
delete static_cast<QImage *>(image);
}
CGImageRef qt_mac_image_to_cgimage(const QImage &img)
// Creates a CGDataProvider with the data from the given image.
// The data provider retains a copy of the image.
CGDataProviderRef qt_mac_CGDataProvider(const QImage &image)
{
if (img.isNull())
return CGDataProviderCreateWithData(new QImage(image), image.bits(),
image.byteCount(), qt_mac_deleteImage);
}
CGImageRef qt_mac_toCGImage(const QImage &inImage)
{
if (inImage.isNull())
return 0;
QImage *image;
if (img.depth() != 32)
image = new QImage(img.convertToFormat(QImage::Format_ARGB32_Premultiplied));
else
image = new QImage(img);
QImage image = (inImage.depth() == 32) ? inImage : inImage.convertToFormat(QImage::Format_ARGB32_Premultiplied);
uint cgflags = kCGImageAlphaNone;
switch (image->format()) {
switch (image.format()) {
case QImage::Format_ARGB32_Premultiplied:
cgflags = kCGImageAlphaPremultipliedFirst;
break;
@ -105,20 +109,26 @@ CGImageRef qt_mac_image_to_cgimage(const QImage &img)
break;
case QImage::Format_RGB32:
cgflags = kCGImageAlphaNoneSkipFirst;
break;
case QImage::Format_RGB888:
cgflags |= kCGImageAlphaNone;
break;
default:
break;
}
cgflags |= kCGBitmapByteOrder32Host;
QCFType<CGDataProviderRef> dataProvider = CGDataProviderCreateWithData(image,
static_cast<const QImage *>(image)->bits(),
image->byteCount(),
drawImageReleaseData);
return CGImageCreate(image->width(), image->height(), 8, 32,
image->bytesPerLine(),
qt_mac_genericColorSpace(),
cgflags, dataProvider, 0, false, kCGRenderingIntentDefault);
QCFType<CGDataProviderRef> dataProvider = qt_mac_CGDataProvider(image);
return CGImageCreate(image.width(), image.height(), 8, 32,
image.bytesPerLine(),
qt_mac_genericColorSpace(),
cgflags, dataProvider, 0, false, kCGRenderingIntentDefault);
}
CGImageRef qt_mac_toCGImageMask(const QImage &image)
{
QCFType<CGDataProviderRef> dataProvider = qt_mac_CGDataProvider(image);
return CGImageMaskCreate(image.width(), image.height(), 8, image.depth(),
image.bytesPerLine(), dataProvider, NULL, false);
}
NSImage *qt_mac_cgimage_to_nsimage(CGImageRef image)
@ -132,7 +142,7 @@ NSImage *qt_mac_create_nsimage(const QPixmap &pm)
if (pm.isNull())
return 0;
QImage image = pm.toImage();
CGImageRef cgImage = qt_mac_image_to_cgimage(image);
CGImageRef cgImage = qt_mac_toCGImage(image);
NSImage *nsImage = qt_mac_cgimage_to_nsimage(cgImage);
CGImageRelease(cgImage);
return nsImage;
@ -147,7 +157,7 @@ NSImage *qt_mac_create_nsimage(const QIcon &icon)
foreach (QSize size, icon.availableSizes()) {
QPixmap pm = icon.pixmap(size);
QImage image = pm.toImage();
CGImageRef cgImage = qt_mac_image_to_cgimage(image);
CGImageRef cgImage = qt_mac_toCGImage(image);
NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithCGImage:cgImage];
[nsImage addRepresentation:imageRep];
[imageRep release];
@ -780,85 +790,6 @@ CGContextRef qt_mac_cg_context(QPaintDevice *pdev)
return ret;
}
// qpaintengine_mac.mm
extern void qt_mac_cgimage_data_free(void *, const void *memoryToFree, size_t);
CGImageRef qt_mac_toCGImage(const QImage &qImage, bool isMask, uchar **dataCopy)
{
int width = qImage.width();
int height = qImage.height();
if (width <= 0 || height <= 0) {
qWarning() << Q_FUNC_INFO <<
"setting invalid size" << width << "x" << height << "for qnsview image";
return 0;
}
const uchar *imageData = qImage.bits();
if (dataCopy) {
*dataCopy = static_cast<uchar *>(malloc(qImage.byteCount()));
memcpy(*dataCopy, imageData, qImage.byteCount());
}
int bitDepth = qImage.depth();
int colorBufferSize = 8;
int bytesPrLine = qImage.bytesPerLine();
CGDataProviderRef cgDataProviderRef = CGDataProviderCreateWithData(
NULL,
dataCopy ? *dataCopy : imageData,
qImage.byteCount(),
dataCopy ? qt_mac_cgimage_data_free : NULL);
CGImageRef cgImage = 0;
if (isMask) {
cgImage = CGImageMaskCreate(width,
height,
colorBufferSize,
bitDepth,
bytesPrLine,
cgDataProviderRef,
NULL,
false);
} else {
CGColorSpaceRef cgColourSpaceRef = qt_mac_displayColorSpace(0);
// Create a CGBitmapInfo contiaining the image format.
// Support the 8-bit per component (A)RGB formats.
CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Little;
switch (qImage.format()) {
case QImage::Format_ARGB32_Premultiplied :
bitmapInfo |= kCGImageAlphaPremultipliedFirst;
break;
case QImage::Format_ARGB32 :
bitmapInfo |= kCGImageAlphaFirst;
break;
case QImage::Format_RGB32 :
bitmapInfo |= kCGImageAlphaNoneSkipFirst;
break;
case QImage::Format_RGB888 :
bitmapInfo |= kCGImageAlphaNone;
break;
default:
qWarning() << "qt_mac_toCGImage: Unsupported image format" << qImage.format();
break;
}
cgImage = CGImageCreate(width,
height,
colorBufferSize,
bitDepth,
bytesPrLine,
cgColourSpaceRef,
bitmapInfo,
cgDataProviderRef,
NULL,
false,
kCGRenderingIntentDefault);
}
CGDataProviderRelease(cgDataProviderRef);
return cgImage;
}
QImage qt_mac_toQImage(CGImageRef image)
{
const size_t w = CGImageGetWidth(image),

View File

@ -248,7 +248,7 @@ void *QCocoaNativeInterface::qMenuBarToNSMenu(QPlatformMenuBar *platformMenuBar)
CGImageRef QCocoaNativeInterface::qImageToCGImage(const QImage &image)
{
return qt_mac_toCGImage(image, false, 0);
return qt_mac_toCGImage(image);
}
QImage QCocoaNativeInterface::cgImageToQImage(CGImageRef image)

View File

@ -565,7 +565,7 @@ QList<QByteArray> QMacPasteboardMimeTiff::convertFromMime(const QString &mime, Q
return ret;
QImage img = qvariant_cast<QImage>(variant);
QCFType<CGImageRef> cgimage = qt_mac_image_to_cgimage(img);
QCFType<CGImageRef> cgimage = qt_mac_toCGImage(img);
QCFType<CFMutableDataRef> data = CFDataCreateMutable(0, 0);
QCFType<CGImageDestinationRef> imageDestination = CGImageDestinationCreateWithData(data, kUTTypeTIFF, 1, 0);

View File

@ -80,7 +80,6 @@ static QTouchDevice *touchDevice = 0;
if (self) {
m_backingStore = 0;
m_maskImage = 0;
m_maskData = 0;
m_shouldInvalidateWindowShadow = false;
m_window = 0;
m_buttons = Qt::NoButton;
@ -106,7 +105,6 @@ static QTouchDevice *touchDevice = 0;
{
CGImageRelease(m_maskImage);
m_maskImage = 0;
m_maskData = 0;
m_window = 0;
m_subscribesForGlobalFrameNotifications = false;
[m_inputSource release];
@ -372,7 +370,7 @@ static QTouchDevice *touchDevice = 0;
- (BOOL) hasMask
{
return m_maskData != 0;
return m_maskImage != 0;
}
- (BOOL) isOpaque
@ -405,7 +403,7 @@ static QTouchDevice *touchDevice = 0;
dst[x] = src[x] & 0xff;
}
}
m_maskImage = qt_mac_toCGImage(maskImage, true, &m_maskData);
m_maskImage = qt_mac_toCGImageMask(maskImage);
}
- (void)invalidateWindowShadowIfNeeded

View File

@ -488,7 +488,7 @@ static void qt_mac_draw_pattern(void *info, CGContextRef c)
if (isBitmap)
pat->image = qt_mac_create_imagemask(pat->data.pixmap, pat->data.pixmap.rect());
else
pat->image = qt_mac_image_to_cgimage(pat->data.pixmap.toImage());
pat->image = qt_mac_toCGImage(pat->data.pixmap.toImage());
}
} else {
w = CGImageGetWidth(pat->image);
@ -963,11 +963,11 @@ void QCoreGraphicsPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, co
CGContextSetFillColorWithColor(d->hd, cgColorForQColor(col, d->pdev));
image = qt_mac_create_imagemask(pm, sr);
} else if (differentSize) {
QCFType<CGImageRef> img = qt_mac_image_to_cgimage(pm.toImage());
QCFType<CGImageRef> img = qt_mac_toCGImage(pm.toImage());
if (img)
image = CGImageCreateWithImageInRect(img, CGRectMake(qRound(sr.x()), qRound(sr.y()), qRound(sr.width()), qRound(sr.height())));
} else {
image = qt_mac_image_to_cgimage(pm.toImage());
image = qt_mac_toCGImage(pm.toImage());
}
qt_mac_drawCGImage(d->hd, &rect, image);
if (doRestore)