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

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

OS/2: Fixed creating HPOINTERs from QPixmaps with no mask.

HPOINTERs are used to assign the icon to the top-level window and
to create custom PM mouse pointer shapes. Prior to to this fix it was
impossible to e.g. set a PNG with no transparency as the window icon.

Author: Rudi Ihle.

File size: 19.0 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
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 qt-info@nokia.com.
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 \a mask is not NULL but the pixmap does neither have a mask nor
111 the alpha channel, an emptpy bitmap mask with no transparency (zeroed AND
112 and XOR parts) will be created and returned.
113
114 It is the caller's responsibility to free both returned \c HBITMAP handes
115 after use.
116
117 \warning This function is only available on OS/2.
118
119 \sa fromPmHBITMAP(), toPmHPOINTER()
120*/
121HBITMAP QPixmap::toPmHBITMAP(HBITMAP *mask, bool embedRealAlpha) const
122{
123 if (data->classId() != QPixmapData::RasterClass) {
124 QPixmapData *data = new QRasterPixmapData(depth() == 1 ?
125 QPixmapData::BitmapType :
126 QPixmapData::PixmapType);
127 data->fromImage(toImage(), Qt::AutoColor);
128 return QPixmap(data).toPmHBITMAP(mask, embedRealAlpha);
129 }
130
131 QRasterPixmapData* d = static_cast<QRasterPixmapData*>(data.data());
132 int w = d->image.width();
133 int h = d->image.height();
134
135 HPS hps = qt_alloc_mem_ps(w, h * 2);
136 if (hps == NULLHANDLE)
137 return NULLHANDLE;
138
139 HBITMAP hbm = NULLHANDLE;
140 HBITMAP hbmMask = NULLHANDLE;
141
142 // Note that we always use ARGB32 even if embedRealAlpha is false because
143 // in this case we will use the alpha channel to dither the 1bpp mask
144 QImage image = d->image.convertToFormat(QImage::Format_ARGB32);
145 // flip the bitmap top to bottom for PM
146 image = image.mirrored();
147
148 // bitmap header + 2 palette entries (for the mask)
149 char bmi[sizeof(BITMAPINFOHEADER2) + 4 * 2];
150 memset(bmi, 0, sizeof(bmi));
151 PBITMAPINFOHEADER2 bmh = (PBITMAPINFOHEADER2)bmi;
152 bmh->cbFix = sizeof(BITMAPINFOHEADER2);
153 PULONG pal = (PULONG)(bmi + sizeof(BITMAPINFOHEADER2));
154
155 // create the normal bitmap from the pixmap data
156 bmh->cx = w;
157 bmh->cy = h;
158 bmh->cPlanes = 1;
159 bmh->cBitCount = 32;
160 hbm = GpiCreateBitmap(hps, bmh, CBM_INIT, (PBYTE)(const uchar *)image.bits(),
161 (PBITMAPINFO2)&bmi);
162
163 if (mask) {
164 // get the mask
165 QImage mask;
166 if (hasAlpha()) {
167 if (!embedRealAlpha) {
168 // We prefer QImage::createAlphaMask() over QPixmap::mask()
169 // since the former will dither while the latter will convert any
170 // non-zero alpha value to an opaque pixel
171 mask = image.createAlphaMask().convertToFormat(QImage::Format_Mono);
172
173 // note: for some strange reason, createAlphaMask() (as opposed to
174 // mask().toImage()) returns an image already flipped top to bottom,
175 // so take it into account
176
177 // create the AND mask
178 mask.invertPixels();
179 // add the XOR mask (and leave it zeroed)
180 mask = mask.copy(0, -h, w, h * 2);
181 } else {
182 // if we embedded real alpha, we still need a mask if we are going
183 // to create a pointer out of this pixmap (WinCreatePointerIndirect()
184 // requirement), but we will use QPixmap::mask() because it won't be
185 // able to destroy the alpha channel of non-fully transparent pixels
186 // when preparing the color bitmap for masking later. We could also
187 // skip this prepare step, but well, let's go this way, it won't hurt.
188 mask = this->mask().toImage().convertToFormat(QImage::Format_Mono);
189
190 // create the AND mask
191 mask.invertPixels();
192 // add the XOR mask (and leave it zeroed)
193 mask = mask.copy(0, 0, w, h * 2);
194 // flip the bitmap top to bottom for PM
195 mask = mask.mirrored(false, true);
196 }
197 } else {
198 mask = QImage(w, h * 2, QImage::Format_Mono);
199 mask.fill(0);
200 }
201
202 // create the mask bitmap
203 bmh->cbFix = sizeof(BITMAPINFOHEADER2);
204 bmh->cx = w;
205 bmh->cy = h * 2;
206 bmh->cPlanes = 1;
207 bmh->cBitCount = 1;
208 bmh->cclrUsed = 2;
209 pal[0] = 0;
210 pal[1] = 0x00FFFFFF;
211 hbmMask = GpiCreateBitmap(hps, bmh, CBM_INIT,
212 (PBYTE)(const uchar *)mask.bits(),
213 (PBITMAPINFO2)&bmi);
214
215 // prepare the bitmap for masking by setting transparent pixels to black
216 GpiSetBitmap(hps, hbm);
217
218 POINTL ptls[] = {
219 { 0, 0 }, { w - 1, h - 1 }, // dst: inclusive-inclusive
220 { 0, h }, { w, h * 2 }, // src: inclusive-exclusive
221 };
222 ptls[0].y -= h;
223 ptls[1].y -= h;
224 enum { AllImageAttrs = IBB_COLOR | IBB_BACK_COLOR |
225 IBB_MIX_MODE | IBB_BACK_MIX_MODE };
226 IMAGEBUNDLE ib = { CLR_TRUE, CLR_FALSE, FM_OVERPAINT, BM_OVERPAINT };
227 GpiSetAttrs(hps, PRIM_IMAGE, AllImageAttrs, 0, (PBUNDLE)&ib);
228 GpiDrawBits(hps, (PBYTE)(const uchar *)mask.bits(), (PBITMAPINFO2)&bmi,
229 4, ptls, ROP_SRCAND, BBO_IGNORE);
230 }
231
232 qt_free_mem_ps(hps);
233
234 if (mask)
235 *mask = hbmMask;
236
237 return hbm;
238}
239
240/*!
241 Returns a QPixmap that is equivalent to the bitmap given in \a hbm. If \a
242 hbmMask is not NULLHANDLE, it should contain vertically adjacent AND and XOR
243 masks for the given bitmap which will be used to create a mask for the
244 returned QPixmap.
245
246 Note that this method will attempt to auto-detect the presence of the real
247 alpha chennel in the high 8 bits of the 32-bit pixel value for each pixel if
248 \a hbm has 1 plane and the 32-bit depth. This alpha channel will be used to
249 create an alpha channel for the returned QPixmap.
250
251 \warning This function is only available on OS/2.
252
253 \sa toPmHBITMAP(), {QPixmap#Pixmap Conversion}{Pixmap Conversion}
254
255*/
256// static
257QPixmap QPixmap::fromPmHBITMAP(HBITMAP hbm, HBITMAP hbmMask)
258{
259 QPixmap res;
260
261 if (hbm == NULLHANDLE)
262 return res;
263
264 // bitmap header + 2 palette entries (for the monochrome bitmap)
265 char bmi[sizeof(BITMAPINFOHEADER2) + 4 * 2];
266 memset(bmi, 0, sizeof(bmi));
267 PBITMAPINFOHEADER2 bmh = (PBITMAPINFOHEADER2)bmi;
268 bmh->cbFix = sizeof(BITMAPINFOHEADER2);
269 PULONG pal = (PULONG)(bmi + sizeof(BITMAPINFOHEADER2));
270
271 if (!GpiQueryBitmapInfoHeader(hbm, bmh))
272 return res;
273
274 HPS hps = qt_alloc_mem_ps(bmh->cx, bmh->cy * 2);
275 if (hps == NULLHANDLE)
276 return res;
277
278 GpiSetBitmap(hps, hbm);
279
280 QImage img;
281 bool succeeded = false;
282
283 if (bmh->cPlanes == 1 && bmh->cBitCount == 1) {
284 // monochrome bitmap
285 img = QImage(bmh->cx, bmh->cy, QImage::Format_Mono);
286 if (GpiQueryBitmapBits(hps, 0, img.height(), (PBYTE)img.bits(),
287 (PBITMAPINFO2)&bmi) != GPI_ALTERROR) {
288 succeeded = true;
289 // take the palette
290 QVector<QRgb> colors(2);
291 colors[0] = QRgb(pal[0]);
292 colors[1] = QRgb(pal[1]);
293 img.setColorTable(colors);
294 }
295 } else {
296 // always convert to 32-bit otherwise
297 img = QImage(bmh->cx, bmh->cy, QImage::Format_RGB32);
298 bmh->cPlanes = 1;
299 bmh->cBitCount = 32;
300 if (GpiQueryBitmapBits(hps, 0, img.height(), (PBYTE)img.bits(),
301 (PBITMAPINFO2)&bmi) != GPI_ALTERROR) {
302 succeeded = true;
303 // try to auto-detect if there is a real alpha channel
304 bool allZero = true;
305 for (int i = 0; i < img.numBytes(); ++i) {
306 if (img.bits()[i] & 0xFF000000) {
307 allZero = false;
308 break;
309 }
310 }
311 if (!allZero) {
312 // assume we've got the alpha channel
313 QImage alphaImg = QImage(bmh->cx, bmh->cy, QImage::Format_ARGB32);
314 memcpy(alphaImg.bits(), img.bits(), img.numBytes());
315 img = alphaImg;
316 }
317 // flip the bitmap top to bottom to cancel PM inversion
318 img = img.mirrored();
319 }
320 }
321
322 QImage mask;
323
324 if (hbmMask != NULLHANDLE && GpiQueryBitmapInfoHeader(hbmMask, bmh)) {
325 // get the AND+XOR mask
326 if ((int)bmh->cx == img.width() &&
327 (int)bmh->cy == img.height() * 2 &&
328 bmh->cPlanes == 1 && bmh->cBitCount == 1) {
329 GpiSetBitmap(hps, hbmMask);
330 mask = QImage(bmh->cx, bmh->cy, QImage::Format_Mono);
331 if (GpiQueryBitmapBits(hps, 0, mask.height(), (PBYTE)mask.bits(),
332 (PBITMAPINFO2)&bmi) != GPI_ALTERROR) {
333 // take the palette
334 QVector<QRgb> colors(2);
335 colors[0] = QRgb(pal[0]);
336 colors[1] = QRgb(pal[1]);
337 mask.setColorTable(colors);
338 // flip the bitmap top to bottom to cancel PM inversion
339 mask = mask.mirrored(false, true);
340 // drop the XOR mask
341 mask = mask.copy(0, 0, mask.width(), mask.height() / 2);
342 // create a normal mask from the AND mask
343 mask.invertPixels();
344 } else {
345 mask = QImage();
346 }
347 GpiSetBitmap(hps, NULLHANDLE);
348 } else {
349 Q_ASSERT(false);
350 }
351 }
352
353 qt_free_mem_ps(hps);
354
355 if (succeeded) {
356 res = QPixmap::fromImage(img);
357 if (!mask.isNull())
358 res.setMask(QBitmap::fromImage(mask));
359 }
360
361 return res;
362}
363
364/*!
365 Creates a \c HPOINTER from the given \a icon. Returns the \c HPOINTER
366 handle.
367
368 If \a isPointer is \c true, an icon size closest to the system pointer size
369 is chosen, otherwise to the system icon size. \a hotX and \a hotY define the
370 hot spot. Note is that the size of the resulting pointer will exactly match
371 the system size no matter what size the matched icon is. Smaller icons will
372 be centered in a box corresponding to the system size, larger icons will
373 be scaled down.
374
375 If \a embedRealAlpha is \c true, the color bitmap in the pointer will have
376 the alpha channel embedded in it (see toPmHBITMAP() for details).
377
378 Note that due to the bug in WinCreatePointerIndirect(), hbmMiniPointer and
379 hbmMiniColor field of the POINTERINFO structure are always ignored. For this
380 reason, the caller must choose what icon size (normal or half-size) he wants
381 to get using the \a isMini argument. A bitmap of the respective size will be
382 created and assigned to the hbmColor field.
383
384 It is the caller's responsibility to free the \c HPOINTER data
385 after use.
386
387 \note \a isMini is ignored when \a isPointer is \c true.
388
389 \warning This function is only available on OS/2.
390
391 \sa toPmHBITMAP()
392*/
393// static
394HPOINTER QPixmap::toPmHPOINTER(const QIcon &icon, bool isPointer,
395 int hotX, int hotY, bool embedRealAlpha,
396 bool isMini)
397{
398 if (icon.isNull())
399 return NULLHANDLE;
400
401 // get the system icon size
402 int w = WinQuerySysValue(HWND_DESKTOP, isPointer ? SV_CXPOINTER : SV_CXICON);
403 int h = WinQuerySysValue(HWND_DESKTOP, isPointer ? SV_CYPOINTER : SV_CYICON);
404 if (!isPointer && isMini) {
405 w = w / 2;
406 h = h / 2;
407 }
408
409 // obtain the closest (but never larger) icon size we have
410 QSize size = icon.actualSize(QSize(w, h));
411
412 QPixmap pm = icon.pixmap(size);
413 if (pm.isNull())
414 return NULLHANDLE;
415
416 // if we got a smaller pixmap then center it inside the box matching the
417 // system size instead of letting WinCreatePointerIndirect() scale (this
418 // covers a usual case when we get 32/16 px pixmaps on a 120 DPI system
419 // where the icon size is 40/20 px respectively): scaling such small images
420 // looks really ugly.
421 if (!pm.isNull() && (pm.width() < w || pm.height() < h)) {
422 Q_ASSERT(pm.width() <= w && pm.height() <= h);
423 QPixmap pmNew(w, h);
424 pmNew.fill(Qt::transparent);
425 QPainter painter(&pmNew);
426 int dx = (w - pm.width()) / 2;
427 int dy = (h - pm.height()) / 2;
428 painter.drawPixmap(dx, dy, pm);
429 pm = pmNew;
430 hotX += dx;
431 hotY += dy;
432 }
433
434 POINTERINFO info;
435 info.fPointer = isPointer;
436 info.xHotspot = hotX;
437 info.yHotspot = pm.height() - hotY - 1;
438 info.hbmColor = pm.toPmHBITMAP(&info.hbmPointer, embedRealAlpha);
439 info.hbmMiniPointer = NULLHANDLE;
440 info.hbmMiniColor = NULLHANDLE;
441
442 HPOINTER hIcon = WinCreatePointerIndirect(HWND_DESKTOP, &info);
443
444 GpiDeleteBitmap(info.hbmPointer);
445 GpiDeleteBitmap(info.hbmColor);
446
447 return hIcon;
448}
449
450/*!
451 Returns a QIcon that is equivalent to the pointer given in \a hpointer.
452 Optionally returns pixmaps used to comprise the icon in \a pixmap and
453 \a pixmapMini.
454
455 Note that this method will attempt to auto-detect the presence of the real
456 alpha chennel in the high 8 bits of the 32-bit pixel value for each pixel if
457 the bitmaps in \a hpointer have 1 plane and the 32-bit depth. This alpha
458 channel will be used to create an alpha channel for the pixmaps comprising
459 the icon.
460
461 \warning This function is only available on OS/2.
462
463 \sa toPmHPOINTER(), {QPixmap#Pixmap Conversion}{Pixmap Conversion}
464
465*/
466// static
467QIcon QPixmap::fromPmHPOINTER(HPOINTER hpointer, QPixmap *pixmap,
468 QPixmap *pixmapMini)
469{
470 QIcon res;
471
472 if (hpointer == NULLHANDLE)
473 return res;
474
475 POINTERINFO info = { 0 };
476 if (!WinQueryPointerInfo(hpointer, &info))
477 return res;
478
479 QPixmap pm = fromPmHBITMAP(info.hbmColor, info.hbmPointer);
480 if (!pm.isNull())
481 res.addPixmap(pm);
482
483 QPixmap pmMini = fromPmHBITMAP(info.hbmMiniColor, info.hbmMiniPointer);
484 if (!pmMini.isNull())
485 res.addPixmap(pmMini);
486
487 if (pixmap)
488 *pixmap = pm;
489 if (pixmapMini)
490 *pixmapMini = pmMini;
491
492 return res;
493}
494
495QPixmap QPixmap::grabWindow(WId winId, int x, int y, int w, int h)
496{
497 QPixmap pm;
498
499 if (w == 0 || h == 0)
500 return pm;
501
502 RECTL rcl;
503 if (!WinQueryWindowRect(winId, &rcl))
504 return pm;
505
506 if (w < 0)
507 w = rcl.xRight;
508 if (h < 0)
509 h = rcl.yTop;
510
511 // flip y coordinate
512 y = rcl.yTop - (y + h);
513
514 HPS hps = qt_alloc_mem_ps(w, h);
515 if (hps == NULLHANDLE)
516 return pm;
517
518 HBITMAP hbm = NULLHANDLE;
519
520 // bitmap header + 2 palette entries (for the mask)
521 BITMAPINFOHEADER2 bmh;
522 bmh.cbFix = sizeof(BITMAPINFOHEADER2);
523
524 // create the uninitialized bitmap to hold window pixels
525 bmh.cx = w;
526 bmh.cy = h;
527 bmh.cPlanes = 1;
528 bmh.cBitCount = 32;
529 hbm = GpiCreateBitmap(hps, &bmh, 0, 0, 0);
530
531 if (hbm != NULLHANDLE) {
532 GpiSetBitmap(hps, hbm);
533 HPS hpsWin = WinGetPS(winId);
534 if (hpsWin != NULLHANDLE) {
535 POINTL pnts[] = { {0, 0}, {w, h}, {x, y} };
536 if (GpiBitBlt(hps, hpsWin, 3, pnts,
537 ROP_SRCCOPY, BBO_IGNORE) != GPI_ERROR) {
538 GpiSetBitmap(hps, NULLHANDLE);
539 pm = fromPmHBITMAP(hbm);
540 }
541 WinReleasePS(hpsWin);
542 }
543 GpiDeleteBitmap(hbm);
544 }
545
546 qt_free_mem_ps(hps);
547
548 return pm;
549}
550
551QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.