source: trunk/src/gui/painting/qregion_win.cpp@ 477

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

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 14.3 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qbitmap.h"
43#include "qbuffer.h"
44#include "qimage.h"
45#include "qpolygon.h"
46#include "qregion.h"
47#include "qt_windows.h"
48
49QT_BEGIN_NAMESPACE
50
51
52/*
53 In Windows versions before Windows Vista CreateRectRgn - when called in a multi-threaded
54 environment - might return an invalid handle. This function works around this limitation
55 by verifying the handle with a quick GetRegionData() call and re-creates the region
56 if necessary.
57*/
58HRGN qt_tryCreateRegion(QRegion::RegionType type, int left, int top, int right, int bottom)
59{
60 const int tries = 10;
61 for (int i = 0; i < tries; ++i) {
62 HRGN region;
63 switch (type) {
64 case QRegion::Rectangle:
65 region = CreateRectRgn(left, top, right, bottom);
66 break;
67 case QRegion::Ellipse:
68#ifndef Q_OS_WINCE
69 region = CreateEllipticRgn(left, top, right, bottom);
70#endif
71 break;
72 }
73 if (region) {
74 if (GetRegionData(region, 0, 0))
75 return region;
76 else
77 DeleteObject(region);
78 }
79 }
80 return 0;
81}
82
83#ifndef Q_OS_WINCE
84HRGN qt_tryCreatePolygonRegion(const QPolygon &a, Qt::FillRule fillRule)
85{
86 const int tries = 10;
87 for (int i = 0; i < tries; ++i) {
88 HRGN region = CreatePolygonRgn(reinterpret_cast<const POINT*>(a.data()), a.size(),
89 fillRule == Qt::OddEvenFill ? ALTERNATE : WINDING);
90 if (region) {
91 if (GetRegionData(region, 0, 0))
92 return region;
93 else
94 DeleteObject(region);
95 }
96 }
97 return 0;
98}
99#endif
100
101QRegion::QRegionData QRegion::shared_empty = { Q_BASIC_ATOMIC_INITIALIZER(1), 0 };
102
103QRegion::QRegion()
104 : d(&shared_empty)
105{
106 d->ref.ref();
107}
108
109#ifndef Q_OS_WINCE //implementation for WindowsCE in qregion_wce.cpp
110QRegion::QRegion(const QRect &r, RegionType t)
111{
112 if (r.isEmpty()) {
113 d = &shared_empty;
114 d->ref.ref();
115 } else {
116 d = new QRegionData;
117 d->ref = 1;
118 if (t == Rectangle)
119 d->rgn = qt_tryCreateRegion(t, r.left(), r.top(), r.x() + r.width(), r.y() + r.height());
120 else if (t == Ellipse) {
121 // need to add 1 to width/height for the ellipse to have correct boundingrect.
122 d->rgn = qt_tryCreateRegion(t, r.x(), r.y(), r.x() + r.width() + 1, r.y() + r.height() + 1);
123 }
124 }
125}
126#endif
127
128#ifndef Q_OS_WINCE //implementation for WindowsCE in qregion_wce.cpp
129QRegion::QRegion(const QPolygon &a, Qt::FillRule fillRule)
130{
131 if (a.size() < 3) {
132 d = &shared_empty;
133 d->ref.ref();
134 } else {
135 d = new QRegionData;
136 d->ref = 1;
137 d->rgn = qt_tryCreatePolygonRegion(a, fillRule);
138 }
139}
140#endif
141
142QRegion::QRegion(const QRegion &r)
143{
144 d = r.d;
145 d->ref.ref();
146}
147
148HRGN qt_win_bitmapToRegion(const QBitmap& bitmap)
149{
150 HRGN region=0;
151 QImage image = bitmap.toImage();
152 const int MAXRECT = 256;
153 struct RData {
154 RGNDATAHEADER header;
155 RECT rect[MAXRECT];
156 };
157 RData data;
158
159#define FlushSpans \
160 { \
161 data.header.dwSize = sizeof(RGNDATAHEADER); \
162 data.header.iType = RDH_RECTANGLES; \
163 data.header.nCount = n; \
164 data.header.nRgnSize = 0; \
165 data.header.rcBound.bottom = y; \
166 HRGN r = ExtCreateRegion(0, \
167 sizeof(RGNDATAHEADER)+n*sizeof(RECT),(RGNDATA*)&data); \
168 if (region) { \
169 CombineRgn(region, region, r, RGN_OR); \
170 DeleteObject(r); \
171 } else { \
172 region = r; \
173 } \
174 data.header.rcBound.top = y; \
175 }
176
177#define AddSpan \
178 { \
179 data.rect[n].left=prev1; \
180 data.rect[n].top=y; \
181 data.rect[n].right=x-1+1; \
182 data.rect[n].bottom=y+1; \
183 n++; \
184 if (n == MAXRECT) { \
185 FlushSpans \
186 n=0; \
187 } \
188 }
189
190 data.header.rcBound.top = 0;
191 data.header.rcBound.left = 0;
192 data.header.rcBound.right = image.width()-1;
193 int n = 0;
194
195 int zero = 0x00;
196
197 int x, y;
198 for (y = 0; y < image.height(); ++y) {
199 uchar *line = image.scanLine(y);
200 int w = image.width();
201 uchar all=zero;
202 int prev1 = -1;
203 for (x = 0; x < w;) {
204 uchar byte = line[x/8];
205 if (x > w - 8 || byte != all) {
206 for (int b = 8; b > 0 && x < w; --b) {
207 if (!(byte & 0x01) == !all) {
208 // More of the same
209 } else {
210 // A change.
211 if (all != zero) {
212 AddSpan;
213 all = zero;
214 } else {
215 prev1 = x;
216 all = ~zero;
217 }
218 }
219 byte >>= 1;
220 ++x;
221 }
222 } else {
223 x += 8;
224 }
225 }
226 if (all != zero) {
227 AddSpan;
228 }
229 }
230 if (n) {
231 FlushSpans;
232 }
233
234 if (!region) {
235 // Surely there is some better way.
236 region = qt_tryCreateRegion(QRegion::Rectangle, 0,0,1,1);
237 CombineRgn(region, region, region, RGN_XOR);
238 }
239 return region;
240}
241
242#ifndef Q_OS_WINCE //implementation for WindowsCE in qregion_wce.cpp
243QRegion::QRegion(const QBitmap &bm)
244{
245 if (bm.isNull()) {
246 d = &shared_empty;
247 d->ref.ref();
248 } else {
249 d = new QRegionData;
250 d->ref = 1;
251 d->rgn = qt_win_bitmapToRegion(bm);
252 }
253}
254#endif
255
256void QRegion::cleanUp(QRegion::QRegionData *x)
257{
258 if (x->rgn)
259 DeleteObject(x->rgn);
260 delete x;
261}
262
263QRegion::~QRegion()
264{
265 if (!d->ref.deref())
266 cleanUp(d);
267}
268
269QRegion &QRegion::operator=(const QRegion &r)
270{
271 r.d->ref.ref();
272 if (!d->ref.deref())
273 cleanUp(d);
274 d = r.d;
275 return *this;
276}
277
278
279QRegion QRegion::copy() const
280{
281 QRegion r;
282 QRegionData *x = new QRegionData;
283 x->ref = 1;
284 if (d->rgn) {
285 x->rgn = qt_tryCreateRegion(QRegion::Rectangle, 0, 0, 2, 2);
286 CombineRgn(x->rgn, d->rgn, 0, RGN_COPY);
287 } else {
288 x->rgn = 0;
289 }
290 if (!r.d->ref.deref())
291 cleanUp(r.d);
292 r.d = x;
293 return r;
294}
295
296bool QRegion::isEmpty() const
297{
298 return (d == &shared_empty || boundingRect().isEmpty());
299}
300
301
302bool QRegion::contains(const QPoint &p) const
303{
304 return d->rgn ? PtInRegion(d->rgn, p.x(), p.y()) : false;
305}
306
307bool QRegion::contains(const QRect &r) const
308{
309 if (!d->rgn)
310 return false;
311 RECT rect;
312 SetRect(&rect, r.left(), r.top(), r.right(), r.bottom());
313 return RectInRegion(d->rgn, &rect);
314}
315
316
317void QRegion::translate(int dx, int dy)
318{
319 if (!d->rgn || (dx == 0 && dy == 0))
320 return;
321 detach();
322 OffsetRgn(d->rgn, dx, dy);
323}
324
325
326#define RGN_NOP -1
327
328// Duplicates of those in qregion.cpp
329#define QRGN_OR 6
330#define QRGN_AND 7
331#define QRGN_SUB 8
332#define QRGN_XOR 9
333
334/*
335 Performs the actual OR, AND, SUB and XOR operation between regions.
336 Sets the resulting region handle to 0 to indicate an empty region.
337*/
338
339QRegion QRegion::winCombine(const QRegion &r, int op) const
340{
341 int both=RGN_NOP,
342 left=RGN_NOP,
343 right=RGN_NOP;
344 switch (op) {
345 case QRGN_OR:
346 both = RGN_OR;
347 left = right = RGN_COPY;
348 break;
349 case QRGN_AND:
350 both = RGN_AND;
351 break;
352 case QRGN_SUB:
353 both = RGN_DIFF;
354 left = RGN_COPY;
355 break;
356 case QRGN_XOR:
357 both = RGN_XOR;
358 left = right = RGN_COPY;
359 break;
360 default:
361 qWarning("QRegion: Internal error in winCombine");
362 }
363
364 int allCombineRgnResults = NULLREGION;
365 QRegion result;
366 result.detach();
367 result.d->rgn = qt_tryCreateRegion(QRegion::Rectangle, 0, 0, 0, 0);
368 if (d->rgn && r.d->rgn)
369 allCombineRgnResults = CombineRgn(result.d->rgn, d->rgn, r.d->rgn, both);
370 else if (d->rgn && left != RGN_NOP)
371 allCombineRgnResults = CombineRgn(result.d->rgn, d->rgn, d->rgn, left);
372 else if (r.d->rgn && right != RGN_NOP)
373 allCombineRgnResults = CombineRgn(result.d->rgn, r.d->rgn, r.d->rgn, right);
374
375 if (allCombineRgnResults == NULLREGION || allCombineRgnResults == ERROR)
376 result = QRegion();
377
378 //##### do not delete this. A null pointer is different from an empty region in SelectClipRgn in qpainter_win! (M)
379// if (allCombineRgnResults == NULLREGION) {
380// if (result.data->rgn)
381// DeleteObject(result.data->rgn);
382// result.data->rgn = 0; // empty region
383// }
384 return result;
385}
386
387QRegion QRegion::unite(const QRegion &r) const
388{
389 if (!d->rgn)
390 return r;
391 if (!r.d->rgn)
392 return *this;
393 return winCombine(r, QRGN_OR);
394}
395
396QRegion QRegion::unite(const QRect &r) const
397{
398 return unite(QRegion(r));
399}
400
401QRegion QRegion::intersect(const QRegion &r) const
402{
403 if (!r.d->rgn || !d->rgn)
404 return QRegion();
405 return winCombine(r, QRGN_AND);
406}
407
408QRegion QRegion::subtract(const QRegion &r) const
409{
410 if (!r.d->rgn || !d->rgn)
411 return *this;
412 return winCombine(r, QRGN_SUB);
413}
414
415QRegion QRegion::eor(const QRegion &r) const
416{
417 if (!d->rgn)
418 return r;
419 if (!r.d->rgn)
420 return *this;
421 return winCombine(r, QRGN_XOR);
422}
423
424
425QRect QRegion::boundingRect() const
426{
427 if (!d->rgn)
428 return QRect();
429 RECT r;
430 if (GetRgnBox(d->rgn, &r) == NULLREGION)
431 return QRect();
432 else
433 return QRect(r.left, r.top, r.right - r.left, r.bottom - r.top);
434}
435
436QVector<QRect> QRegion::rects() const
437{
438 if (d->rgn == 0)
439 return QVector<QRect>();
440
441 int numBytes = GetRegionData(d->rgn, 0, 0);
442 if (numBytes == 0)
443 return QVector<QRect>();
444
445 char *buf = new char[numBytes];
446 if (buf == 0)
447 return QVector<QRect>();
448
449 RGNDATA *rd = reinterpret_cast<RGNDATA*>(buf);
450 if (GetRegionData(d->rgn, numBytes, rd) == 0) {
451 delete [] buf;
452 return QVector<QRect>();
453 }
454
455 QVector<QRect> a(rd->rdh.nCount);
456 RECT *r = reinterpret_cast<RECT*>(rd->Buffer);
457 for (int i = 0; i < a.size(); ++i) {
458 a[i].setCoords(r->left, r->top, r->right - 1, r->bottom - 1);
459 ++r;
460 }
461
462 delete [] buf;
463 return a;
464}
465
466void QRegion::setRects(const QRect *rects, int num)
467{
468 *this = QRegion();
469 if (!rects || num == 0 || (num == 1 && rects->isEmpty()))
470 return;
471 for (int i = 0; i < num; ++i)
472 *this |= rects[i];
473}
474
475int QRegion::numRects() const
476{
477 if (d->rgn == 0)
478 return 0;
479
480 const int numBytes = GetRegionData(d->rgn, 0, 0);
481 if (numBytes == 0)
482 return 0;
483
484 char *buf = new char[numBytes];
485 if (buf == 0)
486 return 0;
487
488 RGNDATA *rd = reinterpret_cast<RGNDATA*>(buf);
489 if (GetRegionData(d->rgn, numBytes, rd) == 0) {
490 delete[] buf;
491 return 0;
492 }
493
494 const int n = rd->rdh.nCount;
495 delete[] buf;
496
497 return n;
498}
499
500bool QRegion::operator==(const QRegion &r) const
501{
502 if (d == r.d)
503 return true;
504 if ((d->rgn == 0) ^ (r.d->rgn == 0)) // one is empty, not both
505 return false;
506 return d->rgn == 0 ? true // both empty
507 : EqualRgn(d->rgn, r.d->rgn); // both non-empty
508}
509
510QRegion& QRegion::operator+=(const QRegion &r)
511{
512 if (!r.d->rgn)
513 return *this;
514
515 if (!d->rgn) {
516 *this = r;
517 return *this;
518 }
519
520 detach();
521
522 int result;
523 result = CombineRgn(d->rgn, d->rgn, r.d->rgn, RGN_OR);
524 if (result == NULLREGION || result == ERROR)
525 *this = QRegion();
526
527 return *this;
528}
529
530QRegion& QRegion::operator-=(const QRegion &r)
531{
532 if (!r.d->rgn || !d->rgn)
533 return *this;
534
535 detach();
536
537 int result;
538 result = CombineRgn(d->rgn, d->rgn, r.d->rgn, RGN_DIFF);
539 if (result == NULLREGION || result == ERROR)
540 *this = QRegion();
541
542 return *this;
543}
544
545QRegion& QRegion::operator&=(const QRegion &r)
546{
547 if (!d->rgn)
548 return *this;
549
550 if (!r.d->rgn) {
551 *this = QRegion();
552 return *this;
553 }
554
555 detach();
556
557 int result;
558 result = CombineRgn(d->rgn, d->rgn, r.d->rgn, RGN_AND);
559 if (result == NULLREGION || result == ERROR)
560 *this = QRegion();
561
562 return *this;
563}
564
565bool qt_region_strictContains(const QRegion &region, const QRect &rect)
566{
567 Q_UNUSED(region);
568 Q_UNUSED(rect);
569 return false;
570}
571
572void QRegion::ensureHandle() const
573{
574}
575
576QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.