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

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

gui/image: Implemented QPixmap::grabWindow().

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Date Revision Author Id
File size: 15.5 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** Copyright (C) 2009 netlabs.org. OS/2 parts.
7**
8** This file is part of the QtGui module of the Qt Toolkit.
9**
10** $QT_BEGIN_LICENSE:LGPL$
11** Commercial Usage
12** Licensees holding valid Qt Commercial licenses may use this file in
13** accordance with the Qt Commercial License Agreement provided with the
14** Software or, alternatively, in accordance with the terms contained in
15** a written agreement between you and Nokia.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 2.1 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 2.1 requirements
23** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24**
25** In addition, as a special exception, Nokia gives you certain
26** additional rights. These rights are described in the Nokia Qt LGPL
27** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
28** 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 are unsure which license is appropriate for your use, please
39** contact the sales department 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 const PSZ 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. 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);
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 BITMAPINFOHEADER2 &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.
236
237 \warning This function is only available on OS/2.
238
239 \sa toPmHBITMAP(), {QPixmap#Pixmap Conversion}{Pixmap Conversion}
240
241*/
242// static
243QPixmap QPixmap::fromPmHBITMAP(HBITMAP hbm)
244{
245 QPixmap res;
246
247 if (hbm == NULLHANDLE)
248 return res;
249
250 // bitmap header + 2 palette entries (for the monochrome bitmap)
251 char bmi[sizeof(BITMAPINFOHEADER2) + 4 * 2];
252 memset(bmi, 0, sizeof(bmi));
253 BITMAPINFOHEADER2 &bmh = *(PBITMAPINFOHEADER2)bmi;
254 bmh.cbFix = sizeof(BITMAPINFOHEADER2);
255 PULONG pal = (PULONG)(bmi + sizeof(BITMAPINFOHEADER2));
256
257 if (!GpiQueryBitmapInfoHeader(hbm, &bmh))
258 return res;
259
260 HPS hps = qt_alloc_mem_ps(bmh.cx, bmh.cy);
261 if (hps == NULLHANDLE)
262 return res;
263
264 GpiSetBitmap(hps, hbm);
265
266 QImage img;
267 bool succeeded = false;
268
269 if (bmh.cPlanes == 1 && bmh.cBitCount == 1) {
270 // monochrome bitmap
271 img = QImage(bmh.cx, bmh.cy, QImage::Format_Mono);
272 if (GpiQueryBitmapBits(hps, 0, bmh.cy, (PBYTE)img.bits(),
273 (PBITMAPINFO2)&bmi) != GPI_ALTERROR) {
274 succeeded = true;
275 // take the palette
276 QVector<QRgb> colors(2);
277 colors[0] = QRgb(pal[0]);
278 colors[1] = QRgb(pal[1]);
279 }
280 } else {
281 // always convert to 32-bit otherwise
282 img = QImage(bmh.cx, bmh.cy, QImage::Format_RGB32);
283 bmh.cPlanes = 1;
284 bmh.cBitCount = 32;
285 if (GpiQueryBitmapBits(hps, 0, bmh.cy, (PBYTE)img.bits(),
286 (PBITMAPINFO2)&bmi) != GPI_ALTERROR) {
287 succeeded = true;
288 // try to auto-detect if there is a real alpha channel
289 bool allZero = true;
290 for (int i = 0; i < img.numBytes(); ++i) {
291 if (img.bits()[i] & 0xFF000000) {
292 allZero = false;
293 break;
294 }
295 }
296 if (!allZero) {
297 // assume we've got the alpha channel
298 QImage alphaImg = QImage(bmh.cx, bmh.cy, QImage::Format_ARGB32);
299 memcpy(alphaImg.bits(), img.bits(), img.numBytes());
300 img = alphaImg;
301 }
302 }
303 }
304
305 qt_free_mem_ps(hps);
306
307 if (succeeded) {
308 // flip the bitmap top to bottom to cancel PM inversion
309 img = img.mirrored();
310 res = QPixmap::fromImage(img);
311 }
312
313 return res;
314}
315
316/*!
317 Creates a \c HPOINTER from the given QIcon. Returns the \c HPOINTER handle.
318
319 If \a isPointer is \c true, an icon size closest to the system pointer size
320 is chosen, otherwise to the system icon size. \a hotX and \a hotY define the
321 hot spot. Note is that the size of the resulting pointer will exactly match
322 the system size no matter what size the matched icon is. Smaller icons will
323 be centered in a box corresponding to the system size, larger icons will
324 be scaled down.
325
326 If \a embedRealAlpha is \c true, the color bitmap in the pointer will have
327 the alpha channel embedded in it (see toPmHBITMAP() for details).
328
329 Note that due to the bug in WinCreatePointerIndirect(), hbmMiniPointer and
330 hbmMiniColor field of the POINTERINFO structure are always ignored. For this
331 reason, the caller must choose what icon size (normal or half-size) he wants
332 to get using the \a isMini argument. A bitmap of the respective size will be
333 created and assigned to the hbmColor field.
334
335 It is the caller's responsibility to free the \c HPOINTER data
336 after use.
337
338 \warning This function is only available on OS/2.
339
340 \sa toPmHBITMAP()
341*/
342// static
343HPOINTER QPixmap::toPmHPOINTER(const QIcon &icon, bool isPointer,
344 int hotX, int hotY, bool embedRealAlpha,
345 bool isMini)
346{
347 if (icon.isNull())
348 return NULLHANDLE;
349
350 // get the system icon size
351 int w = WinQuerySysValue(HWND_DESKTOP, isPointer ? SV_CXPOINTER : SV_CXICON);
352 int h = WinQuerySysValue(HWND_DESKTOP, isPointer ? SV_CYPOINTER : SV_CYICON);
353 if (isMini) {
354 w = w / 2;
355 h = h / 2;
356 }
357
358 // obtain the closest (but never larger) icon size we have
359 QSize size = icon.actualSize(QSize(w, h));
360
361 QPixmap pm = icon.pixmap(size);
362 if (pm.isNull())
363 return NULLHANDLE;
364
365 // if we got a smaller pixmap then center it inside the box matching the
366 // system size instead of letting WinCreatePointerIndirect() scale (this
367 // covers a usual case when we get 32/16 px pixmaps on a 120 DPI system
368 // where the icon size is 40/20 px respectively): scaling such small images
369 // looks really ugly.
370 if (!pm.isNull() && (pm.width() < w || pm.height() < h)) {
371 Q_ASSERT(pm.width() <= w && pm.height() <= h);
372 QPixmap pmNew(w, h);
373 pmNew.fill(Qt::transparent);
374 QPainter painter(&pmNew);
375 painter.drawPixmap((w - pm.width()) / 2, (h - pm.height()) / 2, pm);
376 pm = pmNew;
377 }
378
379 POINTERINFO info;
380 info.fPointer = isPointer;
381 info.xHotspot = hotX;
382 info.yHotspot = hotY;
383 info.hbmColor = pm.toPmHBITMAP(&info.hbmPointer, embedRealAlpha);
384 info.hbmMiniPointer = NULLHANDLE;
385 info.hbmMiniColor = NULLHANDLE;
386
387 HPOINTER hIcon = WinCreatePointerIndirect(HWND_DESKTOP, &info);
388
389 GpiDeleteBitmap(info.hbmPointer);
390 GpiDeleteBitmap(info.hbmColor);
391
392 return hIcon;
393}
394
395QPixmap QPixmap::grabWindow(WId winId, int x, int y, int w, int h)
396{
397 QPixmap pm;
398
399 if (w == 0 || h == 0)
400 return pm;
401
402 RECTL rcl;
403 if (!WinQueryWindowRect(winId, &rcl))
404 return pm;
405
406 if (w < 0)
407 w = rcl.xRight;
408 if (h < 0)
409 h = rcl.yTop;
410
411 // flip y coordinate
412 y = rcl.yTop - (y + h);
413
414 HPS hps = qt_alloc_mem_ps(w, h);
415 if (hps == NULLHANDLE)
416 return pm;
417
418 HBITMAP hbm = NULLHANDLE;
419
420 // bitmap header + 2 palette entries (for the mask)
421 BITMAPINFOHEADER2 bmh;
422 bmh.cbFix = sizeof(BITMAPINFOHEADER2);
423
424 // create the uninitialized bitmap to hold window pixels
425 bmh.cx = w;
426 bmh.cy = h;
427 bmh.cPlanes = 1;
428 bmh.cBitCount = 32;
429 hbm = GpiCreateBitmap(hps, &bmh, 0, 0, 0);
430
431 if (hbm != NULLHANDLE) {
432 GpiSetBitmap(hps, hbm);
433 HPS hpsWin = WinGetPS(winId);
434 if (hpsWin != NULLHANDLE) {
435 POINTL pnts[] = { {0, 0}, {w, h}, {x, y} };
436 if (GpiBitBlt(hps, hpsWin, 3, pnts,
437 ROP_SRCCOPY, BBO_IGNORE) != GPI_ERROR) {
438 GpiSetBitmap(hps, NULLHANDLE);
439 pm = fromPmHBITMAP(hbm);
440 }
441 WinReleasePS(hpsWin);
442 }
443 GpiDeleteBitmap(hbm);
444 }
445
446 qt_free_mem_ps(hps);
447
448 return pm;
449}
450
451QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.