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

Last change on this file since 1056 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 39.8 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this 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 have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qpixmap.h"
43#include "qimage.h"
44#include "qapplication.h"
45#include "qbitmap.h"
46#include "qmatrix.h"
47#include "qtransform.h"
48#include "qlibrary.h"
49#include "qvarlengtharray.h"
50#include "qdebug.h"
51#include <private/qdrawhelper_p.h>
52#include <private/qpixmap_mac_p.h>
53#include <private/qpixmap_raster_p.h>
54#include <private/qpaintengine_mac_p.h>
55#include <private/qt_mac_p.h>
56#include <private/qt_cocoa_helpers_mac_p.h>
57
58#include <limits.h>
59#include <string.h>
60
61QT_BEGIN_NAMESPACE
62
63/*****************************************************************************
64 Externals
65 *****************************************************************************/
66extern const uchar *qt_get_bitflip_array(); //qimage.cpp
67extern CGContextRef qt_mac_cg_context(const QPaintDevice *pdev); //qpaintdevice_mac.cpp
68extern RgnHandle qt_mac_get_rgn(); //qregion_mac.cpp
69extern void qt_mac_dispose_rgn(RgnHandle r); //qregion_mac.cpp
70extern QRegion qt_mac_convert_mac_region(RgnHandle rgn); //qregion_mac.cpp
71
72static int qt_pixmap_serial = 0;
73
74Q_GUI_EXPORT quint32 *qt_mac_pixmap_get_base(const QPixmap *pix)
75{
76 return static_cast<QMacPixmapData*>(pix->data.data())->pixels;
77}
78
79Q_GUI_EXPORT int qt_mac_pixmap_get_bytes_per_line(const QPixmap *pix)
80{
81 return static_cast<QMacPixmapData*>(pix->data.data())->bytesPerRow;
82}
83
84void qt_mac_cgimage_data_free(void *info, const void *memoryToFree, size_t)
85{
86 QMacPixmapData *pmdata = static_cast<QMacPixmapData *>(info);
87 if (!pmdata) {
88 free(const_cast<void *>(memoryToFree));
89 } else {
90 if (QMacPixmapData::validDataPointers.contains(pmdata) == false) {
91 free(const_cast<void *>(memoryToFree));
92 return;
93 }
94 if (pmdata->pixels == pmdata->pixelsToFree) {
95 // something we aren't expecting, just free it.
96 Q_ASSERT(memoryToFree != pmdata->pixelsToFree);
97 free(const_cast<void *>(memoryToFree));
98 } else {
99 free(pmdata->pixelsToFree);
100 pmdata->pixelsToFree = static_cast<quint32 *>(const_cast<void *>(memoryToFree));
101 }
102 pmdata->cg_dataBeingReleased = 0;
103 }
104}
105
106CGImageRef qt_mac_image_to_cgimage(const QImage &image)
107{
108 int bitsPerColor = 8;
109 int bitsPerPixel = 32;
110 if (image.depth() == 1) {
111 bitsPerColor = 1;
112 bitsPerPixel = 1;
113 }
114 QCFType<CGDataProviderRef> provider =
115 CGDataProviderCreateWithData(0, image.bits(), image.bytesPerLine() * image.height(),
116 0);
117
118 uint cgflags = kCGImageAlphaPremultipliedFirst;
119#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version
120 cgflags |= kCGBitmapByteOrder32Host;
121#endif
122
123 CGImageRef cgImage = CGImageCreate(image.width(), image.height(), bitsPerColor, bitsPerPixel,
124 image.bytesPerLine(),
125 QCoreGraphicsPaintEngine::macGenericColorSpace(),
126 cgflags, provider,
127 0,
128 0,
129 kCGRenderingIntentDefault);
130
131 return cgImage;
132}
133
134/*****************************************************************************
135 QPixmap member functions
136 *****************************************************************************/
137
138static inline QRgb qt_conv16ToRgb(ushort c) {
139 static const int qt_rbits = (565/100);
140 static const int qt_gbits = (565/10%10);
141 static const int qt_bbits = (565%10);
142 static const int qt_red_shift = qt_bbits+qt_gbits-(8-qt_rbits);
143 static const int qt_green_shift = qt_bbits-(8-qt_gbits);
144 static const int qt_neg_blue_shift = 8-qt_bbits;
145 static const int qt_blue_mask = (1<<qt_bbits)-1;
146 static const int qt_green_mask = (1<<(qt_gbits+qt_bbits))-((1<<qt_bbits)-1);
147 static const int qt_red_mask = (1<<(qt_rbits+qt_gbits+qt_bbits))-(1<<(qt_gbits+qt_bbits));
148
149 const int r=(c & qt_red_mask);
150 const int g=(c & qt_green_mask);
151 const int b=(c & qt_blue_mask);
152 const int tr = r >> qt_red_shift;
153 const int tg = g >> qt_green_shift;
154 const int tb = b << qt_neg_blue_shift;
155
156 return qRgb(tr,tg,tb);
157}
158
159QSet<QMacPixmapData*> QMacPixmapData::validDataPointers;
160
161QMacPixmapData::QMacPixmapData(PixelType type)
162 : QPixmapData(type, MacClass), has_alpha(0), has_mask(0),
163 uninit(true), pixels(0), pixelsSize(0), pixelsToFree(0),
164 bytesPerRow(0), cg_data(0), cg_dataBeingReleased(0), cg_mask(0),
165 pengine(0)
166{
167}
168
169QPixmapData *QMacPixmapData::createCompatiblePixmapData() const
170{
171 return new QMacPixmapData(pixelType());
172}
173
174#define BEST_BYTE_ALIGNMENT 16
175#define COMPTUE_BEST_BYTES_PER_ROW(bpr) \
176 (((bpr) + (BEST_BYTE_ALIGNMENT - 1)) & ~(BEST_BYTE_ALIGNMENT - 1))
177
178void QMacPixmapData::resize(int width, int height)
179{
180 setSerialNumber(++qt_pixmap_serial);
181
182 w = width;
183 h = height;
184 is_null = (w <= 0 || h <= 0);
185 d = (pixelType() == BitmapType ? 1 : 32);
186 bool make_null = w <= 0 || h <= 0; // create null pixmap
187 if (make_null || d == 0) {
188 w = 0;
189 h = 0;
190 is_null = true;
191 d = 0;
192 if (!make_null)
193 qWarning("Qt: QPixmap: Invalid pixmap parameters");
194 return;
195 }
196
197 if (w < 1 || h < 1)
198 return;
199
200 //create the pixels
201 bytesPerRow = w * sizeof(quint32); // Minimum bytes per row.
202
203 // Quartz2D likes things as a multple of 16 (for now).
204 bytesPerRow = COMPTUE_BEST_BYTES_PER_ROW(bytesPerRow);
205 macCreatePixels();
206}
207
208#undef COMPUTE_BEST_BYTES_PER_ROW
209
210void QMacPixmapData::fromImage(const QImage &img,
211 Qt::ImageConversionFlags flags)
212{
213 setSerialNumber(++qt_pixmap_serial);
214
215 // the conversion code only handles format >=
216 // Format_ARGB32_Premultiplied at the moment..
217 if (img.format() > QImage::Format_ARGB32_Premultiplied) {
218 QImage image;
219 if (img.hasAlphaChannel())
220 image = img.convertToFormat(QImage::Format_ARGB32_Premultiplied);
221 else
222 image = img.convertToFormat(QImage::Format_RGB32);
223 fromImage(image, flags);
224 return;
225 }
226
227 w = img.width();
228 h = img.height();
229 is_null = (w <= 0 || h <= 0);
230 d = (pixelType() == BitmapType ? 1 : img.depth());
231
232 QImage image = img;
233 int dd = QPixmap::defaultDepth();
234 bool force_mono = (dd == 1 ||
235 (flags & Qt::ColorMode_Mask)==Qt::MonoOnly);
236 if (force_mono) { // must be monochrome
237 if (d != 1) {
238 image = image.convertToFormat(QImage::Format_MonoLSB, flags); // dither
239 d = 1;
240 }
241 } else { // can be both
242 bool conv8 = false;
243 if(d > 8 && dd <= 8) { // convert to 8 bit
244 if ((flags & Qt::DitherMode_Mask) == Qt::AutoDither)
245 flags = (flags & ~Qt::DitherMode_Mask)
246 | Qt::PreferDither;
247 conv8 = true;
248 } else if ((flags & Qt::ColorMode_Mask) == Qt::ColorOnly) {
249 conv8 = d == 1; // native depth wanted
250 } else if (d == 1) {
251 if (image.colorCount() == 2) {
252 QRgb c0 = image.color(0); // Auto: convert to best
253 QRgb c1 = image.color(1);
254 conv8 = qMin(c0,c1) != qRgb(0,0,0) || qMax(c0,c1) != qRgb(255,255,255);
255 } else {
256 // eg. 1-color monochrome images (they do exist).
257 conv8 = true;
258 }
259 }
260 if (conv8) {
261 image = image.convertToFormat(QImage::Format_Indexed8, flags);
262 d = 8;
263 }
264 }
265
266 if (image.depth()==1) {
267 image.setColor(0, QColor(Qt::color0).rgba());
268 image.setColor(1, QColor(Qt::color1).rgba());
269 }
270
271 if (d == 16 || d == 24) {
272 image = image.convertToFormat(QImage::Format_RGB32, flags);
273 fromImage(image, flags);
274 return;
275 }
276
277 // different size or depth, make a new pixmap
278 resize(w, h);
279
280 quint32 *dptr = pixels, *drow;
281 const uint dbpr = bytesPerRow;
282
283 const QImage::Format sfmt = image.format();
284 const unsigned short sbpr = image.bytesPerLine();
285
286 // use const_cast to prevent a detach
287 const uchar *sptr = const_cast<const QImage &>(image).bits(), *srow;
288
289 for (int y = 0; y < h; ++y) {
290 drow = dptr + (y * (dbpr / 4));
291 srow = sptr + (y * sbpr);
292 switch(sfmt) {
293 case QImage::Format_MonoLSB:
294 case QImage::Format_Mono:{
295 for (int x = 0; x < w; ++x) {
296 char one_bit = *(srow + (x / 8));
297 if (sfmt == QImage::Format_Mono)
298 one_bit = one_bit >> (7 - (x % 8));
299 else
300 one_bit = one_bit >> (x % 8);
301 if ((one_bit & 0x01))
302 *(drow+x) = 0xFF000000;
303 else
304 *(drow+x) = 0xFFFFFFFF;
305 }
306 break;
307 }
308 case QImage::Format_Indexed8: {
309 int numColors = image.numColors();
310 if (numColors > 0) {
311 for (int x = 0; x < w; ++x) {
312 int index = *(srow + x);
313 *(drow+x) = PREMUL(image.color(qMin(index, numColors)));
314 }
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.colorCount(); 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->colorCount();loopc++) {
359 if(qi->color(loopc)==mycol)
360 return loopc;
361 }
362 qi->setColorCount(qi->colorCount()+1);
363 qi->setColor(qi->colorCount(),mycol);
364 return qi->colorCount();
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.setColorCount(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.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.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 if (cg_mask) {
500 CGImageRelease(cg_mask);
501 cg_mask = 0;
502 }
503
504 delete pengine; // Make sure we aren't drawing on the context anymore.
505 if (cg_data) {
506 CGImageRelease(cg_data);
507 } else if (!cg_dataBeingReleased && pixels != pixelsToFree) {
508 free(pixels);
509 }
510 free(pixelsToFree);
511}
512
513void QMacPixmapData::macSetAlphaChannel(const QMacPixmapData *pix, bool asMask)
514{
515 if (!pixels || !h || !w || pix->w != w || pix->h != h)
516 return;
517
518 quint32 *dptr = pixels, *drow;
519 const uint dbpr = bytesPerRow;
520 const unsigned short sbpr = pix->bytesPerRow;
521 quint32 *sptr = pix->pixels, *srow;
522 for (int y=0; y < h; ++y) {
523 drow = dptr + (y * (dbpr/4));
524 srow = sptr + (y * (sbpr/4));
525 if(d == 1) {
526 for (int x=0; x < w; ++x) {
527 if((*(srow+x) & RGB_MASK))
528 *(drow+x) = 0xFFFFFFFF;
529 }
530 } else if(d == 8) {
531 for (int x=0; x < w; ++x)
532 *(drow+x) = (*(drow+x) & RGB_MASK) | (*(srow+x) << 24);
533 } else if(asMask) {
534 for (int x=0; x < w; ++x) {
535 if(*(srow+x) & RGB_MASK)
536 *(drow+x) = (*(drow+x) & RGB_MASK);
537 else
538 *(drow+x) = (*(drow+x) & RGB_MASK) | 0xFF000000;
539 *(drow+x) = PREMUL(*(drow+x));
540 }
541 } else {
542 for (int x=0; x < w; ++x) {
543 const uchar alpha = qGray(qRed(*(srow+x)), qGreen(*(srow+x)), qBlue(*(srow+x)));
544 const uchar destAlpha = qt_div_255(alpha * qAlpha(*(drow+x)));
545#if 1
546 *(drow+x) = (*(drow+x) & RGB_MASK) | (destAlpha << 24);
547#else
548 *(drow+x) = qRgba(qt_div_255(qRed(*(drow+x) * alpha)),
549 qt_div_255(qGreen(*(drow+x) * alpha)),
550 qt_div_255(qBlue(*(drow+x) * alpha)), destAlpha);
551#endif
552 *(drow+x) = PREMUL(*(drow+x));
553 }
554 }
555 }
556 macSetHasAlpha(true);
557}
558
559void QMacPixmapData::macGetAlphaChannel(QMacPixmapData *pix, bool asMask) const
560{
561 quint32 *dptr = pix->pixels, *drow;
562 const uint dbpr = pix->bytesPerRow;
563 const unsigned short sbpr = bytesPerRow;
564 quint32 *sptr = pixels, *srow;
565 for(int y=0; y < h; ++y) {
566 drow = dptr + (y * (dbpr/4));
567 srow = sptr + (y * (sbpr/4));
568 if(asMask) {
569 for (int x = 0; x < w; ++x) {
570 if (*(srow + x) & qRgba(0, 0, 0, 255))
571 *(drow + x) = 0x00000000;
572 else
573 *(drow + x) = 0xFFFFFFFF;
574 }
575 } else {
576 for (int x = 0; x < w; ++x) {
577 const int alpha = qAlpha(*(srow + x));
578 *(drow + x) = qRgb(alpha, alpha, alpha);
579 }
580 }
581 }
582}
583
584void QMacPixmapData::macSetHasAlpha(bool b)
585{
586 has_alpha = b;
587 macReleaseCGImageRef();
588}
589
590void QMacPixmapData::macCreateCGImageRef()
591{
592 Q_ASSERT(cg_data == 0);
593 //create the cg data
594 CGColorSpaceRef colorspace = QCoreGraphicsPaintEngine::macDisplayColorSpace();
595 QCFType<CGDataProviderRef> provider = CGDataProviderCreateWithData(this,
596 pixels, bytesPerRow * h,
597 qt_mac_cgimage_data_free);
598 validDataPointers.insert(this);
599 uint cgflags = kCGImageAlphaPremultipliedFirst;
600#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version
601 cgflags |= kCGBitmapByteOrder32Host;
602#endif
603 cg_data = CGImageCreate(w, h, 8, 32, bytesPerRow, colorspace,
604 cgflags, provider, 0, 0, kCGRenderingIntentDefault);
605}
606
607void QMacPixmapData::macReleaseCGImageRef()
608{
609 if (!cg_data)
610 return; // There's nothing we need to do
611
612 cg_dataBeingReleased = cg_data;
613 CGImageRelease(cg_data);
614 cg_data = 0;
615
616 if (pixels != pixelsToFree) {
617 macCreatePixels();
618 } else {
619 pixelsToFree = 0;
620 }
621}
622
623
624// We create our space in memory to paint on here. If we already have existing pixels
625// copy them over. This is to preserve the fact that CGImageRef's are immutable.
626void QMacPixmapData::macCreatePixels()
627{
628 const int numBytes = bytesPerRow * h;
629 quint32 *base_pixels;
630 if (pixelsToFree && pixelsToFree != pixels) {
631 // Reuse unused block of memory lying around from a previous callback.
632 base_pixels = pixelsToFree;
633 pixelsToFree = 0;
634 } else {
635 // We need a block of memory to do stuff with.
636 base_pixels = static_cast<quint32 *>(malloc(numBytes));
637 }
638
639 if (pixels)
640 memcpy(base_pixels, pixels, pixelsSize);
641 pixels = base_pixels;
642 pixelsSize = numBytes;
643}
644
645#if 0
646QPixmap QMacPixmapData::transformed(const QTransform &transform,
647 Qt::TransformationMode mode) const
648{
649 int w, h; // size of target pixmap
650 const int ws = width();
651 const int hs = height();
652
653 QTransform mat(transform.m11(), transform.m12(),
654 transform.m21(), transform.m22(), 0., 0.);
655 if (transform.m12() == 0.0F && transform.m21() == 0.0F &&
656 transform.m11() >= 0.0F && transform.m22() >= 0.0F)
657 {
658 h = int(qAbs(mat.m22()) * hs + 0.9999);
659 w = int(qAbs(mat.m11()) * ws + 0.9999);
660 h = qAbs(h);
661 w = qAbs(w);
662 } else { // rotation or shearing
663 QPolygonF a(QRectF(0,0,ws+1,hs+1));
664 a = mat.map(a);
665 QRectF r = a.boundingRect().normalized();
666 w = int(r.width() + 0.9999);
667 h = int(r.height() + 0.9999);
668 }
669 mat = QPixmap::trueMatrix(mat, ws, hs);
670 if (!h || !w)
671 return QPixmap();
672
673 // create destination
674 QMacPixmapData *pm = new QMacPixmapData(pixelType(), w, h);
675 const quint32 *sptr = pixels;
676 quint32 *dptr = pm->pixels;
677 memset(dptr, 0, (pm->bytesPerRow * pm->h));
678
679 // do the transform
680 if (mode == Qt::SmoothTransformation) {
681#warning QMacPixmapData::transformed not properly implemented
682 qWarning("QMacPixmapData::transformed not properly implemented");
683#if 0
684 QPainter p(&pm);
685 p.setRenderHint(QPainter::Antialiasing);
686 p.setRenderHint(QPainter::SmoothPixmapTransform);
687 p.setTransform(mat);
688 p.drawPixmap(0, 0, *this);
689#endif
690 } else {
691 bool invertible;
692 mat = mat.inverted(&invertible);
693 if (!invertible)
694 return QPixmap();
695
696 const int bpp = 32;
697 const int xbpl = (w * bpp) / 8;
698 if (!qt_xForm_helper(mat, 0, QT_XFORM_TYPE_MSBFIRST, bpp,
699 (uchar*)dptr, xbpl, (pm->bytesPerRow) - xbpl,
700 h, (uchar*)sptr, (bytesPerRow), ws, hs)) {
701 qWarning("QMacPixmapData::transform(): failure");
702 return QPixmap();
703 }
704 }
705
706 // update the alpha
707 pm->macSetHasAlpha(true);
708 return QPixmap(pm);
709}
710#endif
711
712QT_BEGIN_INCLUDE_NAMESPACE
713#include <OpenGL/OpenGL.h>
714#include <OpenGL/gl.h>
715QT_END_INCLUDE_NAMESPACE
716
717// Load and resolve the symbols we need from OpenGL manually so QtGui doesn't have to link against the OpenGL framework.
718typedef CGLError (*PtrCGLChoosePixelFormat)(const CGLPixelFormatAttribute *, CGLPixelFormatObj *, long *);
719typedef CGLError (*PtrCGLClearDrawable)(CGLContextObj);
720typedef CGLError (*PtrCGLCreateContext)(CGLPixelFormatObj, CGLContextObj, CGLContextObj *);
721typedef CGLError (*PtrCGLDestroyContext)(CGLContextObj);
722typedef CGLError (*PtrCGLDestroyPixelFormat)(CGLPixelFormatObj);
723typedef CGLError (*PtrCGLSetCurrentContext)(CGLContextObj);
724typedef CGLError (*PtrCGLSetFullScreen)(CGLContextObj);
725typedef void (*PtrglFinish)();
726typedef void (*PtrglPixelStorei)(GLenum, GLint);
727typedef void (*PtrglReadBuffer)(GLenum);
728typedef void (*PtrglReadPixels)(GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, GLvoid *);
729
730static PtrCGLChoosePixelFormat ptrCGLChoosePixelFormat = 0;
731static PtrCGLClearDrawable ptrCGLClearDrawable = 0;
732static PtrCGLCreateContext ptrCGLCreateContext = 0;
733static PtrCGLDestroyContext ptrCGLDestroyContext = 0;
734static PtrCGLDestroyPixelFormat ptrCGLDestroyPixelFormat = 0;
735static PtrCGLSetCurrentContext ptrCGLSetCurrentContext = 0;
736static PtrCGLSetFullScreen ptrCGLSetFullScreen = 0;
737static PtrglFinish ptrglFinish = 0;
738static PtrglPixelStorei ptrglPixelStorei = 0;
739static PtrglReadBuffer ptrglReadBuffer = 0;
740static PtrglReadPixels ptrglReadPixels = 0;
741
742static bool resolveOpenGLSymbols()
743{
744 if (ptrCGLChoosePixelFormat == 0) {
745 QLibrary library(QLatin1String("/System/Library/Frameworks/OpenGL.framework/OpenGL"));
746 ptrCGLChoosePixelFormat = (PtrCGLChoosePixelFormat)(library.resolve("CGLChoosePixelFormat"));
747 ptrCGLClearDrawable = (PtrCGLClearDrawable)(library.resolve("CGLClearDrawable"));
748 ptrCGLCreateContext = (PtrCGLCreateContext)(library.resolve("CGLCreateContext"));
749 ptrCGLDestroyContext = (PtrCGLDestroyContext)(library.resolve("CGLDestroyContext"));
750 ptrCGLDestroyPixelFormat = (PtrCGLDestroyPixelFormat)(library.resolve("CGLDestroyPixelFormat"));
751 ptrCGLSetCurrentContext = (PtrCGLSetCurrentContext)(library.resolve("CGLSetCurrentContext"));
752 ptrCGLSetFullScreen = (PtrCGLSetFullScreen)(library.resolve("CGLSetFullScreen"));
753 ptrglFinish = (PtrglFinish)(library.resolve("glFinish"));
754 ptrglPixelStorei = (PtrglPixelStorei)(library.resolve("glPixelStorei"));
755 ptrglReadBuffer = (PtrglReadBuffer)(library.resolve("glReadBuffer"));
756 ptrglReadPixels = (PtrglReadPixels)(library.resolve("glReadPixels"));
757 }
758 return ptrCGLChoosePixelFormat && ptrCGLClearDrawable && ptrCGLCreateContext
759 && ptrCGLDestroyContext && ptrCGLDestroyPixelFormat && ptrCGLSetCurrentContext
760 && ptrCGLSetFullScreen && ptrglFinish && ptrglPixelStorei
761 && ptrglReadBuffer && ptrglReadPixels;
762}
763
764// Inverts the given pixmap in the y direction.
765static void qt_mac_flipPixmap(void *data, int rowBytes, int height)
766{
767 int bottom = height - 1;
768 void *base = data;
769 void *buffer = malloc(rowBytes);
770
771 int top = 0;
772 while ( top < bottom )
773 {
774 void *topP = (void *)((top * rowBytes) + (intptr_t)base);
775 void *bottomP = (void *)((bottom * rowBytes) + (intptr_t)base);
776
777 bcopy( topP, buffer, rowBytes );
778 bcopy( bottomP, topP, rowBytes );
779 bcopy( buffer, bottomP, rowBytes );
780
781 ++top;
782 --bottom;
783 }
784 free(buffer);
785}
786
787// Grabs displayRect from display and places it into buffer.
788static void qt_mac_grabDisplayRect(CGDirectDisplayID display, const QRect &displayRect, void *buffer)
789{
790 if (display == kCGNullDirectDisplay)
791 return;
792
793 CGLPixelFormatAttribute attribs[] = {
794 kCGLPFAFullScreen,
795 kCGLPFADisplayMask,
796 (CGLPixelFormatAttribute)0, /* Display mask bit goes here */
797 (CGLPixelFormatAttribute)0
798 };
799
800 attribs[2] = (CGLPixelFormatAttribute)CGDisplayIDToOpenGLDisplayMask(display);
801
802 // Build a full-screen GL context
803 CGLPixelFormatObj pixelFormatObj;
804 long numPixelFormats;
805
806 ptrCGLChoosePixelFormat( attribs, &pixelFormatObj, &numPixelFormats );
807
808 if (!pixelFormatObj) // No full screen context support
809 return;
810
811 CGLContextObj glContextObj;
812 ptrCGLCreateContext(pixelFormatObj, 0, &glContextObj);
813 ptrCGLDestroyPixelFormat(pixelFormatObj) ;
814 if (!glContextObj)
815 return;
816
817 ptrCGLSetCurrentContext(glContextObj);
818 ptrCGLSetFullScreen(glContextObj) ;
819
820 ptrglReadBuffer(GL_FRONT);
821
822 ptrglFinish(); // Finish all OpenGL commands
823 ptrglPixelStorei(GL_PACK_ALIGNMENT, 4); // Force 4-byte alignment
824 ptrglPixelStorei(GL_PACK_ROW_LENGTH, 0);
825 ptrglPixelStorei(GL_PACK_SKIP_ROWS, 0);
826 ptrglPixelStorei(GL_PACK_SKIP_PIXELS, 0);
827
828 // Fetch the data in XRGB format, matching the bitmap context.
829 ptrglReadPixels(GLint(displayRect.x()), GLint(displayRect.y()),
830 GLint(displayRect.width()), GLint(displayRect.height()),
831#ifdef __BIG_ENDIAN__
832 GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer
833#else
834 GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, buffer
835#endif
836 );
837
838 ptrCGLSetCurrentContext(0);
839 ptrCGLClearDrawable(glContextObj); // disassociate from full screen
840 ptrCGLDestroyContext(glContextObj); // and destroy the context
841}
842
843// Returns a pixmap containing the screen contents at rect.
844static QPixmap qt_mac_grabScreenRect(const QRect &rect)
845{
846 if (!resolveOpenGLSymbols())
847 return QPixmap();
848
849 const int maxDisplays = 128; // 128 displays should be enough for everyone.
850 CGDirectDisplayID displays[maxDisplays];
851 CGDisplayCount displayCount;
852 const CGRect cgRect = CGRectMake(rect.x(), rect.y(), rect.width(), rect.height());
853 const CGDisplayErr err = CGGetDisplaysWithRect(cgRect, maxDisplays, displays, &displayCount);
854
855 if (err && displayCount == 0)
856 return QPixmap();
857
858 long bytewidth = rect.width() * 4; // Assume 4 bytes/pixel for now
859 bytewidth = (bytewidth + 3) & ~3; // Align to 4 bytes
860 QVarLengthArray<char> buffer(rect.height() * bytewidth);
861
862 for (uint i = 0; i < displayCount; ++i) {
863 const CGRect bounds = CGDisplayBounds(displays[i]);
864 // Translate to display-local coordinates
865 QRect displayRect = rect.translated(qRound(-bounds.origin.x), qRound(-bounds.origin.y));
866 // Adjust for inverted y axis.
867 displayRect.moveTop(qRound(bounds.size.height) - displayRect.y() - rect.height());
868 qt_mac_grabDisplayRect(displays[i], displayRect, buffer.data());
869 }
870
871 qt_mac_flipPixmap(buffer.data(), bytewidth, rect.height());
872 QCFType<CGContextRef> bitmap = CGBitmapContextCreate(buffer.data(), rect.width(),
873 rect.height(), 8, bytewidth,
874 QCoreGraphicsPaintEngine::macGenericColorSpace(),
875 kCGImageAlphaNoneSkipFirst);
876 QCFType<CGImageRef> image = CGBitmapContextCreateImage(bitmap);
877 return QPixmap::fromMacCGImageRef(image);
878}
879
880#ifndef QT_MAC_USE_COCOA // no QuickDraw in 64-bit mode
881static QPixmap qt_mac_grabScreenRect_10_3(int x, int y, int w, int h, QWidget *widget)
882{
883 QPixmap pm = QPixmap(w, h);
884 extern WindowPtr qt_mac_window_for(const QWidget *); // qwidget_mac.cpp
885 const BitMap *windowPort = 0;
886 if((widget->windowType() == Qt::Desktop)) {
887 GDHandle gdh;
888 if(!(gdh=GetMainDevice()))
889 qDebug("Qt: internal: Unexpected condition reached: %s:%d", __FILE__, __LINE__);
890 windowPort = (BitMap*)(*(*gdh)->gdPMap);
891 } else {
892 windowPort = GetPortBitMapForCopyBits(GetWindowPort(qt_mac_window_for(widget)));
893 }
894 const BitMap *pixmapPort = GetPortBitMapForCopyBits(static_cast<GWorldPtr>(pm.macQDHandle()));
895 Rect macSrcRect, macDstRect;
896 SetRect(&macSrcRect, x, y, x + w, y + h);
897 SetRect(&macDstRect, 0, 0, w, h);
898 CopyBits(windowPort, pixmapPort, &macSrcRect, &macDstRect, srcCopy, 0);
899 return pm;
900}
901#endif
902
903QPixmap QPixmap::grabWindow(WId window, int x, int y, int w, int h)
904{
905 QWidget *widget = QWidget::find(window);
906 if (widget == 0)
907 return QPixmap();
908
909 if(w == -1)
910 w = widget->width() - x;
911 if(h == -1)
912 h = widget->height() - y;
913
914 QPoint globalCoord(0, 0);
915 globalCoord = widget->mapToGlobal(globalCoord);
916 QRect rect(globalCoord.x() + x, globalCoord.y() + y, w, h);
917
918#ifdef QT_MAC_USE_COCOA
919 return qt_mac_grabScreenRect(rect);
920#else
921#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
922 if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
923 return qt_mac_grabScreenRect(rect);
924 } else
925#endif
926 {
927 return qt_mac_grabScreenRect_10_3(x, y, w, h, widget);
928 }
929#endif // ifdef Q_WS_MAC64
930}
931
932/*! \internal
933
934 Returns the QuickDraw CGrafPtr of the pixmap. 0 is returned if it can't
935 be obtained. Do not hold the pointer around for long as it can be
936 relocated.
937
938 \warning This function is only available on Mac OS X.
939 \warning As of Qt 4.6, this function \e{always} returns zero.
940*/
941
942Qt::HANDLE QPixmap::macQDHandle() const
943{
944 return 0;
945}
946
947/*! \internal
948
949 Returns the QuickDraw CGrafPtr of the pixmap's alpha channel. 0 is
950 returned if it can't be obtained. Do not hold the pointer around for
951 long as it can be relocated.
952
953 \warning This function is only available on Mac OS X.
954 \warning As of Qt 4.6, this function \e{always} returns zero.
955*/
956
957Qt::HANDLE QPixmap::macQDAlphaHandle() const
958{
959 return 0;
960}
961
962/*! \internal
963
964 Returns the CoreGraphics CGContextRef of the pixmap. 0 is returned if
965 it can't be obtained. It is the caller's responsiblity to
966 CGContextRelease the context when finished using it.
967
968 \warning This function is only available on Mac OS X.
969*/
970
971Qt::HANDLE QPixmap::macCGHandle() const
972{
973 if (isNull())
974 return 0;
975
976 if (data->classId() == QPixmapData::MacClass) {
977 QMacPixmapData *d = static_cast<QMacPixmapData *>(data.data());
978 if (!d->cg_data)
979 d->macCreateCGImageRef();
980 CGImageRef ret = d->cg_data;
981 CGImageRetain(ret);
982 return ret;
983 } else if (data->classId() == QPixmapData::RasterClass) {
984 return qt_mac_image_to_cgimage(static_cast<QRasterPixmapData *>(data.data())->image);
985 }
986 return 0;
987}
988
989bool QMacPixmapData::hasAlphaChannel() const
990{
991 return has_alpha;
992}
993
994CGImageRef qt_mac_create_imagemask(const QPixmap &pixmap, const QRectF &sr)
995{
996 QMacPixmapData *px = static_cast<QMacPixmapData*>(pixmap.data.data());
997 if (px->cg_mask) {
998 if (px->cg_mask_rect == sr) {
999 CGImageRetain(px->cg_mask); //reference for the caller
1000 return px->cg_mask;
1001 }
1002 CGImageRelease(px->cg_mask);
1003 px->cg_mask = 0;
1004 }
1005
1006 const int sx = qRound(sr.x()), sy = qRound(sr.y()), sw = qRound(sr.width()), sh = qRound(sr.height());
1007 const int sbpr = px->bytesPerRow;
1008 const uint nbytes = sw * sh;
1009 // alpha is always 255 for bitmaps, ignore it in this case.
1010 const quint32 mask = px->depth() == 1 ? 0x00ffffff : 0xffffffff;
1011 quint8 *dptr = static_cast<quint8 *>(malloc(nbytes));
1012 quint32 *sptr = px->pixels, *srow;
1013 for(int y = sy, offset=0; y < sh; ++y) {
1014 srow = sptr + (y * (sbpr / 4));
1015 for(int x = sx; x < sw; ++x)
1016 *(dptr+(offset++)) = (*(srow+x) & mask) ? 255 : 0;
1017 }
1018 QCFType<CGDataProviderRef> provider = CGDataProviderCreateWithData(0, dptr, nbytes, qt_mac_cgimage_data_free);
1019 px->cg_mask = CGImageMaskCreate(sw, sh, 8, 8, nbytes / sh, provider, 0, 0);
1020 px->cg_mask_rect = sr;
1021 CGImageRetain(px->cg_mask); //reference for the caller
1022 return px->cg_mask;
1023}
1024
1025#ifndef QT_MAC_USE_COCOA
1026IconRef qt_mac_create_iconref(const QPixmap &px)
1027{
1028 if (px.isNull())
1029 return 0;
1030
1031 //create icon
1032 IconFamilyHandle iconFamily = reinterpret_cast<IconFamilyHandle>(NewHandle(0));
1033 //create data
1034 {
1035 struct {
1036 OSType mac_type;
1037 int width, height, depth;
1038 bool mask;
1039 } images[] = {
1040 { kThumbnail32BitData, 128, 128, 32, false },
1041 { kThumbnail8BitMask, 128, 128, 8, true },
1042 { 0, 0, 0, 0, false } //end marker
1043 };
1044 for(int i = 0; images[i].mac_type; i++) {
1045 //get QPixmap data
1046 QImage scaled_px = px.toImage().scaled(images[i].width, images[i].height);
1047
1048 quint32 *sptr = (quint32 *) scaled_px.bits();
1049 quint32 *srow;
1050 uint sbpr = scaled_px.bytesPerLine();
1051
1052 //get Handle data
1053 const int dbpr = images[i].width * (images[i].depth/8);
1054 Handle hdl = NewHandle(dbpr*images[i].height);
1055 if(!sptr) { //handle null pixmap
1056 memset((*hdl), '\0', dbpr*images[i].height);
1057 } else if(images[i].mask) {
1058 if(images[i].mac_type == kThumbnail8BitMask) {
1059 for(int y = 0, hindex = 0; y < images[i].height; ++y) {
1060 srow = sptr + (y * (sbpr/4));
1061 for(int x = 0; x < images[i].width; ++x)
1062 *((*hdl)+(hindex++)) = qAlpha(*(srow+x));
1063 }
1064 }
1065 } else {
1066 char *dest = (*hdl);
1067#if defined(__i386__)
1068 if(images[i].depth == 32) {
1069 for(int y = 0; y < images[i].height; ++y) {
1070 uint *source = (uint*)((const uchar*)sptr+(sbpr*y));
1071 for(int x = 0; x < images[i].width; ++x, dest += 4)
1072 *((uint*)dest) = CFSwapInt32(*(source + x));
1073 }
1074 } else
1075#endif
1076 {
1077 for(int y = 0; y < images[i].height; ++y)
1078 memcpy(dest+(y*dbpr), ((const uchar*)sptr+(sbpr*y)), dbpr);
1079 }
1080 }
1081
1082 //set the family data to the Handle
1083 OSStatus set = SetIconFamilyData(iconFamily, images[i].mac_type, hdl);
1084 if(set != noErr)
1085 qWarning("%s: %d -- Unable to create icon data[%d]!! %ld",
1086 __FILE__, __LINE__, i, long(set));
1087 DisposeHandle(hdl);
1088 }
1089 }
1090
1091 //acquire and cleanup
1092 IconRef ret;
1093 static int counter = 0;
1094 const OSType kQtCreator = 'CUTE';
1095 RegisterIconRefFromIconFamily(kQtCreator, (OSType)counter, iconFamily, &ret);
1096 AcquireIconRef(ret);
1097 UnregisterIconRef(kQtCreator, (OSType)counter);
1098 DisposeHandle(reinterpret_cast<Handle>(iconFamily));
1099 counter++;
1100 return ret;
1101
1102}
1103#endif
1104
1105/*! \internal */
1106QPaintEngine* QMacPixmapData::paintEngine() const
1107{
1108 if (!pengine) {
1109 QMacPixmapData *that = const_cast<QMacPixmapData*>(this);
1110 that->pengine = new QCoreGraphicsPaintEngine();
1111 }
1112 return pengine;
1113}
1114
1115void QMacPixmapData::copy(const QPixmapData *data, const QRect &rect)
1116{
1117 if (data->pixelType() == BitmapType) {
1118 QBitmap::fromImage(toImage().copy(rect));
1119 return;
1120 }
1121
1122 const QMacPixmapData *macData = static_cast<const QMacPixmapData*>(data);
1123
1124 resize(rect.width(), rect.height());
1125
1126 has_alpha = macData->has_alpha;
1127 has_mask = macData->has_mask;
1128 uninit = false;
1129
1130 const int x = rect.x();
1131 const int y = rect.y();
1132 char *dest = reinterpret_cast<char*>(pixels);
1133 const char *src = reinterpret_cast<const char*>(macData->pixels + x) + y * macData->bytesPerRow;
1134 for (int i = 0; i < h; ++i) {
1135 memcpy(dest, src, w * 4);
1136 dest += bytesPerRow;
1137 src += macData->bytesPerRow;
1138 }
1139
1140 has_alpha = macData->has_alpha;
1141 has_mask = macData->has_mask;
1142}
1143
1144bool QMacPixmapData::scroll(int dx, int dy, const QRect &rect)
1145{
1146 Q_UNUSED(dx);
1147 Q_UNUSED(dy);
1148 Q_UNUSED(rect);
1149 return false;
1150}
1151
1152/*!
1153 \since 4.2
1154
1155 Creates a \c CGImageRef equivalent to the QPixmap. Returns the \c CGImageRef handle.
1156
1157 It is the caller's responsibility to release the \c CGImageRef data
1158 after use.
1159
1160 \warning This function is only available on Mac OS X.
1161
1162 \sa fromMacCGImageRef()
1163*/
1164CGImageRef QPixmap::toMacCGImageRef() const
1165{
1166 return (CGImageRef)macCGHandle();
1167}
1168
1169/*!
1170 \since 4.2
1171
1172 Returns a QPixmap that is equivalent to the given \a image.
1173
1174 \warning This function is only available on Mac OS X.
1175
1176 \sa toMacCGImageRef(), {QPixmap#Pixmap Conversion}{Pixmap Conversion}
1177*/
1178QPixmap QPixmap::fromMacCGImageRef(CGImageRef image)
1179{
1180 const size_t w = CGImageGetWidth(image),
1181 h = CGImageGetHeight(image);
1182 QPixmap ret(w, h);
1183 ret.fill(Qt::transparent);
1184 CGRect rect = CGRectMake(0, 0, w, h);
1185 CGContextRef ctx = qt_mac_cg_context(&ret);
1186 qt_mac_drawCGImage(ctx, &rect, image);
1187 CGContextRelease(ctx);
1188 return ret;
1189}
1190
1191QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.