source: trunk/src/gui/image/qpixmap_mac.cpp@ 5

Last change on this file since 5 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 44.6 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41//#define QT_RASTER_PAINTENGINE
42
43#include "qpixmap.h"
44#include "qimage.h"
45#include "qapplication.h"
46#include "qbitmap.h"
47#include "qmatrix.h"
48#include "qtransform.h"
49#include "qlibrary.h"
50#include "qvarlengtharray.h"
51#include "qdebug.h"
52#include <private/qdrawhelper_p.h>
53#include <private/qpixmap_mac_p.h>
54#include <private/qpixmap_raster_p.h>
55#ifdef QT_RASTER_PAINTENGINE
56# include <private/qpaintengine_raster_p.h>
57#endif
58#include <private/qpaintengine_mac_p.h>
59#include <private/qt_mac_p.h>
60#include <private/qt_cocoa_helpers_mac_p.h>
61
62#include <limits.h>
63#include <string.h>
64
65QT_BEGIN_NAMESPACE
66
67/*****************************************************************************
68 Externals
69 *****************************************************************************/
70extern const uchar *qt_get_bitflip_array(); //qimage.cpp
71extern CGContextRef qt_mac_cg_context(const QPaintDevice *pdev); //qpaintdevice_mac.cpp
72extern RgnHandle qt_mac_get_rgn(); //qregion_mac.cpp
73extern void qt_mac_dispose_rgn(RgnHandle r); //qregion_mac.cpp
74extern QRegion qt_mac_convert_mac_region(RgnHandle rgn); //qregion_mac.cpp
75
76static int qt_pixmap_serial = 0;
77
78Q_GUI_EXPORT quint32 *qt_mac_pixmap_get_base(const QPixmap *pix)
79{
80 return static_cast<QMacPixmapData*>(pix->data)->pixels;
81}
82
83Q_GUI_EXPORT int qt_mac_pixmap_get_bytes_per_line(const QPixmap *pix)
84{
85 return static_cast<QMacPixmapData*>(pix->data)->bytesPerRow;
86}
87
88void qt_mac_cgimage_data_free(void *info, const void *memoryToFree, size_t)
89{
90 QMacPixmapData *pmdata = static_cast<QMacPixmapData *>(info);
91 if (!pmdata) {
92 free(const_cast<void *>(memoryToFree));
93 } else {
94 if (QMacPixmapData::validDataPointers.contains(pmdata) == false) {
95 free(const_cast<void *>(memoryToFree));
96 return;
97 }
98 if (pmdata->pixels == pmdata->pixelsToFree) {
99 // something we aren't expecting, just free it.
100 Q_ASSERT(memoryToFree != pmdata->pixelsToFree);
101 free(const_cast<void *>(memoryToFree));
102 } else {
103 free(pmdata->pixelsToFree);
104 pmdata->pixelsToFree = static_cast<quint32 *>(const_cast<void *>(memoryToFree));
105 }
106 pmdata->cg_dataBeingReleased = 0;
107 }
108}
109
110CGImageRef qt_mac_image_to_cgimage(const QImage &image)
111{
112 int bitsPerColor = 8;
113 int bitsPerPixel = 32;
114 if (image.depth() == 1) {
115 bitsPerColor = 1;
116 bitsPerPixel = 1;
117 }
118 QCFType<CGDataProviderRef> provider =
119 CGDataProviderCreateWithData(0, image.bits(), image.bytesPerLine() * image.height(),
120 0);
121
122#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
123 uint cgflags = kCGImageAlphaPremultipliedFirst;
124#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version
125 if(QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4)
126 cgflags |= kCGBitmapByteOrder32Host;
127#endif
128#else
129 CGImageAlphaInfo cgflags = kCGImageAlphaPremultipliedFirst;
130#endif
131
132 CGImageRef cgImage = CGImageCreate(image.width(), image.height(), bitsPerColor, bitsPerPixel,
133 image.bytesPerLine(),
134 QCoreGraphicsPaintEngine::macGenericColorSpace(),
135 cgflags, provider,
136 0,
137 0,
138 kCGRenderingIntentDefault);
139
140 return cgImage;
141}
142
143/*****************************************************************************
144 QPixmap member functions
145 *****************************************************************************/
146
147static inline QRgb qt_conv16ToRgb(ushort c) {
148 static const int qt_rbits = (565/100);
149 static const int qt_gbits = (565/10%10);
150 static const int qt_bbits = (565%10);
151 static const int qt_red_shift = qt_bbits+qt_gbits-(8-qt_rbits);
152 static const int qt_green_shift = qt_bbits-(8-qt_gbits);
153 static const int qt_neg_blue_shift = 8-qt_bbits;
154 static const int qt_blue_mask = (1<<qt_bbits)-1;
155 static const int qt_green_mask = (1<<(qt_gbits+qt_bbits))-((1<<qt_bbits)-1);
156 static const int qt_red_mask = (1<<(qt_rbits+qt_gbits+qt_bbits))-(1<<(qt_gbits+qt_bbits));
157
158 const int r=(c & qt_red_mask);
159 const int g=(c & qt_green_mask);
160 const int b=(c & qt_blue_mask);
161 const int tr = r >> qt_red_shift;
162 const int tg = g >> qt_green_shift;
163 const int tb = b << qt_neg_blue_shift;
164
165 return qRgb(tr,tg,tb);
166}
167
168QSet<QMacPixmapData*> QMacPixmapData::validDataPointers;
169
170QMacPixmapData::QMacPixmapData(PixelType type)
171 : QPixmapData(type, MacClass), w(0), h(0), d(0), has_alpha(0), has_mask(0),
172 uninit(true), pixels(0), pixelsToFree(0), bytesPerRow(0),
173 cg_data(0), cg_dataBeingReleased(0), cg_mask(0),
174#ifndef QT_MAC_NO_QUICKDRAW
175 qd_data(0), qd_alpha(0),
176#endif
177 pengine(0)
178{
179}
180
181#define BEST_BYTE_ALIGNMENT 16
182#define COMPTUE_BEST_BYTES_PER_ROW(bpr) \
183 (((bpr) + (BEST_BYTE_ALIGNMENT - 1)) & ~(BEST_BYTE_ALIGNMENT - 1))
184
185void QMacPixmapData::resize(int width, int height)
186{
187 setSerialNumber(++qt_pixmap_serial);
188
189 w = width;
190 h = height;
191 d = (pixelType() == BitmapType ? 1 : 32);
192 bool make_null = w <= 0 || h <= 0; // create null pixmap
193 if (make_null || d == 0) {
194 w = 0;
195 h = 0;
196 d = 0;
197 if (!make_null)
198 qWarning("Qt: QPixmap: Invalid pixmap parameters");
199 return;
200 }
201
202 if (w < 1 || h < 1)
203 return;
204
205 //create the pixels
206 bytesPerRow = w * sizeof(quint32); // Minimum bytes per row.
207
208 // Quartz2D likes things as a multple of 16 (for now).
209 bytesPerRow = COMPTUE_BEST_BYTES_PER_ROW(bytesPerRow);
210 macCreatePixels();
211}
212
213#undef COMPUTE_BEST_BYTES_PER_ROW
214
215void QMacPixmapData::fromImage(const QImage &img,
216 Qt::ImageConversionFlags flags)
217{
218 setSerialNumber(++qt_pixmap_serial);
219
220 // the conversion code only handles format >=
221 // Format_ARGB32_Premultiplied at the moment..
222 if (img.format() > QImage::Format_ARGB32_Premultiplied) {
223 QImage image;
224 if (img.hasAlphaChannel())
225 image = img.convertToFormat(QImage::Format_ARGB32_Premultiplied);
226 else
227 image = img.convertToFormat(QImage::Format_RGB32);
228 fromImage(image, flags);
229 return;
230 }
231
232 w = img.width();
233 h = img.height();
234 d = (pixelType() == BitmapType ? 1 : img.depth());
235
236 QImage image = img;
237 int dd = QPixmap::defaultDepth();
238 bool force_mono = (dd == 1 ||
239 (flags & Qt::ColorMode_Mask)==Qt::MonoOnly);
240 if (force_mono) { // must be monochrome
241 if (d != 1) {
242 image = image.convertToFormat(QImage::Format_MonoLSB, flags); // dither
243 d = 1;
244 }
245 } else { // can be both
246 bool conv8 = false;
247 if(d > 8 && dd <= 8) { // convert to 8 bit
248 if ((flags & Qt::DitherMode_Mask) == Qt::AutoDither)
249 flags = (flags & ~Qt::DitherMode_Mask)
250 | Qt::PreferDither;
251 conv8 = true;
252 } else if ((flags & Qt::ColorMode_Mask) == Qt::ColorOnly) {
253 conv8 = d == 1; // native depth wanted
254 } else if (d == 1) {
255 if (image.numColors() == 2) {
256 QRgb c0 = image.color(0); // Auto: convert to best
257 QRgb c1 = image.color(1);
258 conv8 = qMin(c0,c1) != qRgb(0,0,0) || qMax(c0,c1) != qRgb(255,255,255);
259 } else {
260 // eg. 1-color monochrome images (they do exist).
261 conv8 = true;
262 }
263 }
264 if (conv8) {
265 image = image.convertToFormat(QImage::Format_Indexed8, flags);
266 d = 8;
267 }
268 }
269
270 if (image.depth()==1) {
271 image.setColor(0, QColor(Qt::color0).rgba());
272 image.setColor(1, QColor(Qt::color1).rgba());
273 }
274
275 if (d == 16 || d == 24) {
276 image = image.convertToFormat(QImage::Format_RGB32, flags);
277 fromImage(image, flags);
278 return;
279 }
280
281 // different size or depth, make a new pixmap
282 resize(w, h);
283
284 quint32 *dptr = pixels, *drow;
285 const uint dbpr = bytesPerRow;
286
287 const QImage::Format sfmt = image.format();
288 const unsigned short sbpr = image.bytesPerLine();
289
290 // use const_cast to prevent a detach
291 const uchar *sptr = const_cast<const QImage &>(image).bits(), *srow;
292
293 for (int y = 0; y < h; ++y) {
294 drow = dptr + (y * (dbpr / 4));
295 srow = sptr + (y * sbpr);
296 switch(sfmt) {
297 case QImage::Format_MonoLSB:
298 case QImage::Format_Mono:{
299 for (int x = 0; x < w; ++x) {
300 char one_bit = *(srow + (x / 8));
301 if (sfmt == QImage::Format_Mono)
302 one_bit = one_bit >> (7 - (x % 8));
303 else
304 one_bit = one_bit >> (x % 8);
305 if ((one_bit & 0x01))
306 *(drow+x) = 0xFF000000;
307 else
308 *(drow+x) = 0xFFFFFFFF;
309 }
310 break;
311 }
312 case QImage::Format_Indexed8:
313 for (int x = 0; x < w; ++x) {
314 *(drow+x) = PREMUL(image.color(*(srow + x)));
315 }
316 break;
317 case QImage::Format_RGB32:
318 for (int x = 0; x < w; ++x)
319 *(drow+x) = *(((quint32*)srow) + x) | 0xFF000000;
320 break;
321 case QImage::Format_ARGB32:
322 case QImage::Format_ARGB32_Premultiplied:
323 for (int x = 0; x < w; ++x) {
324 if(sfmt == QImage::Format_RGB32)
325 *(drow+x) = 0xFF000000 | (*(((quint32*)srow) + x) & 0x00FFFFFF);
326 else if(sfmt == QImage::Format_ARGB32_Premultiplied)
327 *(drow+x) = *(((quint32*)srow) + x);
328 else
329 *(drow+x) = PREMUL(*(((quint32*)srow) + x));
330 }
331 break;
332 default:
333 qWarning("Qt: internal: Oops: Forgot a format [%d] %s:%d", sfmt,
334 __FILE__, __LINE__);
335 break;
336 }
337 }
338 if (sfmt != QImage::Format_RGB32) { //setup the alpha
339 bool alphamap = image.depth() == 32;
340 if (sfmt == QImage::Format_Indexed8) {
341 const QVector<QRgb> rgb = image.colorTable();
342 for (int i = 0, count = image.numColors(); i < count; ++i) {
343 const int alpha = qAlpha(rgb[i]);
344 if (alpha != 0xff) {
345 alphamap = true;
346 break;
347 }
348 }
349 }
350 macSetHasAlpha(alphamap);
351 }
352 uninit = false;
353}
354
355int get_index(QImage * qi,QRgb mycol)
356{
357 int loopc;
358 for(loopc=0;loopc<qi->numColors();loopc++) {
359 if(qi->color(loopc)==mycol)
360 return loopc;
361 }
362 qi->setNumColors(qi->numColors()+1);
363 qi->setColor(qi->numColors(),mycol);
364 return qi->numColors();
365}
366
367QImage QMacPixmapData::toImage() const
368{
369 QImage::Format format = QImage::Format_MonoLSB;
370 if (d != 1) //Doesn't support index color modes
371 format = (has_alpha ? QImage::Format_ARGB32_Premultiplied :
372 QImage::Format_RGB32);
373
374 QImage image(w, h, format);
375 quint32 *sptr = pixels, *srow;
376 const uint sbpr = bytesPerRow;
377 if (format == QImage::Format_MonoLSB) {
378 image.fill(0);
379 image.setNumColors(2);
380 image.setColor(0, QColor(Qt::color0).rgba());
381 image.setColor(1, QColor(Qt::color1).rgba());
382 for (int y = 0; y < h; ++y) {
383 uchar *scanLine = image.scanLine(y);
384 srow = sptr + (y * (sbpr/4));
385 for (int x = 0; x < w; ++x) {
386 if (!(*(srow + x) & RGB_MASK))
387 scanLine[x >> 3] |= (1 << (x & 7));
388 }
389 }
390 } else {
391 for (int y = 0; y < h; ++y) {
392 srow = sptr + (y * (sbpr / 4));
393 memcpy(image.scanLine(y), srow, w * 4);
394 }
395
396 }
397
398 return image;
399}
400
401void QMacPixmapData::fill(const QColor &fillColor)
402
403{
404 { //we don't know what backend to use so we cannot paint here
405 quint32 *dptr = pixels;
406 Q_ASSERT_X(dptr, "QPixmap::fill", "No dptr");
407 const quint32 colr = PREMUL(fillColor.rgba());
408 const int nbytes = bytesPerRow * h;
409 if (!colr) {
410 memset(dptr, 0, nbytes);
411 } else {
412 for (uint i = 0; i < nbytes / sizeof(quint32); ++i)
413 *(dptr + i) = colr;
414 }
415 }
416 macSetHasAlpha(fillColor.alpha() != 255);
417}
418
419QPixmap QMacPixmapData::alphaChannel() const
420{
421 if (!has_alpha)
422 return QPixmap();
423
424 QMacPixmapData *alpha = new QMacPixmapData(PixmapType);
425 alpha->resize(w, h);
426 macGetAlphaChannel(alpha, false);
427 return QPixmap(alpha);
428}
429
430void QMacPixmapData::setAlphaChannel(const QPixmap &alpha)
431{
432 has_mask = true;
433 QMacPixmapData *alphaData = static_cast<QMacPixmapData*>(alpha.data);
434 macSetAlphaChannel(alphaData, false);
435}
436
437QBitmap QMacPixmapData::mask() const
438{
439 if (!has_mask && !has_alpha)
440 return QBitmap();
441
442 QMacPixmapData *mask = new QMacPixmapData(BitmapType);
443 mask->resize(w, h);
444 macGetAlphaChannel(mask, true);
445 return QPixmap(mask);
446}
447
448void QMacPixmapData::setMask(const QBitmap &mask)
449{
450 if (mask.isNull()) {
451 QMacPixmapData opaque(PixmapType);
452 opaque.resize(w, h);
453 opaque.fill(QColor(255, 255, 255, 255));
454 macSetAlphaChannel(&opaque, true);
455 has_alpha = has_mask = false;
456 return;
457 }
458
459 has_alpha = false;
460 has_mask = true;
461 QMacPixmapData *maskData = static_cast<QMacPixmapData*>(mask.data);
462 macSetAlphaChannel(maskData, true);
463}
464
465int QMacPixmapData::metric(QPaintDevice::PaintDeviceMetric theMetric) const
466{
467 switch (theMetric) {
468 case QPaintDevice::PdmWidth:
469 return w;
470 case QPaintDevice::PdmHeight:
471 return h;
472 case QPaintDevice::PdmWidthMM:
473 return qRound(metric(QPaintDevice::PdmWidth) * 25.4 / qreal(metric(QPaintDevice::PdmDpiX)));
474 case QPaintDevice::PdmHeightMM:
475 return qRound(metric(QPaintDevice::PdmHeight) * 25.4 / qreal(metric(QPaintDevice::PdmDpiY)));
476 case QPaintDevice::PdmNumColors:
477 return 1 << d;
478 case QPaintDevice::PdmDpiX:
479 case QPaintDevice::PdmPhysicalDpiX: {
480 extern float qt_mac_defaultDpi_x(); //qpaintdevice_mac.cpp
481 return int(qt_mac_defaultDpi_x());
482 }
483 case QPaintDevice::PdmDpiY:
484 case QPaintDevice::PdmPhysicalDpiY: {
485 extern float qt_mac_defaultDpi_y(); //qpaintdevice_mac.cpp
486 return int(qt_mac_defaultDpi_y());
487 }
488 case QPaintDevice::PdmDepth:
489 return d;
490 default:
491 qWarning("QPixmap::metric: Invalid metric command");
492 }
493 return 0;
494}
495
496QMacPixmapData::~QMacPixmapData()
497{
498 validDataPointers.remove(this);
499#ifndef QT_MAC_NO_QUICKDRAW
500 macQDDisposeAlpha();
501 if (qd_data) {
502 DisposeGWorld(qd_data);
503 qd_data = 0;
504 }
505#endif
506 if (cg_mask) {
507 CGImageRelease(cg_mask);
508 cg_mask = 0;
509 }
510
511 delete pengine; // Make sure we aren't drawing on the context anymore.
512 if (cg_data) {
513 CGImageRelease(cg_data);
514 } else if (!cg_dataBeingReleased && pixels != pixelsToFree) {
515 free(pixels);
516 }
517 free(pixelsToFree);
518}
519
520void QMacPixmapData::macSetAlphaChannel(const QMacPixmapData *pix, bool asMask)
521{
522 if (!pixels || !h || !w || pix->w != w || pix->h != h)
523 return;
524
525 quint32 *dptr = pixels, *drow;
526 const uint dbpr = bytesPerRow;
527 const unsigned short sbpr = pix->bytesPerRow;
528 quint32 *sptr = pix->pixels, *srow;
529 for (int y=0; y < h; ++y) {
530 drow = dptr + (y * (dbpr/4));
531 srow = sptr + (y * (sbpr/4));
532 if(d == 1) {
533 for (int x=0; x < w; ++x) {
534 if((*(srow+x) & RGB_MASK))
535 *(drow+x) = 0xFFFFFFFF;
536 }
537 } else if(d == 8) {
538 for (int x=0; x < w; ++x)
539 *(drow+x) = (*(drow+x) & RGB_MASK) | (*(srow+x) << 24);
540 } else if(asMask) {
541 for (int x=0; x < w; ++x) {
542 if(*(srow+x) & RGB_MASK)
543 *(drow+x) = (*(drow+x) & RGB_MASK);
544 else
545 *(drow+x) = (*(drow+x) & RGB_MASK) | 0xFF000000;
546 *(drow+x) = PREMUL(*(drow+x));
547 }
548 } else {
549 for (int x=0; x < w; ++x) {
550 const uchar alpha = qGray(qRed(*(srow+x)), qGreen(*(srow+x)), qBlue(*(srow+x)));
551 const uchar destAlpha = qt_div_255(alpha * qAlpha(*(drow+x)));
552#if 1
553 *(drow+x) = (*(drow+x) & RGB_MASK) | (destAlpha << 24);
554#else
555 *(drow+x) = qRgba(qt_div_255(qRed(*(drow+x) * alpha)),
556 qt_div_255(qGreen(*(drow+x) * alpha)),
557 qt_div_255(qBlue(*(drow+x) * alpha)), destAlpha);
558#endif
559 *(drow+x) = PREMUL(*(drow+x));
560 }
561 }
562 }
563 macSetHasAlpha(true);
564}
565
566void QMacPixmapData::macGetAlphaChannel(QMacPixmapData *pix, bool asMask) const
567{
568 quint32 *dptr = pix->pixels, *drow;
569 const uint dbpr = pix->bytesPerRow;
570 const unsigned short sbpr = bytesPerRow;
571 quint32 *sptr = pixels, *srow;
572 for(int y=0; y < h; ++y) {
573 drow = dptr + (y * (dbpr/4));
574 srow = sptr + (y * (sbpr/4));
575 if(asMask) {
576 for (int x = 0; x < w; ++x) {
577 if (*(srow + x) & qRgba(0, 0, 0, 255))
578 *(drow + x) = 0x00000000;
579 else
580 *(drow + x) = 0xFFFFFFFF;
581 }
582 } else {
583 for (int x = 0; x < w; ++x) {
584 const int alpha = qAlpha(*(srow + x));
585 *(drow + x) = qRgb(alpha, alpha, alpha);
586 }
587 }
588 }
589}
590
591void QMacPixmapData::macSetHasAlpha(bool b)
592{
593 has_alpha = b;
594#ifndef QT_MAC_NO_QUICKDRAW
595 macQDDisposeAlpha(); //let it get created lazily
596#endif
597 macReleaseCGImageRef();
598}
599
600#ifndef QT_MAC_NO_QUICKDRAW
601void QMacPixmapData::macQDDisposeAlpha()
602{
603 if (qd_alpha) {
604 DisposeGWorld(qd_alpha);
605 qd_alpha = 0;
606 }
607}
608
609void QMacPixmapData::macQDUpdateAlpha()
610{
611 macQDDisposeAlpha(); // get rid of alpha pixmap
612 if (!has_alpha && !has_mask)
613 return;
614
615 //setup
616 Rect rect;
617 SetRect(&rect, 0, 0, w, h);
618 const int params = alignPix | stretchPix | newDepth;
619 NewGWorld(&qd_alpha, 32, &rect, 0, 0, params);
620 int *dptr = (int *)GetPixBaseAddr(GetGWorldPixMap(qd_alpha)), *drow;
621 unsigned short dbpr = GetPixRowBytes(GetGWorldPixMap(qd_alpha));
622 const int *sptr = (int*)pixels, *srow;
623 const uint sbpr = bytesPerRow;
624 uchar clr;
625 for (int y = 0; y < h; ++y) {
626 drow = (int*)((char *)dptr + (y * dbpr));
627 srow = (int*)((char *)sptr + (y * sbpr));
628 for (int x=0; x < w; x++) {
629 clr = qAlpha(*(srow + x));
630 *(drow + x) = qRgba(~clr, ~clr, ~clr, 0);
631 }
632 }
633}
634#endif
635
636void QMacPixmapData::macCreateCGImageRef()
637{
638 Q_ASSERT(cg_data == 0);
639 //create the cg data
640 CGColorSpaceRef colorspace = QCoreGraphicsPaintEngine::macDisplayColorSpace();
641 QCFType<CGDataProviderRef> provider = CGDataProviderCreateWithData(this,
642 pixels, bytesPerRow * h,
643 qt_mac_cgimage_data_free);
644 validDataPointers.insert(this);
645#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
646 uint cgflags = kCGImageAlphaPremultipliedFirst;
647#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version
648 if(QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4)
649 cgflags |= kCGBitmapByteOrder32Host;
650#endif
651#else
652 CGImageAlphaInfo cgflags = kCGImageAlphaPremultipliedFirst;
653#endif
654 cg_data = CGImageCreate(w, h, 8, 32, bytesPerRow, colorspace,
655 cgflags, provider, 0, 0, kCGRenderingIntentDefault);
656}
657
658void QMacPixmapData::macReleaseCGImageRef()
659{
660 if (!cg_data)
661 return; // There's nothing we need to do
662
663 cg_dataBeingReleased = cg_data;
664 CGImageRelease(cg_data);
665 cg_data = 0;
666
667 if (pixels != pixelsToFree) {
668 macCreatePixels();
669 } else {
670 pixelsToFree = 0;
671 }
672}
673
674
675// We create our space in memory to paint on here. If we already have existing pixels
676// copy them over. This is to preserve the fact that CGImageRef's are immutable.
677void QMacPixmapData::macCreatePixels()
678{
679 const int numBytes = bytesPerRow * h;
680 quint32 *base_pixels;
681 if (pixelsToFree && pixelsToFree != pixels) {
682 // Reuse unused block of memory lying around from a previous callback.
683 base_pixels = pixelsToFree;
684 pixelsToFree = 0;
685 } else {
686 // We need a block of memory to do stuff with.
687 base_pixels = static_cast<quint32 *>(malloc(numBytes));
688 }
689
690 if (pixels)
691 memcpy(base_pixels, pixels, numBytes);
692 pixels = base_pixels;
693}
694
695#if 0
696QPixmap QMacPixmapData::transformed(const QTransform &transform,
697 Qt::TransformationMode mode) const
698{
699 int w, h; // size of target pixmap
700 const int ws = width();
701 const int hs = height();
702
703 QTransform mat(transform.m11(), transform.m12(),
704 transform.m21(), transform.m22(), 0., 0.);
705 if (transform.m12() == 0.0F && transform.m21() == 0.0F &&
706 transform.m11() >= 0.0F && transform.m22() >= 0.0F)
707 {
708 h = int(qAbs(mat.m22()) * hs + 0.9999);
709 w = int(qAbs(mat.m11()) * ws + 0.9999);
710 h = qAbs(h);
711 w = qAbs(w);
712 } else { // rotation or shearing
713 QPolygonF a(QRectF(0,0,ws+1,hs+1));
714 a = mat.map(a);
715 QRectF r = a.boundingRect().normalized();
716 w = int(r.width() + 0.9999);
717 h = int(r.height() + 0.9999);
718 }
719 mat = QPixmap::trueMatrix(mat, ws, hs);
720 if (!h || !w)
721 return QPixmap();
722
723 // create destination
724 QMacPixmapData *pm = new QMacPixmapData(pixelType(), w, h);
725 const quint32 *sptr = pixels;
726 quint32 *dptr = pm->pixels;
727 memset(dptr, 0, (pm->bytesPerRow * pm->h));
728
729 // do the transform
730 if (mode == Qt::SmoothTransformation) {
731#warning QMacPixmapData::transformed not properly implemented
732 qWarning("QMacPixmapData::transformed not properly implemented");
733#if 0
734 QPainter p(&pm);
735 p.setRenderHint(QPainter::Antialiasing);
736 p.setRenderHint(QPainter::SmoothPixmapTransform);
737 p.setTransform(mat);
738 p.drawPixmap(0, 0, *this);
739#endif
740 } else {
741 bool invertible;
742 mat = mat.inverted(&invertible);
743 if (!invertible)
744 return QPixmap();
745
746 const int bpp = 32;
747 const int xbpl = (w * bpp) / 8;
748 if (!qt_xForm_helper(mat, 0, QT_XFORM_TYPE_MSBFIRST, bpp,
749 (uchar*)dptr, xbpl, (pm->bytesPerRow) - xbpl,
750 h, (uchar*)sptr, (bytesPerRow), ws, hs)) {
751 qWarning("QMacPixmapData::transform(): failure");
752 return QPixmap();
753 }
754 }
755
756 // update the alpha
757 pm->macSetHasAlpha(true);
758 return QPixmap(pm);
759}
760#endif
761
762QT_BEGIN_INCLUDE_NAMESPACE
763#include <OpenGL/OpenGL.h>
764#include <OpenGL/gl.h>
765QT_END_INCLUDE_NAMESPACE
766
767// Load and resolve the symbols we need from OpenGL manually so QtGui doesn't have to link against the OpenGL framework.
768typedef CGLError (*PtrCGLChoosePixelFormat)(const CGLPixelFormatAttribute *, CGLPixelFormatObj *, long *);
769typedef CGLError (*PtrCGLClearDrawable)(CGLContextObj);
770typedef CGLError (*PtrCGLCreateContext)(CGLPixelFormatObj, CGLContextObj, CGLContextObj *);
771typedef CGLError (*PtrCGLDestroyContext)(CGLContextObj);
772typedef CGLError (*PtrCGLDestroyPixelFormat)(CGLPixelFormatObj);
773typedef CGLError (*PtrCGLSetCurrentContext)(CGLContextObj);
774typedef CGLError (*PtrCGLSetFullScreen)(CGLContextObj);
775typedef void (*PtrglFinish)();
776typedef void (*PtrglPixelStorei)(GLenum, GLint);
777typedef void (*PtrglReadBuffer)(GLenum);
778typedef void (*PtrglReadPixels)(GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, GLvoid *);
779
780static PtrCGLChoosePixelFormat ptrCGLChoosePixelFormat = 0;
781static PtrCGLClearDrawable ptrCGLClearDrawable = 0;
782static PtrCGLCreateContext ptrCGLCreateContext = 0;
783static PtrCGLDestroyContext ptrCGLDestroyContext = 0;
784static PtrCGLDestroyPixelFormat ptrCGLDestroyPixelFormat = 0;
785static PtrCGLSetCurrentContext ptrCGLSetCurrentContext = 0;
786static PtrCGLSetFullScreen ptrCGLSetFullScreen = 0;
787static PtrglFinish ptrglFinish = 0;
788static PtrglPixelStorei ptrglPixelStorei = 0;
789static PtrglReadBuffer ptrglReadBuffer = 0;
790static PtrglReadPixels ptrglReadPixels = 0;
791
792static bool resolveOpenGLSymbols()
793{
794 if (ptrCGLChoosePixelFormat == 0) {
795 QLibrary library(QLatin1String("/System/Library/Frameworks/OpenGL.framework/OpenGL"));
796 ptrCGLChoosePixelFormat = (PtrCGLChoosePixelFormat)(library.resolve("CGLChoosePixelFormat"));
797 ptrCGLClearDrawable = (PtrCGLClearDrawable)(library.resolve("CGLClearDrawable"));
798 ptrCGLCreateContext = (PtrCGLCreateContext)(library.resolve("CGLCreateContext"));
799 ptrCGLDestroyContext = (PtrCGLDestroyContext)(library.resolve("CGLDestroyContext"));
800 ptrCGLDestroyPixelFormat = (PtrCGLDestroyPixelFormat)(library.resolve("CGLDestroyPixelFormat"));
801 ptrCGLSetCurrentContext = (PtrCGLSetCurrentContext)(library.resolve("CGLSetCurrentContext"));
802 ptrCGLSetFullScreen = (PtrCGLSetFullScreen)(library.resolve("CGLSetFullScreen"));
803 ptrglFinish = (PtrglFinish)(library.resolve("glFinish"));
804 ptrglPixelStorei = (PtrglPixelStorei)(library.resolve("glPixelStorei"));
805 ptrglReadBuffer = (PtrglReadBuffer)(library.resolve("glReadBuffer"));
806 ptrglReadPixels = (PtrglReadPixels)(library.resolve("glReadPixels"));
807 }
808 return ptrCGLChoosePixelFormat && ptrCGLClearDrawable && ptrCGLCreateContext
809 && ptrCGLDestroyContext && ptrCGLDestroyPixelFormat && ptrCGLSetCurrentContext
810 && ptrCGLSetFullScreen && ptrglFinish && ptrglPixelStorei
811 && ptrglReadBuffer && ptrglReadPixels;
812}
813
814// Inverts the given pixmap in the y direction.
815static void qt_mac_flipPixmap(void *data, int rowBytes, int height)
816{
817 int bottom = height - 1;
818 void *base = data;
819 void *buffer = malloc(rowBytes);
820
821 int top = 0;
822 while ( top < bottom )
823 {
824 void *topP = (void *)((top * rowBytes) + (intptr_t)base);
825 void *bottomP = (void *)((bottom * rowBytes) + (intptr_t)base);
826
827 bcopy( topP, buffer, rowBytes );
828 bcopy( bottomP, topP, rowBytes );
829 bcopy( buffer, bottomP, rowBytes );
830
831 ++top;
832 --bottom;
833 }
834 free(buffer);
835}
836
837// Grabs displayRect from display and places it into buffer.
838static void qt_mac_grabDisplayRect(CGDirectDisplayID display, const QRect &displayRect, void *buffer)
839{
840 if (display == kCGNullDirectDisplay)
841 return;
842
843 CGLPixelFormatAttribute attribs[] = {
844 kCGLPFAFullScreen,
845 kCGLPFADisplayMask,
846 (CGLPixelFormatAttribute)0, /* Display mask bit goes here */
847 (CGLPixelFormatAttribute)0
848 };
849
850 attribs[2] = (CGLPixelFormatAttribute)CGDisplayIDToOpenGLDisplayMask(display);
851
852 // Build a full-screen GL context
853 CGLPixelFormatObj pixelFormatObj;
854 long numPixelFormats;
855
856 ptrCGLChoosePixelFormat( attribs, &pixelFormatObj, &numPixelFormats );
857
858 if (!pixelFormatObj) // No full screen context support
859 return;
860
861 CGLContextObj glContextObj;
862 ptrCGLCreateContext(pixelFormatObj, 0, &glContextObj);
863 ptrCGLDestroyPixelFormat(pixelFormatObj) ;
864 if (!glContextObj)
865 return;
866
867 ptrCGLSetCurrentContext(glContextObj);
868 ptrCGLSetFullScreen(glContextObj) ;
869
870 ptrglReadBuffer(GL_FRONT);
871
872 ptrglFinish(); // Finish all OpenGL commands
873 ptrglPixelStorei(GL_PACK_ALIGNMENT, 4); // Force 4-byte alignment
874 ptrglPixelStorei(GL_PACK_ROW_LENGTH, 0);
875 ptrglPixelStorei(GL_PACK_SKIP_ROWS, 0);
876 ptrglPixelStorei(GL_PACK_SKIP_PIXELS, 0);
877
878 // Fetch the data in XRGB format, matching the bitmap context.
879 ptrglReadPixels(GLint(displayRect.x()), GLint(displayRect.y()),
880 GLint(displayRect.width()), GLint(displayRect.height()),
881#ifdef __BIG_ENDIAN__
882 GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer
883#else
884 GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, buffer
885#endif
886 );
887
888 ptrCGLSetCurrentContext(0);
889 ptrCGLClearDrawable(glContextObj); // disassociate from full screen
890 ptrCGLDestroyContext(glContextObj); // and destroy the context
891}
892
893static CGImageRef qt_mac_createImageFromBitmapContext(CGContextRef c)
894{
895 void *rasterData = CGBitmapContextGetData(c);
896 const int width = CGBitmapContextGetBytesPerRow(c),
897 height = CGBitmapContextGetHeight(c);
898 size_t imageDataSize = width*height;
899
900 if(!rasterData)
901 return 0;
902
903 // Create the data provider from the image data, using
904 // the image releaser function releaseBitmapContextImageData.
905 CGDataProviderRef dataProvider = CGDataProviderCreateWithData(0, rasterData,
906 imageDataSize,
907 qt_mac_cgimage_data_free);
908
909 if(!dataProvider)
910 return 0;
911
912 uint bitmapInfo = 0;
913#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
914 if(CGBitmapContextGetBitmapInfo)
915 bitmapInfo = CGBitmapContextGetBitmapInfo(c);
916 else
917#endif
918 bitmapInfo = CGBitmapContextGetAlphaInfo(c);
919 return CGImageCreate(width, height, CGBitmapContextGetBitsPerComponent(c),
920 CGBitmapContextGetBitsPerPixel(c), CGBitmapContextGetBytesPerRow(c),
921 CGBitmapContextGetColorSpace(c), bitmapInfo, dataProvider,
922 0, true, kCGRenderingIntentDefault);
923}
924
925// Returns a pixmap containing the screen contents at rect.
926static QPixmap qt_mac_grabScreenRect(const QRect &rect)
927{
928 if (!resolveOpenGLSymbols())
929 return QPixmap();
930
931 const int maxDisplays = 128; // 128 displays should be enough for everyone.
932 CGDirectDisplayID displays[maxDisplays];
933 CGDisplayCount displayCount;
934 const CGRect cgRect = CGRectMake(rect.x(), rect.y(), rect.width(), rect.height());
935 const CGDisplayErr err = CGGetDisplaysWithRect(cgRect, maxDisplays, displays, &displayCount);
936
937 if (err && displayCount == 0)
938 return QPixmap();