source: trunk/src/kernel/qregion_pm.cpp@ 140

Last change on this file since 140 was 140, checked in by dmik, 19 years ago

Compiling: Applied a workaround for the Innotek GCC compiler bug related to attribute((system)) processing (see http://svn.netlabs.org/libc/ticket/129 for more details).

  • Property svn:keywords set to Id
File size: 14.9 KB
Line 
1/****************************************************************************
2** $Id: qregion_pm.cpp 140 2006-10-20 21:46:43Z dmik $
3**
4** Implementation of QRegion class for OS/2
5**
6** Copyright (C) 1992-2000 Trolltech AS. All rights reserved.
7** Copyright (C) 2004 Norman ASA. Initial OS/2 Port.
8** Copyright (C) 2005 netlabs.org. Further OS/2 Development.
9**
10** This file is part of the kernel module of the Qt GUI Toolkit.
11**
12** This file may be distributed under the terms of the Q Public License
13** as defined by Trolltech AS of Norway and appearing in the file
14** LICENSE.QPL included in the packaging of this file.
15**
16** This file may be distributed and/or modified under the terms of the
17** GNU General Public License version 2 as published by the Free Software
18** Foundation and appearing in the file LICENSE.GPL included in the
19** packaging of this file.
20**
21** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
22** licenses may use this file in accordance with the Qt Commercial License
23** Agreement provided with the Software.
24**
25** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
26** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27**
28** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
29** information about Qt Commercial License Agreements.
30** See http://www.trolltech.com/qpl/ for QPL licensing information.
31** See http://www.trolltech.com/gpl/ for GPL licensing information.
32**
33** Contact info@trolltech.com if any conditions of this licensing are
34** not clear to you.
35**
36**********************************************************************/
37
38#include "qregion.h"
39#include "qpointarray.h"
40#include "qbuffer.h"
41#include "qbitmap.h"
42#include "qimage.h"
43
44// To compensate the difference between Qt (where y axis goes downwards) and
45// GPI (where y axis goes upwards) coordinate spaces when dealing with regions
46// we use the following technique: when a GPI resource is allocated for a Qt
47// region, we simply change the sign of all y coordinates to quickly flip it
48// top to bottom in a manner that doesn't depend on the target device height.
49// All we have to do to apply the created GPI region to a particular GPI device
50// is to align its y axis to the top of the device (i.e. offset the region
51// up by the height of the device), and unalign it afterwards to bring it back
52// to the coordinate space of other device-independent (unaligned) regions.
53// To optimize this, we remember (in data->hgt) the last height value used to
54// align the region, and align it again only if the target device height
55// changes. Zero height indicates a device-independent target (such as other
56// unaligned Qt region).
57//
58// The handle() function, used for external access to the region, takes an
59// argument that must be always set to the height of the target device to
60// guarantee the correct coordinate space alignment.
61
62#if defined(Q_CC_GNU) && !defined(USE_OS2_TOOLKIT_HEADERS)
63
64// Innotek GCC lacks some API functions in its version of OS/2 Toolkit headers
65
66extern "C" HRGN APIENTRY GpiCreateEllipticRegion( HPS hps,
67 PRECTL prclRect );
68
69extern "C" HRGN APIENTRY GpiCreatePolygonRegion( HPS hps,
70 ULONG ulCount,
71 PPOLYGON paplgn,
72 ULONG flOptions );
73#endif
74
75// this include statement is *after* the above extern definitions because of the
76// Innotek GCC bug regarding the __attribute__((__system__)) processing, see
77// http://svn.netlabs.org/libc/ticket/129 for more info.
78#include "qt_os2.h"
79
80QRegion::QRegion()
81{
82 data = new QRegionData;
83 Q_CHECK_PTR( data );
84 data->rgn = 0;
85 data->hgt = 0;
86 data->is_null = TRUE;
87}
88
89QRegion::QRegion( bool is_null )
90{
91 data = new QRegionData;
92 Q_CHECK_PTR( data );
93 data->rgn = 0;
94 data->hgt = 0;
95 data->is_null = is_null;
96}
97
98QRegion::QRegion( HRGN hrgn, int target_height )
99{
100 data = new QRegionData;
101 Q_CHECK_PTR( data );
102 data->rgn = hrgn;
103 data->hgt = target_height;
104 data->is_null = FALSE;
105}
106
107QRegion::QRegion( const QRect &r, RegionType t )
108{
109 data = new QRegionData;
110 Q_CHECK_PTR( data );
111 data->hgt = 0;
112 data->is_null = FALSE;
113 if ( r.isEmpty() ) {
114 data->rgn = 0;
115 } else {
116 HPS hps = qt_display_ps();
117 if ( t == Rectangle ) { // rectangular region
118 RECTL rcl = { r.left(), -(r.bottom()+1), r.right()+1, -r.top() };
119 data->rgn = GpiCreateRegion( hps, 1, &rcl );
120 } else if ( t == Ellipse ) { // elliptic region
121 // if the width or height of the ellipse is odd, GPI always
122 // converts it to a nearest even value, which is obviously stupid
123 // (see also QPainter::drawArcInternal()). So, we don't use
124 // GpiCreateEllipticRegion(), but create an array of points to
125 // call GpiCreatePolygonRegion() instead.
126 QPointArray a;
127 a.makeArc( r.x(), r.y(), r.width(), r.height(), 0, 360 * 16 );
128 for ( uint i = 0; i < a.size(); ++ i )
129 a[i].ry() = -(a[i].y() + 1);
130 // GpiCreatePolygonRegion() is bogus and always starts a poligon from
131 // the current position. Make the last point the current one and reduce
132 // the number of points by one.
133 GpiMove( hps, (PPOINTL) &a[ a.size() - 1 ] );
134 POLYGON poly = { a.size() - 1, (PPOINTL) a.data() };
135 data->rgn = GpiCreatePolygonRegion( hps, 1, &poly, POLYGON_ALTERNATE );
136 }
137 }
138}
139
140QRegion::QRegion( const QPointArray &a, bool winding )
141{
142 data = new QRegionData;
143 Q_CHECK_PTR( data );
144 data->hgt = 0;
145 data->is_null = FALSE;
146 QRect r = a.boundingRect();
147 if ( a.isEmpty() || r.isEmpty() ) {
148 data->rgn = 0;
149 } else {
150 HPS hps = qt_display_ps();
151 POINTL *pts = new POINTL[ a.size() ];
152 for ( uint i = 0; i < a.size(); ++ i ) {
153 pts[i].x = a[i].x();
154 pts[i].y = - (a[i].y() + 1);
155 }
156 // GpiCreatePolygonRegion() is bogus and always starts a poligon from
157 // the current position. Make the last point the current one and reduce
158 // the number of points by one.
159 GpiMove( hps, &pts[ a.size() - 1 ] );
160 POLYGON poly = { a.size() - 1, pts };
161 ULONG opts = winding ? POLYGON_WINDING : POLYGON_ALTERNATE;
162 data->rgn = GpiCreatePolygonRegion( hps, 1, &poly, opts );
163 delete[] pts;
164 }
165}
166
167QRegion::QRegion( const QRegion &r )
168{
169 data = r.data;
170 data->ref();
171}
172
173HRGN qt_pm_bitmapToRegion( const QBitmap& bitmap )
174{
175 HRGN region = 0;
176 QImage image = bitmap.convertToImage();
177 const int maxrect = 256;
178 RECTL rects[maxrect];
179 HPS hps = qt_display_ps();
180
181#define FlushSpans \
182 { \
183 HRGN r = GpiCreateRegion( hps, n, rects ); \
184 if ( region ) { \
185 GpiCombineRegion( hps, region, region, r, CRGN_OR ); \
186 GpiDestroyRegion( hps, r ); \
187 } else { \
188 region = r; \
189 } \
190 }
191
192#define AddSpan \
193 { \
194 rects[n].xLeft = prev1; \
195 rects[n].yBottom = -(y+1); \
196 rects[n].xRight = x-1+1; \
197 rects[n].yTop = -y; \
198 n++; \
199 if ( n == maxrect ) { \
200 FlushSpans \
201 n = 0; \
202 } \
203 }
204
205 int n = 0;
206 int zero = 0x00;
207
208 int x, y;
209 for ( y = 0; y < image.height(); y++ ) {
210 uchar *line = image.scanLine(y);
211 int w = image.width();
212 uchar all = zero;
213 int prev1 = -1;
214 for ( x = 0; x < w; ) {
215 uchar byte = line[x/8];
216 if ( x > w-8 || byte != all ) {
217 for ( int b = 8; b > 0 && x < w; b-- ) {
218 if ( !(byte & 0x80) == !all ) {
219 // More of the same
220 } else {
221 // A change.
222 if ( all != zero ) {
223 AddSpan;
224 all = zero;
225 } else {
226 prev1 = x;
227 all = ~zero;
228 }
229 }
230 byte <<= 1;
231 x++;
232 }
233 } else {
234 x += 8;
235 }
236 }
237 if ( all != zero ) {
238 AddSpan;
239 }
240 }
241 if ( n ) {
242 FlushSpans;
243 }
244
245 if ( !region )
246 region = GpiCreateRegion( hps, 0, NULL );
247
248 return region;
249}
250
251
252QRegion::QRegion( const QBitmap & bm )
253{
254 data = new QRegionData;
255 Q_CHECK_PTR( data );
256 data->hgt = 0;
257 data->is_null = FALSE;
258 if ( bm.isNull() )
259 data->rgn = 0;
260 else
261 data->rgn = qt_pm_bitmapToRegion( bm );
262}
263
264
265QRegion::~QRegion()
266{
267 if ( data->deref() ) {
268 if ( data->rgn )
269 GpiDestroyRegion( qt_display_ps(), data->rgn );
270 delete data;
271 }
272}
273
274QRegion &QRegion::operator=( const QRegion &r )
275{
276 r.data->ref(); // beware of r = r
277 if ( data->deref() ) {
278 if ( data->rgn )
279 GpiDestroyRegion( qt_display_ps(), data->rgn );
280 delete data;
281 }
282 data = r.data;
283 return *this;
284}
285
286
287QRegion QRegion::copy() const
288{
289 QRegion r ( data->is_null );
290 r.data->hgt = 0;
291 if ( !data->is_null && data->rgn ) {
292 HPS hps = qt_display_ps();
293 r.data->rgn = GpiCreateRegion( hps, 0, NULL );
294 GpiCombineRegion( hps, r.data->rgn, data->rgn, NULL, CRGN_COPY );
295 r.data->hgt = data->hgt;
296 }
297 return r;
298}
299
300
301bool QRegion::isNull() const
302{
303 return data->is_null;
304}
305
306bool QRegion::isEmpty() const
307{
308 if ( data->is_null || data->rgn == 0 )
309 return TRUE;
310 RECTL rcl;
311 return GpiQueryRegionBox( qt_display_ps(), data->rgn, &rcl ) == RGN_NULL;
312}
313
314
315bool QRegion::contains( const QPoint &p ) const
316{
317 LONG rc = PRGN_OUTSIDE;
318 if ( data->rgn ) {
319 POINTL ptl = { p.x(), data->hgt - (p.y() + 1) };
320 rc = GpiPtInRegion( qt_display_ps(), data->rgn, &ptl );
321 }
322 return rc == PRGN_INSIDE;
323}
324
325bool QRegion::contains( const QRect &r ) const
326{
327 LONG rc = PRGN_OUTSIDE;
328 if ( data->rgn ) {
329 RECTL rcl = { r.left(), data->hgt - (r.bottom() + 1),
330 r.right() + 1, data->hgt - r.top() };
331 rc = GpiRectInRegion( qt_display_ps(), data->rgn, &rcl );
332 }
333 return rc == RRGN_INSIDE || rc == RRGN_PARTIAL;
334}
335
336
337void QRegion::translate( int dx, int dy )
338{
339 if ( !data->rgn )
340 return;
341 detach();
342 POINTL ptl = { dx, -dy };
343 GpiOffsetRegion( qt_display_ps(), data->rgn, &ptl);
344}
345
346
347#define CRGN_NOP -1
348
349/*
350 Performs the actual OR, AND, SUB and XOR operation between regions.
351 Sets the resulting region handle to 0 to indicate an empty region.
352*/
353
354QRegion QRegion::pmCombine( const QRegion &r, int op ) const
355{
356 LONG both = CRGN_NOP, left = CRGN_NOP, right = CRGN_NOP;
357 switch ( op ) {
358 case QRGN_OR:
359 both = CRGN_OR;
360 left = right = CRGN_COPY;
361 break;
362 case QRGN_AND:
363 both = CRGN_AND;
364 break;
365 case QRGN_SUB:
366 both = CRGN_DIFF;
367 left = CRGN_COPY;
368 break;
369 case QRGN_XOR:
370 both = CRGN_XOR;
371 left = right = CRGN_COPY;
372 break;
373 default:
374#if defined(QT_CHECK_RANGE)
375 qWarning( "QRegion: Internal error in pmCombine" );
376#else
377 ;
378#endif
379 }
380
381 QRegion result( FALSE );
382 if ( !data->rgn && !r.data->rgn )
383 return result;
384 HPS hps = qt_display_ps();
385 result.data->rgn = GpiCreateRegion( hps, 0, NULL );
386 LONG rc = RGN_NULL;
387 if ( data->rgn && r.data->rgn ) {
388 updateHandle( r.data->hgt ); // bring to the same coordinate space
389 rc = GpiCombineRegion( hps, result.data->rgn, data->rgn, r.data->rgn, both );
390 result.data->hgt = r.data->hgt;
391 } else if ( data->rgn && left != CRGN_NOP ) {
392 rc = GpiCombineRegion( hps, result.data->rgn, data->rgn, 0, left );
393 result.data->hgt = data->hgt;
394 } else if ( r.data->rgn && right != CRGN_NOP ) {
395 rc = GpiCombineRegion( hps, result.data->rgn, r.data->rgn, 0, right );
396 result.data->hgt = r.data->hgt;
397 }
398 if ( rc == RGN_NULL || rc == RGN_ERROR ) {
399 GpiDestroyRegion( hps, result.data->rgn );
400 result.data->rgn = result.data->hgt = 0;
401 }
402 return result;
403}
404
405QRegion QRegion::unite( const QRegion &r ) const
406{
407 return pmCombine( r, QRGN_OR );
408}
409
410QRegion QRegion::intersect( const QRegion &r ) const
411{
412 return pmCombine( r, QRGN_AND );
413}
414
415QRegion QRegion::subtract( const QRegion &r ) const
416{
417 return pmCombine( r, QRGN_SUB );
418}
419
420QRegion QRegion::eor( const QRegion &r ) const
421{
422 return pmCombine( r, QRGN_XOR );
423}
424
425
426QRect QRegion::boundingRect() const
427{
428 RECTL rcl;
429 LONG rc = RGN_NULL;
430 if ( data->rgn )
431 rc = GpiQueryRegionBox( qt_display_ps(), data->rgn, &rcl );
432 if ( rc == RGN_NULL || rc == RGN_ERROR )
433 return QRect(0,0,0,0);
434 else
435 return QRect( rcl.xLeft, data->hgt - rcl.yTop,
436 rcl.xRight - rcl.xLeft, rcl.yTop - rcl.yBottom );
437}
438
439
440QMemArray<QRect> QRegion::rects() const
441{
442 QMemArray<QRect> a;
443 if ( !data->rgn )
444 return a;
445
446 HPS hps = qt_display_ps();
447 RGNRECT ctl = {1, 0, 0, RECTDIR_LFRT_TOPBOT};
448 if ( !GpiQueryRegionRects( hps, data->rgn, NULL, &ctl, NULL ) )
449 return a;
450
451 ctl.crc = ctl.crcReturned;
452 PRECTL rcls = new RECTL[ctl.crcReturned];
453 if ( !GpiQueryRegionRects( hps, data->rgn, NULL, &ctl, rcls ) ) {
454 delete [] rcls;
455 return a;
456 }
457
458 a = QMemArray<QRect>( ctl.crcReturned );
459 PRECTL r = rcls;
460 for ( int i=0; i<(int)a.size(); i++ ) {
461 a[i].setRect( r->xLeft, data->hgt - r->yTop,
462 r->xRight - r->xLeft, r->yTop - r->yBottom );
463 r++;
464 }
465
466 delete [] rcls;
467
468 return a;
469}
470
471void QRegion::setRects( const QRect *rects, int num )
472{
473 // Could be optimized
474 *this = QRegion();
475 for (int i=0; i<num; i++)
476 *this |= rects[i];
477}
478
479bool QRegion::operator==( const QRegion &r ) const
480{
481 if ( data == r.data ) // share the same data
482 return TRUE;
483 bool is_empty = data->is_null || data->rgn == 0;
484 bool r_is_empty = r.data->is_null || r.data->rgn == 0;
485 if ( (is_empty ^ r_is_empty ) ) // one is empty, not both
486 return FALSE;
487 if ( is_empty ) // both empty
488 return TRUE;
489 updateHandle( r.data->hgt ); // bring to the same coordinate space
490 return
491 GpiEqualRegion( qt_display_ps(), data->rgn, r.data->rgn ) == EQRGN_EQUAL;
492}
493
494/*!
495 * \internal
496 * Updates the region handle so that it is suitable for selection to
497 * a device with the given \a height.
498 */
499void QRegion::updateHandle( int target_height ) const
500{
501 QRegion *that = const_cast< QRegion *>( this ); // we're const here
502 if ( !data->rgn ) {
503 // a handle of a null region is requested, allocate an empty region
504 that->data->rgn = GpiCreateRegion( qt_display_ps(), 0, NULL );
505 that->data->hgt = target_height;
506 } else if ( data->hgt != target_height ) {
507 // align region y axis to the top of the device
508 POINTL ptl = { 0, target_height - data->hgt };
509 GpiOffsetRegion( qt_display_ps(), data->rgn, &ptl );
510 that->data->hgt = target_height;
511 }
512}
Note: See TracBrowser for help on using the repository browser.