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

Last change on this file since 61 was 61, checked in by dmik, 20 years ago

Implemented QRegion(..., QRegion::Eclipse) and QRegion(QPointArray &,...) constructors.
Improved Qt<->GPI region coordinates translation (it's now implicit), QRegion::handle() takes a height of the target devise as an argument (defaults to 0).

  • Property svn:keywords set to Id
File size: 62.6 KB
Line 
1/****************************************************************************
2** $Id: qpainter_pm.cpp 61 2006-02-06 21:43:39Z 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::ClipAll );
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 } else {
934 cur_crgn = def_crgn;
935 }
936
937 // clip region in GPI cannot be used for anything else, so detach it
938 cur_crgn.detach();
939
940 if ( !cur_crgn.isNull() )
941 GpiSetClipRegion( hps, cur_crgn.handle( devh ), NULL );
942}
943
944
945void QPainter::setClipRect( const QRect &r, CoordinateMode m )
946{
947 QRegion rgn( r );
948 setClipRegion( rgn, m );
949}
950
951void QPainter::setClipRegion( const QRegion &rgn, CoordinateMode m )
952{
953#if defined(QT_CHECK_STATE)
954 if ( !isActive() )
955 qWarning( "QPainter::setClipRegion: Will be reset by begin()" );
956#endif
957 if ( m == CoordDevice )
958 crgn = rgn;
959 else
960 crgn = xmat * rgn;
961
962 if ( testf(ExtDev) ) {
963 if ( block_ext )
964 return;
965 QPDevCmdParam param[2];
966 param[0].rgn = &rgn;
967 param[1].ival = m;
968 if ( !pdev->cmd(QPaintDevice::PdcSetClipRegion,this,param) || !hps )
969 return;
970 }
971 clearf( ClipOn ); // be sure to update clip rgn
972 setClipping( TRUE );
973}
974
975// Note: this function does not check the array range!
976void QPainter::drawPolyInternal(
977 const QPointArray &a, bool close, bool winding, int index, int npoints,
978 bool disjoint
979)
980{
981 // QCOORD is a double word that is the same as POINTL members, and since
982 // the order of xp and yp fields in the QPoint is also the same, we simply
983 // cast QPointArray::data() to PPOINTL below.
984
985 if ( npoints < 0 )
986 npoints = a.size() - index;
987
988 QPointArray pa = a;
989 // flip y coordinates
990 if ( devh ) {
991 pa = QPointArray( npoints );
992 for ( int i = 0; i < npoints; i++ ) {
993 pa[i].setX( a[index+i].x() );
994 pa[i].setY( devh - (a[index+i].y() + 1) );
995 }
996 index = 0;
997 }
998
999 ULONG opts = winding ? BA_WINDING : BA_ALTERNATE;
1000 const PPOINTL ptls = ((PPOINTL) pa.data()) + index;
1001 if ( cpen.width() > 1 ) {
1002 if ( close ) {
1003 GpiBeginArea( hps, BA_NOBOUNDARY | opts );
1004 if ( disjoint ) {
1005 GpiPolyLineDisjoint( hps, npoints, ptls );
1006 } else {
1007 GpiMove( hps, ptls );
1008 GpiPolyLine( hps, npoints - 1, ptls + 1 );
1009 }
1010 GpiEndArea( hps );
1011 }
1012 qt_geom_line_handle gh;
1013 qt_begin_geom_line( hps, COLOR_VALUE(cpen.color()), cpen.style() == NoPen, &gh );
1014 if ( disjoint ) {
1015 GpiPolyLineDisjoint( hps, npoints, ptls );
1016 } else {
1017 GpiMove( hps, ptls );
1018 GpiPolyLine( hps, npoints - 1, ptls + 1 );
1019 }
1020 if ( close )
1021 GpiCloseFigure( hps );
1022 qt_end_geom_line( hps, &gh );
1023 } else {
1024 if ( close )
1025 GpiBeginArea( hps, BA_BOUNDARY | opts );
1026 else
1027 GpiBeginPath( hps, 1 );
1028 if ( disjoint ) {
1029 GpiPolyLineDisjoint( hps, npoints, ptls );
1030 } else {
1031 GpiMove( hps, ptls );
1032 GpiPolyLine( hps, npoints - 1, ptls + 1 );
1033 }
1034 if ( close )
1035 GpiEndArea( hps );
1036 else {
1037 GpiEndPath( hps );
1038 GpiOutlinePath( hps, 1, 0 );
1039 }
1040 }
1041}
1042
1043// angles in Qt are 1/16th of a degree, so the integer part of FIXED is
1044// angle/16 and the fixed part is (65536/16) * angle%16.
1045#define MAKEDEGREE(a16) MAKEFIXED((a16>>4), (a16<<12))
1046
1047static void doDrawArc( HPS hps, PPOINTL cptl, int first, int a, int alen )
1048{
1049 if ( first >= 0 ) {
1050 int sa, ea;
1051 for ( int i = first; i < 4; i++, cptl++ ) {
1052 sa = i * 90*16;
1053 ea = sa + 90*16;
1054 if (sa < a) sa = a;
1055 if (ea > a + alen) ea = a + alen;
1056 if (ea <= sa) break;
1057 ea -= sa;
1058 GpiPartialArc( hps, cptl, MAKEFIXED(1,0), MAKEDEGREE(sa), MAKEDEGREE(ea) );
1059 }
1060 } else {
1061 GpiPartialArc( hps, cptl, MAKEFIXED(1,0), MAKEDEGREE(a), MAKEDEGREE(alen) );
1062 }
1063}
1064
1065void QPainter::drawArcInternal(
1066 int x, int y, int w, int h, int a, int alen, ArcClose close
1067)
1068{
1069 if ( w <= 0 || h <= 0 ) {
1070 if ( w == 0 || h == 0 )
1071 return;
1072 fix_neg_rect( &x, &y, &w, &h );
1073 }
1074
1075 const int FullArc = 360*16;
1076 // correct angles
1077 if ( alen < 0 ) {
1078 a += alen;
1079 alen = -alen;
1080 }
1081 alen = QMIN( alen, FullArc );
1082 if ( a < 0 )
1083 a = FullArc - ((-a) % (FullArc));
1084 else
1085 a = a % FullArc;
1086
1087 int axr = (w - 1) / 2;
1088 int ayr = (h - 1) / 2;
1089 ARCPARAMS arcparams = { axr, ayr, 0, 0 };
1090 GpiSetArcParams( hps, &arcparams );
1091 int xc, yc;
1092 xc = x + axr;
1093 yc = y + ayr;
1094 // flip y coordinate
1095 if ( devh ) yc = devh - (yc + 1);
1096
1097 POINTL sptl = { xc, yc }, cptls [4];
1098 PPOINTL cptl = cptls;
1099 int first = -1;
1100
1101 // in order to draw arcs whose bounding reactangle has even width
1102 // and/or height we draw each quarter of the arc separately,
1103 // correspondingly moving its center point by one pixel.
1104 int extX = (w - 1) % 2;
1105 int extY = (h - 1) % 2;
1106 if ( extX != 0 || extY != 0 ) {
1107 for ( int i = 0, sa = 90*16; i < 4; i++, sa += 90*16, cptl++ ) {
1108 if ( first < 0 && sa > a )
1109 first = i;
1110 if ( first >= 0 ) {
1111 switch ( i ) {
1112 case 0: cptl->x = xc + extX; cptl->y = yc; break;
1113 case 1: cptl->x = xc; cptl->y = yc; break;
1114 case 2: cptl->x = xc; cptl->y = yc - extY; break;
1115 case 3: cptl->x = xc + extX; cptl->y = yc - extY; break;
1116 }
1117 }
1118 }
1119 cptl = cptls + first;
1120 } else {
1121 *cptl = sptl;
1122 }
1123
1124 if ( close != ClosePie ) {
1125 // set the current position to the start of the arc
1126 LONG oldMix = GpiQueryMix( hps );
1127 LONG oldType = GpiQueryLineType( hps );
1128 GpiSetMix( hps, FM_LEAVEALONE );
1129 GpiSetLineType( hps, LINETYPE_SOLID );
1130 GpiPartialArc( hps, cptl, MAKEFIXED(1,0), MAKEDEGREE(a), 0 );
1131 GpiSetLineType( hps, oldType );
1132 GpiSetMix( hps, oldMix );
1133 // cause the start point to be drawn (seems like a bug in OS/2 GPI)
1134 GpiQueryCurrentPosition( hps, &sptl );
1135 }
1136
1137 if ( cpen.width() > 1 ) {
1138 if ( close != CloseNone ) {
1139 GpiBeginArea( hps, BA_NOBOUNDARY | BA_ALTERNATE );
1140 GpiMove( hps, &sptl );
1141 doDrawArc( hps, cptl, first, a, alen );
1142 GpiEndArea( hps );
1143 }
1144 qt_geom_line_handle gh;
1145 qt_begin_geom_line( hps, COLOR_VALUE(cpen.color()), 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
1311
1312void QPainter::drawRectInternal(
1313 int x, int y, int w, int h, int wRnd, int hRnd
1314) {
1315 if ( w <= 0 || h <= 0 ) {
1316 if ( w == 0 || h == 0 )
1317 return;
1318 fix_neg_rect( &x, &y, &w, &h );
1319 }
1320
1321 POINTL ptl1 = { x, y };
1322 if ( devh ) ptl1.y = devh - ( ptl1.y + h );
1323 POINTL ptl2 = { x + w - 1, ptl1.y + h - 1 };
1324
1325 if ( cpen.width() > 1 ) {
1326 GpiMove( hps, &ptl1 );
1327 GpiBox( hps, DRO_FILL, &ptl2, wRnd, hRnd );
1328 qt_geom_line_handle gh;
1329 qt_begin_geom_line( hps, COLOR_VALUE(cpen.color()), cpen.style() == NoPen, &gh );
1330 GpiMove( hps, &ptl1 );
1331 GpiBox( hps, DRO_OUTLINE, &ptl2, wRnd, hRnd );
1332 qt_end_geom_line( hps, &gh );
1333 } else {
1334 GpiMove( hps, &ptl1 );
1335 GpiBox( hps, DRO_FILL | DRO_OUTLINE, &ptl2, wRnd, hRnd );
1336 }
1337}
1338
1339void QPainter::drawRect( int x, int y, int w, int h )
1340{
1341 if ( !isActive() )
1342 return;
1343 if ( testf(ExtDev|VxF|WxF) ) {
1344 if ( testf(ExtDev) ) {
1345 QPDevCmdParam param[1];
1346 QRect r( x, y, w, h );
1347 param[0].rect = &r;
1348 if ( !pdev->cmd(QPaintDevice::PdcDrawRect,this,param) || !hps )
1349 return;
1350 }
1351 if ( txop == TxRotShear ) { // rotate/shear polygon
1352 QPointArray a( QRect(x,y,w,h) );
1353 drawPolyInternal( xForm(a) );
1354 return;
1355 }
1356 map( x, y, w, h, &x, &y, &w, &h );
1357 }
1358
1359 drawRectInternal( x, y, w, h, 0, 0 );
1360}
1361
1362void QPainter::drawWinFocusRectInternal(
1363 int x, int y, int w, int h, const QColor &bgCol, bool xormode
1364)
1365{
1366 if ( !isActive() || txop == TxRotShear )
1367 return;
1368
1369 if ( testf(ExtDev|VxF|WxF) ) {
1370 if ( testf(ExtDev) ) {
1371 QPDevCmdParam param[1];
1372 QRect r( x, y, w, h );
1373 param[0].rect = &r;
1374 if ( !pdev->cmd(QPaintDevice::PdcDrawRect,this,param) || !hps )
1375 return;
1376 }
1377 map( x, y, w, h, &x, &y, &w, &h );
1378 }
1379 if ( w <= 0 || h <= 0 ) {
1380 if ( w == 0 || h == 0 )
1381 return;
1382 fix_neg_rect( &x, &y, &w, &h );
1383 }
1384
1385 // flip y coordinate
1386 if ( devh ) y = devh - (y + h);
1387
1388 ULONG la =
1389 LBB_COLOR | LBB_MIX_MODE | LBB_BACK_MIX_MODE | LBB_TYPE;
1390 LINEBUNDLE oldLb, lb;
1391 GpiQueryAttrs( hps, PRIM_LINE, la, &oldLb );
1392 if ( !xormode ) { // black/white mode
1393 if( qGray( bgCol.rgb() ) <= 128 )
1394 lb.lColor = CLR_WHITE;
1395 else
1396 lb.lColor = CLR_BLACK;
1397 lb.usMixMode = FM_OVERPAINT;
1398 } else { // xor mode
1399 lb.lColor = CLR_TRUE;
1400 lb.usMixMode = FM_XOR;
1401 }
1402 lb.usBackMixMode = BM_LEAVEALONE;
1403 lb.usType = LINETYPE_ALTERNATE;
1404 GpiSetAttrs( hps, PRIM_LINE, la, 0, &lb );
1405
1406 POINTL ptl = { x, y };
1407 GpiMove( hps, &ptl );
1408 ptl.x += w - 1;
1409 ptl.y += h - 1;
1410 GpiBox( hps, DRO_OUTLINE, &ptl, 0, 0 );
1411
1412 GpiSetAttrs( hps, PRIM_LINE, la, 0, &oldLb );
1413}
1414
1415void QPainter::drawWinFocusRect( int x, int y, int w, int h, const QColor &bgCol )
1416{
1417 drawWinFocusRectInternal( x, y, w, h, bgCol, FALSE );
1418}
1419
1420void QPainter::drawWinFocusRect( int x, int y, int w, int h )
1421{
1422 if (
1423 pdev->devType() == QInternal::Widget &&
1424 GpiQueryClipRegion( hps )
1425 ) {
1426 // GPI bug: LINETYPE_ALTERNATE lines are drawn wrongly in XOR mode
1427 // when the clip region is set on the hps. we use doublebuffering
1428 // to avoid this.
1429 QSize ws = ((QWidget*) pdev)->size();
1430 POINTL ptls[] = { { 0, 0 }, { ws.width(), ws.height() }, { 0, 0 } };
1431 QPixmap pm( ws );
1432 GpiBitBlt( pm.hps, hps, 3, ptls, ROP_SRCCOPY, BBO_IGNORE );
1433 HPS thisHps = hps;
1434 hps = pm.hps;
1435 drawWinFocusRectInternal( x, y, w, h, bg_col, TRUE );
1436 hps = thisHps;
1437 GpiBitBlt( hps, pm.hps, 3, ptls, ROP_SRCCOPY, BBO_IGNORE );
1438 } else {
1439 drawWinFocusRectInternal( x, y, w, h, bg_col, TRUE );
1440 }
1441}
1442
1443
1444void QPainter::drawRoundRect( int x, int y, int w, int h, int xRnd, int yRnd )
1445{
1446 if ( !isActive() )
1447 return;
1448 if ( xRnd <= 0 || yRnd <= 0 ) {
1449 drawRect( x, y, w, h ); // draw normal rectangle
1450 return;
1451 }
1452 if ( xRnd >= 100 ) // fix ranges
1453 xRnd = 99;
1454 if ( yRnd >= 100 )
1455 yRnd = 99;
1456 if ( testf(ExtDev|VxF|WxF) ) {
1457 if ( testf(ExtDev) ) {
1458 QPDevCmdParam param[3];
1459 QRect r( x, y, w, h );
1460 param[0].rect = &r;
1461 param[1].ival = xRnd;
1462 param[2].ival = yRnd;
1463 if ( !pdev->cmd(QPaintDevice::PdcDrawRoundRect,this,param) || !hps)
1464 return;
1465 }
1466 if ( txop == TxRotShear ) { // rotate/shear polygon
1467 if ( w <= 0 || h <= 0 )
1468 fix_neg_rect( &x, &y, &w, &h );
1469 w--;
1470 h--;
1471 int rxx = w*xRnd/200;
1472 int ryy = h*yRnd/200;
1473 // were there overflows?
1474 if ( rxx < 0 )
1475 rxx = w/200*xRnd;
1476 if ( ryy < 0 )
1477 ryy = h/200*yRnd;
1478 int rxx2 = 2*rxx;
1479 int ryy2 = 2*ryy;
1480 QPointArray a[4];
1481 a[0].makeArc( x, y, rxx2, ryy2, 1*16*90, 16*90, xmat );
1482 a[1].makeArc( x, y+h-ryy2, rxx2, ryy2, 2*16*90, 16*90, xmat );
1483 a[2].makeArc( x+w-rxx2, y+h-ryy2, rxx2, ryy2, 3*16*90, 16*90, xmat );
1484 a[3].makeArc( x+w-rxx2, y, rxx2, ryy2, 0*16*90, 16*90, xmat );
1485 // ### is there a better way to join QPointArrays?
1486 QPointArray aa;
1487 aa.resize( a[0].size() + a[1].size() + a[2].size() + a[3].size() );
1488 uint j = 0;
1489 for ( int k=0; k<4; k++ ) {
1490 for ( uint i=0; i<a[k].size(); i++ ) {
1491 aa.setPoint( j, a[k].point(i) );
1492 j++;
1493 }
1494 }
1495 drawPolyInternal( aa );
1496 return;
1497 }
1498 map( x, y, w, h, &x, &y, &w, &h );
1499 }
1500
1501 drawRectInternal( x, y, w, h, w*xRnd/100, h*yRnd/100 );
1502}
1503
1504
1505void QPainter::drawEllipse( int x, int y, int w, int h )
1506{
1507 if ( !isActive() )
1508 return;
1509 if ( testf(ExtDev|VxF|WxF) ) {
1510 if ( testf(ExtDev) ) {
1511 QPDevCmdParam param[1];
1512 QRect r( x, y, w, h );
1513 param[0].rect = &r;
1514 if ( !pdev->cmd(QPaintDevice::PdcDrawEllipse,this,param) || !hps )
1515 return;
1516 }
1517 if ( txop == TxRotShear ) { // rotate/shear polygon
1518 QPointArray a;
1519 int increment = cpen.style() == NoPen ? 1 : 0;
1520 a.makeArc( x, y, w+increment, h+increment, 0, 360*16, xmat );
1521 drawPolyInternal( a );
1522 return;
1523 }
1524 map( x, y, w, h, &x, &y, &w, &h );
1525 }
1526
1527 drawArcInternal( x, y, w, h );
1528}
1529
1530
1531void QPainter::drawArc( int x, int y, int w, int h, int a, int alen )
1532{
1533 if ( !isActive() )
1534 return;
1535 if ( testf(ExtDev|VxF|WxF) ) {
1536 if ( testf(ExtDev) ) {
1537 QPDevCmdParam param[3];
1538 QRect r( x, y, w, h );
1539 param[0].rect = &r;
1540 param[1].ival = a;
1541 param[2].ival = alen;
1542 if ( !pdev->cmd(QPaintDevice::PdcDrawArc,this,param) || !hps )
1543 return;
1544 }
1545 if ( txop == TxRotShear ) { // rotate/shear
1546 QPointArray pa;
1547 pa.makeArc( x, y, w, h, a, alen, xmat ); // arc polyline
1548 drawPolyInternal( pa, FALSE );
1549 return;
1550 }
1551 map( x, y, w, h, &x, &y, &w, &h );
1552 }
1553
1554 drawArcInternal( x, y, w, h, a, alen, CloseNone );
1555}
1556
1557
1558void QPainter::drawPie( int x, int y, int w, int h, int a, int alen )
1559{
1560 if ( !isActive() )
1561 return;
1562 if ( testf(ExtDev|VxF|WxF) ) {
1563 if ( testf(ExtDev) ) {
1564 QPDevCmdParam param[3];
1565 QRect r( x, y, w, h );
1566 param[0].rect = &r;
1567 param[1].ival = a;
1568 param[2].ival = alen;
1569 if ( !pdev->cmd(QPaintDevice::PdcDrawPie,this,param) || !hps )
1570 return;
1571 }
1572 if ( txop == TxRotShear ) { // rotate/shear
1573 QPointArray pa;
1574 int increment = cpen.style() == NoPen ? 1 : 0;
1575 pa.makeArc( x, y, w+increment, h+increment, a, alen, xmat ); // arc polyline
1576 int n = pa.size();
1577 int cx, cy;
1578 xmat.map(x+w/2, y+h/2, &cx, &cy);
1579 pa.resize( n+1 );
1580 pa.setPoint( n, cx, cy ); // add legs
1581 drawPolyInternal( pa );
1582 return;
1583 }
1584 map( x, y, w, h, &x, &y, &w, &h );
1585 }
1586
1587 drawArcInternal( x, y, w, h, a, alen, ClosePie );
1588}
1589
1590
1591void QPainter::drawChord( int x, int y, int w, int h, int a, int alen )
1592{
1593 if ( !isActive() )
1594 return;
1595 if ( testf(ExtDev|VxF|WxF) ) {
1596 if ( testf(ExtDev) ) {
1597 QPDevCmdParam param[3];
1598 QRect r( x, y, w, h );
1599 param[0].rect = &r;
1600 param[1].ival = a;
1601 param[2].ival = alen;
1602 if ( !pdev->cmd(QPaintDevice::PdcDrawChord,this,param) || !hps )
1603 return;
1604 }
1605 if ( txop == TxRotShear ) { // rotate/shear
1606 QPointArray pa;
1607 int increment = cpen.style() == NoPen ? 1 : 0;
1608 pa.makeArc( x, y, w+increment, h+increment, a, alen, xmat ); // arc polygon
1609 int n = pa.size();
1610 pa.resize( n+1 );
1611 pa.setPoint( n, pa.at(0) ); // connect endpoints
1612 drawPolyInternal( pa );
1613 return;
1614 }
1615 map( x, y, w, h, &x, &y, &w, &h );
1616 }
1617
1618 drawArcInternal( x, y, w, h, a, alen, CloseChord );
1619}
1620
1621
1622void QPainter::drawLineSegments( const QPointArray &a, int index, int nlines )
1623{
1624 if ( nlines < 0 )
1625 nlines = a.size()/2 - index/2;
1626 if ( index + nlines*2 > (int)a.size() )
1627 nlines = (a.size() - index)/2;
1628 if ( !isActive() || nlines < 1 || index < 0 )
1629 return;
1630 QPointArray pa = a;
1631 if ( testf(ExtDev|VxF|WxF) ) {
1632 if ( testf(ExtDev) ) {
1633 if ( 2*nlines != (int)pa.size() ) {
1634 pa = QPointArray( nlines*2 );
1635 for ( int i=0; i<nlines*2; i++ )
1636 pa.setPoint( i, a.point(index+i) );
1637 index = 0;
1638 }
1639 QPDevCmdParam param[1];
1640 param[0].ptarr = (QPointArray*)&pa;
1641 if ( !pdev->cmd(QPaintDevice::PdcDrawLineSegments,this,param)
1642 || !hps )
1643 return;
1644 }
1645 if ( txop != TxNone ) {
1646 pa = xForm( a, index, nlines*2 );
1647 if ( pa.size() != a.size() ) {
1648 index = 0;
1649 nlines = pa.size()/2;
1650 }
1651 }
1652 }
1653 drawPolyInternal( pa, FALSE, FALSE, index, nlines*2, TRUE );
1654}
1655
1656
1657void QPainter::drawPolyline( const QPointArray &a, int index, int npoints )
1658{
1659 if ( npoints < 0 )
1660 npoints = a.size() - index;
1661 if ( index + npoints > (int)a.size() )
1662 npoints = a.size() - index;
1663 if ( !isActive() || npoints < 2 || index < 0 )
1664 return;
1665 QPointArray pa = a;
1666 if ( testf(ExtDev|VxF|WxF) ) {
1667 if ( testf(ExtDev) ) {
1668 if ( npoints != (int)pa.size() ) {
1669 pa = QPointArray( npoints );
1670 for ( int i=0; i<npoints; i++ )
1671 pa.setPoint( i, a.point(index+i) );
1672 index = 0;
1673 }
1674 QPDevCmdParam param[1];
1675 param[0].ptarr = (QPointArray*)&pa;
1676 if ( !pdev->cmd(QPaintDevice::PdcDrawPolyline,this,param) || !hps )
1677 return;
1678 }
1679 if ( txop != TxNone ) {
1680 pa = xForm( pa, index, npoints );
1681 if ( pa.size() != a.size() ) {
1682 index = 0;
1683 npoints = pa.size();
1684 }
1685 }
1686 }
1687 drawPolyInternal( pa, FALSE, FALSE, index, npoints );
1688}
1689
1690
1691void QPainter::drawConvexPolygon( const QPointArray &pa,
1692 int index, int npoints )
1693{
1694 // Any efficient way?
1695 drawPolygon(pa,FALSE,index,npoints);
1696}
1697
1698void QPainter::drawPolygon( const QPointArray &a, bool winding, int index,
1699 int npoints )
1700{
1701 if ( npoints < 0 )
1702 npoints = a.size() - index;
1703 if ( index + npoints > (int)a.size() )
1704 npoints = a.size() - index;
1705 if ( !isActive() || npoints < 2 || index < 0 )
1706 return;
1707 QPointArray pa = a;
1708 if ( testf(ExtDev|VxF|WxF) ) {
1709 if ( testf(ExtDev) ) {
1710 if ( npoints != (int)a.size() ) {
1711 pa = QPointArray( npoints );
1712 for ( int i=0; i<npoints; i++ )
1713 pa.setPoint( i, a.point(index+i) );
1714 }
1715 QPDevCmdParam param[2];
1716 param[0].ptarr = (QPointArray*)&pa;
1717 param[1].ival = winding;
1718 if ( !pdev->cmd(QPaintDevice::PdcDrawPolygon,this,param) || !hps )
1719 return;
1720 }
1721 if ( txop != TxNone ) {
1722 pa = xForm( a, index, npoints );
1723 if ( pa.size() != a.size() ) {
1724 index = 0;
1725 npoints = pa.size();
1726 }
1727 }
1728 }
1729 drawPolyInternal( pa, TRUE, winding, index, npoints );
1730}
1731
1732#ifndef QT_NO_BEZIER
1733void QPainter::drawCubicBezier( const QPointArray &a, int index )
1734{
1735 if ( !isActive() )
1736 return;
1737 if ( (int)a.size() - index < 4 ) {
1738#if defined(QT_CHECK_RANGE)
1739 qWarning( "QPainter::drawCubicBezier: Cubic Bezier needs 4 control "
1740 "points" );
1741#endif
1742 return;
1743 }
1744 QPointArray pa( a );
1745 if ( testf(ExtDev|VxF|WxF) ) {
1746 if ( index != 0 || a.size() > 4 ) {
1747 pa = QPointArray( 4 );
1748 for ( int i=0; i<4; i++ )
1749 pa.setPoint( i, a.point(index+i) );
1750 index = 0;
1751 }
1752 if ( testf(ExtDev) ) {
1753 QPDevCmdParam param[1];
1754 param[0].ptarr = (QPointArray*)&pa;
1755 if ( !pdev->cmd(QPaintDevice::PdcDrawCubicBezier,this,param)
1756 || !hps )
1757 return;
1758 }
1759 if ( txop != TxNone )
1760 pa = xForm( pa );
1761 }
1762
1763 // flip y coordinates
1764 if ( devh ) {
1765 for ( int i = index; i < 4; i++ )
1766 pa[i].setY( devh - (pa[i].y() + 1) );
1767 }
1768
1769 qt_geom_line_handle gh;
1770 if ( cpen.width() > 1 )
1771 qt_begin_geom_line( hps, COLOR_VALUE(cpen.color()), cpen.style() == NoPen, &gh );
1772 GpiMove( hps, (PPOINTL)(pa.data()+index) );
1773 GpiPolySpline( hps, 3, (PPOINTL)(pa.data()+index+1) );
1774 if ( cpen.width() > 1 )
1775 qt_end_geom_line( hps, &gh );
1776}
1777#endif // QT_NO_BEZIER
1778
1779void QPainter::drawPixmap( int x, int y, const QPixmap &pixmap,
1780 int sx, int sy, int sw, int sh )
1781{
1782 if ( !isActive() || pixmap.isNull() )
1783 return;
1784 if ( sw < 0 )
1785 sw = pixmap.width() - sx;
1786 if ( sh < 0 )
1787 sh = pixmap.height() - sy;
1788
1789 // Sanity-check clipping
1790 if ( sx < 0 ) {
1791 x -= sx;
1792 sw += sx;
1793 sx = 0;
1794 }
1795 if ( sw + sx > pixmap.width() )
1796 sw = pixmap.width() - sx;
1797 if ( sy < 0 ) {
1798 y -= sy;
1799 sh += sy;
1800 sy = 0;
1801 }
1802 if ( sh + sy > pixmap.height() )
1803 sh = pixmap.height() - sy;
1804
1805 if ( sw <= 0 || sh <= 0 )
1806 return;
1807
1808 if ( testf(ExtDev|VxF|WxF) ) {
1809 if ( testf(ExtDev) || (txop == TxScale && pixmap.mask()) ||
1810 txop == TxRotShear ) {
1811 if ( sx != 0 || sy != 0 ||
1812 sw != pixmap.width() || sh != pixmap.height() ) {
1813 QPixmap tmp( sw, sh, pixmap.depth(), QPixmap::NormalOptim );
1814 bitBlt( &tmp, 0, 0, &pixmap, sx, sy, sw, sh, CopyROP, TRUE );
1815 if ( pixmap.mask() ) {
1816 QBitmap mask( sw, sh );
1817 bitBlt( &mask, 0, 0, pixmap.mask(), sx, sy, sw, sh,
1818 CopyROP, TRUE );
1819 tmp.setMask( mask );
1820 }
1821 drawPixmap( x, y, tmp );
1822 return;
1823 }
1824 if ( testf(ExtDev) ) {
1825 QPDevCmdParam param[2];
1826 QRect r( x, y, pixmap.width(), pixmap.height() );
1827 param[0].rect = &r;
1828 param[1].pixmap = &pixmap;
1829 if ( !pdev->cmd(QPaintDevice::PdcDrawPixmap,this,param)
1830 || !hps )
1831 return;
1832 }
1833 }
1834 if ( txop == TxTranslate )
1835 map( x, y, &x, &y );
1836 }
1837
1838 if ( txop <= TxTranslate ) { // use optimized bitBlt
1839 bitBlt( pdev, x, y, &pixmap, sx, sy, sw, sh, (RasterOp)rop );
1840 return;
1841 }
1842
1843 QPixmap *pm = (QPixmap*)&pixmap;
1844
1845/// @todo (dmik):
1846//
1847// For some unknown reason, the code below doesn't work for some selected
1848// target paint device width and scale factor combinations on my machine
1849// with ATI Radeon 9600 Pro and SNAP Version 2.9.2, build 446 -- GpiBitBlt
1850// simply does nothing (leaving the target hps untouched) but returns
1851// GPI_OK. This looks like a (video driver?) bug...
1852//
1853// An easy way to reproduce is to run the xform example, select the
1854// Image mode and set the scale factor to 360% (making sure the window
1855// size was not changed after starting the application).
1856//
1857#if 0
1858 if ( txop == TxScale && !pm->mask() ) {
1859 // Plain scaling and no mask, then GpiBitBlt is faster
1860 int w, h;
1861 map( x, y, sw, sh, &x, &y, &w, &h );
1862 // flip y coordinate
1863 if ( devh )
1864 y = devh - (y + h);
1865 POINTL ptls[] = {
1866 { x, y }, { x + w, y + h },
1867 { sx, sy }, { sw, sh }
1868 };
1869 GpiBitBlt( hps, pm->handle(), 4, ptls, ROP_SRCCOPY, BBO_IGNORE );
1870 } else
1871#endif
1872 {
1873#ifndef QT_NO_PIXMAP_TRANSFORMATION
1874 // We have a complex xform or scaling with mask, then xform the
1875 // pixmap (and possible mask) and bitBlt again.
1876 QWMatrix mat( m11(), m12(),
1877 m21(), m22(),
1878 dx(), dy() );
1879 mat = QPixmap::trueMatrix( mat, sw, sh );
1880 QPixmap pmx;
1881 if ( sx == 0 && sy == 0 &&
1882 sw == pixmap.width() && sh == pixmap.height() ) {
1883 pmx = pixmap; // xform the whole pixmap
1884 } else {
1885 pmx = QPixmap( sw, sh ); // xform subpixmap
1886 bitBlt( &pmx, 0, 0, pm, sx, sy, sw, sh );
1887 }
1888 pmx = pmx.xForm( mat );
1889 if ( pmx.isNull() ) // xformed into nothing
1890 return;
1891 if ( !pmx.mask() && txop == TxRotShear ) {
1892 QBitmap bm_clip( sw, sh, 1 ); // make full mask, xform it
1893 bm_clip.fill( color1 );
1894 pmx.setMask( bm_clip.xForm(mat) );
1895 }
1896 map( x, y, &x, &y ); //### already done above? // compute position of pixmap
1897 int dx, dy;
1898 mat.map( 0, 0, &dx, &dy );
1899 bitBlt( pdev, x - dx, y - dy, &pmx );
1900#endif
1901 }
1902}
1903
1904/* Internal, used by drawTiledPixmap */
1905
1906static void drawTile( QPainter *p, int x, int y, int w, int h,
1907 const QPixmap &pixmap, int xOffset, int yOffset )
1908{
1909 int yPos, xPos, drawH, drawW, yOff, xOff;
1910 yPos = y;
1911 yOff = yOffset;
1912 while( yPos < y + h ) {
1913 drawH = pixmap.height() - yOff; // Cropping first row
1914 if ( yPos + drawH > y + h ) // Cropping last row
1915 drawH = y + h - yPos;
1916 xPos = x;
1917 xOff = xOffset;
1918 while( xPos < x + w ) {
1919 drawW = pixmap.width() - xOff; // Cropping first column
1920 if ( xPos + drawW > x + w ) // Cropping last column
1921 drawW = x + w - xPos;
1922 p->drawPixmap( xPos, yPos, pixmap, xOff, yOff, drawW, drawH );
1923 xPos += drawW;
1924 xOff = 0;
1925 }
1926 yPos += drawH;
1927 yOff = 0;
1928 }
1929}
1930
1931void QPainter::drawTiledPixmap( int x, int y, int w, int h,
1932 const QPixmap &pixmap, int sx, int sy )
1933{
1934 int sw = pixmap.width();
1935 int sh = pixmap.height();
1936 if (!sw || !sh )
1937 return;
1938 if ( sx < 0 )
1939 sx = sw - -sx % sw;
1940 else
1941 sx = sx % sw;
1942 if ( sy < 0 )
1943 sy = sh - -sy % sh;
1944 else
1945 sy = sy % sh;
1946 /*
1947 Requirements for optimizing tiled pixmaps:
1948 - not an external device
1949 - not scale or rotshear
1950 - no mask
1951 */
1952 QBitmap *mask = (QBitmap *)pixmap.mask();
1953 if ( !testf(ExtDev) && txop <= TxTranslate && mask == 0 ) {
1954 if ( txop == TxTranslate )
1955 map( x, y, &x, &y );
1956 qt_draw_tiled_pixmap( hps, x, y, w, h, &pixmap, sx, sy, devh );
1957 return;
1958 }
1959 if ( sw*sh < 8192 && sw*sh < 16*w*h ) {
1960 int tw = sw, th = sh;
1961 while ( tw*th < 32678 && tw < w/2 )
1962 tw *= 2;
1963 while ( tw*th < 32678 && th < h/2 )
1964 th *= 2;
1965 QPixmap tile( tw, th, pixmap.depth(), QPixmap::BestOptim );
1966 qt_fill_tile( &tile, pixmap );
1967 if ( mask ) {
1968 QBitmap tilemask( tw, th, FALSE, QPixmap::NormalOptim );
1969 qt_fill_tile( &tilemask, *mask );
1970 tile.setMask( tilemask );
1971 }
1972 drawTile( this, x, y, w, h, tile, sx, sy );
1973 } else {
1974 drawTile( this, x, y, w, h, pixmap, sx, sy );
1975 }
1976}
1977
1978#if 0
1979//
1980// Generate a string that describes a transformed bitmap. This string is used
1981// to insert and find bitmaps in the global pixmap cache.
1982//
1983
1984static QString gen_text_bitmap_key( const QWMatrix &m, const QFont &font,
1985 const QString &str, int pos, int len )
1986{
1987 QString fk = font.key();
1988 int sz = 4*2 + len*2 + fk.length()*2 + sizeof(double)*6;
1989 QByteArray buf(sz);
1990 uchar *p = (uchar *)buf.data();
1991 *((double*)p)=m.m11(); p+=sizeof(double);
1992 *((double*)p)=m.m12(); p+=sizeof(double);
1993 *((double*)p)=m.m21(); p+=sizeof(double);
1994 *((double*)p)=m.m22(); p+=sizeof(double);
1995 *((double*)p)=m.dx(); p+=sizeof(double);
1996 *((double*)p)=m.dy(); p+=sizeof(double);
1997 QChar h1( '$' );
1998 QChar h2( 'q' );
1999 QChar h3( 't' );
2000 QChar h4( '$' );
2001 *((QChar*)p)=h1; p+=2;
2002 *((QChar*)p)=h2; p+=2;
2003 *((QChar*)p)=h3; p+=2;
2004 *((QChar*)p)=h4; p+=2;
2005 memcpy( (char*)p, (char*)(str.unicode()+pos), len*2 ); p += len*2;
2006 memcpy( (char*)p, (char*)fk.unicode(), fk.length()*2 ); p += fk.length()*2;
2007 return QString( (QChar*)buf.data(), buf.size()/2 );
2008}
2009
2010static QBitmap *get_text_bitmap( const QString &key )
2011{
2012 return (QBitmap*)QPixmapCache::find( key );
2013}
2014
2015static void ins_text_bitmap( const QString &key, QBitmap *bm )
2016{
2017 if ( !QPixmapCache::insert(key,bm) ) // cannot insert pixmap
2018 delete bm;
2019}
2020#endif
2021
2022void QPainter::drawText( int x, int y, const QString &str, int len, QPainter::TextDirection dir )
2023{
2024 drawText( x, y, str, 0, len, dir );
2025}
2026
2027void QPainter::drawText( int x, int y, const QString &str, int pos, int len, QPainter::TextDirection dir)
2028{
2029 if ( !isActive() )
2030 return;
2031
2032 const int slen = str.length();
2033 if (len < 0)
2034 len = slen - pos;
2035 if ( len == 0 || pos >= slen ) // empty string
2036 return;
2037 if ( pos + len > slen )
2038 len = slen - pos;
2039
2040 if ( testf(DirtyFont) )
2041 updateFont();
2042
2043 if ( testf(ExtDev) ) {
2044 QPDevCmdParam param[3];
2045 QString string = str.mid( pos, len );
2046 QPoint p( x, y );
2047 param[0].point = &p;
2048 param[1].str = &string;
2049 param[2].ival = QFont::NoScript;
2050 if ( !pdev->cmd(QPaintDevice::PdcDrawText2,this,param) || !hps )
2051 return;
2052 }
2053
2054 // we can't take the complete string here as we would otherwise
2055 // get quadratic behaviour when drawing long strings in parts.
2056 // we do however need some chars around the part we paint to get arabic shaping correct.
2057 // ### maybe possible to remove after cursor restrictions work in QRT
2058 int start = QMAX( 0, pos - 8 );
2059 int end = QMIN( (int)str.length(), pos + len + 8 );
2060 QConstString cstr( str.unicode() + start, end - start );
2061 pos -= start;
2062
2063 QTextLayout layout( cstr.string(), this );
2064 layout.beginLayout( QTextLayout::SingleLine );
2065
2066 layout.setBoundary( pos );
2067 layout.setBoundary( pos + len );
2068
2069 QTextEngine *engine = layout.d;
2070 if ( dir != Auto ) {
2071 int level = (dir == RTL) ? 1 : 0;
2072 for ( int i = engine->items.size(); i >= 0; i-- )
2073 engine->items[i].analysis.bidiLevel = level;
2074 }
2075
2076 // small hack to force skipping of unneeded items
2077 start = 0;
2078 while ( engine->items[start].position < pos )
2079 ++start;
2080 engine->currentItem = start;
2081 layout.beginLine( 0xfffffff );
2082 end = start;
2083 while ( !layout.atEnd() && layout.currentItem().from() < pos + len ) {
2084 layout.addCurrentItem();
2085 end++;
2086 }
2087 int ascent = fontMetrics().ascent();
2088 layout.endLine( 0, 0, Qt::SingleLine|Qt::AlignLeft, &ascent, 0 );
2089
2090 // do _not_ call endLayout() here, as it would clean up the shaped items and we would do shaping another time
2091 // for painting.
2092
2093 for ( int i = start; i < end; i++ ) {
2094 QScriptItem *si = &engine->items[i];
2095
2096 QFontEngine *fe = si->fontEngine;
2097 Q_ASSERT( fe );
2098
2099 int xpos = x + si->x;
2100 int ypos = y + si->y - ascent;
2101
2102/// @todo (dmik) do we really need this? we already have painter->handle()
2103// inside QFontEngine::draw(), and the font should have been selected into
2104// hps by updateFont()
2105// HPS oldPs = fe->hps;
2106// fe->hps = hps;
2107// fe->selectTo( hps );
2108
2109 int textFlags = 0;
2110 if ( cfont.d->underline ) textFlags |= Qt::Underline;
2111 if ( cfont.d->overline ) textFlags |= Qt::Overline;
2112 if ( cfont.d->strikeOut ) textFlags |= Qt::StrikeOut;
2113
2114 fe->draw( this, xpos, ypos, engine, si, textFlags );
2115/// @todo (dmik) need?
2116// fe->hps = oldPs;
2117 }
2118}
2119
2120
2121
2122void QPainter::drawTextItem( int x, int y, const QTextItem &ti, int textFlags )
2123{
2124 if ( testf(ExtDev) ) {
2125 QPDevCmdParam param[2];
2126 QPoint p(x, y);
2127 param[0].point = &p;
2128 param[1].textItem = &ti;
2129 bool retval = pdev->cmd(QPaintDevice::PdcDrawTextItem, this, param);
2130 if ( !retval || !hps )
2131 return;
2132 }
2133
2134 QTextEngine *engine = ti.engine;
2135 QScriptItem *si = &engine->items[ti.item];
2136
2137 engine->shape( ti.item );
2138 QFontEngine *fe = si->fontEngine;
2139 Q_ASSERT( fe );
2140
2141 x += si->x;
2142 y += si->y;
2143
2144/// @todo (dmik) do we really need this? we already have painter->handle()
2145// inside QFontEngine::draw(), and the font should have been selected into
2146// hps by updateFont()
2147// HPS oldPs = fe->hps;
2148// fe->hps = hps;
2149// fe->selectTo( hps );
2150
2151 fe->draw( this, x, y, engine, si, textFlags );
2152/// @todo (dmik) need?
2153// fe->hps = oldPs;
2154}
2155
2156
2157QPoint QPainter::pos() const
2158{
2159 QPoint p;
2160 if ( !isActive() || !hps )
2161 return p;
2162 POINTL ptl;
2163 GpiQueryCurrentPosition( hps, &ptl );
2164 if ( devh ) ptl.y = devh - ( ptl.y + 1 );
2165 p.rx() = ptl.x;
2166 p.ry() = ptl.y;
2167 return xFormDev( p );
2168}
2169
Note: See TracBrowser for help on using the repository browser.