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

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

global: Updated year to 2010 in OS/2-specific headers.

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