source: trunk/src/gui/painting/qregion_pm.cpp@ 563

Last change on this file since 563 was 563, checked in by Dmitry A. Kuminov, 15 years ago

general: Remove svn:executable from text files as well as svn:eol and svn:keywords completely.

File size: 16.0 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 "qbitmap.h"
45#include "qbuffer.h"
46#include "qimage.h"
47#include "qpolygon.h"
48#include "qregion.h"
49
50#include "qt_os2.h"
51
52QT_BEGIN_NAMESPACE
53
54// To compensate the difference between Qt (where y axis goes downwards) and
55// GPI (where y axis goes upwards) coordinate spaces when dealing with regions
56// we use the following technique: when a GPI resource is allocated for a Qt
57// region, we simply change the sign of all y coordinates to quickly flip it
58// top to bottom in a manner that doesn't depend on the target device height.
59// All we have to do to apply the created GPI region to a particular GPI device
60// is to align its y axis to the top of the device (i.e. offset the region
61// up by the height of the device), and unalign it afterwards to bring it back
62// to the coordinate space of other device-independent (unaligned) regions.
63// To optimize this, we remember (in data->hgt) the last height value used to
64// align the region, and align it again only if the target device height
65// changes. Zero height indicates a device-independent target (such as other
66// unaligned Qt region).
67//
68// The handle() function, used for external access to the region, takes an
69// argument that must be always set to the height of the target device to
70// guarantee the correct coordinate space alignment.
71
72#if defined(__GNUC__) && defined(__INNOTEK_LIBC__)
73
74// Innotek GCC lacks some API functions in its version of OS/2 Toolkit headers
75
76extern "C" HRGN APIENTRY GpiCreateEllipticRegion(HPS hps,
77 PRECTL prclRect);
78
79extern "C" HRGN APIENTRY GpiCreatePolygonRegion(HPS hps,
80 ULONG ulCount,
81 PPOLYGON paplgn,
82 ULONG flOptions);
83#endif
84
85QRegion::QRegionData QRegion::shared_empty = { Q_BASIC_ATOMIC_INITIALIZER(1),
86 NULLHANDLE, 0 };
87
88QRegion::QRegion()
89 : d(&shared_empty)
90{
91 d->ref.ref();
92}
93
94QRegion::QRegion(const QRect &r, RegionType t)
95{
96 if (r.isEmpty()) {
97 d = &shared_empty;
98 d->ref.ref();
99 } else {
100 d = new QRegionData;
101 d->ref = 1;
102 d->height = 0;
103 HPS hps = qt_display_ps();
104 if (t == Rectangle) {
105 RECTL rcl = { r.left(), -(r.bottom()+1), r.right()+1, -r.top() };
106 d->rgn = GpiCreateRegion(hps, 1, &rcl);
107 } else if (t == Ellipse) {
108 // if the width or height of the ellipse is odd, GPI always
109 // converts it to a nearest even value, which is obviously stupid
110 // So, we don't use GpiCreateEllipticRegion(), but create an array
111 // of points to call GpiCreatePolygonRegion() instead.
112 QPainterPath p(QPointF(r.x(), r.y()));
113 p.arcTo(r.x(), r.y(), r.width(), r.height(), 0, 360);
114 QPolygon a = p.toFillPolygon().toPolygon();
115 for (int i = 0; i < a.size(); ++ i)
116 a[i].ry() = -(a[i].y() + 1);
117 // GpiCreatePolygonRegion() is bogus and always starts a poligon from
118 // the current position. Make the last point the current one and reduce
119 // the number of points by one.
120 GpiMove(hps, reinterpret_cast<PPOINTL>(&a[a.size() - 1]));
121 POLYGON poly = { a.size() - 1, reinterpret_cast<PPOINTL>(a.data()) };
122 d->rgn = GpiCreatePolygonRegion(hps, 1, &poly, POLYGON_ALTERNATE);
123 }
124 }
125}
126
127QRegion::QRegion(const QPolygon &a, Qt::FillRule fillRule)
128{
129 if (a.size() < 3) {
130 d = &shared_empty;
131 d->ref.ref();
132 } else {
133 d = new QRegionData;
134 d->ref = 1;
135 d->height = 0;
136 HPS hps = qt_display_ps();
137 POINTL *pts = new POINTL[a.size()];
138 for (int i = 0; i < a.size(); ++ i) {
139 pts[i].x = a[i].x();
140 pts[i].y = - (a[i].y() + 1);
141 }
142 // GpiCreatePolygonRegion() is bogus and always starts a poligon from
143 // the current position. Make the last point the current one and reduce
144 // the number of points by one.
145 GpiMove(hps, &pts[a.size() - 1]);
146 POLYGON poly = { a.size() - 1, pts };
147 ULONG opts = Qt::OddEvenFill ? POLYGON_ALTERNATE : POLYGON_WINDING;
148 d->rgn = GpiCreatePolygonRegion(hps, 1, &poly, opts);
149 delete[] pts;
150 }
151}
152
153QRegion::QRegion(const QRegion &r)
154{
155 d = r.d;
156 d->ref.ref();
157}
158
159static HRGN bitmapToRegion(const QBitmap& bitmap)
160{
161 HRGN region = 0;
162 QImage image = bitmap.toImage();
163 const int maxrect = 256;
164 RECTL rects[maxrect];
165 HPS hps = qt_display_ps();
166
167#define FlushSpans \
168 { \
169 HRGN r = GpiCreateRegion(hps, n, rects); \
170 if (region) { \
171 GpiCombineRegion(hps, region, region, r, CRGN_OR); \
172 GpiDestroyRegion(hps, r); \
173 } else { \
174 region = r; \
175 } \
176 }
177
178#define AddSpan \
179 { \
180 rects[n].xLeft = prev1; \
181 rects[n].yBottom = -(y+1); \
182 rects[n].xRight = x-1+1; \
183 rects[n].yTop = -y; \
184 n++; \
185 if (n == maxrect) { \
186 FlushSpans \
187 n = 0; \
188 } \
189 }
190
191 int n = 0;
192 int zero = 0x00;
193
194 int x, y;
195 for (y = 0; y < image.height(); y++) {
196 uchar *line = image.scanLine(y);
197 int w = image.width();
198 uchar all = zero;
199 int prev1 = -1;
200 for (x = 0; x < w;) {
201 uchar byte = line[x/8];
202 if (x > w-8 || byte != all) {
203 for (int b = 8; b > 0 && x < w; b--) {
204 if (!(byte & 0x80) == !all) {
205 // More of the same
206 } else {
207 // A change.
208 if (all != zero) {
209 AddSpan;
210 all = zero;
211 } else {
212 prev1 = x;
213 all = ~zero;
214 }
215 }
216 byte <<= 1;
217 x++;
218 }
219 } else {
220 x += 8;
221 }
222 }
223 if (all != zero) {
224 AddSpan;
225 }
226 }
227 if (n) {
228 FlushSpans;
229 }
230
231 if (!region)
232 region = GpiCreateRegion(hps, 0, NULL);
233
234 return region;
235}
236
237QRegion::QRegion(const QBitmap &bm)
238{
239 if (bm.isNull()) {
240 d = &shared_empty;
241 d->ref.ref();
242 } else {
243 d = new QRegionData;
244 d->ref = 1;
245 d->height = 0;
246 d->rgn = bitmapToRegion(bm);
247 }
248}
249
250void QRegion::cleanUp(QRegion::QRegionData *x)
251{
252 if (x->rgn != NULLHANDLE)
253 GpiDestroyRegion(qt_display_ps(), x->rgn);
254 delete x;
255}
256
257QRegion::~QRegion()
258{
259 if (!d->ref.deref())
260 cleanUp(d);
261}
262
263QRegion &QRegion::operator=(const QRegion &r)
264{
265 r.d->ref.ref();
266 if (!d->ref.deref())
267 cleanUp(d);
268 d = r.d;
269 return *this;
270}
271
272
273QRegion QRegion::copy() const
274{
275 QRegion r;
276 QRegionData *x = new QRegionData;
277 x->ref = 1;
278 if (d->rgn != NULLHANDLE) {
279 x->height = d->height;
280 HPS hps = qt_display_ps();
281 x->rgn = GpiCreateRegion(hps, 0, NULL);
282 GpiCombineRegion(hps, x->rgn, d->rgn, NULL, CRGN_COPY);
283 } else {
284 x->height = 0;
285 x->rgn = NULLHANDLE;
286 }
287 if (!r.d->ref.deref())
288 cleanUp(r.d);
289 r.d = x;
290 return r;
291}
292
293bool QRegion::isEmpty() const
294{
295 return (d == &shared_empty || boundingRect().isEmpty());
296}
297
298bool QRegion::contains(const QPoint &p) const
299{
300 LONG rc = PRGN_OUTSIDE;
301 if (d->rgn != NULLHANDLE) {
302 POINTL ptl = { p.x(), d->height - (p.y() + 1) };
303 rc = GpiPtInRegion(qt_display_ps(), d->rgn, &ptl);
304 }
305 return rc == PRGN_INSIDE;
306}
307
308bool QRegion::contains(const QRect &r) const
309{
310 LONG rc = PRGN_OUTSIDE;
311 if (d->rgn != NULLHANDLE) {
312 RECTL rcl = { r.left(), d->height - (r.bottom() + 1),
313 r.right() + 1, d->height - r.top() };
314 rc = GpiRectInRegion(qt_display_ps(), d->rgn, &rcl);
315 }
316 return rc == RRGN_INSIDE || rc == RRGN_PARTIAL;
317}
318
319void QRegion::translate(int dx, int dy)
320{
321 if (d->rgn == NULLHANDLE || (dx == 0 && dy == 0))
322 return;
323 detach();
324 POINTL ptl = { dx, -dy };
325 GpiOffsetRegion(qt_display_ps(), d->rgn, &ptl);
326}
327
328#define CRGN_NOP -1
329
330// Duplicates of those in qregion.cpp
331#define QRGN_OR 6
332#define QRGN_AND 7
333#define QRGN_SUB 8
334#define QRGN_XOR 9
335
336/*
337 Performs the actual OR, AND, SUB and XOR operation between regions.
338 Sets the resulting region handle to 0 to indicate an empty region.
339*/
340
341QRegion QRegion::pmCombine(const QRegion &r, int op) const
342{
343 LONG both = CRGN_NOP, left = CRGN_NOP, right = CRGN_NOP;
344 switch (op) {
345 case QRGN_OR:
346 both = CRGN_OR;
347 left = right = CRGN_COPY;
348 break;
349 case QRGN_AND:
350 both = CRGN_AND;
351 break;
352 case QRGN_SUB:
353 both = CRGN_DIFF;
354 left = CRGN_COPY;
355 break;
356 case QRGN_XOR:
357 both = CRGN_XOR;
358 left = right = CRGN_COPY;
359 break;
360 default:
361 qWarning( "QRegion: Internal error in pmCombine" );
362 }
363
364 QRegion result;
365 if (d->rgn == NULLHANDLE && r.d->rgn == NULLHANDLE)
366 return result;
367 HPS hps = qt_display_ps();
368 result.detach();
369 result.d->rgn = GpiCreateRegion(hps, 0, NULL);
370 LONG rc = RGN_NULL;
371 if (d->rgn != NULLHANDLE && r.d->rgn != NULLHANDLE) {
372 updateHandle(r.d->height); // bring to the same coordinate space
373 rc = GpiCombineRegion(hps, result.d->rgn, d->rgn, r.d->rgn, both);
374 result.d->height = r.d->height;
375 } else if (d->rgn && left != CRGN_NOP) {
376 rc = GpiCombineRegion(hps, result.d->rgn, d->rgn, 0, left);
377 result.d->height = d->height;
378 } else if (r.d->rgn != NULLHANDLE && right != CRGN_NOP) {
379 rc = GpiCombineRegion(hps, result.d->rgn, r.d->rgn, 0, right);
380 result.d->height = r.d->height;
381 }
382 if (rc == RGN_NULL || rc == RGN_ERROR) {
383 result = QRegion(); // shared_empty
384 }
385 return result;
386}
387
388QRegion QRegion::unite(const QRegion &r) const
389{
390 if (d->rgn == NULLHANDLE)
391 return r;
392 if (r.d->rgn == NULLHANDLE)
393 return *this;
394 return pmCombine(r, QRGN_OR);
395}
396
397QRegion QRegion::unite(const QRect &r) const
398{
399 return unite(QRegion(r));
400}
401
402QRegion QRegion::intersect(const QRegion &r) const
403{
404 if (r.d->rgn == NULLHANDLE || d->rgn == NULLHANDLE)
405 return QRegion();
406 return pmCombine(r, QRGN_AND);
407}
408
409QRegion QRegion::subtract(const QRegion &r) const
410{
411 if (r.d->rgn == NULLHANDLE || d->rgn == NULLHANDLE)
412 return *this;
413 return pmCombine(r, QRGN_SUB);
414}
415
416QRegion QRegion::eor(const QRegion &r) const
417{
418 if (d->rgn == NULLHANDLE)
419 return r;
420 if (r.d->rgn == NULLHANDLE)
421 return *this;
422 return pmCombine(r, QRGN_XOR);
423}
424
425QRect QRegion::boundingRect() const
426{
427 if (!d->rgn)
428 return QRect();
429
430 RECTL rcl;
431 LONG rc = RGN_NULL;
432 if (d->rgn != NULLHANDLE)
433 rc = GpiQueryRegionBox(qt_display_ps(), d->rgn, &rcl);
434 if (rc == RGN_NULL || rc == RGN_ERROR)
435 return QRect();
436 else
437 return QRect(rcl.xLeft, d->height - rcl.yTop,
438 rcl.xRight - rcl.xLeft, rcl.yTop - rcl.yBottom);
439}
440
441QVector<QRect> QRegion::rects() const
442{
443 QVector<QRect> a;
444
445 if (d->rgn == NULLHANDLE)
446 return a;
447
448 HPS hps = qt_display_ps();
449 RGNRECT ctl = {1, 0, 0, RECTDIR_LFRT_TOPBOT};
450 if (!GpiQueryRegionRects(hps, d->rgn, NULL, &ctl, NULL))
451 return a;
452
453 ctl.crc = ctl.crcReturned;
454 PRECTL rcls = new RECTL[ctl.crcReturned];
455 if (rcls == 0)
456 return a;
457 if (!GpiQueryRegionRects(hps, d->rgn, NULL, &ctl, rcls)) {
458 delete [] rcls;
459 return a;
460 }
461
462 a = QVector<QRect>(ctl.crcReturned);
463 PRECTL r = rcls;
464 for (int i = 0; i < a.size(); ++i) {
465 a[i].setRect(r->xLeft, d->height - r->yTop,
466 r->xRight - r->xLeft, r->yTop - r->yBottom);
467 ++r;
468 }
469
470 delete [] rcls;
471 return a;
472 }
473
474void QRegion::setRects(const QRect *rects, int num)
475{
476 *this = QRegion();
477 if (!rects || num == 0 || (num == 1 && rects->isEmpty()))
478 return;
479 for (int i = 0; i < num; ++i)
480 *this |= rects[i];
481}
482
483int QRegion::numRects() const
484{
485 if (d->rgn == NULLHANDLE)
486 return 0;
487
488 RGNRECT ctl = {1, 0, 0, RECTDIR_LFRT_TOPBOT};
489 if (!GpiQueryRegionRects(qt_display_ps(), d->rgn, NULL, &ctl, NULL))
490 return 0;
491
492 return ctl.crcReturned;
493}
494
495bool QRegion::operator==(const QRegion &r) const
496{
497 if (d == r.d)
498 return true;
499 if ((d->rgn == NULLHANDLE) ^ (r.d->rgn == NULLHANDLE)) // one is empty, not both
500 return false;
501 if (d->rgn == NULLHANDLE) // both empty
502 return true;
503 updateHandle(r.d->height); // bring to the same coordinate space
504 return // both not empty
505 GpiEqualRegion(qt_display_ps(), d->rgn, r.d->rgn) == EQRGN_EQUAL;
506}
507
508QRegion& QRegion::operator+=(const QRegion &r)
509{
510 if (r.d->rgn == NULLHANDLE)
511 return *this;
512
513 if (d->rgn == NULLHANDLE) {
514 *this = r;
515 return *this;
516 }
517
518 *this = unite(r);
519 return *this;
520}
521
522QRegion& QRegion::operator-=(const QRegion &r)
523{
524 if (r.d->rgn == NULLHANDLE || d->rgn == NULLHANDLE)
525 return *this;
526
527 *this = subtract(r);
528 return *this;
529}
530
531QRegion& QRegion::operator&=(const QRegion &r)
532{
533 if (d->rgn == NULLHANDLE)
534 return *this;
535
536 if (r.d->rgn == NULLHANDLE) {
537 *this = QRegion();
538 return *this;
539 }
540
541 *this = intersect(r);
542 return *this;
543}
544
545bool qt_region_strictContains(const QRegion &region, const QRect &rect)
546{
547 Q_UNUSED(region);
548 Q_UNUSED(rect);
549 return false;
550}
551
552/*!
553 \internal
554
555 Updates the region handle so that it is suitable for selection to
556 a device with the given \a height.
557 */
558void QRegion::updateHandle(int targetHeight) const
559{
560 QRegion *that = const_cast<QRegion*>(this); // we're const here
561 if (d->rgn == NULLHANDLE) {
562 // a handle of a null region is requested, allocate an empty region
563 that->detach();
564 that->d->rgn = GpiCreateRegion(qt_display_ps(), 0, NULL);
565 that->d->height = targetHeight;
566 } else if (d->height != targetHeight) {
567 // align region y axis to the top of the device
568 that->detach();
569 POINTL ptl = { 0, targetHeight - d->height };
570 GpiOffsetRegion(qt_display_ps(), d->rgn, &ptl);
571 that->d->height = targetHeight;
572 }
573}
574
575QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.