source: trunk/src/gui/image/qpixmap_pm.cpp@ 940

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

OS/2: Fixed assertion in QPixmap::setMask().

The mask size could be different due to incorrect error
handling in QPixmap::fromPmHBITMAP(). It was also
possible that a valid mask HBITMAP were incorrectly
processed. That would result into a QPixmap without a
mask.

File size: 18.7 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** Copyright (C) 2010 netlabs.org. OS/2 parts.
8**
9** This file is part of the QtGui module of the Qt Toolkit.
10**
11** $QT_BEGIN_LICENSE:LGPL$
12** Commercial Usage
13** Licensees holding valid Qt Commercial licenses may use this file in
14** accordance with the Qt Commercial License Agreement provided with the
15** Software or, alternatively, in accordance with the terms contained in
16** a written agreement between you and Nokia.
17**
18** GNU Lesser General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU Lesser
20** General Public License version 2.1 as published by the Free Software
21** Foundation and appearing in the file LICENSE.LGPL included in the
22** packaging of this file. Please review the following information to
23** ensure the GNU Lesser General Public License version 2.1 requirements
24** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25**
26** In addition, as a special exception, Nokia gives you certain additional
27** rights. These rights are described in the Nokia Qt LGPL Exception
28** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29**
30** GNU General Public License Usage
31** Alternatively, this file may be used under the terms of the GNU
32** General Public License version 3.0 as published by the Free Software
33** Foundation and appearing in the file LICENSE.GPL included in the
34** packaging of this file. Please review the following information to
35** ensure the GNU General Public License version 3.0 requirements will be
36** met: http://www.gnu.org/copyleft/gpl.html.
37**
38** If you have questions regarding the use of this file, please contact
39** Nokia at [email protected].
40** $QT_END_LICENSE$
41**
42****************************************************************************/
43
44#include "qpixmap.h"
45#include "qpixmap_raster_p.h"
46
47#include "qicon.h"
48#include "qbitmap.h"
49#include "qpainter.h"
50
51#include "qt_os2.h"
52
53QT_BEGIN_NAMESPACE
54
55HPS qt_alloc_mem_ps(int w, int h, HPS compat = 0)
56{
57 HDC hdcCompat = NULLHANDLE;
58 if (compat)
59 hdcCompat = GpiQueryDevice(compat);
60
61 static PCSZ hdcData[4] = { "Display", NULL, NULL, NULL };
62 HDC hdc = DevOpenDC(0, OD_MEMORY, "*", 4, (PDEVOPENDATA) hdcData, hdcCompat);
63 if (!hdc) {
64 qWarning( "alloc_mem_dc: DevOpenDC failed with %08lX!", WinGetLastError(0));
65 return NULLHANDLE;
66 }
67 SIZEL size = { w, h };
68 HPS hps = GpiCreatePS(0, hdc, &size, PU_PELS | GPIA_ASSOC | GPIT_MICRO);
69 if (hps == NULLHANDLE) {
70 qWarning("alloc_mem_dc: GpiCreatePS failed wit %08lX!", WinGetLastError(0));
71 return NULLHANDLE;
72 }
73 // @todo later
74// if (QColor::hPal()) {
75// GpiSelectPalette(hps, QColor::hPal());
76// } else {
77 // direct RGB mode
78 GpiCreateLogColorTable(hps, 0, LCOLF_RGB, 0, 0, NULL);
79// }
80 return hps;
81}
82
83void qt_free_mem_ps(HPS hps)
84{
85 HDC hdc = GpiQueryDevice(hps);
86 GpiAssociate(hps, NULLHANDLE);
87 GpiDestroyPS(hps);
88 DevCloseDC(hdc);
89}
90
91/*!
92 Creates a \c HBITMAP equivalent to the QPixmap. Returns the \c HBITMAP
93 handle.
94
95 If \a mask is not NULL, the mask mode is turned on. In this mode, the bitmap
96 mask is also created from the QPixmap's mask and returned in the given
97 variable. This bitmap mask will contain two vertically adjacent sections,
98 the first of which is the AND mask and the second one is the XOR mask
99 (according to WinCreatePointer() specification). Also, in mask mode, the
100 HBITMAP returned for the pixmap itself will be prepared for masking (with
101 transparent pixels made black). This mode is useful for creating system
102 icons and pointers (\sa toPmHPOINTER()).
103
104 if \a embedRealAlpha is \c true, the real alpha chennel (not the 1bpp mask)
105 will be embedded in the high 8 bits of the 32-bit pixel value for each pixel
106 in the created bitmap (which always has 1 plane and the 32-bit depth). This
107 extra information isn't touched by PM/GPI but may be used by custom drawing
108 routines to do real alpha blending.
109
110 Note that if the pixmap does neither have a mask nor the alpha channel but
111 \a mask is not NULL, a NULLHANDLE value will be stored there.
112
113 It is the caller's responsibility to free both returned \c HBITMAP handes
114 after use.
115
116 \warning This function is only available on OS/2.
117
118 \sa fromPmHBITMAP(), toPmHPOINTER()
119*/
120HBITMAP QPixmap::toPmHBITMAP(HBITMAP *mask, bool embedRealAlpha) const
121{
122 if (data->classId() != QPixmapData::RasterClass) {
123 QPixmapData *data = new QRasterPixmapData(depth() == 1 ?
124 QPixmapData::BitmapType :
125 QPixmapData::PixmapType);
126 data->fromImage(toImage(), Qt::AutoColor);
127 return QPixmap(data).toPmHBITMAP(mask, embedRealAlpha);
128 }
129
130 QRasterPixmapData* d = static_cast<QRasterPixmapData*>(data.data());
131 int w = d->image.width();
132 int h = d->image.height();
133
134 HPS hps = qt_alloc_mem_ps(w, h * 2);
135 if (hps == NULLHANDLE)
136 return NULLHANDLE;
137
138 HBITMAP hbm = NULLHANDLE;
139 HBITMAP hbmMask = NULLHANDLE;
140
141 // Note that we always use ARGB32 even if embedRealAlpha is false because
142 // in this case we will use the alpha channel to dither the 1bpp mask
143 QImage image = d->image.convertToFormat(QImage::Format_ARGB32);
144 // flip the bitmap top to bottom for PM
145 image = image.mirrored();
146
147 // bitmap header + 2 palette entries (for the mask)
148 char bmi[sizeof(BITMAPINFOHEADER2) + 4 * 2];
149 memset(bmi, 0, sizeof(bmi));
150 PBITMAPINFOHEADER2 bmh = (PBITMAPINFOHEADER2)bmi;
151 bmh->cbFix = sizeof(BITMAPINFOHEADER2);
152 PULONG pal = (PULONG)(bmi + sizeof(BITMAPINFOHEADER2));
153
154 // create the normal bitmap from the pixmap data
155 bmh->cx = w;
156 bmh->cy = h;
157 bmh->cPlanes = 1;
158 bmh->cBitCount = 32;
159 hbm = GpiCreateBitmap(hps, bmh, CBM_INIT, (PBYTE)(const uchar *)image.bits(),
160 (PBITMAPINFO2)&bmi);
161
162 if (mask && hasAlpha()) {
163 // get the mask
164 QImage mask;
165 if (!embedRealAlpha) {
166 // We prefer QImage::createAlphaMask() over QPixmap::mask()
167 // since the former will dither while the latter will convert any
168 // non-zero alpha value to an opaque pixel
169 mask = image.createAlphaMask().convertToFormat(QImage::Format_Mono);
170
171 // note: for some strange reason, createAlphaMask() (as opposed to
172 // mask().toImage()) returns an image already flipped top to bottom,
173 // so take it into account
174
175 // create the AND mask
176 mask.invertPixels();
177 // add the XOR mask (and leave it zeroed)
178 mask = mask.copy(0, -h, w, h * 2);
179 } else {
180 // if we embedded real alpha, we still need a mask if we are going
181 // to create a pointer out of this pixmap (WinCreatePointerIndirect()
182 // requirement), but we will use QPixmap::mask() because it won't be
183 // able to destroy the alpha channel of non-fully transparent pixels
184 // when preparing the color bitmap for masking later. We could also
185 // skip this prepare step, but well, let's go this way, it won't hurt.
186 mask = this->mask().toImage().convertToFormat(QImage::Format_Mono);
187
188 // create the AND mask
189 mask.invertPixels();
190 // add the XOR mask (and leave it zeroed)
191 mask = mask.copy(0, 0, w, h * 2);
192 // flip the bitmap top to bottom for PM
193 mask = mask.mirrored(false, true);
194 }
195
196 // create the mask bitmap
197 bmh->cbFix = sizeof(BITMAPINFOHEADER2);
198 bmh->cx = w;
199 bmh->cy = h * 2;
200 bmh->cPlanes = 1;
201 bmh->cBitCount = 1;
202 bmh->cclrUsed = 2;
203 pal[0] = 0;
204 pal[1] = 0x00FFFFFF;
205 hbmMask = GpiCreateBitmap(hps, bmh, CBM_INIT,
206 (PBYTE)(const uchar *)mask.bits(),
207 (PBITMAPINFO2)&bmi);
208
209 // prepare the bitmap for masking by setting transparent pixels to black
210 GpiSetBitmap(hps, hbm);
211
212 POINTL ptls[] = {
213 { 0, 0 }, { w - 1, h - 1 }, // dst: inclusive-inclusive
214 { 0, h }, { w, h * 2 }, // src: inclusive-exclusive
215 };
216 ptls[0].y -= h;
217 ptls[1].y -= h;
218 enum { AllImageAttrs = IBB_COLOR | IBB_BACK_COLOR |
219 IBB_MIX_MODE | IBB_BACK_MIX_MODE };
220 IMAGEBUNDLE ib = { CLR_TRUE, CLR_FALSE, FM_OVERPAINT, BM_OVERPAINT };
221 GpiSetAttrs(hps, PRIM_IMAGE, AllImageAttrs, 0, (PBUNDLE)&ib);
222 GpiDrawBits(hps, (PBYTE)(const uchar *)mask.bits(), (PBITMAPINFO2)&bmi,
223 4, ptls, ROP_SRCAND, BBO_IGNORE);
224 }
225
226 qt_free_mem_ps(hps);
227
228 if (mask)
229 *mask = hbmMask;
230
231 return hbm;
232}
233
234/*!
235 Returns a QPixmap that is equivalent to the bitmap given in \a hbm. If \a
236 hbmMask is not NULLHANDLE, it should contain vertically adjacent AND and XOR
237 masks for the given bitmap which will be used to create a mask for the
238 returned QPixmap.
239
240 Note that this method will attempt to auto-detect the presence of the real
241 alpha chennel in the high 8 bits of the 32-bit pixel value for each pixel if
242 \a hbm has 1 plane and the 32-bit depth. This alpha channel will be used to
243 create an alpha channel for the returned QPixmap.
244
245 \warning This function is only available on OS/2.
246
247 \sa toPmHBITMAP(), {QPixmap#Pixmap Conversion}{Pixmap Conversion}
248
249*/
250// static
251QPixmap QPixmap::fromPmHBITMAP(HBITMAP hbm, HBITMAP hbmMask)
252{
253 QPixmap res;
254
255 if (hbm == NULLHANDLE)
256 return res;
257
258 // bitmap header + 2 palette entries (for the monochrome bitmap)
259 char bmi[sizeof(BITMAPINFOHEADER2) + 4 * 2];
260 memset(bmi, 0, sizeof(bmi));
261 PBITMAPINFOHEADER2 bmh = (PBITMAPINFOHEADER2)bmi;
262 bmh->cbFix = sizeof(BITMAPINFOHEADER2);
263 PULONG pal = (PULONG)(bmi + sizeof(BITMAPINFOHEADER2));
264
265 if (!GpiQueryBitmapInfoHeader(hbm, bmh))
266 return res;
267
268 HPS hps = qt_alloc_mem_ps(bmh->cx, bmh->cy * 2);
269 if (hps == NULLHANDLE)
270 return res;
271
272 GpiSetBitmap(hps, hbm);
273
274 QImage img;
275 bool succeeded = false;
276
277 if (bmh->cPlanes == 1 && bmh->cBitCount == 1) {
278 // monochrome bitmap
279 img = QImage(bmh->cx, bmh->cy, QImage::Format_Mono);
280 if (GpiQueryBitmapBits(hps, 0, img.height(), (PBYTE)img.bits(),
281 (PBITMAPINFO2)&bmi) != GPI_ALTERROR) {
282 succeeded = true;
283 // take the palette
284 QVector<QRgb> colors(2);
285 colors[0] = QRgb(pal[0]);
286 colors[1] = QRgb(pal[1]);
287 img.setColorTable(colors);
288 }
289 } else {
290 // always convert to 32-bit otherwise
291 img = QImage(bmh->cx, bmh->cy, QImage::Format_RGB32);
292 bmh->cPlanes = 1;
293 bmh->cBitCount = 32;
294 if (GpiQueryBitmapBits(hps, 0, img.height(), (PBYTE)img.bits(),
295 (PBITMAPINFO2)&bmi) != GPI_ALTERROR) {
296 succeeded = true;
297 // try to auto-detect if there is a real alpha channel
298 bool allZero = true;
299 for (int i = 0; i < img.numBytes(); ++i) {
300 if (img.bits()[i] & 0xFF000000) {
301 allZero = false;
302 break;
303 }
304 }
305 if (!allZero) {
306 // assume we've got the alpha channel
307 QImage alphaImg = QImage(bmh->cx, bmh->cy, QImage::Format_ARGB32);
308 memcpy(alphaImg.bits(), img.bits(), img.numBytes());
309 img = alphaImg;
310 }
311 // flip the bitmap top to bottom to cancel PM inversion
312 img = img.mirrored();
313 }
314 }
315
316 QImage mask;
317
318 if (hbmMask != NULLHANDLE && GpiQueryBitmapInfoHeader(hbmMask, bmh)) {
319 // get the AND+XOR mask
320 if ((int)bmh->cx == img.width() &&
321 (int)bmh->cy == img.height() * 2 &&
322 bmh->cPlanes == 1 && bmh->cBitCount == 1) {
323 GpiSetBitmap(hps, hbmMask);
324 mask = QImage(bmh->cx, bmh->cy, QImage::Format_Mono);
325 if (GpiQueryBitmapBits(hps, 0, mask.height(), (PBYTE)mask.bits(),
326 (PBITMAPINFO2)&bmi) != GPI_ALTERROR) {
327 // take the palette
328 QVector<QRgb> colors(2);
329 colors[0] = QRgb(pal[0]);
330 colors[1] = QRgb(pal[1]);
331 mask.setColorTable(colors);
332 // flip the bitmap top to bottom to cancel PM inversion
333 mask = mask.mirrored(false, true);
334 // drop the XOR mask
335 mask = mask.copy(0, 0, mask.width(), mask.height() / 2);
336 // create a normal mask from the AND mask
337 mask.invertPixels();
338 } else {
339 mask = QImage();
340 }
341 GpiSetBitmap(hps, NULLHANDLE);
342 } else {
343 Q_ASSERT(false);
344 }
345 }
346
347 qt_free_mem_ps(hps);
348
349 if (succeeded) {
350 res = QPixmap::fromImage(img);
351 if (!mask.isNull())
352 res.setMask(QBitmap::fromImage(mask));
353 }
354
355 return res;
356}
357
358/*!
359 Creates a \c HPOINTER from the given \a icon. Returns the \c HPOINTER
360 handle.
361
362 If \a isPointer is \c true, an icon size closest to the system pointer size
363 is chosen, otherwise to the system icon size. \a hotX and \a hotY define the
364 hot spot. Note is that the size of the resulting pointer will exactly match
365 the system size no matter what size the matched icon is. Smaller icons will
366 be centered in a box corresponding to the system size, larger icons will
367 be scaled down.
368
369 If \a embedRealAlpha is \c true, the color bitmap in the pointer will have
370 the alpha channel embedded in it (see toPmHBITMAP() for details).
371
372 Note that due to the bug in WinCreatePointerIndirect(), hbmMiniPointer and
373 hbmMiniColor field of the POINTERINFO structure are always ignored. For this
374 reason, the caller must choose what icon size (normal or half-size) he wants
375 to get using the \a isMini argument. A bitmap of the respective size will be
376 created and assigned to the hbmColor field.
377
378 It is the caller's responsibility to free the \c HPOINTER data
379 after use.
380
381 \note \a isMini is ignored when \a isPointer is \c true.
382
383 \warning This function is only available on OS/2.
384
385 \sa toPmHBITMAP()
386*/
387// static
388HPOINTER QPixmap::toPmHPOINTER(const QIcon &icon, bool isPointer,
389 int hotX, int hotY, bool embedRealAlpha,
390 bool isMini)
391{
392 if (icon.isNull())
393 return NULLHANDLE;
394
395 // get the system icon size
396 int w = WinQuerySysValue(HWND_DESKTOP, isPointer ? SV_CXPOINTER : SV_CXICON);
397 int h = WinQuerySysValue(HWND_DESKTOP, isPointer ? SV_CYPOINTER : SV_CYICON);
398 if (!isPointer && isMini) {
399 w = w / 2;
400 h = h / 2;
401 }
402
403 // obtain the closest (but never larger) icon size we have
404 QSize size = icon.actualSize(QSize(w, h));
405
406 QPixmap pm = icon.pixmap(size);
407 if (pm.isNull())
408 return NULLHANDLE;
409
410 // if we got a smaller pixmap then center it inside the box matching the
411 // system size instead of letting WinCreatePointerIndirect() scale (this
412 // covers a usual case when we get 32/16 px pixmaps on a 120 DPI system
413 // where the icon size is 40/20 px respectively): scaling such small images
414 // looks really ugly.
415 if (!pm.isNull() && (pm.width() < w || pm.height() < h)) {
416 Q_ASSERT(pm.width() <= w && pm.height() <= h);
417 QPixmap pmNew(w, h);
418 pmNew.fill(Qt::transparent);
419 QPainter painter(&pmNew);
420 int dx = (w - pm.width()) / 2;
421 int dy = (h - pm.height()) / 2;
422 painter.drawPixmap(dx, dy, pm);
423 pm = pmNew;
424 hotX += dx;
425 hotY += dy;
426 }
427
428 POINTERINFO info;
429 info.fPointer = isPointer;
430 info.xHotspot = hotX;
431 info.yHotspot = pm.height() - hotY - 1;
432 info.hbmColor = pm.toPmHBITMAP(&info.hbmPointer, embedRealAlpha);
433 info.hbmMiniPointer = NULLHANDLE;
434 info.hbmMiniColor = NULLHANDLE;
435
436 HPOINTER hIcon = WinCreatePointerIndirect(HWND_DESKTOP, &info);
437
438 GpiDeleteBitmap(info.hbmPointer);
439 GpiDeleteBitmap(info.hbmColor);
440
441 return hIcon;
442}
443
444/*!
445 Returns a QIcon that is equivalent to the pointer given in \a hpointer.
446 Optionally returns pixmaps used to comprise the icon in \a pixmap and
447 \a pixmapMini.
448
449 Note that this method will attempt to auto-detect the presence of the real
450 alpha chennel in the high 8 bits of the 32-bit pixel value for each pixel if
451 the bitmaps in \a hpointer have 1 plane and the 32-bit depth. This alpha
452 channel will be used to create an alpha channel for the pixmaps comprising
453 the icon.
454
455 \warning This function is only available on OS/2.
456
457 \sa toPmHPOINTER(), {QPixmap#Pixmap Conversion}{Pixmap Conversion}
458
459*/
460// static
461QIcon QPixmap::fromPmHPOINTER(HPOINTER hpointer, QPixmap *pixmap,
462 QPixmap *pixmapMini)
463{
464 QIcon res;
465
466 if (hpointer == NULLHANDLE)
467 return res;
468
469 POINTERINFO info = { 0 };
470 if (!WinQueryPointerInfo(hpointer, &info))
471 return res;
472
473 QPixmap pm = fromPmHBITMAP(info.hbmColor, info.hbmPointer);
474 if (!pm.isNull())
475 res.addPixmap(pm);
476
477 QPixmap pmMini = fromPmHBITMAP(info.hbmMiniColor, info.hbmMiniPointer);
478 if (!pmMini.isNull())
479 res.addPixmap(pmMini);
480
481 if (pixmap)
482 *pixmap = pm;
483 if (pixmapMini)
484 *pixmapMini = pmMini;
485
486 return res;
487}
488
489QPixmap QPixmap::grabWindow(WId winId, int x, int y, int w, int h)
490{
491 QPixmap pm;
492
493 if (w == 0 || h == 0)
494 return pm;
495
496 RECTL rcl;
497 if (!WinQueryWindowRect(winId, &rcl))
498 return pm;
499
500 if (w < 0)
501 w = rcl.xRight;
502 if (h < 0)
503 h = rcl.yTop;
504
505 // flip y coordinate
506 y = rcl.yTop - (y + h);
507
508 HPS hps = qt_alloc_mem_ps(w, h);
509 if (hps == NULLHANDLE)
510 return pm;
511
512 HBITMAP hbm = NULLHANDLE;
513
514 // bitmap header + 2 palette entries (for the mask)
515 BITMAPINFOHEADER2 bmh;
516 bmh.cbFix = sizeof(BITMAPINFOHEADER2);
517
518 // create the uninitialized bitmap to hold window pixels
519 bmh.cx = w;
520 bmh.cy = h;
521 bmh.cPlanes = 1;
522 bmh.cBitCount = 32;
523 hbm = GpiCreateBitmap(hps, &bmh, 0, 0, 0);
524
525 if (hbm != NULLHANDLE) {
526 GpiSetBitmap(hps, hbm);
527 HPS hpsWin = WinGetPS(winId);
528 if (hpsWin != NULLHANDLE) {
529 POINTL pnts[] = { {0, 0}, {w, h}, {x, y} };
530 if (GpiBitBlt(hps, hpsWin, 3, pnts,
531 ROP_SRCCOPY, BBO_IGNORE) != GPI_ERROR) {
532 GpiSetBitmap(hps, NULLHANDLE);
533 pm = fromPmHBITMAP(hbm);
534 }
535 WinReleasePS(hpsWin);
536 }
537 GpiDeleteBitmap(hbm);
538 }
539
540 qt_free_mem_ps(hps);
541
542 return pm;
543}
544
545QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.