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

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

qregion_pm.cpp: Reverted back to revision 139 because of changeset:141.

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