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

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

gui: QPixmap: Enhanced fromPmHBITMAO, added fromPmHPOINTER().

File size: 18.4 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** Copyright (C) 2009 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);