diff options
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoabackingstore.h | 5 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoabackingstore.mm | 62 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoahelpers.h | 1 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoahelpers.mm | 56 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qnsview.h | 6 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qnsview.mm | 96 |
6 files changed, 125 insertions, 101 deletions
diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.h b/src/plugins/platforms/cocoa/qcocoabackingstore.h index 9f9159ac64..0e1998170a 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.h +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.h @@ -61,9 +61,12 @@ public: void flush(QWindow *widget, const QRegion ®ion, const QPoint &offset); void resize (const QSize &size, const QRegion &); bool scroll(const QRegion &area, int dx, int dy); + CGImageRef getBackingStoreCGImage(); private: - QImage *m_image; + QImage m_qImage; + CGImageRef m_cgImage; + QSize m_requestedSize; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm index 8a20ed83f7..7bd7e4ce38 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.mm +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm @@ -40,54 +40,51 @@ ****************************************************************************/ #include "qcocoabackingstore.h" -#include "qcocoaautoreleasepool.h" - -#include <QtCore/qdebug.h> #include <QtGui/QPainter> +#include "qcocoahelpers.h" QT_BEGIN_NAMESPACE QCocoaBackingStore::QCocoaBackingStore(QWindow *window) : QPlatformBackingStore(window) + , m_cgImage(0) { - m_image = new QImage(window->geometry().size(),QImage::Format_ARGB32_Premultiplied); } QCocoaBackingStore::~QCocoaBackingStore() { - delete m_image; + CGImageRelease(m_cgImage); + m_cgImage = 0; } QPaintDevice *QCocoaBackingStore::paintDevice() { - return m_image; + if (m_qImage.size() != m_requestedSize) { + CGImageRelease(m_cgImage); + m_cgImage = 0; + m_qImage = QImage(m_requestedSize, QImage::Format_ARGB32_Premultiplied); + } + return &m_qImage; } -void QCocoaBackingStore::flush(QWindow *widget, const QRegion ®ion, const QPoint &offset) +void QCocoaBackingStore::flush(QWindow *win, const QRegion ®ion, const QPoint &offset) { - Q_UNUSED(widget); - Q_UNUSED(offset); - QCocoaAutoReleasePool pool; - - QRect geo = region.boundingRect(); - NSRect rect = NSMakeRect(geo.x(), geo.y(), geo.width(), geo.height()); - QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(window()->handle()); - if (cocoaWindow) { - // setImage call is needed here to make the displayRect call - // have effect - even if the image has not changed. - [cocoaWindow->m_contentView setImage:m_image]; - [cocoaWindow->m_contentView displayRect:rect]; - } + // A flush means that qImage has changed. Since CGImages are seen as + // immutable, CoreImage fails to pick up this change for m_cgImage + // (since it usually cached), so we must recreate it. We await doing this + // until one of the views needs it, since, together with calling + // "setNeedsDisplayInRect" instead of "displayRect" we will, in most + // cases, get away with doing this once for every repaint. Also note that + // m_cgImage is only a reference to the data inside m_qImage, it is not a copy. + CGImageRelease(m_cgImage); + m_cgImage = 0; + if (QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(win->handle())) + [cocoaWindow->m_contentView flushBackingStore:this region:region offset:offset]; } void QCocoaBackingStore::resize(const QSize &size, const QRegion &) { - delete m_image; - m_image = new QImage(size, QImage::Format_ARGB32_Premultiplied); - - QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(window()->handle()); - if (cocoaWindow) - [static_cast<QNSView *>(cocoaWindow->m_contentView) setImage:m_image]; + m_requestedSize = size; } bool QCocoaBackingStore::scroll(const QRegion &area, int dx, int dy) @@ -97,9 +94,20 @@ bool QCocoaBackingStore::scroll(const QRegion &area, int dx, int dy) const QVector<QRect> qrects = area.rects(); for (int i = 0; i < qrects.count(); ++i) { const QRect &qrect = qrects.at(i); - qt_scrollRectInImage(*m_image, qrect, qpoint); + qt_scrollRectInImage(m_qImage, qrect, qpoint); } return true; } +CGImageRef QCocoaBackingStore::getBackingStoreCGImage() +{ + if (!m_cgImage) + m_cgImage = qt_mac_toCGImage(m_qImage, false, 0); + + // Warning: do not retain/release/cache the returned image from + // outside the backingstore since it shares data with a QImage and + // needs special memory considerations. + return m_cgImage; +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.h b/src/plugins/platforms/cocoa/qcocoahelpers.h index de98d5219e..b0222738e1 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.h +++ b/src/plugins/platforms/cocoa/qcocoahelpers.h @@ -159,6 +159,7 @@ public: }; CGContextRef qt_mac_cg_context(const QPaintDevice *pdev); +CGImageRef qt_mac_toCGImage(const QImage &qImage, bool isMask, uchar **dataCopy); QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm index 0a8da0a956..008d9da2cf 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.mm +++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm @@ -751,4 +751,60 @@ CGContextRef qt_mac_cg_context(const QPaintDevice *pdev) return 0; } +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) { + delete[] *dataCopy; + *dataCopy = new uchar[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(), + NULL); + + CGImageRef cgImage = 0; + if (isMask) { + cgImage = CGImageMaskCreate(width, + height, + colorBufferSize, + bitDepth, + bytesPrLine, + cgDataProviderRef, + NULL, + false); + } else { + CGColorSpaceRef cgColourSpaceRef = CGColorSpaceCreateDeviceRGB(); + cgImage = CGImageCreate(width, + height, + colorBufferSize, + bitDepth, + bytesPrLine, + cgColourSpaceRef, + kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst, + cgDataProviderRef, + NULL, + false, + kCGRenderingIntentDefault); + CGColorSpaceRelease(cgColourSpaceRef); + } + CGDataProviderRelease(cgDataProviderRef); + return cgImage; +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qnsview.h b/src/plugins/platforms/cocoa/qnsview.h index 246d311f7b..1ee3697858 100644 --- a/src/plugins/platforms/cocoa/qnsview.h +++ b/src/plugins/platforms/cocoa/qnsview.h @@ -50,10 +50,12 @@ QT_BEGIN_NAMESPACE class QCocoaWindow; +class QCocoaBackingStore; QT_END_NAMESPACE @interface QNSView : NSView <NSTextInputClient> { - CGImageRef m_cgImage; + QCocoaBackingStore* m_backingStore; + QPoint m_backingStoreOffset; CGImageRef m_maskImage; uchar *m_maskData; QWindow *m_window; @@ -69,7 +71,7 @@ QT_END_NAMESPACE - (id)init; - (id)initWithQWindow:(QWindow *)window platformWindow:(QCocoaWindow *) platformWindow; -- (void)setImage:(QImage *)image; +- (void)flushBackingStore:(QCocoaBackingStore *)backingStore region:(const QRegion &)region offset:(QPoint)offset; - (void)setMaskRegion:(const QRegion *)region; - (void)drawRect:(NSRect)dirtyRect; - (void)updateGeometry; diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm index 4fb099341e..8e18ca3708 100644 --- a/src/plugins/platforms/cocoa/qnsview.mm +++ b/src/plugins/platforms/cocoa/qnsview.mm @@ -55,6 +55,7 @@ #include <QtGui/QTextFormat> #include <QtCore/QDebug> #include <private/qguiapplication_p.h> +#include "qcocoabackingstore.h" #ifdef QT_COCOA_ENABLE_ACCESSIBILITY_INSPECTOR #include <accessibilityinspector.h> @@ -74,7 +75,7 @@ static QTouchDevice *touchDevice = 0; { self = [super initWithFrame : NSMakeRect(0,0, 300,300)]; if (self) { - m_cgImage = 0; + m_backingStore = 0; m_maskImage = 0; m_maskData = 0; m_window = 0; @@ -93,8 +94,6 @@ static QTouchDevice *touchDevice = 0; - (void)dealloc { - CGImageRelease(m_cgImage); - m_cgImage = 0; CGImageRelease(m_maskImage); m_maskImage = 0; delete[] m_maskData; @@ -211,66 +210,12 @@ static QTouchDevice *touchDevice = 0; } } -static CGImageRef qt_mac_toCGImage(QImage *qImage, bool isMask, uchar **dataCopy) +- (void) flushBackingStore:(QCocoaBackingStore *)backingStore region:(const QRegion &)region offset:(QPoint)offset { - 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) { - delete[] *dataCopy; - *dataCopy = new uchar[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(), - NULL); - - CGImageRef cgImage = 0; - if (isMask) { - cgImage = CGImageMaskCreate(width, - height, - colorBufferSize, - bitDepth, - bytesPrLine, - cgDataProviderRef, - NULL, - false); - } else { - CGColorSpaceRef cgColourSpaceRef = CGColorSpaceCreateDeviceRGB(); - cgImage = CGImageCreate(width, - height, - colorBufferSize, - bitDepth, - bytesPrLine, - cgColourSpaceRef, - kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst, - cgDataProviderRef, - NULL, - false, - kCGRenderingIntentDefault); - CGColorSpaceRelease(cgColourSpaceRef); - } - CGDataProviderRelease(cgDataProviderRef); - return cgImage; -} - -- (void) setImage:(QImage *)image -{ - CGImageRelease(m_cgImage); - m_cgImage = qt_mac_toCGImage(image, false, 0); + m_backingStore = backingStore; + m_backingStoreOffset = offset; + QRect br = region.boundingRect(); + [self setNeedsDisplayInRect:NSMakeRect(br.x(), br.y(), br.width(), br.height())]; } - (void) setMaskRegion:(const QRegion *)region @@ -291,36 +236,45 @@ static CGImageRef qt_mac_toCGImage(QImage *qImage, bool isMask, uchar **dataCopy p.end(); maskImage = maskImage.convertToFormat(QImage::Format_Indexed8); - m_maskImage = qt_mac_toCGImage(&maskImage, true, &m_maskData); + m_maskImage = qt_mac_toCGImage(maskImage, true, &m_maskData); } - (void) drawRect:(NSRect)dirtyRect { - if (!m_cgImage) + if (!m_backingStore) return; CGRect dirtyCGRect = NSRectToCGRect(dirtyRect); - NSGraphicsContext *nsGraphicsContext = [NSGraphicsContext currentContext]; CGContextRef cgContext = (CGContextRef) [nsGraphicsContext graphicsPort]; - CGContextSaveGState( cgContext ); + // Translate coordiate system from CoreGraphics (bottom-left) to NSView (top-left): + CGContextSaveGState(cgContext); int dy = dirtyCGRect.origin.y + CGRectGetMaxY(dirtyCGRect); CGContextTranslateCTM(cgContext, 0, dy); CGContextScaleCTM(cgContext, 1, -1); + // If a mask is set, modify the sub image accordingly: CGImageRef subMask = 0; if (m_maskImage) { subMask = CGImageCreateWithImageInRect(m_maskImage, dirtyCGRect); CGContextClipToMask(cgContext, dirtyCGRect, subMask); } - CGImageRef subImage = CGImageCreateWithImageInRect(m_cgImage, dirtyCGRect); - CGContextDrawImage(cgContext,dirtyCGRect,subImage); - + // Clip out and draw the correct sub image from the (shared) backingstore: + CGRect backingStoreRect = CGRectMake( + dirtyRect.origin.x + m_backingStoreOffset.x(), + dirtyRect.origin.y + m_backingStoreOffset.y(), + dirtyRect.size.width, + dirtyRect.size.height + ); + CGImageRef bsCGImage = m_backingStore->getBackingStoreCGImage(); + CGImageRef cleanImg = CGImageCreateWithImageInRect(bsCGImage, backingStoreRect); + CGContextDrawImage(cgContext, dirtyCGRect, cleanImg); + + // Clean-up: CGContextRestoreGState(cgContext); - - CGImageRelease(subImage); + CGImageRelease(cleanImg); CGImageRelease(subMask); } |