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

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

docs: Fixed warnings and added some missing documentation for OS/2 specific classes.

File size: 18.4 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) {
319 // get the AND+XOR mask
320 GpiSetBitmap(hps, hbmMask);
321 mask = QImage(bmh->cx, bmh->cy * 2, QImage::Format_Mono);
322 bmh->cPlanes = 1;
323 bmh->cBitCount = 1;
324 if (GpiQueryBitmapBits(hps, 0, mask.height(), (PBYTE)mask.bits(),
325 (PBITMAPINFO2)&bmi) != GPI_ALTERROR) {
326 // take the palette
327 QVector<QRgb> colors(2);
328 colors[0] = QRgb(pal[0]);
329 colors[1] = QRgb(pal[1]);
330 mask.setColorTable(colors);
331 // flip the bitmap top to bottom to cancel PM inversion
332 mask = mask.mirrored(false, true);
333 // drop the XOR mask
334 mask = mask.copy(0, 0, mask.width(), mask.height() / 2);
335 // create a normal mask from the AND mask
336 mask.invertPixels();
337 }
338 }
339
340 qt_free_mem_ps(hps);
341
342 if (succeeded) {
343 res = QPixmap::fromImage(img);
344 if (!mask.isNull())
345 res.setMask(QBitmap::fromImage(mask));
346 }
347
348 return res;
349}
350
351/*!
352 Creates a \c HPOINTER from the given \a icon. Returns the \c HPOINTER
353 handle.
354
355 If \a isPointer is \c true, an icon size closest to the system pointer size
356 is chosen, otherwise to the system icon size. \a hotX and \a hotY define the
357 hot spot. Note is that the size of the resulting pointer will exactly match
358 the system size no matter what size the matched icon is. Smaller icons will
359 be centered in a box corresponding to the system size, larger icons will
360 be scaled down.
361
362 If \a embedRealAlpha is \c true, the color bitmap in the pointer will have
363 the alpha channel embedded in it (see toPmHBITMAP() for details).
364
365 Note that due to the bug in WinCreatePointerIndirect(), hbmMiniPointer and
366 hbmMiniColor field of the POINTERINFO structure are always ignored. For this
367 reason, the caller must choose what icon size (normal or half-size) he wants
368 to get using the \a isMini argument. A bitmap of the respective size will be
369 created and assigned to the hbmColor field.
370
371 It is the caller's responsibility to free the \c HPOINTER data
372 after use.
373
374 \note \a isMini is ignored when \a isPointer is \c true.
375
376 \warning This function is only available on OS/2.
377
378 \sa toPmHBITMAP()
379*/
380// static
381HPOINTER QPixmap::toPmHPOINTER(const QIcon &icon, bool isPointer,
382 int hotX, int hotY, bool embedRealAlpha,
383 bool isMini)
384{
385 if (icon.isNull())
386 return NULLHANDLE;
387
388 // get the system icon size
389 int w = WinQuerySysValue(HWND_DESKTOP, isPointer ? SV_CXPOINTER : SV_CXICON);
390 int h = WinQuerySysValue(HWND_DESKTOP, isPointer ? SV_CYPOINTER : SV_CYICON);
391 if (!isPointer && isMini) {
392 w = w / 2;
393 h = h / 2;
394 }
395
396 // obtain the closest (but never larger) icon size we have
397 QSize size = icon.actualSize(QSize(w, h));
398
399 QPixmap pm = icon.pixmap(size);
400 if (pm.isNull())
401 return NULLHANDLE;
402
403 // if we got a smaller pixmap then center it inside the box matching the
404 // system size instead of letting WinCreatePointerIndirect() scale (this
405 // covers a usual case when we get 32/16 px pixmaps on a 120 DPI system
406 // where the icon size is 40/20 px respectively): scaling such small images
407 // looks really ugly.
408 if (!pm.isNull() && (pm.width() < w || pm.height() < h)) {
409 Q_ASSERT(pm.width() <= w && pm.height() <= h);
410 QPixmap pmNew(w, h);
411 pmNew.fill(Qt::transparent);
412 QPainter painter(&pmNew);
413 int dx = (w - pm.width()) / 2;
414 int dy = (h - pm.height()) / 2;
415 painter.drawPixmap(dx, dy, pm);
416 pm = pmNew;
417 hotX += dx;
418 hotY += dy;
419 }
420
421 POINTERINFO info;
422 info.fPointer = isPointer;
423 info.xHotspot = hotX;
424 info.yHotspot = pm.height() - hotY - 1;
425 info.hbmColor = pm.toPmHBITMAP(&info.hbmPointer, embedRealAlpha);
426 info.hbmMiniPointer = NULLHANDLE;
427 info.hbmMiniColor = NULLHANDLE;
428
429 HPOINTER hIcon = WinCreatePointerIndirect(HWND_DESKTOP, &info);
430
431 GpiDeleteBitmap(info.hbmPointer);
432 GpiDeleteBitmap(info.hbmColor);
433
434 return hIcon;
435}
436
437/*!
438 Returns a QIcon that is equivalent to the pointer given in \a hpointer.
439 Optionally returns pixmaps used to comprise the icon in \a pixmap and
440 \a pixmapMini.
441
442 Note that this method will attempt to auto-detect the presence of the real
443 alpha chennel in the high 8 bits of the 32-bit pixel value for each pixel if
444 the bitmaps in \a hpointer have 1 plane and the 32-bit depth. This alpha
445 channel will be used to create an alpha channel for the pixmaps comprising
446 the icon.
447
448 \warning This function is only available on OS/2.
449
450 \sa toPmHPOINTER(), {QPixmap#Pixmap Conversion}{Pixmap Conversion}
451
452*/
453// static
454QIcon QPixmap::fromPmHPOINTER(HPOINTER hpointer, QPixmap *pixmap,
455 QPixmap *pixmapMini)
456{
457 QIcon res;
458
459 if (hpointer == NULLHANDLE)
460 return res;
461
462 POINTERINFO info = { 0 };
463 if (!WinQueryPointerInfo(hpointer, &info))
464 return res;
465
466 QPixmap pm = fromPmHBITMAP(info.hbmColor, info.hbmPointer);
467 if (!pm.isNull())
468 res.addPixmap(pm);
469
470 QPixmap pmMini = fromPmHBITMAP(info.hbmMiniColor, info.hbmMiniPointer);
471 if (!pmMini.isNull())
472 res.addPixmap(pmMini);
473
474 if (pixmap)
475 *pixmap = pm;
476 if (pixmapMini)
477 *pixmapMini = pmMini;
478
479 return res;
480}
481
482QPixmap QPixmap::grabWindow(WId winId, int x, int y, int w, int h)
483{
484 QPixmap pm;
485
486 if (w == 0 || h == 0)
487 return pm;
488
489 RECTL rcl;
490 if (!WinQueryWindowRect(winId, &rcl))
491 return pm;
492
493 if (w < 0)
494 w = rcl.xRight;
495 if (h < 0)
496 h = rcl.yTop;
497
498 // flip y coordinate
499 y = rcl.yTop - (y + h);
500
501 HPS hps = qt_alloc_mem_ps(w, h);
502 if (hps == NULLHANDLE)
503 return pm;
504
505 HBITMAP hbm = NULLHANDLE;
506
507 // bitmap header + 2 palette entries (for the mask)
508 BITMAPINFOHEADER2 bmh;
509 bmh.cbFix = sizeof(BITMAPINFOHEADER2);
510
511 // create the uninitialized bitmap to hold window pixels
512 bmh.cx = w;
513 bmh.cy = h;
514 bmh.cPlanes = 1;
515 bmh.cBitCount = 32;
516 hbm = GpiCreateBitmap(hps, &bmh, 0, 0, 0);
517
518 if (hbm != NULLHANDLE) {
519 GpiSetBitmap(hps, hbm);
520 HPS hpsWin = WinGetPS(winId);
521 if (hpsWin != NULLHANDLE) {
522 POINTL pnts[] = { {0, 0}, {w, h}, {x, y} };
523 if (GpiBitBlt(hps, hpsWin, 3, pnts,
524 ROP_SRCCOPY, BBO_IGNORE) != GPI_ERROR) {
525 GpiSetBitmap(hps, NULLHANDLE);
526 pm = fromPmHBITMAP(hbm);
527 }
528 WinReleasePS(hpsWin);
529 }
530 GpiDeleteBitmap(hbm);
531 }
532
533 qt_free_mem_ps(hps);
534
535 return pm;
536}
537
538QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.