source: trunk/src/kernel/qpainter_pm.cpp@ 135

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

Fixed QWidget::erase() broken by changeset:61.

  • Property svn:keywords set to Id
File size: 64.6 KB
Line 
1/****************************************************************************
2** $Id: qpainter_pm.cpp 63 2006-02-25 20:58:01Z dmik $
3**
4** Implementation of QPainter 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 "qpainter.h"
39#include "qpaintdevicemetrics.h"
40#include "qwidget.h"
41#include "qbitmap.h"
42#include "qpixmapcache.h"
43#include "qptrlist.h"
44/// @todo (dmik) later
45//#include "qprinter.h"
46#include <stdlib.h>
47#include <math.h>
48#include "qapplication_p.h"
49#include "qfontdata_p.h"
50#include "qt_os2.h"
51#include "qtextlayout_p.h"
52#include "qtextengine_p.h"
53#include "qfontengine_p.h"
54
55// QRgb has the same RGB format (0xaarrggbb) as OS/2 uses (ignoring the
56// highest alpha byte) so we just mask alpha to get the valid OS/2 color.
57#define COLOR_VALUE(c) ((flags & RGBColor) ? (c.rgb() & RGB_MASK) : c.pixel())
58
59// Innotek GCC lacks some API functions in its version of OS/2 Toolkit headers
60#if defined(Q_CC_GNU) && !defined(USE_OS2_TOOLKIT_HEADERS)
61extern "C" LONG APIENTRY GpiQueryNearestPaletteIndex(HPAL hpal, ULONG color);
62#endif
63
64// this function fixes a bug in OS/2 GPI: GpiSetBackColor() doesn't
65// change the background color attribute of line primitives.
66BOOL qt_GpiSetBackColor( HPS hps, LONG color )
67{
68 BOOL rc = FALSE;
69 rc = GpiSetBackColor( hps, color );
70 LINEBUNDLE lb;
71 lb.lBackColor = color;
72 rc &= GpiSetAttrs( hps, PRIM_LINE, LBB_BACK_COLOR, 0, (PBUNDLE) &lb );
73 return rc;
74}
75
76// this function fixes a bug in OS/2 GPI: GpiSetBackMix() doesn't
77// change the mix color attribute of line primitives. it also takes into
78// account the current pen style.
79BOOL qt_GpiSetBackMix( HPS hps, LONG mix, BOOL noPen )
80{
81 BOOL rc = FALSE;
82 rc = GpiSetBackMix( hps, mix );
83 LINEBUNDLE lb;
84 lb.usBackMixMode = noPen ? BM_LEAVEALONE : mix;
85 rc &= GpiSetAttrs( hps, PRIM_LINE, LBB_BACK_MIX_MODE, 0, (PBUNDLE) &lb );
86 return rc;
87}
88
89// helpers to draw geometric width lines
90
91struct qt_geom_line_handle
92{
93 enum { AreaAttrs = ABB_COLOR | ABB_SET | ABB_SYMBOL | ABB_BACK_MIX_MODE };
94 LONG aa;
95 AREABUNDLE ab;
96};
97
98void qt_begin_geom_line( HPS hps, LONG color, BOOL noPen, qt_geom_line_handle *h )
99{
100 Q_ASSERT( h );
101 h->aa = 0;
102 GpiQueryAttrs( hps, PRIM_AREA, h->AreaAttrs, &h->ab );
103 AREABUNDLE ab;
104 if ( h->ab.lColor != color ) {
105 ab.lColor = color;
106 h->aa |= ABB_COLOR;
107 }
108 if ( h->ab.usSet != LCID_DEFAULT ) {
109 ab.usSet = LCID_DEFAULT;
110 h->aa |= ABB_SET;
111 }
112 ab.usSymbol = noPen ? PATSYM_BLANK : PATSYM_SOLID;
113 if ( h->ab.usSymbol != ab.usSymbol ) {
114 h->aa |= ABB_SYMBOL;
115 }
116 if ( noPen && h->ab.usBackMixMode != BM_LEAVEALONE ) {
117 ab.usBackMixMode = BM_LEAVEALONE;
118 h->aa |= ABB_BACK_MIX_MODE;
119 }
120 GpiSetAttrs( hps, PRIM_AREA, h->aa, 0, &ab );
121 GpiBeginPath( hps, 1 );
122}
123
124void qt_end_geom_line( HPS hps, qt_geom_line_handle *h )
125{
126 GpiEndPath( hps );
127 GpiStrokePath( hps, 1, 0 );
128 GpiSetAttrs( hps, PRIM_AREA, h->aa, 0, &h->ab );
129}
130
131/* These functions are implemented in qapplication_pm.cpp */
132extern void qt_fill_tile( QPixmap *, const QPixmap & );
133extern void qt_draw_tiled_pixmap( HPS, int, int, int, int,
134 const QPixmap *, int, int, int );
135
136void qt_erase_background(
137 HPS hps, int x, int y, int w, int h,
138 const QColor &bg_color,
139 const QPixmap *bg_pixmap, int off_x, int off_y, int devh
140)
141{
142 if ( bg_pixmap && bg_pixmap->isNull() ) // empty background
143 return;
144 HPAL oldPal = 0;
145 if ( QColor::hPal() ) {
146 oldPal = GpiSelectPalette( hps, QColor::hPal() );
147 } else {
148 // direct RGB mode
149 GpiCreateLogColorTable( hps, 0, LCOLF_RGB, 0, 0, NULL );
150 }
151 if ( bg_pixmap ) {
152 // normalize pixmap offsets according to x and y
153 // to ensure that the offsets are always relative to
154 // the background origin point, regardless of what x and y are.
155 off_x = (off_x + x) % bg_pixmap->width();
156 off_y = (off_y + y) % bg_pixmap->height();
157 qt_draw_tiled_pixmap( hps, x, y, w, h, bg_pixmap, off_x, off_y, devh );
158 } else {
159 // flip y coordinate
160 if ( devh )
161 y = devh - (y + h);
162 // this function should be called only with window presentation space
163 // so simply use WinFillRect instead of GpiBox.
164 RECTL rcl = { x, y, x + w, y + h };
165 WinFillRect( hps, &rcl, bg_color.pixel() );
166 }
167 if ( QColor::hPal() ) {
168 GpiSelectPalette( hps, oldPal );
169 }
170}
171
172/*****************************************************************************
173 QPainter member functions
174 *****************************************************************************/
175
176void QPainter::initialize()
177{
178}
179
180void QPainter::cleanup()
181{
182}
183
184void QPainter::destroy()
185{
186
187}
188
189void QPainter::init()
190{
191 d = 0;
192 flags = IsStartingUp;
193 pdev = 0;
194 bg_col = white; // default background color
195 bg_mode = TransparentMode; // default background mode
196 rop = CopyROP; // default ROP
197 tabstops = 0; // default tabbing
198 tabarray = 0;
199 tabarraylen = 0;
200 block_ext = FALSE;
201 txop = txinv = 0;
202 ps_stack = 0;
203 wm_stack = 0;
204 hps = 0;
205 holdpal = 0;
206 holdrgn = HRGN_ERROR;
207 hbrushbm = 0;
208 pixmapBrush = nocolBrush = FALSE;
209 devh = 0;
210 pfont = 0;
211}
212
213
214void QPainter::setFont( const QFont &font )
215{
216#if defined(QT_CHECK_STATE)
217 if ( !isActive() )
218 qWarning( "QPainter::setFont: Will be reset by begin()" );
219#endif
220 if ( cfont.d != font.d || testf(VolatileDC) ) {
221 cfont = font;
222 setf(DirtyFont);
223 }
224}
225
226
227void QPainter::updateFont()
228{
229 QFont *temp = 0;
230 clearf(DirtyFont);
231 if ( testf(ExtDev) ) {
232 QPDevCmdParam param[1];
233 param[0].font = &cfont;
234 if ( !pdev->cmd( QPaintDevice::PdcSetFont, this, param ) || !hps )
235 return;
236 }
237 if ( pdev->devType() == QInternal::Printer ) {
238 //int dw = pdev->metric( QPaintDeviceMetrics::PdmWidth );
239 //int dh = pdev->metric( QPaintDeviceMetrics::PdmHeight );
240 // ### fix compat mode
241 //bool vxfScale = testf(Qt2Compat) && testf(VxF)
242 // && ( dw != ww || dw != vw || dh != wh || dh != vh );
243
244 if ( pfont ) temp = pfont;
245 pfont = new QFont( cfont.d, pdev );
246 pfont->selectTo( hps );
247 } else {
248 if ( pfont ) {
249 temp = pfont;
250 pfont = 0;
251 }
252 cfont.selectTo( hps );
253 }
254 delete temp;
255}
256
257
258void QPainter::updatePen()
259{
260 if ( testf(ExtDev) ) {
261 QPDevCmdParam param[1];
262 param[0].pen = &cpen;
263 if ( !pdev->cmd(QPaintDevice::PdcSetPen,this,param) || !hps )
264 return;
265 }
266
267 ULONG LineAttrs = LBB_COLOR | LBB_TYPE | LBB_BACK_MIX_MODE;
268 LINEBUNDLE lb;
269 lb.lColor = COLOR_VALUE( cpen.color() );
270 lb.usBackMixMode = GpiQueryBackMix( hps );
271
272 switch ( cpen.style() ) {
273 case NoPen:
274 lb.usType = LINETYPE_INVISIBLE;
275 lb.usBackMixMode = BM_LEAVEALONE;
276 break;
277 case SolidLine:
278 lb.usType = LINETYPE_SOLID;
279 break;
280 case DashLine:
281 lb.usType = LINETYPE_LONGDASH;
282 break;
283 case DotLine:
284 lb.usType = LINETYPE_DOT;
285 break;
286 case DashDotLine:
287 lb.usType = LINETYPE_DASHDOT;
288 break;
289 case DashDotDotLine:
290 lb.usType = LINETYPE_DASHDOUBLEDOT;
291 break;
292 case MPenStyle:
293#if defined(QT_CHECK_STATE)
294 qWarning("QPainter::updatePen: MPenStyle used as a value. It is a mask!");
295#endif
296 break;
297 }
298
299 if ( cpen.width() > 1 ) {
300 LineAttrs |= LBB_END;
301 switch ( cpen.capStyle() ) {
302 case SquareCap:
303 lb.usEnd = LINEEND_SQUARE;
304 break;
305 case RoundCap:
306 lb.usEnd = LINEEND_ROUND;
307 break;
308 case FlatCap:
309 lb.usEnd = LINEEND_FLAT;
310 break;
311 case MPenCapStyle:
312 LineAttrs &= ~LBB_END;
313#if defined(QT_CHECK_STATE)
314 qWarning("QPainter::updatePen: MPenCapStyle used as a value. It is a mask!");
315#endif
316 break;
317 }
318 LineAttrs |= LBB_JOIN;
319 switch ( cpen.joinStyle() ) {
320 case BevelJoin:
321 lb.usJoin = LINEJOIN_BEVEL;
322 break;
323 case RoundJoin:
324 lb.usJoin = LINEJOIN_ROUND;
325 break;
326 case MiterJoin:
327 lb.usJoin = LINEJOIN_MITRE;
328 break;
329 case MPenJoinStyle:
330 LineAttrs &= ~LBB_JOIN;
331#if defined(QT_CHECK_STATE)
332 qWarning("QPainter::updatePen: MPenJoinStyle used as a value. It is a mask!");
333#endif
334 break;
335 }
336 LineAttrs |= LBB_GEOM_WIDTH;
337 lb.lGeomWidth = cpen.width();
338 }
339 GpiSetAttrs( hps, PRIM_LINE, LineAttrs, 0, (PBUNDLE) &lb );
340 // also set the image color attribute used to draw bitmaps
341 // and char color attribute for drawing text
342 GpiSetAttrs( hps, PRIM_IMAGE, IBB_COLOR, 0, (PBUNDLE) &lb );
343 GpiSetAttrs( hps, PRIM_CHAR, CBB_COLOR, 0, (PBUNDLE) &lb );
344}
345
346void QPainter::updateBrush()
347{
348 if ( testf(ExtDev) ) {
349 QPDevCmdParam param[1];
350 param[0].brush = &cbrush;
351 if ( !pdev->cmd(QPaintDevice::PdcSetBrush,this,param) || !hps )
352 return;
353 }
354
355 AREABUNDLE ab;
356 ab.lColor = COLOR_VALUE( cbrush.data->color );
357 GpiSetAttrs( hps, PRIM_AREA, ABB_COLOR, 0, &ab );
358
359 // pattern attrs make sence for area attrs only, so we use global
360 // GpiSetPattern*() calls below (it's necessary to set them separately).
361
362 // delete the brush LCID if any
363 if ( hbrushbm ) {
364 GpiSetPatternSet( hps, LCID_DEFAULT );
365 GpiDeleteSetId( hps, LCID_QTPixmapBrush );
366 }
367
368 /// @todo (dmik) possibly pixmapBrush is not needed, hbrushbm and
369 // CustomPattern can substitute it.
370 pixmapBrush = nocolBrush = FALSE;
371 hbrushbm = 0;
372
373 int bs = cbrush.style();
374 if ( bs == CustomPattern ) {
375 HPS hbrushps = cbrush.pixmap()->hps;
376 hbrushbm = cbrush.pixmap()->hbm();
377 pixmapBrush = TRUE;
378 nocolBrush = cbrush.pixmap()->depth() == 1;
379 GpiSetBitmap( hbrushps, 0 );
380 GpiSetBitmapId( hps, hbrushbm, LCID_QTPixmapBrush );
381 GpiSetPatternSet( hps, LCID_QTPixmapBrush );
382 GpiSetBitmap( hbrushps, hbrushbm );
383 } else {
384 LONG pat;
385 if ( bs >= Dense1Pattern && bs <= Dense7Pattern ) {
386 pat = PATSYM_DENSE2 + (bs - Dense1Pattern);
387 } else {
388 switch ( bs ) {
389 case NoBrush:
390 pat = PATSYM_BLANK;
391 break;
392 case SolidPattern:
393 pat = PATSYM_SOLID;
394 break;
395 case HorPattern:
396 pat = PATSYM_HORIZ;
397 break;
398 case VerPattern:
399 pat = PATSYM_VERT;
400 break;
401 case CrossPattern:
402 pat = PATSYM_HATCH;
403 break;
404 case BDiagPattern:
405 pat = PATSYM_DIAG1;
406 break;
407 case FDiagPattern:
408 pat = PATSYM_DIAG3;
409 break;
410 case DiagCrossPattern:
411 pat = PATSYM_DIAGHATCH;
412 break;
413 default:
414 pat = PATSYM_HORIZ;
415 }
416 }
417 nocolBrush = TRUE;
418 GpiSetPatternSet( hps, LCID_DEFAULT );
419 GpiSetPattern( hps, pat );
420 }
421}
422
423
424bool QPainter::begin( const QPaintDevice *pd, bool unclipped )
425{
426 if ( isActive() ) { // already active painting
427#if defined(QT_CHECK_STATE)
428 qWarning( "QPainter::begin: Painter is already active."
429 "\n\tYou must end() the painter before a second begin()" );
430#endif
431 return FALSE;
432 }
433 if ( pd == 0 ) {
434#if defined(QT_CHECK_NULL)
435 qWarning( "QPainter::begin: Paint device cannot be null" );
436#endif
437 return FALSE;
438 }
439
440 const QWidget *copyFrom = 0;
441 pdev = redirect( (QPaintDevice*)pd );
442 if ( pdev ) { // redirected paint device?
443 if ( pd->devType() == QInternal::Widget )
444 copyFrom = (const QWidget *)pd; // copy widget settings
445 } else {
446 pdev = (QPaintDevice*)pd;
447 }
448
449 if ( pdev->isExtDev() && pdev->paintingActive() ) { // somebody else is already painting
450#if defined(QT_CHECK_STATE)
451 qWarning( "QPainter::begin: Another QPainter is already painting "
452 "this device;\n\tAn extended paint device can only be "
453 "painted by one QPainter at a time" );
454#endif
455 return FALSE;
456 }
457
458 bool reinit = flags != IsStartingUp; // 2nd, 3rd,.... time called
459 flags = 0x0; // init flags
460 int dt = pdev->devType(); // get the device type
461
462 if ( (pdev->devFlags & QInternal::ExternalDevice) != 0 )
463 setf(ExtDev); // this is an extended device
464 if ( (pdev->devFlags & QInternal::CompatibilityMode) != 0 )
465 setf(Qt2Compat);
466 else if ( dt == QInternal::Pixmap ) { // device is a pixmap
467 QPixmap *pm = (QPixmap*) pdev; // will modify it
468 pm->detach();
469 // Ensure the auxiliary masked bitmap created for masking is destroyed
470 // (to cause it to be recreated when the pixmap is blitted next time).
471 // Also ensure the alpha channel of the pixmap is unfolded -- any GPI
472 // call will clear the high byte that may be storing the alpha bits,
473 // so the pixmap will appear as fully transparent.
474 if ( pm->data->mask )
475 pm->prepareForMasking( FALSE, TRUE );
476 if ( pm->data->hasRealAlpha )
477 pm->unfoldAlphaChannel();
478 }
479
480 hps = 0;
481 holdpal = 0;
482 holdrgn = HRGN_ERROR;
483 devh = 0;
484
485 QRegion *repaintRgn = 0;
486
487 if ( testf(ExtDev) ) { // external device
488 if ( !pdev->cmd(QPaintDevice::PdcBegin,this,0) ) {
489 pdev = 0; // could not begin
490 clearf( IsActive | DirtyFont | ExtDev );
491 return FALSE;
492 }
493 if ( tabstops ) // update tabstops for device
494 setTabStops( tabstops );
495 if ( tabarray ) // update tabarray for device
496 setTabArray( tabarray );
497 }
498
499 setf( IsActive );
500 pdev->painters++; // also tell paint device
501 bro = QPoint( 0, 0 );
502 if ( reinit ) {
503 bg_mode = TransparentMode; // default background mode
504 rop = CopyROP; // default ROP
505 wxmat.reset(); // reset world xform matrix
506 xmat.reset();
507 ixmat.reset();
508 txop = txinv = 0;
509 if ( dt != QInternal::Widget ) {
510 QFont defaultFont; // default drawing tools
511 QPen defaultPen;
512 QBrush defaultBrush;
513 cfont = defaultFont; // set these drawing tools
514 cpen = defaultPen;
515 cbrush = defaultBrush;
516 bg_col = white; // default background color
517 }
518 }
519 wx = wy = vx = vy = 0; // default view origins
520 ww = 0;
521
522 if ( dt == QInternal::Widget ) { // device is a widget
523 QWidget *w = (QWidget*)pdev;
524 cfont = w->font(); // use widget font
525 cpen = QPen( w->foregroundColor() ); // use widget fg color
526 if ( reinit ) {
527 QBrush defaultBrush;
528 cbrush = defaultBrush;
529 }
530 bg_col = w->backgroundColor(); // use widget bg color
531 ww = vw = w->width(); // default view size
532 wh = vh = w->height();
533 devh = wh; // for final y coord. flipping
534 if ( !w->isDesktop() )
535 repaintRgn = // clip rgn passed from repaint()
536 (QRegion *) WinQueryWindowULong( w->winId(), QWL_QTCLIPRGN );
537 if ( w->testWState(Qt::WState_InPaintEvent) ) {
538 hps = w->hps; // during paint event
539 } else {
540 hps = w->getTargetPS ( unclipped ? QWidget::Unclipped :
541 QWidget::ClipDefault );
542 w->hps = hps;
543 }
544 } else if ( dt == QInternal::Pixmap ) { // device is a pixmap
545 QPixmap *pm = (QPixmap*)pdev;
546 if ( pm->isNull() ) {
547#if defined(QT_CHECK_NULL)
548 qWarning( "QPainter::begin: Cannot paint null pixmap" );
549#endif
550 end();
551 return FALSE;
552 }
553 hps = pm->hps;
554 ww = vw = pm->width(); // default view size
555 wh = vh = pm->height();
556 devh = wh; // for final y coord. flipping
557 if ( pm->depth() == 1 ) { // monochrome pixmap
558 setf( MonoDev );
559 bg_col = color0;
560 cpen.setColor( color1 );
561 }
562/// @todo (dmik) later
563// } else if ( dt == QInternal::Printer ) { // device is a printer
564// if ( pdev->handle() )
565// hdc = pdev->handle();
566// flags |= (NoCache | RGBColor);
567// if ( qt_winver & WV_DOS_based )
568// flags |= VolatileDC;
569 } else if ( dt == QInternal::System ) { // system-dependent device
570 hps = pdev->handle();
571 if ( hps ) {
572 SIZEL sz;
573 GpiQueryPS( hps, &sz );
574 ww = vw = sz.cx;
575 wh = vh = sz.cy;
576 }
577 }
578 if ( testf(ExtDev) ) {
579 ww = vw = pdev->metric( QPaintDeviceMetrics::PdmWidth );
580 wh = vh = pdev->metric( QPaintDeviceMetrics::PdmHeight );
581 }
582 if ( ww == 0 )
583 ww = wh = vw = vh = 1024;
584 if ( copyFrom ) { // copy redirected widget
585 cfont = copyFrom->font();
586 cpen = QPen( copyFrom->foregroundColor() );
587 bg_col = copyFrom->backgroundColor();
588 repaintRgn =
589 (QRegion *) WinQueryWindowULong( copyFrom->winId(), QWL_QTCLIPRGN );
590 }
591 if ( testf(ExtDev) && hps == 0 ) { // external device
592 setBackgroundColor( bg_col ); // default background color
593 setBackgroundMode( TransparentMode ); // default background mode
594 setRasterOp( CopyROP ); // default raster operation
595 }
596 if ( hps ) { // initialize hps
597 if ( QColor::hPal() && dt != QInternal::Printer ) {
598 holdpal = GpiSelectPalette( hps, QColor::hPal() );
599 } else {
600 // direct RGB mode
601 GpiCreateLogColorTable( hps, 0, LCOLF_RGB, 0, 0, NULL );
602 }
603 qt_GpiSetBackColor( hps, COLOR_VALUE( bg_col ) );
604 GpiSetMix( hps, FM_OVERPAINT );
605 qt_GpiSetBackMix( hps, BM_LEAVEALONE, cpen.style() == NoPen );
606 GpiSetTextAlignment( hps, TA_NORMAL_HORIZ, TA_BASE );
607 }
608 updatePen();
609 updateBrush();
610 setBrushOrigin( 0, 0 );
611 if ( hps ) {
612 holdrgn = GpiQueryClipRegion( hps );
613 if ( repaintRgn ) {
614 // ensure that def_crgn is not null after assignment in any case
615 def_crgn = repaintRgn->isNull() ? QRegion( FALSE ) : *repaintRgn;
616 }
617 // clip region in GPI cannot be used for anything else, so detach it
618 def_crgn.detach();
619 if ( holdrgn ) {
620 // intersect the original clip region and the region from repaint()
621 if ( def_crgn.isNull() )
622 def_crgn = QRegion( 0, 0, ww, wh );
623 GpiSetClipRegion( hps, 0, NULL ); // deselect holdrgn
624 HRGN hrgn = def_crgn.handle( devh );
625 GpiCombineRegion( hps, hrgn, hrgn, holdrgn, CRGN_AND );
626 }
627 cur_crgn = def_crgn;
628 if ( !cur_crgn.isNull() )
629 GpiSetClipRegion( hps, cur_crgn.handle( devh ), NULL );
630 } else {
631 holdrgn = HRGN_ERROR;
632 }
633 setf(DirtyFont);
634
635 return TRUE;
636}
637
638bool QPainter::end()
639{
640 if ( !isActive() ) {
641#if defined(QT_CHECK_STATE)
642 qWarning( "QPainter::end: Missing begin() or begin() failed" );
643#endif
644 return FALSE;
645 }
646
647 killPStack();
648
649 // delete the brush LCID if any
650 if ( hbrushbm ) {
651 GpiSetPatternSet( hps, LCID_DEFAULT );
652 GpiDeleteSetId( hps, LCID_QTPixmapBrush );
653 hbrushbm = 0;
654 }
655 if ( holdrgn != HRGN_ERROR ) {
656 GpiSetClipRegion( hps, holdrgn, NULL );
657 holdrgn = HRGN_ERROR;
658 }
659 if ( holdpal ) {
660 GpiSelectPalette( hps, holdpal );
661 holdpal = 0;
662 }
663 if ( !pdev )
664 return FALSE;
665
666 if ( testf(ExtDev) )
667 pdev->cmd( QPaintDevice::PdcEnd, this, 0 );
668
669 if ( pdev->devType() == QInternal::Widget ) {
670 if ( !((QWidget*)pdev)->testWState(Qt::WState_InPaintEvent) ) {
671 QWidget *w = (QWidget*)pdev;
672 WinReleasePS( w->hps );
673 w->hps = 0;
674 }
675 }
676
677 if ( !def_crgn.isNull() )
678 def_crgn = QRegion();
679 cur_crgn = def_crgn;
680
681 if ( pfont ) {
682 delete pfont;
683 pfont = 0;
684 }
685 // cleanup printer font engines that could have been created by updateFont()
686 if( testf(ExtDev) )
687 QFontCache::instance->cleanupPrinterFonts();
688
689 flags = 0;
690 pdev->painters--;
691 pdev = 0;
692 hps = 0;
693 return TRUE;
694}
695
696void QPainter::flush(const QRegion &, CoordinateMode)
697{
698 flush();
699}
700
701void QPainter::flush()
702{
703}
704
705
706void QPainter::setBackgroundColor( const QColor &c )
707{
708 if ( !isActive() ) {
709#if defined(QT_CHECK_STATE)
710 qWarning( "QPainter::setBackgroundColor: Call begin() first" );
711#endif
712 return;
713 }
714 bg_col = c;
715 if ( testf(ExtDev) ) {
716 QPDevCmdParam param[1];
717 param[0].color = &bg_col;
718 if ( !pdev->cmd(QPaintDevice::PdcSetBkColor,this,param) || !hps )
719 return;
720 }
721 // set the back color for all primitives
722 qt_GpiSetBackColor( hps, COLOR_VALUE(c) );
723}
724
725// background rop codes are the same as foreground according to os2tk45
726static const LONG qt_ropCodes[] = {
727 FM_OVERPAINT, // CopyROP
728 FM_OR, // OrROP
729 FM_XOR, // XorROP
730 FM_SUBTRACT, // NotAndROP
731 FM_NOTCOPYSRC, // NotCopyROP
732 FM_MERGENOTSRC, // NotOrROP
733 FM_NOTXORSRC, // NotXorROP ??
734 FM_AND, // AndROP
735 FM_INVERT, // NotROP
736 FM_ZERO, // ClearROP
737 FM_ONE, // SetROP
738 FM_LEAVEALONE, // NopROP
739 FM_MASKSRCNOT, // AndNotROP
740 FM_MERGESRCNOT, // OrNotROP
741 FM_NOTMASKSRC, // NandROP
742 FM_NOTMERGESRC // NorROP
743};
744
745void QPainter::setBackgroundMode( BGMode m )
746{
747 if ( !isActive() ) {
748#if defined(QT_CHECK_STATE)
749 qWarning( "QPainter::setBackgroundMode: Call begin() first" );
750#endif
751 return;
752 }
753 if ( m != TransparentMode && m != OpaqueMode ) {
754#if defined(QT_CHECK_RANGE)
755 qWarning( "QPainter::setBackgroundMode: Invalid mode" );
756#endif
757 return;
758 }
759 bg_mode = m;
760 if ( testf(ExtDev) ) {
761 QPDevCmdParam param[1];
762 param[0].ival = m;
763 if ( !pdev->cmd(QPaintDevice::PdcSetBkMode,this,param) || !hps )
764 return;
765 }
766 // set the back mix for all primitives
767 qt_GpiSetBackMix(
768 hps, m == TransparentMode ? BM_LEAVEALONE : qt_ropCodes[rop],
769 cpen.style() == NoPen
770 );
771}
772
773void QPainter::setRasterOp( RasterOp r )
774{
775 if ( !isActive() ) {
776#if defined(QT_CHECK_STATE)
777 qWarning( "QPainter::setRasterOp: Call begin() first" );
778#endif
779 return;
780 }
781 if ( (uint)r > LastROP ) {
782#if defined(QT_CHECK_RANGE)
783 qWarning( "QPainter::setRasterOp: Invalid ROP code" );
784#endif
785 return;
786 }
787 rop = r;
788 if ( testf(ExtDev) ) {
789 QPDevCmdParam param[1];
790 param[0].ival = r;
791 if ( !pdev->cmd(QPaintDevice::PdcSetROP,this,param) || !hps )
792 return;
793 }
794 // set the mixes for all primitives
795 GpiSetMix( hps, qt_ropCodes[r] );
796 if ( bg_mode != TransparentMode )
797 qt_GpiSetBackMix( hps, qt_ropCodes[r], cpen.style() == NoPen );
798}
799
800void QPainter::setBrushOrigin( int x, int y )
801{
802 if ( !isActive() ) {
803#if defined(QT_CHECK_STATE)
804 qWarning( "QPainter::setBrushOrigin: Call begin() first" );
805#endif
806 return;
807 }
808 bro = QPoint(x,y);
809 if ( testf(ExtDev) ) {
810 QPDevCmdParam param[1];
811 param[0].point = &bro;
812 if ( !pdev->cmd(QPaintDevice::PdcSetBrushOrigin,this,param) || !hps )
813 return;
814 }
815 POINTL ptl = { x, y };
816 // make y coordinate bottom-relative
817 if ( devh ) ptl.y = devh - ptl.y;
818 GpiSetPatternRefPoint( hps, &ptl );
819}
820
821/**
822 * Sets up a native transformation matrix on this painter.
823 * If the paint device has a defined height (i.e. it's QPixmap or
824 * QWidget), the matrix is adjusted to automatically flip the y axis
825 * (to make it increase downwards).
826 *
827 * @param assumeYNegation
828 * If true, it is assumed that all y coordinates are negated before
829 * passing them to GPI calls. This mode is useful for drawing text
830 * because font letters are already flipped by GPI when the text is drawn,
831 * so using full y axis flip would flip letters again (that is definitely
832 * unwanted).
833 */
834bool QPainter::setNativeXForm( bool assumeYNegation )
835{
836 QWMatrix mtx;
837
838 if ( testf(VxF) ) {
839 mtx.translate( vx, vy );
840 mtx.scale( 1.0*vw/ww, 1.0*vh/wh );
841 mtx.translate( -wx, -wy );
842 mtx = wxmat * mtx;
843 } else {
844 mtx = wxmat;
845 }
846
847 // Qt rotates clockwise (despite stated in QWMatrix::rotate() docs),
848 // while GPI rotates counterclockwise. Correct this by changing
849 // m12 and m21 signs and also apply optional y translation.
850
851 MATRIXLF m;
852 m.fxM11 = (FIXED) (mtx.m11() * 65536.0);
853 m.fxM12 = (FIXED) (-mtx.m12() * 65536.0);
854 m.fxM21 = (FIXED) (-mtx.m21() * 65536.0);
855 m.fxM22 = (FIXED) (mtx.m22() * 65536.0);
856 m.lM31 = (LONG) mtx.dx();
857 m.lM32 = devh && assumeYNegation ? ((LONG) -mtx.dy()) : (LONG) mtx.dy();
858 m.lM13 = m.lM23 = 0;
859
860 BOOL ok = GpiSetDefaultViewMatrix( hps, 8, &m, TRANSFORM_REPLACE );
861 if (ok && devh) {
862 // add a matrix to do automatic y flip or just to move negative
863 // y coordinates back to the viewable area
864 m.fxM11 = MAKEFIXED( 1, 0 );
865 m.fxM12 = 0;
866 m.fxM21 = 0;
867 m.fxM22 = assumeYNegation ? MAKEFIXED( 1, 0 ) : MAKEFIXED( -1, 0 );
868 m.lM31 = 0;
869 m.lM32 = devh - 1;
870 m.lM13 = m.lM23 = 0;
871 ok = GpiSetDefaultViewMatrix( hps, 8, &m, TRANSFORM_ADD );
872 }
873
874 return ok;
875}
876
877void QPainter::clearNativeXForm()
878{
879 // restore identity matrix
880 GpiSetDefaultViewMatrix( hps, 0, NULL, TRANSFORM_REPLACE );
881}
882
883void QPainter::setClipping( bool enable )
884{
885 if ( !isActive() ) {
886#if defined(QT_CHECK_STATE)
887 qWarning( "QPainter::setClipping: Will be reset by begin()" );
888#endif
889 return;
890 }
891
892 if ( !isActive() || enable == testf(ClipOn) )
893 return;
894
895 setf( ClipOn, enable );
896 if ( testf(ExtDev) ) {
897 if ( block_ext )
898 return;
899 QPDevCmdParam param[1];
900 param[0].ival = enable;
901 if ( !pdev->cmd(QPaintDevice::PdcSetClip,this,param) || !hps )
902 return;
903 }
904
905 // the current clip region is stored in cur_crgn, so deselect the handle
906 // to let us operate on it. Note that the assignment op below will destroy
907 // the handle if it is no more referred, so there is no need to do it
908 // explicitly.
909 GpiSetClipRegion( hps, 0, NULL );
910
911 if ( enable ) {
912 if ( !def_crgn.isNull() )
913 cur_crgn = crgn.intersect( def_crgn );
914 else
915 cur_crgn = crgn.isNull() ? QRegion( FALSE ) : crgn;
916#ifndef QT_NO_PRINTER
917/// @todo (dmik) later
918// if ( pdev->devType() == QInternal::Printer ) {
919// double xscale = ((float)pdev->metric( QPaintDeviceMetrics::PdmPhysicalDpiX )) /
920// ((float)pdev->metric( QPaintDeviceMetrics::PdmDpiX ));
921// double yscale = ((float)pdev->metric( QPaintDeviceMetrics::PdmPhysicalDpiY )) /
922// ((float)pdev->metric( QPaintDeviceMetrics::PdmDpiY ));
923// double xoff = 0;
924// double yoff = 0;
925// QPrinter* printer = (QPrinter*)pdev;
926// if ( printer->fullPage() ) { // must adjust for margins
927// xoff = - GetDeviceCaps( printer->handle(), PHYSICALOFFSETX );
928// yoff = - GetDeviceCaps( printer->handle(), PHYSICALOFFSETY );
929// }
930// rgn = QWMatrix( xscale, 0, 0, yscale, xoff, yoff ) * rgn;
931// }
932#endif
933 // clip region in GPI cannot be used for anything else, so detach it
934 cur_crgn.detach();
935 } else {
936 cur_crgn = def_crgn;
937 }
938
939 if ( !cur_crgn.isNull() )
940 GpiSetClipRegion( hps, cur_crgn.handle( devh ), NULL );
941}
942
943
944void QPainter::setClipRect( const QRect &r, CoordinateMode m )
945{
946 QRegion rgn( r );
947 setClipRegion( rgn, m );
948}
949
950void QPainter::setClipRegion( const QRegion &rgn, CoordinateMode m )
951{
952#if defined(QT_CHECK_STATE)
953 if ( !isActive() )
954 qWarning( "QPainter::setClipRegion: Will be reset by begin()" );
955#endif
956 if ( m == CoordDevice )
957 crgn = rgn;
958 else
959 crgn = xmat * rgn;
960
961 if ( testf(ExtDev) ) {
962 if ( block_ext )
963 return;
964 QPDevCmdParam param[2];
965 param[0].rgn = &rgn;
966 param[1].ival = m;
967 if ( !pdev->cmd(QPaintDevice::PdcSetClipRegion,this,param) || !hps )
968 return;
969 }
970 clearf( ClipOn ); // be sure to update clip rgn
971 setClipping( TRUE );
972}
973
974// Note: this function does not check the array range!
975void QPainter::drawPolyInternal(
976 const QPointArray &a, bool close, bool winding, int index, int npoints,
977 bool disjoint
978)
979{
980 // QCOORD is a double word that is the same as POINTL members, and since
981 // the order of xp and yp fields in the QPoint is also the same, we simply
982 // cast QPointArray::data() to PPOINTL below.
983
984 if ( npoints < 0 )
985 npoints = a.size() - index;
986
987 QPointArray pa = a;
988 // flip y coordinates
989 if ( devh ) {
990 pa = QPointArray( npoints );
991 for ( int i = 0; i < npoints; i++ ) {
992 pa[i].setX( a[index+i].x() );
993 pa[i].setY( devh - (a[index+i].y() + 1) );
994 }
995 index = 0;
996 }
997
998 ULONG opts = winding ? BA_WINDING : BA_ALTERNATE;
999 const PPOINTL ptls = ((PPOINTL) pa.data()) + index;
1000 if ( cpen.width() > 1 ) {
1001 if ( close ) {
1002 GpiBeginArea( hps, BA_NOBOUNDARY | opts );
1003 if ( disjoint ) {
1004 GpiPolyLineDisjoint( hps, npoints, ptls );
1005 } else {
1006 GpiMove( hps, ptls );
1007 GpiPolyLine( hps, npoints - 1, ptls + 1 );
1008 }
1009 GpiEndArea( hps );
1010 }
1011 qt_geom_line_handle gh;
1012 qt_begin_geom_line( hps, COLOR_VALUE(cpen.color()), cpen.style() == NoPen, &gh );
1013 if ( disjoint ) {
1014 GpiPolyLineDisjoint( hps, npoints, ptls );
1015 } else {
1016 GpiMove( hps, ptls );
1017 GpiPolyLine( hps, npoints - 1, ptls + 1 );
1018 }
1019 if ( close )
1020 GpiCloseFigure( hps );
1021 qt_end_geom_line( hps, &gh );
1022 } else {
1023 if ( close )
1024 GpiBeginArea( hps, BA_BOUNDARY | opts );
1025 else
1026 GpiBeginPath( hps, 1 );
1027 if ( disjoint ) {
1028 GpiPolyLineDisjoint( hps, npoints, ptls );
1029 } else {
1030 GpiMove( hps, ptls );
1031 GpiPolyLine( hps, npoints - 1, ptls + 1 );
1032 }
1033 if ( close )
1034 GpiEndArea( hps );
1035 else {
1036 GpiEndPath( hps );
1037 GpiOutlinePath( hps, 1, 0 );
1038 }
1039 }
1040}
1041
1042// angles in Qt are 1/16th of a degree, so the integer part of FIXED is
1043// angle/16 and the fixed part is (65536/16) * angle%16.
1044#define MAKEDEGREE(a16) MAKEFIXED((a16>>4), (a16<<12))
1045
1046static void doDrawArc( HPS hps, PPOINTL cptl, int first, int a, int alen )
1047{
1048 if ( first >= 0 ) {
1049 int sa, ea;
1050 for ( int i = first; i < 4; i++, cptl++ ) {
1051 sa = i * 90*16;
1052 ea = sa + 90*16;
1053 if (sa < a) sa = a;
1054 if (ea > a + alen) ea = a + alen;
1055 if (ea <= sa) break;
1056 ea -= sa;
1057 GpiPartialArc( hps, cptl, MAKEFIXED(1,0), MAKEDEGREE(sa), MAKEDEGREE(ea) );
1058 }
1059 } else {
1060 GpiPartialArc( hps, cptl, MAKEFIXED(1,0), MAKEDEGREE(a), MAKEDEGREE(alen) );
1061 }
1062}
1063
1064void QPainter::drawArcInternal(
1065 int x, int y, int w, int h, int a, int alen, ArcClose close
1066)
1067{
1068 if ( w <= 0 || h <= 0 ) {
1069 if ( w == 0 || h == 0 )
1070 return;
1071 fix_neg_rect( &x, &y, &w, &h );
1072 }
1073
1074 const int FullArc = 360*16;
1075 // correct angles
1076 if ( alen < 0 ) {
1077 a += alen;
1078 alen = -alen;
1079 }
1080 alen = QMIN( alen, FullArc );
1081 if ( a < 0 )
1082 a = FullArc - ((-a) % (FullArc));
1083 else
1084 a = a % FullArc;
1085
1086 int axr = (w - 1) / 2;
1087 int ayr = (h - 1) / 2;
1088 ARCPARAMS arcparams = { axr, ayr, 0, 0 };
1089 GpiSetArcParams( hps, &arcparams );
1090 int xc, yc;
1091 xc = x + axr;
1092 yc = y + ayr;
1093 // flip y coordinate
1094 if ( devh ) yc = devh - (yc + 1);
1095
1096 POINTL sptl = { xc, yc }, cptls [4];
1097 PPOINTL cptl = cptls;
1098 int first = -1;
1099
1100 // in order to draw arcs whose bounding reactangle has even width
1101 // and/or height we draw each quarter of the arc separately,
1102 // correspondingly moving its center point by one pixel.
1103 int extX = (w - 1) % 2;
1104 int extY = (h - 1) % 2;
1105 if ( extX != 0 || extY != 0 ) {
1106 for ( int i = 0, sa = 90*16; i < 4; i++, sa += 90*16, cptl++ ) {
1107 if ( first < 0 && sa > a )
1108 first = i;
1109 if ( first >= 0 ) {
1110 switch ( i ) {
1111 case 0: cptl->x = xc + extX; cptl->y = yc; break;
1112 case 1: cptl->x = xc; cptl->y = yc; break;
1113 case 2: cptl->x = xc; cptl->y = yc - extY; break;
1114 case 3: cptl->x = xc + extX; cptl->y = yc - extY; break;
1115 }
1116 }
1117 }
1118 cptl = cptls + first;
1119 } else {
1120 *cptl = sptl;
1121 }
1122
1123 if ( close != ClosePie ) {
1124 // set the current position to the start of the arc
1125 LONG oldMix = GpiQueryMix( hps );
1126 LONG oldType = GpiQueryLineType( hps );
1127 GpiSetMix( hps, FM_LEAVEALONE );
1128 GpiSetLineType( hps, LINETYPE_SOLID );
1129 GpiPartialArc( hps, cptl, MAKEFIXED(1,0), MAKEDEGREE(a), 0 );
1130 GpiSetLineType( hps, oldType );
1131 GpiSetMix( hps, oldMix );
1132 // cause the start point to be drawn (seems like a bug in OS/2 GPI)
1133 GpiQueryCurrentPosition( hps, &sptl );
1134 }
1135
1136 if ( cpen.width() > 1 ) {
1137 if ( close != CloseNone ) {
1138 GpiBeginArea( hps, BA_NOBOUNDARY | BA_ALTERNATE );
1139 GpiMove( hps, &sptl );
1140 doDrawArc( hps, cptl, first, a, alen );
1141 GpiEndArea( hps );
1142 }
1143 qt_geom_line_handle gh;
1144 qt_begin_geom_line( hps, COLOR_VALUE(cpen.color()),
1145 cpen.style() == NoPen, &gh );
1146
1147 GpiMove( hps, &sptl );
1148 doDrawArc( hps, cptl, first, a, alen );
1149
1150 if ( close != CloseNone )
1151 GpiCloseFigure( hps );
1152 qt_end_geom_line( hps, &gh );
1153 } else {
1154 if ( close != CloseNone )
1155 GpiBeginArea( hps, BA_BOUNDARY | BA_ALTERNATE );
1156 else
1157 GpiBeginPath( hps, 1 );
1158
1159 GpiMove( hps, &sptl );
1160 doDrawArc( hps, cptl, first, a, alen );
1161
1162 if ( close != CloseNone )
1163 GpiEndArea( hps );
1164 else {
1165 GpiEndPath( hps );
1166 GpiOutlinePath( hps, 1, 0 );
1167 }
1168 }
1169}
1170
1171void QPainter::drawPoint( int x, int y )
1172{
1173 if ( !isActive() )
1174 return;
1175 if ( testf(ExtDev|VxF|WxF) ) {
1176 if ( testf(ExtDev) ) {
1177 QPDevCmdParam param[1];
1178 QPoint p( x, y );
1179 param[0].point = &p;
1180 if ( !pdev->cmd(QPaintDevice::PdcDrawPoint,this,param) || !hps )
1181 return;
1182 }
1183 map( x, y, &x, &y );
1184 }
1185 POINTL ptl = { x, y };
1186 if ( devh ) ptl.y = devh - ( ptl.y + 1 );
1187 GpiSetPel( hps, &ptl );
1188}
1189
1190
1191void QPainter::drawPoints( const QPointArray& a, int index, int npoints )
1192{
1193 if ( npoints < 0 )
1194 npoints = a.size() - index;
1195 if ( index + npoints > (int)a.size() )
1196 npoints = a.size() - index;
1197 if ( !isActive() || npoints < 1 || index < 0 )
1198 return;
1199 QPointArray pa = a;
1200 if ( testf(ExtDev|VxF|WxF) ) {
1201 if ( testf(ExtDev) ) {
1202 QPDevCmdParam param[1];
1203 for (int i=0; i<npoints; i++) {
1204 QPoint p( pa[index+i].x(), pa[index+i].y() );
1205 param[0].point = &p;
1206 if ( !pdev->cmd(QPaintDevice::PdcDrawPoint,this,param))
1207 return;
1208 }
1209 if ( !hps ) return;
1210 }
1211 if ( txop != TxNone ) {
1212 pa = xForm( a, index, npoints );
1213 if ( pa.size() != a.size() ) {
1214 index = 0;
1215 npoints = pa.size();
1216 }
1217 }
1218 }
1219 for (int i=0; i<npoints; i++) {
1220 POINTL ptl = { pa[index+i].x(), pa[index+i].y() };
1221 if ( devh ) ptl.y = devh - ( ptl.y + 1 );
1222 GpiSetPel( hps, &ptl );
1223 }
1224}
1225
1226void QPainter::moveTo( int x, int y )
1227{
1228 if ( !isActive() )
1229 return;
1230 if ( testf(ExtDev|VxF|WxF) ) {
1231 if ( testf(ExtDev) ) {
1232 QPDevCmdParam param[1];
1233 QPoint p( x, y );
1234 param[0].point = &p;
1235 if ( !pdev->cmd(QPaintDevice::PdcMoveTo,this,param) || !hps )
1236 return;
1237 }
1238 map( x, y, &x, &y );
1239 }
1240 POINTL ptl = { x, y };
1241 if ( devh ) ptl.y = devh - ( ptl.y + 1 );
1242 GpiMove( hps, &ptl );
1243}
1244
1245
1246void QPainter::lineTo( int x, int y )
1247{
1248 if ( !isActive() )
1249 return;
1250 if ( testf(ExtDev|VxF|WxF) ) {
1251 if ( testf(ExtDev) ) {
1252 QPDevCmdParam param[1];
1253 QPoint p( x, y );
1254 param[0].point = &p;
1255 if ( !pdev->cmd(QPaintDevice::PdcLineTo,this,param) || !hps )
1256 return;
1257 }
1258 map( x, y, &x, &y );
1259 }
1260
1261 qt_geom_line_handle gh;
1262 if ( cpen.width() > 1 ) {
1263 qt_begin_geom_line( hps, COLOR_VALUE(cpen.color()), cpen.style() == NoPen, &gh );
1264 }
1265
1266 POINTL ptl = { x, y };
1267 if ( devh ) ptl.y = devh - ( ptl.y + 1 );
1268 GpiLine( hps, &ptl );
1269
1270 if ( cpen.width() > 1 ) {
1271 qt_end_geom_line( hps, &gh );
1272 }
1273}
1274
1275
1276void QPainter::drawLine( int x1, int y1, int x2, int y2 )
1277{
1278 if ( !isActive() )
1279 return;
1280 if ( testf(ExtDev|VxF|WxF) ) {
1281 if ( testf(ExtDev) ) {
1282 QPDevCmdParam param[2];
1283 QPoint p1(x1, y1), p2(x2, y2);
1284 param[0].point = &p1;
1285 param[1].point = &p2;
1286 if ( !pdev->cmd(QPaintDevice::PdcDrawLine,this,param) || !hps )
1287 return;
1288 }
1289 map( x1, y1, &x1, &y1 );
1290 map( x2, y2, &x2, &y2 );
1291 }
1292
1293 qt_geom_line_handle gh;
1294 if ( cpen.width() > 1 ) {
1295 qt_begin_geom_line( hps, COLOR_VALUE(cpen.color()), cpen.style() == NoPen, &gh );
1296 }
1297
1298 POINTL ptl = { x1, y1 };
1299 if ( devh ) ptl.y = devh - (ptl.y + 1);
1300 GpiMove( hps, &ptl );
1301 ptl.x = x2;
1302 ptl.y = y2;
1303 if ( devh ) ptl.y = devh - (ptl.y + 1);
1304 GpiLine( hps, &ptl );
1305
1306 if ( cpen.width() > 1 ) {
1307 qt_end_geom_line( hps, &gh );
1308 }
1309}
1310
1311static void doDrawRect( HPS hps, int x, int y, int w, int h, int axr, int ayr )
1312{
1313 const FIXED RightAngle = MAKEFIXED(90,0);
1314 const FIXED One = MAKEFIXED(1,0);
1315
1316 // top/right coordinates are inclusive
1317 -- w;
1318 -- h;
1319
1320 // beginning of the bottom side
1321 POINTL ptl = { x + axr, y };
1322 GpiMove( hps, &ptl );
1323 // center of the bottom-right arc
1324 ptl.x = x + w - axr; ptl.y = y + ayr;
1325 GpiPartialArc( hps, &ptl, One, MAKEFIXED(270,0), RightAngle );
1326 // center of the top-right arc
1327 /* ptl.x = x + w - axr; */ ptl.y = y + h - ayr;
1328 GpiPartialArc( hps, &ptl, One, MAKEFIXED(0,0), RightAngle );
1329 // center of the top-left arc
1330 ptl.x = x + axr; /* ptl.y = y + h - ayr; */
1331 GpiPartialArc( hps, &ptl, One, RightAngle, RightAngle );
1332 // center of the bottom-left arc
1333 /* ptl.x = x + axr; */ ptl.y = y + ayr;
1334 GpiPartialArc( hps, &ptl, One, MAKEFIXED(180,0), RightAngle );
1335}
1336
1337void QPainter::drawRectInternal(
1338 int x, int y, int w, int h, int wRnd, int hRnd
1339) {
1340 if ( w <= 0 || h <= 0 ) {
1341 if ( w == 0 || h == 0 )
1342 return;
1343 fix_neg_rect( &x, &y, &w, &h );
1344 }
1345
1346 // GpiBox API is buggy when it comes to rounded corners
1347 // combined with filling the interior (see ticket:16).
1348 // The fix is to draw rounded corners ourselves.
1349
1350 if ( wRnd == 0 || hRnd == 0 ) {
1351 POINTL ptl1 = { x, y };
1352 if ( devh ) ptl1.y = devh - ( ptl1.y + h );
1353 POINTL ptl2 = { x + w - 1, ptl1.y + h - 1 };
1354 if ( cpen.width() > 1 ) {
1355 GpiMove( hps, &ptl1 );
1356 GpiBox( hps, DRO_FILL, &ptl2, wRnd, hRnd );
1357 qt_geom_line_handle gh;
1358 qt_begin_geom_line( hps, COLOR_VALUE(cpen.color()),
1359 cpen.style() == NoPen, &gh );
1360 GpiMove( hps, &ptl1 );
1361 GpiBox( hps, DRO_OUTLINE, &ptl2, 0, 0 );
1362 qt_end_geom_line( hps, &gh );
1363 } else {
1364 GpiMove( hps, &ptl1 );
1365 GpiBox( hps, DRO_FILL | DRO_OUTLINE, &ptl2, 0, 0 );
1366 }
1367 return;
1368 }
1369
1370 // flip y coordinate
1371 if ( devh ) y = devh - ( y + h );
1372
1373 int axr = (wRnd - 1) / 2;
1374 int ayr = (hRnd - 1) / 2;
1375 ARCPARAMS arcparams = { axr, ayr, 0, 0 };
1376 GpiSetArcParams( hps, &arcparams );
1377
1378 if ( cpen.width() > 1 ) {
1379 GpiBeginArea( hps, BA_NOBOUNDARY | BA_ALTERNATE );
1380 doDrawRect( hps, x, y, w, h, axr, ayr );
1381 GpiEndArea( hps );
1382 qt_geom_line_handle gh;
1383 qt_begin_geom_line( hps, COLOR_VALUE(cpen.color()),
1384 cpen.style() == NoPen, &gh );
1385 doDrawRect( hps, x, y, w, h, axr, ayr );
1386 GpiCloseFigure( hps );
1387 qt_end_geom_line( hps, &gh );
1388 } else {
1389 GpiBeginArea( hps, BA_BOUNDARY | BA_ALTERNATE );
1390 doDrawRect( hps, x, y, w, h, axr, ayr );
1391 GpiEndArea( hps );
1392 }
1393}
1394
1395void QPainter::drawRect( int x, int y, int w, int h )
1396{
1397 if ( !isActive() )
1398 return;
1399 if ( testf(ExtDev|VxF|WxF) ) {
1400 if ( testf(ExtDev) ) {
1401 QPDevCmdParam param[1];
1402 QRect r( x, y, w, h );
1403 param[0].rect = &r;
1404 if ( !pdev->cmd(QPaintDevice::PdcDrawRect,this,param) || !hps )
1405 return;
1406 }
1407 if ( txop == TxRotShear ) { // rotate/shear polygon
1408 QPointArray a( QRect(x,y,w,h) );
1409 drawPolyInternal( xForm(a) );
1410 return;
1411 }
1412 map( x, y, w, h, &x, &y, &w, &h );
1413 }
1414
1415 drawRectInternal( x, y, w, h, 0, 0 );
1416}
1417
1418void QPainter::drawWinFocusRectInternal(
1419 int x, int y, int w, int h, const QColor &bgCol, bool xormode
1420)
1421{
1422 if ( !isActive() || txop == TxRotShear )
1423 return;
1424
1425 if ( testf(ExtDev|VxF|WxF) ) {
1426 if ( testf(ExtDev) ) {
1427 QPDevCmdParam param[1];
1428 QRect r( x, y, w, h );
1429 param[0].rect = &r;
1430 if ( !pdev->cmd(QPaintDevice::PdcDrawRect,this,param) || !hps )
1431 return;
1432 }
1433 map( x, y, w, h, &x, &y, &w, &h );
1434 }
1435 if ( w <= 0 || h <= 0 ) {
1436 if ( w == 0 || h == 0 )
1437 return;
1438 fix_neg_rect( &x, &y, &w, &h );
1439 }
1440
1441 // flip y coordinate
1442 if ( devh ) y = devh - (y + h);
1443
1444 ULONG la =
1445 LBB_COLOR | LBB_MIX_MODE | LBB_BACK_MIX_MODE | LBB_TYPE;
1446 LINEBUNDLE oldLb, lb;
1447 GpiQueryAttrs( hps, PRIM_LINE, la, &oldLb );
1448 if ( !xormode ) { // black/white mode
1449 if( qGray( bgCol.rgb() ) <= 128 )
1450 lb.lColor = CLR_WHITE;
1451 else
1452 lb.lColor = CLR_BLACK;
1453 lb.usMixMode = FM_OVERPAINT;
1454 } else { // xor mode
1455 lb.lColor = CLR_TRUE;
1456 lb.usMixMode = FM_XOR;
1457 }
1458 lb.usBackMixMode = BM_LEAVEALONE;
1459 lb.usType = LINETYPE_ALTERNATE;
1460 GpiSetAttrs( hps, PRIM_LINE, la, 0, &lb );
1461
1462 POINTL ptl = { x, y };
1463 GpiMove( hps, &ptl );
1464 ptl.x += w - 1;
1465 ptl.y += h - 1;
1466 GpiBox( hps, DRO_OUTLINE, &ptl, 0, 0 );
1467
1468 GpiSetAttrs( hps, PRIM_LINE, la, 0, &oldLb );
1469}
1470
1471void QPainter::drawWinFocusRect( int x, int y, int w, int h, const QColor &bgCol )
1472{
1473 drawWinFocusRectInternal( x, y, w, h, bgCol, FALSE );
1474}
1475
1476void QPainter::drawWinFocusRect( int x, int y, int w, int h )
1477{
1478 if (
1479 pdev->devType() == QInternal::Widget &&
1480 GpiQueryClipRegion( hps )
1481 ) {
1482 // GPI bug: LINETYPE_ALTERNATE lines are drawn wrongly in XOR mode
1483 // when the clip region is set on the hps. we use doublebuffering
1484 // to avoid this.
1485 QSize ws = ((QWidget*) pdev)->size();
1486 POINTL ptls[] = { { 0, 0 }, { ws.width(), ws.height() }, { 0, 0 } };
1487 QPixmap pm( ws );
1488 GpiBitBlt( pm.hps, hps, 3, ptls, ROP_SRCCOPY, BBO_IGNORE );
1489 HPS thisHps = hps;
1490 hps = pm.hps;
1491 drawWinFocusRectInternal( x, y, w, h, bg_col, TRUE );
1492 hps = thisHps;
1493 GpiBitBlt( hps, pm.hps, 3, ptls, ROP_SRCCOPY, BBO_IGNORE );
1494 } else {
1495 drawWinFocusRectInternal( x, y, w, h, bg_col, TRUE );
1496 }
1497}
1498
1499
1500void QPainter::drawRoundRect( int x, int y, int w, int h, int xRnd, int yRnd )
1501{
1502 if ( !isActive() )
1503 return;
1504 if ( xRnd <= 0 || yRnd <= 0 ) {
1505 drawRect( x, y, w, h ); // draw normal rectangle
1506 return;
1507 }
1508 if ( xRnd >= 100 ) // fix ranges
1509 xRnd = 99;
1510 if ( yRnd >= 100 )
1511 yRnd = 99;
1512 if ( testf(ExtDev|VxF|WxF) ) {
1513 if ( testf(ExtDev) ) {
1514 QPDevCmdParam param[3];
1515 QRect r( x, y, w, h );
1516 param[0].rect = &r;
1517 param[1].ival = xRnd;
1518 param[2].ival = yRnd;
1519 if ( !pdev->cmd(QPaintDevice::PdcDrawRoundRect,this,param) || !hps)
1520 return;
1521 }
1522 if ( txop == TxRotShear ) { // rotate/shear polygon
1523 if ( w <= 0 || h <= 0 )
1524 fix_neg_rect( &x, &y, &w, &h );
1525 int rxx = w*xRnd/200;
1526 int ryy = h*yRnd/200;
1527 // were there overflows?
1528 if ( rxx < 0 )
1529 rxx = w/200*xRnd;
1530 if ( ryy < 0 )
1531 ryy = h/200*yRnd;
1532 int rxx2 = 2*rxx;
1533 int ryy2 = 2*ryy;
1534 QPointArray a[4];
1535 a[0].makeArc( x, y, rxx2, ryy2, 1*16*90, 16*90, xmat );
1536 a[1].makeArc( x, y+h-ryy2, rxx2, ryy2, 2*16*90, 16*90, xmat );
1537 a[2].makeArc( x+w-rxx2, y+h-ryy2, rxx2, ryy2, 3*16*90, 16*90, xmat );
1538 a[3].makeArc( x+w-rxx2, y, rxx2, ryy2, 0*16*90, 16*90, xmat );
1539 // ### is there a better way to join QPointArrays?
1540 QPointArray aa;
1541 aa.resize( a[0].size() + a[1].size() + a[2].size() + a[3].size() );
1542 uint j = 0;
1543 for ( int k=0; k<4; k++ ) {
1544 for ( uint i=0; i<a[k].size(); i++ ) {
1545 aa.setPoint( j, a[k].point(i) );
1546 j++;
1547 }
1548 }
1549 drawPolyInternal( aa );
1550 return;
1551 }
1552 map( x, y, w, h, &x, &y, &w, &h );
1553 }
1554
1555 drawRectInternal( x, y, w, h, w*xRnd/99, h*yRnd/99 );
1556}
1557
1558
1559void QPainter::drawEllipse( int x, int y, int w, int h )
1560{
1561 if ( !isActive() )
1562 return;
1563 if ( testf(ExtDev|VxF|WxF) ) {
1564 if ( testf(ExtDev) ) {
1565 QPDevCmdParam param[1];
1566 QRect r( x, y, w, h );
1567 param[0].rect = &r;
1568 if ( !pdev->cmd(QPaintDevice::PdcDrawEllipse,this,param) || !hps )
1569 return;
1570 }
1571 if ( txop == TxRotShear ) { // rotate/shear polygon
1572 QPointArray a;
1573 int increment = cpen.style() == NoPen ? 1 : 0;
1574 a.makeArc( x, y, w+increment, h+increment, 0, 360*16, xmat );
1575 drawPolyInternal( a );
1576 return;
1577 }
1578 map( x, y, w, h, &x, &y, &w, &h );
1579 }
1580
1581 drawArcInternal( x, y, w, h );
1582}
1583
1584
1585void QPainter::drawArc( int x, int y, int w, int h, int a, int alen )
1586{
1587 if ( !isActive() )
1588 return;
1589 if ( testf(ExtDev|VxF|WxF) ) {
1590 if ( testf(ExtDev) ) {
1591 QPDevCmdParam param[3];
1592 QRect r( x, y, w, h );
1593 param[0].rect = &r;
1594 param[1].ival = a;
1595 param[2].ival = alen;
1596 if ( !pdev->cmd(QPaintDevice::PdcDrawArc,this,param) || !hps )
1597 return;
1598 }
1599 if ( txop == TxRotShear ) { // rotate/shear
1600 QPointArray pa;
1601 pa.makeArc( x, y, w, h, a, alen, xmat ); // arc polyline
1602 drawPolyInternal( pa, FALSE );
1603 return;
1604 }
1605 map( x, y, w, h, &x, &y, &w, &h );
1606 }
1607
1608 drawArcInternal( x, y, w, h, a, alen, CloseNone );
1609}
1610
1611
1612void QPainter::drawPie( int x, int y, int w, int h, int a, int alen )
1613{
1614 if ( !isActive() )
1615 return;
1616 if ( testf(ExtDev|VxF|WxF) ) {
1617 if ( testf(ExtDev) ) {
1618 QPDevCmdParam param[3];
1619 QRect r( x, y, w, h );
1620 param[0].rect = &r;
1621 param[1].ival = a;
1622 param[2].ival = alen;
1623 if ( !pdev->cmd(QPaintDevice::PdcDrawPie,this,param) || !hps )
1624 return;
1625 }
1626 if ( txop == TxRotShear ) { // rotate/shear
1627 QPointArray pa;
1628 int increment = cpen.style() == NoPen ? 1 : 0;
1629 pa.makeArc( x, y, w+increment, h+increment, a, alen, xmat ); // arc polyline
1630 int n = pa.size();
1631 int cx, cy;
1632 xmat.map(x+w/2, y+h/2, &cx, &cy);
1633 pa.resize( n+1 );
1634 pa.setPoint( n, cx, cy ); // add legs
1635 drawPolyInternal( pa );
1636 return;
1637 }
1638 map( x, y, w, h, &x, &y, &w, &h );
1639 }
1640
1641 drawArcInternal( x, y, w, h, a, alen, ClosePie );
1642}
1643
1644
1645void QPainter::drawChord( int x, int y, int w, int h, int a, int alen )
1646{
1647 if ( !isActive() )
1648 return;
1649 if ( testf(ExtDev|VxF|WxF) ) {
1650 if ( testf(ExtDev) ) {
1651 QPDevCmdParam param[3];
1652 QRect r( x, y, w, h );
1653 param[0].rect = &r;
1654 param[1].ival = a;
1655 param[2].ival = alen;
1656 if ( !pdev->cmd(QPaintDevice::PdcDrawChord,this,param) || !hps )
1657 return;
1658 }
1659 if ( txop == TxRotShear ) { // rotate/shear
1660 QPointArray pa;
1661 int increment = cpen.style() == NoPen ? 1 : 0;
1662 pa.makeArc( x, y, w+increment, h+increment, a, alen, xmat ); // arc polygon
1663 int n = pa.size();
1664 pa.resize( n+1 );
1665 pa.setPoint( n, pa.at(0) ); // connect endpoints
1666 drawPolyInternal( pa );
1667 return;
1668 }
1669 map( x, y, w, h, &x, &y, &w, &h );
1670 }
1671
1672 drawArcInternal( x, y, w, h, a, alen, CloseChord );
1673}
1674
1675
1676void QPainter::drawLineSegments( const QPointArray &a, int index, int nlines )
1677{
1678 if ( nlines < 0 )
1679 nlines = a.size()/2 - index/2;
1680 if ( index + nlines*2 > (int)a.size() )
1681 nlines = (a.size() - index)/2;
1682 if ( !isActive() || nlines < 1 || index < 0 )
1683 return;
1684 QPointArray pa = a;
1685 if ( testf(ExtDev|VxF|WxF) ) {
1686 if ( testf(ExtDev) ) {
1687 if ( 2*nlines != (int)pa.size() ) {
1688 pa = QPointArray( nlines*2 );
1689 for ( int i=0; i<nlines*2; i++ )
1690 pa.setPoint( i, a.point(index+i) );
1691 index = 0;
1692 }
1693 QPDevCmdParam param[1];
1694 param[0].ptarr = (QPointArray*)&pa;
1695 if ( !pdev->cmd(QPaintDevice::PdcDrawLineSegments,this,param)
1696 || !hps )
1697 return;
1698 }
1699 if ( txop != TxNone ) {
1700 pa = xForm( a, index, nlines*2 );
1701 if ( pa.size() != a.size() ) {
1702 index = 0;
1703 nlines = pa.size()/2;
1704 }
1705 }
1706 }
1707 drawPolyInternal( pa, FALSE, FALSE, index, nlines*2, TRUE );
1708}
1709
1710
1711void QPainter::drawPolyline( const QPointArray &a, int index, int npoints )
1712{
1713 if ( npoints < 0 )
1714 npoints = a.size() - index;
1715 if ( index + npoints > (int)a.size() )
1716 npoints = a.size() - index;
1717 if ( !isActive() || npoints < 2 || index < 0 )
1718 return;
1719 QPointArray pa = a;
1720 if ( testf(ExtDev|VxF|WxF) ) {
1721 if ( testf(ExtDev) ) {
1722 if ( npoints != (int)pa.size() ) {
1723 pa = QPointArray( npoints );
1724 for ( int i=0; i<npoints; i++ )
1725 pa.setPoint( i, a.point(index+i) );
1726 index = 0;
1727 }
1728 QPDevCmdParam param[1];
1729 param[0].ptarr = (QPointArray*)&pa;
1730 if ( !pdev->cmd(QPaintDevice::PdcDrawPolyline,this,param) || !hps )
1731 return;
1732 }
1733 if ( txop != TxNone ) {
1734 pa = xForm( pa, index, npoints );
1735 if ( pa.size() != a.size() ) {
1736 index = 0;
1737 npoints = pa.size();
1738 }
1739 }
1740 }
1741 drawPolyInternal( pa, FALSE, FALSE, index, npoints );
1742}
1743
1744
1745void QPainter::drawConvexPolygon( const QPointArray &pa,
1746 int index, int npoints )
1747{
1748 // Any efficient way?
1749 drawPolygon(pa,FALSE,index,npoints);
1750}
1751
1752void QPainter::drawPolygon( const QPointArray &a, bool winding, int index,
1753 int npoints )
1754{
1755 if ( npoints < 0 )
1756 npoints = a.size() - index;
1757 if ( index + npoints > (int)a.size() )
1758 npoints = a.size() - index;
1759 if ( !isActive() || npoints < 2 || index < 0 )
1760 return;
1761 QPointArray pa = a;
1762 if ( testf(ExtDev|VxF|WxF) ) {
1763 if ( testf(ExtDev) ) {
1764 if ( npoints != (int)a.size() ) {
1765 pa = QPointArray( npoints );
1766 for ( int i=0; i<npoints; i++ )
1767 pa.setPoint( i, a.point(index+i) );
1768 }
1769 QPDevCmdParam param[2];
1770 param[0].ptarr = (QPointArray*)&pa;
1771 param[1].ival = winding;
1772 if ( !pdev->cmd(QPaintDevice::PdcDrawPolygon,this,param) || !hps )
1773 return;
1774 }
1775 if ( txop != TxNone ) {
1776 pa = xForm( a, index, npoints );
1777 if ( pa.size() != a.size() ) {
1778 index = 0;
1779 npoints = pa.size();
1780 }
1781 }
1782 }
1783 drawPolyInternal( pa, TRUE, winding, index, npoints );
1784}
1785
1786#ifndef QT_NO_BEZIER
1787void QPainter::drawCubicBezier( const QPointArray &a, int index )
1788{
1789 if ( !isActive() )
1790 return;
1791 if ( (int)a.size() - index < 4 ) {
1792#if defined(QT_CHECK_RANGE)
1793 qWarning( "QPainter::drawCubicBezier: Cubic Bezier needs 4 control "
1794 "points" );
1795#endif
1796 return;
1797 }
1798 QPointArray pa( a );
1799 if ( testf(ExtDev|VxF|WxF) ) {
1800 if ( index != 0 || a.size() > 4 ) {
1801 pa = QPointArray( 4 );
1802 for ( int i=0; i<4; i++ )
1803 pa.setPoint( i, a.point(index+i) );
1804 index = 0;
1805 }
1806 if ( testf(ExtDev) ) {
1807 QPDevCmdParam param[1];
1808 param[0].ptarr = (QPointArray*)&pa;
1809 if ( !pdev->cmd(QPaintDevice::PdcDrawCubicBezier,this,param)
1810 || !hps )
1811 return;
1812 }
1813 if ( txop != TxNone )
1814 pa = xForm( pa );
1815 }
1816
1817 // flip y coordinates
1818 if ( devh ) {
1819 for ( int i = index; i < 4; i++ )
1820 pa[i].setY( devh - (pa[i].y() + 1) );
1821 }
1822
1823 qt_geom_line_handle gh;
1824 if ( cpen.width() > 1 )
1825 qt_begin_geom_line( hps, COLOR_VALUE(cpen.color()), cpen.style() == NoPen, &gh );
1826 GpiMove( hps, (PPOINTL)(pa.data()+index) );
1827 GpiPolySpline( hps, 3, (PPOINTL)(pa.data()+index+1) );
1828 if ( cpen.width() > 1 )
1829 qt_end_geom_line( hps, &gh );
1830}
1831#endif // QT_NO_BEZIER
1832
1833void QPainter::drawPixmap( int x, int y, const QPixmap &pixmap,
1834 int sx, int sy, int sw, int sh )
1835{
1836 if ( !isActive() || pixmap.isNull() )
1837 return;
1838 if ( sw < 0 )
1839 sw = pixmap.width() - sx;
1840 if ( sh < 0 )
1841 sh = pixmap.height() - sy;
1842
1843 // Sanity-check clipping
1844 if ( sx < 0 ) {
1845 x -= sx;
1846 sw += sx;
1847 sx = 0;
1848 }
1849 if ( sw + sx > pixmap.width() )
1850 sw = pixmap.width() - sx;
1851 if ( sy < 0 ) {
1852 y -= sy;
1853 sh += sy;
1854 sy = 0;
1855 }
1856 if ( sh + sy > pixmap.height() )
1857 sh = pixmap.height() - sy;
1858
1859 if ( sw <= 0 || sh <= 0 )
1860 return;
1861
1862 if ( testf(ExtDev|VxF|WxF) ) {
1863 if ( testf(ExtDev) || (txop == TxScale && pixmap.mask()) ||
1864 txop == TxRotShear ) {
1865 if ( sx != 0 || sy != 0 ||
1866 sw != pixmap.width() || sh != pixmap.height() ) {
1867 QPixmap tmp( sw, sh, pixmap.depth(), QPixmap::NormalOptim );
1868 bitBlt( &tmp, 0, 0, &pixmap, sx, sy, sw, sh, CopyROP, TRUE );
1869 if ( pixmap.mask() ) {
1870 QBitmap mask( sw, sh );
1871 bitBlt( &mask, 0, 0, pixmap.mask(), sx, sy, sw, sh,
1872 CopyROP, TRUE );
1873 tmp.setMask( mask );
1874 }
1875 drawPixmap( x, y, tmp );
1876 return;
1877 }
1878 if ( testf(ExtDev) ) {
1879 QPDevCmdParam param[2];
1880 QRect r( x, y, pixmap.width(), pixmap.height() );
1881 param[0].rect = &r;
1882 param[1].pixmap = &pixmap;
1883 if ( !pdev->cmd(QPaintDevice::PdcDrawPixmap,this,param)
1884 || !hps )
1885 return;
1886 }
1887 }
1888 if ( txop == TxTranslate )
1889 map( x, y, &x, &y );
1890 }
1891
1892 if ( txop <= TxTranslate ) { // use optimized bitBlt
1893 bitBlt( pdev, x, y, &pixmap, sx, sy, sw, sh, (RasterOp)rop );
1894 return;
1895 }
1896
1897 QPixmap *pm = (QPixmap*)&pixmap;
1898
1899/// @todo (dmik):
1900//
1901// For some unknown reason, the code below doesn't work for some selected
1902// target paint device width and scale factor combinations on my machine
1903// with ATI Radeon 9600 Pro and SNAP Version 2.9.2, build 446 -- GpiBitBlt
1904// simply does nothing (leaving the target hps untouched) but returns
1905// GPI_OK. This looks like a (video driver?) bug...
1906//
1907// An easy way to reproduce is to run the xform example, select the
1908// Image mode and set the scale factor to 360% (making sure the window
1909// size was not changed after starting the application).
1910//
1911#if 0
1912 if ( txop == TxScale && !pm->mask() ) {
1913 // Plain scaling and no mask, then GpiBitBlt is faster
1914 int w, h;
1915 map( x, y, sw, sh, &x, &y, &w, &h );
1916 // flip y coordinate
1917 if ( devh )
1918 y = devh - (y + h);
1919 POINTL ptls[] = {
1920 { x, y }, { x + w, y + h },
1921 { sx, sy }, { sw, sh }
1922 };
1923 GpiBitBlt( hps, pm->handle(), 4, ptls, ROP_SRCCOPY, BBO_IGNORE );
1924 } else
1925#endif
1926 {
1927#ifndef QT_NO_PIXMAP_TRANSFORMATION
1928 // We have a complex xform or scaling with mask, then xform the
1929 // pixmap (and possible mask) and bitBlt again.
1930 QWMatrix mat( m11(), m12(),
1931 m21(), m22(),
1932 dx(), dy() );
1933 mat = QPixmap::trueMatrix( mat, sw, sh );
1934 QPixmap pmx;
1935 if ( sx == 0 && sy == 0 &&
1936 sw == pixmap.width() && sh == pixmap.height() ) {
1937 pmx = pixmap; // xform the whole pixmap
1938 } else {
1939 pmx = QPixmap( sw, sh ); // xform subpixmap
1940 bitBlt( &pmx, 0, 0, pm, sx, sy, sw, sh );
1941 }
1942 pmx = pmx.xForm( mat );
1943 if ( pmx.isNull() ) // xformed into nothing
1944 return;
1945 if ( !pmx.mask() && txop == TxRotShear ) {
1946 QBitmap bm_clip( sw, sh, 1 ); // make full mask, xform it
1947 bm_clip.fill( color1 );
1948 pmx.setMask( bm_clip.xForm(mat) );
1949 }
1950 map( x, y, &x, &y ); //### already done above? // compute position of pixmap
1951 int dx, dy;
1952 mat.map( 0, 0, &dx, &dy );
1953 bitBlt( pdev, x - dx, y - dy, &pmx );
1954#endif
1955 }
1956}
1957
1958/* Internal, used by drawTiledPixmap */
1959
1960static void drawTile( QPainter *p, int x, int y, int w, int h,
1961 const QPixmap &pixmap, int xOffset, int yOffset )
1962{
1963 int yPos, xPos, drawH, drawW, yOff, xOff;
1964 yPos = y;
1965 yOff = yOffset;
1966 while( yPos < y + h ) {
1967 drawH = pixmap.height() - yOff; // Cropping first row
1968 if ( yPos + drawH > y + h ) // Cropping last row
1969 drawH = y + h - yPos;
1970 xPos = x;
1971 xOff = xOffset;
1972 while( xPos < x + w ) {
1973 drawW = pixmap.width() - xOff; // Cropping first column
1974 if ( xPos + drawW > x + w ) // Cropping last column
1975 drawW = x + w - xPos;
1976 p->drawPixmap( xPos, yPos, pixmap, xOff, yOff, drawW, drawH );
1977 xPos += drawW;
1978 xOff = 0;
1979 }
1980 yPos += drawH;
1981 yOff = 0;
1982 }
1983}
1984
1985void QPainter::drawTiledPixmap( int x, int y, int w, int h,
1986 const QPixmap &pixmap, int sx, int sy )
1987{
1988 int sw = pixmap.width();
1989 int sh = pixmap.height();
1990 if (!sw || !sh )
1991 return;
1992 if ( sx < 0 )
1993 sx = sw - -sx % sw;
1994 else
1995 sx = sx % sw;
1996 if ( sy < 0 )
1997 sy = sh - -sy % sh;
1998 else
1999 sy = sy % sh;
2000 /*
2001 Requirements for optimizing tiled pixmaps:
2002 - not an external device
2003 - not scale or rotshear
2004 - no mask
2005 */
2006 QBitmap *mask = (QBitmap *)pixmap.mask();
2007 if ( !testf(ExtDev) && txop <= TxTranslate && mask == 0 ) {
2008 if ( txop == TxTranslate )
2009 map( x, y, &x, &y );
2010 qt_draw_tiled_pixmap( hps, x, y, w, h, &pixmap, sx, sy, devh );
2011 return;
2012 }
2013 if ( sw*sh < 8192 && sw*sh < 16*w*h ) {
2014 int tw = sw, th = sh;
2015 while ( tw*th < 32678 && tw < w/2 )
2016 tw *= 2;
2017 while ( tw*th < 32678 && th < h/2 )
2018 th *= 2;
2019 QPixmap tile( tw, th, pixmap.depth(), QPixmap::BestOptim );
2020 qt_fill_tile( &tile, pixmap );
2021 if ( mask ) {
2022 QBitmap tilemask( tw, th, FALSE, QPixmap::NormalOptim );
2023 qt_fill_tile( &tilemask, *mask );
2024 tile.setMask( tilemask );
2025 }
2026 drawTile( this, x, y, w, h, tile, sx, sy );
2027 } else {
2028 drawTile( this, x, y, w, h, pixmap, sx, sy );
2029 }
2030}
2031
2032#if 0
2033//
2034// Generate a string that describes a transformed bitmap. This string is used
2035// to insert and find bitmaps in the global pixmap cache.
2036//
2037
2038static QString gen_text_bitmap_key( const QWMatrix &m, const QFont &font,
2039 const QString &str, int pos, int len )
2040{
2041 QString fk = font.key();
2042 int sz = 4*2 + len*2 + fk.length()*2 + sizeof(double)*6;
2043 QByteArray buf(sz);
2044 uchar *p = (uchar *)buf.data();
2045 *((double*)p)=m.m11(); p+=sizeof(double);
2046 *((double*)p)=m.m12(); p+=sizeof(double);
2047 *((double*)p)=m.m21(); p+=sizeof(double);
2048 *((double*)p)=m.m22(); p+=sizeof(double);
2049 *((double*)p)=m.dx(); p+=sizeof(double);
2050 *((double*)p)=m.dy(); p+=sizeof(double);
2051 QChar h1( '$' );
2052 QChar h2( 'q' );
2053 QChar h3( 't' );
2054 QChar h4( '$' );
2055 *((QChar*)p)=h1; p+=2;
2056 *((QChar*)p)=h2; p+=2;
2057 *((QChar*)p)=h3; p+=2;
2058 *((QChar*)p)=h4; p+=2;
2059 memcpy( (char*)p, (char*)(str.unicode()+pos), len*2 ); p += len*2;
2060 memcpy( (char*)p, (char*)fk.unicode(), fk.length()*2 ); p += fk.length()*2;
2061 return QString( (QChar*)buf.data(), buf.size()/2 );
2062}
2063
2064static QBitmap *get_text_bitmap( const QString &key )
2065{
2066 return (QBitmap*)QPixmapCache::find( key );
2067}
2068
2069static void ins_text_bitmap( const QString &key, QBitmap *bm )
2070{
2071 if ( !QPixmapCache::insert(key,bm) ) // cannot insert pixmap
2072 delete bm;
2073}
2074#endif
2075
2076void QPainter::drawText( int x, int y, const QString &str, int len, QPainter::TextDirection dir )
2077{
2078 drawText( x, y, str, 0, len, dir );
2079}
2080
2081void QPainter::drawText( int x, int y, const QString &str, int pos, int len, QPainter::TextDirection dir)
2082{
2083 if ( !isActive() )
2084 return;
2085
2086 const int slen = str.length();
2087 if (len < 0)
2088 len = slen - pos;
2089 if ( len == 0 || pos >= slen ) // empty string
2090 return;
2091 if ( pos + len > slen )
2092 len = slen - pos;
2093
2094 if ( testf(DirtyFont) )
2095 updateFont();
2096
2097 if ( testf(ExtDev) ) {
2098 QPDevCmdParam param[3];
2099 QString string = str.mid( pos, len );
2100 QPoint p( x, y );
2101 param[0].point = &p;
2102 param[1].str = &string;
2103 param[2].ival = QFont::NoScript;
2104 if ( !pdev->cmd(QPaintDevice::PdcDrawText2,this,param) || !hps )
2105 return;
2106 }
2107
2108 // we can't take the complete string here as we would otherwise
2109 // get quadratic behaviour when drawing long strings in parts.
2110 // we do however need some chars around the part we paint to get arabic shaping correct.
2111 // ### maybe possible to remove after cursor restrictions work in QRT
2112 int start = QMAX( 0, pos - 8 );
2113 int end = QMIN( (int)str.length(), pos + len + 8 );
2114 QConstString cstr( str.unicode() + start, end - start );
2115 pos -= start;
2116
2117 QTextLayout layout( cstr.string(), this );
2118 layout.beginLayout( QTextLayout::SingleLine );
2119
2120 layout.setBoundary( pos );
2121 layout.setBoundary( pos + len );
2122
2123 QTextEngine *engine = layout.d;
2124 if ( dir != Auto ) {
2125 int level = (dir == RTL) ? 1 : 0;
2126 for ( int i = engine->items.size(); i >= 0; i-- )
2127 engine->items[i].analysis.bidiLevel = level;
2128 }
2129
2130 // small hack to force skipping of unneeded items
2131 start = 0;
2132 while ( engine->items[start].position < pos )
2133 ++start;
2134 engine->currentItem = start;
2135 layout.beginLine( 0xfffffff );
2136 end = start;
2137 while ( !layout.atEnd() && layout.currentItem().from() < pos + len ) {
2138 layout.addCurrentItem();
2139 end++;
2140 }
2141 int ascent = fontMetrics().ascent();
2142 layout.endLine( 0, 0, Qt::SingleLine|Qt::AlignLeft, &ascent, 0 );
2143
2144 // do _not_ call endLayout() here, as it would clean up the shaped items and we would do shaping another time
2145 // for painting.
2146
2147 for ( int i = start; i < end; i++ ) {
2148 QScriptItem *si = &engine->items[i];
2149
2150 QFontEngine *fe = si->fontEngine;
2151 Q_ASSERT( fe );
2152
2153 int xpos = x + si->x;
2154 int ypos = y + si->y - ascent;
2155
2156/// @todo (dmik) do we really need this? we already have painter->handle()
2157// inside QFontEngine::draw(), and the font should have been selected into
2158// hps by updateFont()
2159// HPS oldPs = fe->hps;
2160// fe->hps = hps;
2161// fe->selectTo( hps );
2162
2163 int textFlags = 0;
2164 if ( cfont.d->underline ) textFlags |= Qt::Underline;
2165 if ( cfont.d->overline ) textFlags |= Qt::Overline;
2166 if ( cfont.d->strikeOut ) textFlags |= Qt::StrikeOut;
2167
2168 fe->draw( this, xpos, ypos, engine, si, textFlags );
2169/// @todo (dmik) need?
2170// fe->hps = oldPs;
2171 }
2172}
2173
2174
2175
2176void QPainter::drawTextItem( int x, int y, const QTextItem &ti, int textFlags )
2177{
2178 if ( testf(ExtDev) ) {
2179 QPDevCmdParam param[2];
2180 QPoint p(x, y);
2181 param[0].point = &p;
2182 param[1].textItem = &ti;
2183 bool retval = pdev->cmd(QPaintDevice::PdcDrawTextItem, this, param);
2184 if ( !retval || !hps )
2185 return;
2186 }
2187
2188 QTextEngine *engine = ti.engine;
2189 QScriptItem *si = &engine->items[ti.item];
2190
2191 engine->shape( ti.item );
2192 QFontEngine *fe = si->fontEngine;
2193 Q_ASSERT( fe );
2194
2195 x += si->x;
2196 y += si->y;
2197
2198/// @todo (dmik) do we really need this? we already have painter->handle()
2199// inside QFontEngine::draw(), and the font should have been selected into
2200// hps by updateFont()
2201// HPS oldPs = fe->hps;
2202// fe->hps = hps;
2203// fe->selectTo( hps );
2204
2205 fe->draw( this, x, y, engine, si, textFlags );
2206/// @todo (dmik) need?
2207// fe->hps = oldPs;
2208}
2209
2210
2211QPoint QPainter::pos() const
2212{
2213 QPoint p;
2214 if ( !isActive() || !hps )
2215 return p;
2216 POINTL ptl;
2217 GpiQueryCurrentPosition( hps, &ptl );
2218 if ( devh ) ptl.y = devh - ( ptl.y + 1 );
2219 p.rx() = ptl.x;
2220 p.ry() = ptl.y;
2221 return xFormDev( p );
2222}
2223
Note: See TracBrowser for help on using the repository browser.