summaryrefslogtreecommitdiffstats
path: root/src/gui/painting
diff options
context:
space:
mode:
authorQt by Nokia <qt-info@nokia.com>2011-04-27 12:05:43 +0200
committeraxis <qt-info@nokia.com>2011-04-27 12:05:43 +0200
commit38be0d13830efd2d98281c645c3a60afe05ffece (patch)
tree6ea73f3ec77f7d153333779883e8120f82820abe /src/gui/painting
Initial import from the monolithic Qt.
This is the beginning of revision history for this module. If you want to look at revision history older than this, please refer to the Qt Git wiki for how to use Git history grafting. At the time of writing, this wiki is located here: http://qt.gitorious.org/qt/pages/GitIntroductionWithQt If you have already performed the grafting and you don't see any history beyond this commit, try running "git log" with the "--follow" argument. Branched from the monolithic repo, Qt master branch, at commit 896db169ea224deb96c59ce8af800d019de63f12
Diffstat (limited to 'src/gui/painting')
-rwxr-xr-xsrc/gui/painting/makepsheader.pl195
-rw-r--r--src/gui/painting/painting.pri269
-rw-r--r--src/gui/painting/qbackingstore.cpp1668
-rw-r--r--src/gui/painting/qbackingstore_p.h278
-rw-r--r--src/gui/painting/qbezier.cpp702
-rw-r--r--src/gui/painting/qbezier_p.h274
-rw-r--r--src/gui/painting/qblendfunctions.cpp1644
-rw-r--r--src/gui/painting/qblendfunctions_p.h497
-rw-r--r--src/gui/painting/qblittable.cpp105
-rw-r--r--src/gui/painting/qblittable_p.h91
-rw-r--r--src/gui/painting/qbrush.cpp2196
-rw-r--r--src/gui/painting/qbrush.h338
-rw-r--r--src/gui/painting/qcolor.cpp2720
-rw-r--r--src/gui/painting/qcolor.h308
-rw-r--r--src/gui/painting/qcolor_p.cpp370
-rw-r--r--src/gui/painting/qcolor_p.h71
-rw-r--r--src/gui/painting/qcolormap.h97
-rw-r--r--src/gui/painting/qcolormap.qdoc148
-rw-r--r--src/gui/painting/qcolormap_mac.cpp111
-rw-r--r--src/gui/painting/qcolormap_qpa.cpp231
-rw-r--r--src/gui/painting/qcolormap_qws.cpp185
-rw-r--r--src/gui/painting/qcolormap_s60.cpp107
-rw-r--r--src/gui/painting/qcolormap_win.cpp201
-rw-r--r--src/gui/painting/qcolormap_x11.cpp670
-rw-r--r--src/gui/painting/qcssutil.cpp408
-rw-r--r--src/gui/painting/qcssutil_p.h84
-rw-r--r--src/gui/painting/qcups.cpp401
-rw-r--r--src/gui/painting/qcups_p.h121
-rw-r--r--src/gui/painting/qdatabuffer_p.h145
-rw-r--r--src/gui/painting/qdrawhelper.cpp8078
-rw-r--r--src/gui/painting/qdrawhelper_arm_simd.cpp332
-rw-r--r--src/gui/painting/qdrawhelper_arm_simd_p.h76
-rw-r--r--src/gui/painting/qdrawhelper_iwmmxt.cpp148
-rw-r--r--src/gui/painting/qdrawhelper_mmx.cpp155
-rw-r--r--src/gui/painting/qdrawhelper_mmx3dnow.cpp128
-rw-r--r--src/gui/painting/qdrawhelper_mmx_p.h892
-rw-r--r--src/gui/painting/qdrawhelper_neon.cpp961
-rw-r--r--src/gui/painting/qdrawhelper_neon_asm.S297
-rw-r--r--src/gui/painting/qdrawhelper_neon_p.h146
-rw-r--r--src/gui/painting/qdrawhelper_p.h2033
-rw-r--r--src/gui/painting/qdrawhelper_sse.cpp168
-rw-r--r--src/gui/painting/qdrawhelper_sse2.cpp496
-rw-r--r--src/gui/painting/qdrawhelper_sse3dnow.cpp143
-rw-r--r--src/gui/painting/qdrawhelper_sse_p.h182
-rw-r--r--src/gui/painting/qdrawhelper_ssse3.cpp183
-rw-r--r--src/gui/painting/qdrawhelper_x86_p.h141
-rw-r--r--src/gui/painting/qdrawingprimitive_sse2_p.h241
-rw-r--r--src/gui/painting/qdrawutil.cpp1360
-rw-r--r--src/gui/painting/qdrawutil.h195
-rw-r--r--src/gui/painting/qemulationpaintengine.cpp289
-rw-r--r--src/gui/painting/qemulationpaintengine_p.h113
-rw-r--r--src/gui/painting/qfixed_p.h221
-rw-r--r--src/gui/painting/qgraphicssystem.cpp97
-rw-r--r--src/gui/painting/qgraphicssystem_mac.cpp59
-rw-r--r--src/gui/painting/qgraphicssystem_mac_p.h69
-rw-r--r--src/gui/painting/qgraphicssystem_p.h85
-rw-r--r--src/gui/painting/qgraphicssystem_qws.cpp62
-rw-r--r--src/gui/painting/qgraphicssystem_qws_p.h79
-rw-r--r--src/gui/painting/qgraphicssystem_raster.cpp72
-rw-r--r--src/gui/painting/qgraphicssystem_raster_p.h69
-rw-r--r--src/gui/painting/qgraphicssystem_runtime.cpp426
-rw-r--r--src/gui/painting/qgraphicssystem_runtime_p.h187
-rw-r--r--src/gui/painting/qgraphicssystemfactory.cpp123
-rw-r--r--src/gui/painting/qgraphicssystemfactory_p.h78
-rw-r--r--src/gui/painting/qgraphicssystemplugin.cpp56
-rw-r--r--src/gui/painting/qgraphicssystemplugin_p.h92
-rw-r--r--src/gui/painting/qgrayraster.c1942
-rw-r--r--src/gui/painting/qgrayraster_p.h105
-rw-r--r--src/gui/painting/qimagescale.cpp1031
-rw-r--r--src/gui/painting/qimagescale_p.h66
-rw-r--r--src/gui/painting/qmath_p.h72
-rw-r--r--src/gui/painting/qmatrix.cpp1229
-rw-r--r--src/gui/painting/qmatrix.h206
-rw-r--r--src/gui/painting/qmemrotate.cpp648
-rw-r--r--src/gui/painting/qmemrotate_p.h115
-rw-r--r--src/gui/painting/qoutlinemapper.cpp393
-rw-r--r--src/gui/painting/qoutlinemapper_p.h241
-rw-r--r--src/gui/painting/qpaintbuffer.cpp2287
-rw-r--r--src/gui/painting/qpaintbuffer_p.h461
-rw-r--r--src/gui/painting/qpaintdevice.cpp75
-rw-r--r--src/gui/painting/qpaintdevice.h177
-rw-r--r--src/gui/painting/qpaintdevice.qdoc286
-rw-r--r--src/gui/painting/qpaintdevice_mac.cpp152
-rw-r--r--src/gui/painting/qpaintdevice_qpa.cpp68
-rw-r--r--src/gui/painting/qpaintdevice_qws.cpp56
-rw-r--r--src/gui/painting/qpaintdevice_win.cpp62
-rw-r--r--src/gui/painting/qpaintdevice_x11.cpp198
-rw-r--r--src/gui/painting/qpaintengine.cpp1030
-rw-r--r--src/gui/painting/qpaintengine.h368
-rw-r--r--src/gui/painting/qpaintengine_alpha.cpp516
-rw-r--r--src/gui/painting/qpaintengine_alpha_p.h135
-rw-r--r--src/gui/painting/qpaintengine_blitter.cpp663
-rw-r--r--src/gui/painting/qpaintengine_blitter_p.h113
-rw-r--r--src/gui/painting/qpaintengine_mac.cpp1749
-rw-r--r--src/gui/painting/qpaintengine_mac_p.h254
-rw-r--r--src/gui/painting/qpaintengine_p.h125
-rw-r--r--src/gui/painting/qpaintengine_preview.cpp223
-rw-r--r--src/gui/painting/qpaintengine_preview_p.h106
-rw-r--r--src/gui/painting/qpaintengine_raster.cpp6325
-rw-r--r--src/gui/painting/qpaintengine_raster_p.h567
-rw-r--r--src/gui/painting/qpaintengine_s60.cpp145
-rw-r--r--src/gui/painting/qpaintengine_s60_p.h84
-rw-r--r--src/gui/painting/qpaintengine_x11.cpp2507
-rw-r--r--src/gui/painting/qpaintengine_x11_p.h246
-rw-r--r--src/gui/painting/qpaintengineex.cpp1015
-rw-r--r--src/gui/painting/qpaintengineex_p.h268
-rw-r--r--src/gui/painting/qpainter.cpp9572
-rw-r--r--src/gui/painting/qpainter.h1008
-rw-r--r--src/gui/painting/qpainter_p.h273
-rw-r--r--src/gui/painting/qpainterpath.cpp3420
-rw-r--r--src/gui/painting/qpainterpath.h435
-rw-r--r--src/gui/painting/qpainterpath_p.h278
-rw-r--r--src/gui/painting/qpathclipper.cpp2149
-rw-r--r--src/gui/painting/qpathclipper_p.h494
-rw-r--r--src/gui/painting/qpdf.cpp2106
-rw-r--r--src/gui/painting/qpdf_p.h299
-rw-r--r--src/gui/painting/qpen.cpp1032
-rw-r--r--src/gui/painting/qpen.h145
-rw-r--r--src/gui/painting/qpen_p.h78
-rw-r--r--src/gui/painting/qpolygon.cpp1003
-rw-r--r--src/gui/painting/qpolygon.h189
-rw-r--r--src/gui/painting/qpolygonclipper_p.h317
-rw-r--r--src/gui/painting/qprintengine.h119
-rw-r--r--src/gui/painting/qprintengine_mac.mm911
-rw-r--r--src/gui/painting/qprintengine_mac_p.h166
-rw-r--r--src/gui/painting/qprintengine_pdf.cpp1241
-rw-r--r--src/gui/painting/qprintengine_pdf_p.h195
-rw-r--r--src/gui/painting/qprintengine_ps.cpp972
-rw-r--r--src/gui/painting/qprintengine_ps_p.h137
-rw-r--r--src/gui/painting/qprintengine_qws.cpp886
-rw-r--r--src/gui/painting/qprintengine_qws_p.h213
-rw-r--r--src/gui/painting/qprintengine_win.cpp1776
-rw-r--r--src/gui/painting/qprintengine_win_p.h261
-rw-r--r--src/gui/painting/qprinter.cpp2453
-rw-r--r--src/gui/painting/qprinter.h337
-rw-r--r--src/gui/painting/qprinter_p.h140
-rw-r--r--src/gui/painting/qprinterinfo.cpp173
-rw-r--r--src/gui/painting/qprinterinfo.h90
-rw-r--r--src/gui/painting/qprinterinfo_mac.cpp120
-rw-r--r--src/gui/painting/qprinterinfo_p.h107
-rw-r--r--src/gui/painting/qprinterinfo_unix.cpp927
-rw-r--r--src/gui/painting/qprinterinfo_unix_p.h125
-rw-r--r--src/gui/painting/qprinterinfo_win.cpp122
-rw-r--r--src/gui/painting/qpsprinter.agl452
-rw-r--r--src/gui/painting/qpsprinter.ps449
-rw-r--r--src/gui/painting/qrasterdefs_p.h1279
-rw-r--r--src/gui/painting/qrasterizer.cpp1270
-rw-r--r--src/gui/painting/qrasterizer_p.h95
-rw-r--r--src/gui/painting/qregion.cpp4365
-rw-r--r--src/gui/painting/qregion.h239
-rw-r--r--src/gui/painting/qregion_mac.cpp286
-rw-r--r--src/gui/painting/qregion_qws.cpp3183
-rw-r--r--src/gui/painting/qregion_s60.cpp52
-rw-r--r--src/gui/painting/qregion_win.cpp149
-rw-r--r--src/gui/painting/qregion_x11.cpp92
-rw-r--r--src/gui/painting/qrgb.h88
-rw-r--r--src/gui/painting/qstroker.cpp1263
-rw-r--r--src/gui/painting/qstroker_p.h394
-rw-r--r--src/gui/painting/qstylepainter.cpp176
-rw-r--r--src/gui/painting/qstylepainter.h112
-rw-r--r--src/gui/painting/qtessellator.cpp1498
-rw-r--r--src/gui/painting/qtessellator_p.h102
-rw-r--r--src/gui/painting/qtextureglyphcache.cpp468
-rw-r--r--src/gui/painting/qtextureglyphcache_p.h189
-rw-r--r--src/gui/painting/qtransform.cpp2301
-rw-r--r--src/gui/painting/qtransform.h395
-rw-r--r--src/gui/painting/qunifiedtoolbarsurface_mac.cpp264
-rw-r--r--src/gui/painting/qunifiedtoolbarsurface_mac_p.h107
-rw-r--r--src/gui/painting/qvectorpath_p.h184
-rw-r--r--src/gui/painting/qwindowsurface.cpp383
-rw-r--r--src/gui/painting/qwindowsurface_mac.cpp139
-rw-r--r--src/gui/painting/qwindowsurface_mac_p.h84
-rw-r--r--src/gui/painting/qwindowsurface_p.h138
-rw-r--r--src/gui/painting/qwindowsurface_qws.cpp1433
-rw-r--r--src/gui/painting/qwindowsurface_qws_p.h355
-rw-r--r--src/gui/painting/qwindowsurface_raster.cpp487
-rw-r--r--src/gui/painting/qwindowsurface_raster_p.h132
-rw-r--r--src/gui/painting/qwindowsurface_s60.cpp254
-rw-r--r--src/gui/painting/qwindowsurface_s60_p.h93
-rw-r--r--src/gui/painting/qwindowsurface_x11.cpp265
-rw-r--r--src/gui/painting/qwindowsurface_x11_p.h92
-rw-r--r--src/gui/painting/qwmatrix.h61
182 files changed, 117113 insertions, 0 deletions
diff --git a/src/gui/painting/makepsheader.pl b/src/gui/painting/makepsheader.pl
new file mode 100755
index 0000000000..8f952720df
--- /dev/null
+++ b/src/gui/painting/makepsheader.pl
@@ -0,0 +1,195 @@
+#!/usr/bin/perl
+#############################################################################
+##
+## Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+## All rights reserved.
+## Contact: Nokia Corporation (qt-info@nokia.com)
+##
+## This file is part of the QtGui module of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:LGPL$
+## No Commercial Usage
+## This file contains pre-release code and may not be distributed.
+## You may use this file in accordance with the terms and conditions
+## contained in the Technology Preview License Agreement accompanying
+## this package.
+##
+## GNU Lesser General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU Lesser
+## General Public License version 2.1 as published by the Free Software
+## Foundation and appearing in the file LICENSE.LGPL included in the
+## packaging of this file. Please review the following information to
+## ensure the GNU Lesser General Public License version 2.1 requirements
+## will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+##
+## In addition, as a special exception, Nokia gives you certain additional
+## rights. These rights are described in the Nokia Qt LGPL Exception
+## version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+##
+## If you have questions regarding the use of this file, please contact
+## Nokia at qt-info@nokia.com.
+##
+##
+##
+##
+##
+##
+##
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+open(INPUT, 'qpsprinter.ps')
+ or die "Can't open qpsprinter.ps";
+
+$dontcompress = 1;
+while(<INPUT>) {
+ $line = $_;
+ chomp $line;
+ if ( /ENDUNCOMPRESS/ ) {
+ $dontcompress = 0;
+ }
+ $line =~ s/%.*$//;
+ $line = $line;
+ if ( $dontcompress eq 1 ) {
+ push(@uncompressed, $line);
+ } else {
+ push(@lines, $line);
+ }
+# print "$line\n";
+}
+
+$uc = join(" ", @uncompressed);
+$uc =~ s,\t+, ,g;
+$uc=~ s, +, ,g;
+
+$h = join(" ", @lines);
+$h =~ s,\t+, ,g;
+$h =~ s, +, ,g;
+$h = $h.' ';
+
+# now compress as much as possible
+$h =~ s/ bind def / BD /g;
+$h =~ s/ dup dup / d2 /g;
+$h =~ s/ exch def / ED /g;
+$h =~ s/ setfont / F /g;
+$h =~ s/ rlineto / RL /g;
+$h =~ s/ newpath / n /g;
+$h =~ s/ currentmatrix / CM /g;
+$h =~ s/ setmatrix / SM /g;
+$h =~ s/ translate / TR /g;
+$h =~ s/ setdash / SD /g;
+$h =~ s/ aload pop setrgbcolor / SC /g;
+$h =~ s/ currentfile read pop / CR /g;
+$h =~ s/ index / i /g;
+$h =~ s/ bitshift / bs /g;
+$h =~ s/ setcolorspace / scs /g;
+$h =~ s/ dict dup begin / DB /g;
+$h =~ s/ end def / DE /g;
+$h =~ s/ ifelse / ie /g;
+
+# PDF compatible naming
+$h =~ s/ setlinewidth / w /g;
+$h =~ s/ setdash / d /g;
+
+$h =~ s/ lineto / l /g;
+$h =~ s/ moveto / m /g;
+$h =~ s/ curveto / c /g;
+$h =~ s/ closepath / h /g;
+$h =~ s/ clip / W /g;
+$h =~ s/ eoclip / W* /g;
+
+$h =~ s/ gsave / gs /g;
+$h =~ s/ grestore / gr /g;
+
+# add the uncompressed part of the header before
+$h = $uc.' '.$h;
+
+
+
+#print $h;
+
+# wordwrap at col 76
+@head = split(' ', $h);
+$line = shift @head;
+while( @head ) {
+ $token = shift @head;
+ chomp $token;
+# print "\nl=$l, len=$len, token=$token.";
+ $newline = $line.' '.$token;
+ $newline =~ s, /,/,g;
+ $newline =~ s, \{,\{,g;
+ $newline =~ s, \},\},g;
+ $newline =~ s, \[,\[,g;
+ $newline =~ s, \],\],g;
+ $newline =~ s,\{ ,\{,g;
+ $newline =~ s,\} ,\},g;
+ $newline =~ s,\[ ,\[,g;
+ $newline =~ s,\] ,\],g;
+ if ( length( $newline ) > 76 ) {
+# print "\nline=$line\n";
+ $header = $header."\n\"".$line."\\n\"";
+ $newline = $token;
+ }
+ $line = $newline;
+}
+$header = $header."\n\"".$line."\\n\"";
+
+
+print "static const char *const ps_header =";
+print $header.";\n\n";
+
+close(INPUT);
+exit;
+
+open(INPUT, 'qpsprinter.agl')
+ or die "Can't open qpsprinter.ps";
+
+print "static const char * const agl =\n";
+
+$str = "\"";
+$string ="";
+$i = 0;
+while(<INPUT>) {
+ $line = $_;
+ chomp $line;
+ $line =~ s/#.*//;
+ if(length($line) ne 0) {
+ $num = $line;
+ $name = $line;
+ $num =~ s/,.*//;
+ $name =~ s/.*, \"//;
+ $name =~ s/\".*//;
+ push(@qchar, $num);
+ push(@index, $i);
+ if(length($str.$name) > 76) {
+ $str = $str."\"\n";
+ $string = $string.$str;
+ $str = "\"";
+ }
+ $str = $str.$name."\\0";
+ $i += length($name)+1;
+ }
+}
+
+print $string.";\n\n";
+
+print "static const struct { quint16 u; quint16 index; } unicodetoglyph[] = {\n ";
+
+$loop = 0;
+while( @qchar ) {
+ $loop = $loop + 1;
+ $ch = shift @qchar;
+ $i = shift @index;
+ print "{".$ch.", ".$i."}";
+ if($ch ne "0xFFFF") {
+ print ", ";
+ }
+ if(!($loop % 4)) {
+ print "\n ";
+ }
+};
+
+print "\n};\n\n";
+
diff --git a/src/gui/painting/painting.pri b/src/gui/painting/painting.pri
new file mode 100644
index 0000000000..65e7af4742
--- /dev/null
+++ b/src/gui/painting/painting.pri
@@ -0,0 +1,269 @@
+# Qt gui library, paint module
+
+HEADERS += \
+ painting/qbezier_p.h \
+ painting/qbrush.h \
+ painting/qcolor.h \
+ painting/qcolor_p.h \
+ painting/qcolormap.h \
+ painting/qdrawutil.h \
+ painting/qemulationpaintengine_p.h \
+ painting/qgraphicssystem_p.h \
+ painting/qmatrix.h \
+ painting/qmemrotate_p.h \
+ painting/qoutlinemapper_p.h \
+ painting/qpaintdevice.h \
+ painting/qpaintengine.h \
+ painting/qpaintengine_p.h \
+ painting/qpaintengine_alpha_p.h \
+ painting/qpaintengine_preview_p.h \
+ painting/qpaintengineex_p.h \
+ painting/qpainter.h \
+ painting/qpainter_p.h \
+ painting/qpainterpath.h \
+ painting/qpainterpath_p.h \
+ painting/qvectorpath_p.h \
+ painting/qpathclipper_p.h \
+ painting/qpdf_p.h \
+ painting/qpen.h \
+ painting/qpolygon.h \
+ painting/qpolygonclipper_p.h \
+ painting/qprintengine.h \
+ painting/qprintengine_pdf_p.h \
+ painting/qprintengine_ps_p.h \
+ painting/qprinter.h \
+ painting/qprinter_p.h \
+ painting/qprinterinfo.h \
+ painting/qprinterinfo_p.h \
+ painting/qrasterizer_p.h \
+ painting/qregion.h \
+ painting/qstroker_p.h \
+ painting/qstylepainter.h \
+ painting/qtessellator_p.h \
+ painting/qtextureglyphcache_p.h \
+ painting/qtransform.h \
+ painting/qwindowsurface_p.h \
+ painting/qwmatrix.h \
+ painting/qpaintbuffer_p.h
+
+
+SOURCES += \
+ painting/qbezier.cpp \
+ painting/qblendfunctions.cpp \
+ painting/qbrush.cpp \
+ painting/qcolor.cpp \
+ painting/qcolor_p.cpp \
+ painting/qcssutil.cpp \
+ painting/qdrawutil.cpp \
+ painting/qemulationpaintengine.cpp \
+ painting/qgraphicssystem.cpp \
+ painting/qmatrix.cpp \
+ painting/qmemrotate.cpp \
+ painting/qoutlinemapper.cpp \
+ painting/qpaintdevice.cpp \
+ painting/qpaintengine.cpp \
+ painting/qpaintengine_alpha.cpp \
+ painting/qpaintengine_preview.cpp \
+ painting/qpaintengineex.cpp \
+ painting/qpainter.cpp \
+ painting/qpainterpath.cpp \
+ painting/qpathclipper.cpp \
+ painting/qpdf.cpp \
+ painting/qpen.cpp \
+ painting/qpolygon.cpp \
+ painting/qprintengine_pdf.cpp \
+ painting/qprintengine_ps.cpp \
+ painting/qprinter.cpp \
+ painting/qprinterinfo.cpp \
+ painting/qrasterizer.cpp \
+ painting/qregion.cpp \
+ painting/qstroker.cpp \
+ painting/qstylepainter.cpp \
+ painting/qtessellator.cpp \
+ painting/qtextureglyphcache.cpp \
+ painting/qtransform.cpp \
+ painting/qwindowsurface.cpp \
+ painting/qpaintbuffer.cpp
+
+ SOURCES += \
+ painting/qpaintengine_raster.cpp \
+ painting/qdrawhelper.cpp \
+ painting/qimagescale.cpp \
+ painting/qgrayraster.c \
+ painting/qpaintengine_blitter.cpp \
+ painting/qblittable.cpp \
+
+ HEADERS += \
+ painting/qpaintengine_raster_p.h \
+ painting/qdrawhelper_p.h \
+ painting/qblendfunctions_p.h \
+ painting/qrasterdefs_p.h \
+ painting/qgrayraster_p.h \
+ painting/qpaintengine_blitter_p.h \
+ painting/qblittable_p.h \
+
+win32 {
+ HEADERS += painting/qprintengine_win_p.h
+
+ SOURCES += \
+ painting/qcolormap_win.cpp \
+ painting/qpaintdevice_win.cpp \
+ painting/qprintengine_win.cpp \
+ painting/qprinterinfo_win.cpp
+
+ !win32-borland:!wince*:LIBS += -lmsimg32
+}
+
+embedded {
+ HEADERS += \
+ painting/qgraphicssystem_qws_p.h \
+
+ SOURCES += \
+ painting/qgraphicssystem_qws.cpp \
+
+} else: if(!qpa) {
+ HEADERS += \
+ painting/qgraphicssystem_raster_p.h \
+ painting/qgraphicssystem_runtime_p.h \
+ painting/qgraphicssystemfactory_p.h \
+ painting/qgraphicssystemplugin_p.h \
+ painting/qwindowsurface_raster_p.h
+
+ SOURCES += \
+ painting/qgraphicssystem_raster.cpp \
+ painting/qgraphicssystem_runtime.cpp \
+ painting/qgraphicssystemfactory.cpp \
+ painting/qgraphicssystemplugin.cpp \
+ painting/qwindowsurface_raster.cpp
+}
+
+unix:x11 {
+ HEADERS += \
+ painting/qpaintengine_x11_p.h
+
+ SOURCES += \
+ painting/qcolormap_x11.cpp \
+ painting/qpaintdevice_x11.cpp \
+ painting/qpaintengine_x11.cpp
+}
+
+!embedded:!qpa:!x11:mac {
+ HEADERS += \
+ painting/qpaintengine_mac_p.h \
+ painting/qgraphicssystem_mac_p.h \
+ painting/qprintengine_mac_p.h
+
+ SOURCES += \
+ painting/qcolormap_mac.cpp \
+ painting/qpaintdevice_mac.cpp \
+ painting/qpaintengine_mac.cpp \
+ painting/qgraphicssystem_mac.cpp \
+ painting/qprinterinfo_mac.cpp
+ OBJECTIVE_SOURCES += \
+ painting/qprintengine_mac.mm \
+}
+
+unix:!mac:!symbian|qpa {
+ HEADERS += \
+ painting/qprinterinfo_unix_p.h
+ SOURCES += \
+ painting/qprinterinfo_unix.cpp
+}
+
+win32|x11|mac|embedded|qpa|symbian {
+ SOURCES += painting/qbackingstore.cpp
+ HEADERS += painting/qbackingstore_p.h
+}
+
+embedded {
+ contains(QT_CONFIG,qtopia) {
+ DEFINES += QTOPIA_PRINTENGINE
+ HEADERS += painting/qprintengine_qws_p.h
+ SOURCES += painting/qprintengine_qws.cpp
+ }
+
+ SOURCES += \
+ painting/qcolormap_qws.cpp \
+ painting/qpaintdevice_qws.cpp
+}
+
+qpa {
+ SOURCES += \
+ painting/qcolormap_qpa.cpp \
+ painting/qpaintdevice_qpa.cpp
+}
+
+symbian {
+ SOURCES += \
+ painting/qpaintengine_s60.cpp \
+ painting/qregion_s60.cpp \
+ painting/qcolormap_s60.cpp
+
+ HEADERS += \
+ painting/qpaintengine_s60_p.h
+}
+
+x11|embedded|qpa {
+ contains(QT_CONFIG,qtopia) {
+ DEFINES += QT_NO_CUPS QT_NO_LPR
+ } else {
+ SOURCES += painting/qcups.cpp
+ HEADERS += painting/qcups_p.h
+ }
+} else {
+ DEFINES += QT_NO_CUPS QT_NO_LPR
+}
+
+if(mmx|3dnow|sse|sse2|iwmmxt) {
+ HEADERS += painting/qdrawhelper_x86_p.h \
+ painting/qdrawhelper_mmx_p.h \
+ painting/qdrawhelper_sse_p.h \
+ painting/qdrawingprimitive_sse2_p.h
+ MMX_SOURCES += painting/qdrawhelper_mmx.cpp
+ MMX3DNOW_SOURCES += painting/qdrawhelper_mmx3dnow.cpp
+ SSE3DNOW_SOURCES += painting/qdrawhelper_sse3dnow.cpp
+ SSE_SOURCES += painting/qdrawhelper_sse.cpp
+ SSE2_SOURCES += painting/qdrawhelper_sse2.cpp
+ SSSE3_SOURCES += painting/qdrawhelper_ssse3.cpp
+ IWMMXT_SOURCES += painting/qdrawhelper_iwmmxt.cpp
+}
+
+x11 {
+ HEADERS += painting/qwindowsurface_x11_p.h
+ SOURCES += painting/qwindowsurface_x11.cpp
+}
+
+!embedded:!qpa:mac {
+ HEADERS += painting/qwindowsurface_mac_p.h \
+ painting/qunifiedtoolbarsurface_mac_p.h
+ SOURCES += painting/qwindowsurface_mac.cpp \
+ painting/qunifiedtoolbarsurface_mac.cpp
+}
+
+embedded {
+ HEADERS += painting/qwindowsurface_qws_p.h
+ SOURCES += painting/qwindowsurface_qws.cpp
+}
+
+
+
+symbian {
+ HEADERS += painting/qwindowsurface_s60_p.h \
+ painting/qdrawhelper_arm_simd_p.h
+ SOURCES += painting/qwindowsurface_s60.cpp
+ armccIfdefBlock = \
+ "$${LITERAL_HASH}if defined(ARMV6)" \
+ "MACRO QT_HAVE_ARM_SIMD" \
+ "SOURCEPATH painting" \
+ "SOURCE qdrawhelper_arm_simd.cpp" \
+ "$${LITERAL_HASH}endif"
+
+ MMP_RULES += armccIfdefBlock
+ QMAKE_CXXFLAGS.ARMCC *= -O3
+}
+
+NEON_SOURCES += painting/qdrawhelper_neon.cpp
+NEON_HEADERS += painting/qdrawhelper_neon_p.h
+NEON_ASM += ../3rdparty/pixman/pixman-arm-neon-asm.S painting/qdrawhelper_neon_asm.S
+
+include($$PWD/../../3rdparty/zlib_dependency.pri)
diff --git a/src/gui/painting/qbackingstore.cpp b/src/gui/painting/qbackingstore.cpp
new file mode 100644
index 0000000000..42c1c354b6
--- /dev/null
+++ b/src/gui/painting/qbackingstore.cpp
@@ -0,0 +1,1668 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include "qplatformdefs.h"
+
+#include "qbackingstore_p.h"
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qvarlengtharray.h>
+#include <QtGui/qevent.h>
+#include <QtGui/qapplication.h>
+#include <QtGui/qpaintengine.h>
+#include <QtGui/qgraphicsproxywidget.h>
+
+#include <private/qwidget_p.h>
+#include <private/qwindowsurface_raster_p.h>
+#include <private/qapplication_p.h>
+#include <private/qpaintengine_raster_p.h>
+#include <private/qgraphicseffect_p.h>
+
+#include "qgraphicssystem_p.h"
+
+#ifdef Q_WS_QWS
+#include <QtGui/qwsmanager_qws.h>
+#include <private/qwsmanager_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+extern QRegion qt_dirtyRegion(QWidget *);
+
+/*
+ A version of QRect::intersects() that does not normalize the rects.
+*/
+static inline bool qRectIntersects(const QRect &r1, const QRect &r2)
+{
+ return (qMax(r1.left(), r2.left()) <= qMin(r1.right(), r2.right())
+ && qMax(r1.top(), r2.top()) <= qMin(r1.bottom(), r2.bottom()));
+}
+
+/**
+ * Flushes the contents of the \a windowSurface into the screen area of \a widget.
+ * \a tlwOffset is the position of the top level widget relative to the window surface.
+ * \a region is the region to be updated in \a widget coordinates.
+ */
+static inline void qt_flush(QWidget *widget, const QRegion &region, QWindowSurface *windowSurface,
+ QWidget *tlw, const QPoint &tlwOffset)
+{
+ Q_ASSERT(widget);
+ Q_ASSERT(!region.isEmpty());
+ Q_ASSERT(windowSurface);
+ Q_ASSERT(tlw);
+
+#if !defined(QT_NO_PAINT_DEBUG) && !defined(Q_WS_QWS)
+ // QWS does flush update in QWindowSurface::flush (because it needs to lock the surface etc).
+ static int flushUpdate = qgetenv("QT_FLUSH_UPDATE").toInt();
+ if (flushUpdate > 0)
+ QWidgetBackingStore::showYellowThing(widget, region, flushUpdate * 10, false);
+#endif
+
+ //The performance hit by doing this should be negligible. However, be aware that
+ //using this FPS when you have > 1 windowsurface can give you inaccurate FPS
+ static bool fpsDebug = qgetenv("QT_DEBUG_FPS").toInt();
+ if (fpsDebug) {
+ static QTime time = QTime::currentTime();
+ static int frames = 0;
+
+ frames++;
+
+ if(time.elapsed() > 5000) {
+ double fps = double(frames * 1000) /time.restart();
+ fprintf(stderr,"FPS: %.1f\n",fps);
+ frames = 0;
+ }
+ }
+ if (widget != tlw)
+ windowSurface->flush(widget, region, tlwOffset + widget->mapTo(tlw, QPoint()));
+ else
+ windowSurface->flush(widget, region, tlwOffset);
+}
+
+#ifndef QT_NO_PAINT_DEBUG
+#ifdef Q_WS_WIN
+static void showYellowThing_win(QWidget *widget, const QRegion &region, int msec)
+{
+ HBRUSH brush;
+ static int i = 0;
+ switch (i) {
+ case 0:
+ brush = CreateSolidBrush(RGB(255, 255, 0));
+ break;
+ case 1:
+ brush = CreateSolidBrush(RGB(255, 200, 55));
+ break;
+ case 2:
+ brush = CreateSolidBrush(RGB(200, 255, 55));
+ break;
+ case 3:
+ brush = CreateSolidBrush(RGB(200, 200, 0));
+ break;
+ }
+ i = (i + 1) & 3;
+
+ HDC hdc = widget->getDC();
+
+ const QVector<QRect> &rects = region.rects();
+ foreach (QRect rect, rects) {
+ RECT winRect;
+ SetRect(&winRect, rect.left(), rect.top(), rect.right(), rect.bottom());
+ FillRect(hdc, &winRect, brush);
+ }
+
+ widget->releaseDC(hdc);
+ ::Sleep(msec);
+}
+#endif
+
+void QWidgetBackingStore::showYellowThing(QWidget *widget, const QRegion &toBePainted, int msec, bool unclipped)
+{
+#ifdef Q_WS_QWS
+ Q_UNUSED(widget);
+ Q_UNUSED(unclipped);
+ static QWSYellowSurface surface(true);
+ surface.setDelay(msec);
+ surface.flush(widget, toBePainted, QPoint());
+#else
+ QRegion paintRegion = toBePainted;
+ QRect widgetRect = widget->rect();
+
+ if (!widget->internalWinId()) {
+ QWidget *nativeParent = widget->nativeParentWidget();
+ const QPoint offset = widget->mapTo(nativeParent, QPoint(0, 0));
+ paintRegion.translate(offset);
+ widgetRect.translate(offset);
+ widget = nativeParent;
+ }
+
+#ifdef Q_WS_WIN
+ Q_UNUSED(unclipped);
+ showYellowThing_win(widget, paintRegion, msec);
+#else
+ //flags to fool painter
+ bool paintUnclipped = widget->testAttribute(Qt::WA_PaintUnclipped);
+ if (unclipped && !widget->d_func()->paintOnScreen())
+ widget->setAttribute(Qt::WA_PaintUnclipped);
+
+ const bool setFlag = !widget->testAttribute(Qt::WA_WState_InPaintEvent);
+ if (setFlag)
+ widget->setAttribute(Qt::WA_WState_InPaintEvent);
+
+ //setup the engine
+ QPaintEngine *pe = widget->paintEngine();
+ if (pe) {
+ pe->setSystemClip(paintRegion);
+ {
+ QPainter p(widget);
+ p.setClipRegion(paintRegion);
+ static int i = 0;
+ switch (i) {
+ case 0:
+ p.fillRect(widgetRect, QColor(255,255,0));
+ break;
+ case 1:
+ p.fillRect(widgetRect, QColor(255,200,55));
+ break;
+ case 2:
+ p.fillRect(widgetRect, QColor(200,255,55));
+ break;
+ case 3:
+ p.fillRect(widgetRect, QColor(200,200,0));
+ break;
+ }
+ i = (i+1) & 3;
+ p.end();
+ }
+ }
+
+ if (setFlag)
+ widget->setAttribute(Qt::WA_WState_InPaintEvent, false);
+
+ //restore
+ widget->setAttribute(Qt::WA_PaintUnclipped, paintUnclipped);
+
+ if (pe)
+ pe->setSystemClip(QRegion());
+
+ QApplication::syncX();
+
+#if defined(Q_OS_UNIX)
+ ::usleep(1000 * msec);
+#endif
+#endif // Q_WS_WIN
+#endif // Q_WS_QWS
+}
+
+bool QWidgetBackingStore::flushPaint(QWidget *widget, const QRegion &rgn)
+{
+ if (!widget)
+ return false;
+
+ int delay = 0;
+ if (widget->testAttribute(Qt::WA_WState_InPaintEvent)) {
+ static int flushPaintEvent = qgetenv("QT_FLUSH_PAINT_EVENT").toInt();
+ if (!flushPaintEvent)
+ return false;
+ delay = flushPaintEvent;
+ } else {
+ static int flushPaint = qgetenv("QT_FLUSH_PAINT").toInt();
+ if (!flushPaint)
+ return false;
+ delay = flushPaint;
+ }
+
+ QWidgetBackingStore::showYellowThing(widget, rgn, delay * 10, true);
+ return true;
+}
+
+void QWidgetBackingStore::unflushPaint(QWidget *widget, const QRegion &rgn)
+{
+ if (widget->d_func()->paintOnScreen() || rgn.isEmpty())
+ return;
+
+ QWidget *tlw = widget->window();
+ QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData();
+ if (!tlwExtra)
+ return;
+
+ const QPoint offset = widget->mapTo(tlw, QPoint());
+ qt_flush(widget, rgn, tlwExtra->backingStore->windowSurface, tlw, offset);
+}
+#endif // QT_NO_PAINT_DEBUG
+
+/*
+ Moves the whole rect by (dx, dy) in widget's coordinate system.
+ Doesn't generate any updates.
+*/
+bool QWidgetBackingStore::bltRect(const QRect &rect, int dx, int dy, QWidget *widget)
+{
+ const QPoint pos(tlwOffset + widget->mapTo(tlw, rect.topLeft()));
+ const QRect tlwRect(QRect(pos, rect.size()));
+ if (fullUpdatePending || dirty.intersects(tlwRect))
+ return false; // We don't want to scroll junk.
+ return windowSurface->scroll(tlwRect, dx, dy);
+}
+
+void QWidgetBackingStore::releaseBuffer()
+{
+ if (windowSurface)
+#if defined(Q_WS_QPA)
+ windowSurface->resize(QSize());
+#else
+ windowSurface->setGeometry(QRect());
+#endif
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+ for (int i = 0; i < subSurfaces.size(); ++i)
+ subSurfaces.at(i)->setGeometry(QRect());
+#endif
+}
+
+/*!
+ Prepares the window surface to paint a\ toClean region of the \a widget and
+ updates the BeginPaintInfo struct accordingly.
+
+ The \a toClean region might be clipped by the window surface.
+*/
+void QWidgetBackingStore::beginPaint(QRegion &toClean, QWidget *widget, QWindowSurface *windowSurface,
+ BeginPaintInfo *returnInfo, bool toCleanIsInTopLevelCoordinates)
+{
+#ifdef Q_WS_QWS
+ QWSWindowSurface *surface = static_cast<QWSWindowSurface *>(windowSurface);
+ QWidget *surfaceWidget = surface->window();
+
+ if (!surface->isValid()) {
+ // this looks strange but it really just releases the surface
+ surface->releaseSurface();
+ // the old window surface is deleted in setWindowSurface, which is
+ // called from QWindowSurface constructor.
+ windowSurface = tlw->d_func()->createDefaultWindowSurface();
+ surface = static_cast<QWSWindowSurface *>(windowSurface);
+ // createDefaultWindowSurface() will set topdata->windowSurface on the
+ // widget to zero. However, if this is a sub-surface, it should point
+ // to the widget's sub windowSurface, so we set that here:
+ if (!surfaceWidget->isWindow())
+ surfaceWidget->d_func()->topData()->windowSurface = windowSurface;
+ surface->setGeometry(topLevelRect());
+ returnInfo->windowSurfaceRecreated = true;
+ }
+
+ const QRegion toCleanUnclipped(toClean);
+
+ if (surfaceWidget->isWindow())
+ tlwOffset = surface->painterOffset();
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+ else if (toCleanIsInTopLevelCoordinates)
+ toClean &= surface->clipRegion().translated(surfaceWidget->mapTo(tlw, QPoint()));
+ if (!toCleanIsInTopLevelCoordinates && windowSurface == this->windowSurface)
+ toClean &= surface->clipRegion().translated(-widget->mapTo(surfaceWidget, QPoint()));
+#else
+ toClean &= surface->clipRegion();
+#endif
+
+ if (toClean.isEmpty()) {
+ if (surfaceWidget->isWindow()) {
+ dirtyFromPreviousSync += toCleanUnclipped;
+ hasDirtyFromPreviousSync = true;
+ }
+
+ returnInfo->nothingToPaint = true;
+ // Nothing to repaint. However, we might have newly exposed areas on the
+ // screen, so we have to make sure those are flushed.
+ flush();
+ return;
+ }
+
+ if (surfaceWidget->isWindow()) {
+ if (toCleanUnclipped != toClean) {
+ dirtyFromPreviousSync += (toCleanUnclipped - surface->clipRegion());
+ hasDirtyFromPreviousSync = true;
+ }
+ if (hasDirtyFromPreviousSync) {
+ dirtyFromPreviousSync -= toClean;
+ hasDirtyFromPreviousSync = !dirtyFromPreviousSync.isEmpty();
+ }
+ }
+
+#endif // Q_WS_QWS
+
+ Q_UNUSED(widget);
+ Q_UNUSED(toCleanIsInTopLevelCoordinates);
+
+ // Always flush repainted areas.
+ dirtyOnScreen += toClean;
+
+#if defined(Q_WS_QWS) && !defined(Q_BACKINGSTORE_SUBSURFACES)
+ toClean.translate(tlwOffset);
+#endif
+
+#ifdef QT_NO_PAINT_DEBUG
+ windowSurface->beginPaint(toClean);
+#else
+ returnInfo->wasFlushed = QWidgetBackingStore::flushPaint(tlw, toClean);
+ // Avoid deadlock with QT_FLUSH_PAINT: the server will wait for
+ // the BackingStore lock, so if we hold that, the server will
+ // never release the Communication lock that we are waiting for in
+ // sendSynchronousCommand
+ if (!returnInfo->wasFlushed)
+ windowSurface->beginPaint(toClean);
+#endif
+
+ Q_UNUSED(returnInfo);
+}
+
+void QWidgetBackingStore::endPaint(const QRegion &cleaned, QWindowSurface *windowSurface,
+ BeginPaintInfo *beginPaintInfo)
+{
+#ifndef QT_NO_PAINT_DEBUG
+ if (!beginPaintInfo->wasFlushed)
+ windowSurface->endPaint(cleaned);
+ else
+ QWidgetBackingStore::unflushPaint(tlw, cleaned);
+#else
+ Q_UNUSED(beginPaintInfo);
+ windowSurface->endPaint(cleaned);
+#endif
+
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+ flush(static_cast<QWSWindowSurface *>(windowSurface)->window(), windowSurface);
+#else
+ flush();
+#endif
+}
+
+/*!
+ Returns the region (in top-level coordinates) that needs repaint and/or flush.
+
+ If the widget is non-zero, only the dirty region for the widget is returned
+ and the region will be in widget coordinates.
+*/
+QRegion QWidgetBackingStore::dirtyRegion(QWidget *widget) const
+{
+ const bool widgetDirty = widget && widget != tlw;
+ const QRect tlwRect(topLevelRect());
+#if defined(Q_WS_QPA)
+ const QRect surfaceGeometry(tlwRect.topLeft(), windowSurface->size());
+#else
+ const QRect surfaceGeometry(windowSurface->geometry());
+#endif
+ if (fullUpdatePending || (surfaceGeometry != tlwRect && surfaceGeometry.size() != tlwRect.size())) {
+ if (widgetDirty) {
+ const QRect dirtyTlwRect = QRect(QPoint(), tlwRect.size());
+ const QPoint offset(widget->mapTo(tlw, QPoint()));
+ const QRect dirtyWidgetRect(dirtyTlwRect & widget->rect().translated(offset));
+ return dirtyWidgetRect.translated(-offset);
+ }
+ return QRect(QPoint(), tlwRect.size());
+ }
+
+ // Calculate the region that needs repaint.
+ QRegion r(dirty);
+ for (int i = 0; i < dirtyWidgets.size(); ++i) {
+ QWidget *w = dirtyWidgets.at(i);
+ if (widgetDirty && w != widget && !widget->isAncestorOf(w))
+ continue;
+ r += w->d_func()->dirty.translated(w->mapTo(tlw, QPoint()));
+ }
+
+ // Append the region that needs flush.
+ r += dirtyOnScreen;
+
+ if (dirtyOnScreenWidgets) { // Only in use with native child widgets.
+ for (int i = 0; i < dirtyOnScreenWidgets->size(); ++i) {
+ QWidget *w = dirtyOnScreenWidgets->at(i);
+ if (widgetDirty && w != widget && !widget->isAncestorOf(w))
+ continue;
+ QWidgetPrivate *wd = w->d_func();
+ Q_ASSERT(wd->needsFlush);
+ r += wd->needsFlush->translated(w->mapTo(tlw, QPoint()));
+ }
+ }
+
+ if (widgetDirty) {
+ // Intersect with the widget geometry and translate to its coordinates.
+ const QPoint offset(widget->mapTo(tlw, QPoint()));
+ r &= widget->rect().translated(offset);
+ r.translate(-offset);
+ }
+ return r;
+}
+
+/*!
+ Returns the static content inside the \a parent if non-zero; otherwise the static content
+ for the entire backing store is returned. The content will be clipped to \a withinClipRect
+ if non-empty.
+*/
+QRegion QWidgetBackingStore::staticContents(QWidget *parent, const QRect &withinClipRect) const
+{
+ if (!parent && tlw->testAttribute(Qt::WA_StaticContents)) {
+#if defined(Q_WS_QPA)
+ const QSize surfaceGeometry(windowSurface->size());
+#else
+ const QRect surfaceGeometry(windowSurface->geometry());
+#endif
+ QRect surfaceRect(0, 0, surfaceGeometry.width(), surfaceGeometry.height());
+ if (!withinClipRect.isEmpty())
+ surfaceRect &= withinClipRect;
+ return QRegion(surfaceRect);
+ }
+
+ QRegion region;
+ if (parent && parent->d_func()->children.isEmpty())
+ return region;
+
+ const bool clipToRect = !withinClipRect.isEmpty();
+ const int count = staticWidgets.count();
+ for (int i = 0; i < count; ++i) {
+ QWidget *w = staticWidgets.at(i);
+ QWidgetPrivate *wd = w->d_func();
+ if (!wd->isOpaque || !wd->extra || wd->extra->staticContentsSize.isEmpty()
+ || !w->isVisible() || (parent && !parent->isAncestorOf(w))) {
+ continue;
+ }
+
+ QRect rect(0, 0, wd->extra->staticContentsSize.width(), wd->extra->staticContentsSize.height());
+ const QPoint offset = w->mapTo(parent ? parent : tlw, QPoint());
+ if (clipToRect)
+ rect &= withinClipRect.translated(-offset);
+ if (rect.isEmpty())
+ continue;
+
+ rect &= wd->clipRect();
+ if (rect.isEmpty())
+ continue;
+
+ QRegion visible(rect);
+ wd->clipToEffectiveMask(visible);
+ if (visible.isEmpty())
+ continue;
+ wd->subtractOpaqueSiblings(visible, 0, /*alsoNonOpaque=*/true);
+
+ visible.translate(offset);
+ region += visible;
+ }
+
+ return region;
+}
+
+static inline void sendUpdateRequest(QWidget *widget, bool updateImmediately)
+{
+ if (!widget)
+ return;
+
+ if (updateImmediately) {
+ QEvent event(QEvent::UpdateRequest);
+ QApplication::sendEvent(widget, &event);
+ } else {
+ QApplication::postEvent(widget, new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority);
+ }
+}
+
+/*!
+ Marks the region of the widget as dirty (if not already marked as dirty) and
+ posts an UpdateRequest event to the top-level widget (if not already posted).
+
+ If updateImmediately is true, the event is sent immediately instead of posted.
+
+ If invalidateBuffer is true, all widgets intersecting with the region will be dirty.
+
+ If the widget paints directly on screen, the event is sent to the widget
+ instead of the top-level widget, and invalidateBuffer is completely ignored.
+
+ ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore).
+*/
+void QWidgetBackingStore::markDirty(const QRegion &rgn, QWidget *widget, bool updateImmediately,
+ bool invalidateBuffer)
+{
+ Q_ASSERT(tlw->d_func()->extra);
+ Q_ASSERT(tlw->d_func()->extra->topextra);
+ Q_ASSERT(!tlw->d_func()->extra->topextra->inTopLevelResize);
+ Q_ASSERT(widget->isVisible() && widget->updatesEnabled());
+ Q_ASSERT(widget->window() == tlw);
+ Q_ASSERT(!rgn.isEmpty());
+
+#ifndef QT_NO_GRAPHICSEFFECT
+ widget->d_func()->invalidateGraphicsEffectsRecursively();
+#endif //QT_NO_GRAPHICSEFFECT
+
+ if (widget->d_func()->paintOnScreen()) {
+ if (widget->d_func()->dirty.isEmpty()) {
+ widget->d_func()->dirty = rgn;
+ sendUpdateRequest(widget, updateImmediately);
+ return;
+ } else if (qt_region_strictContains(widget->d_func()->dirty, widget->rect())) {
+ if (updateImmediately)
+ sendUpdateRequest(widget, updateImmediately);
+ return; // Already dirty.
+ }
+
+ const bool eventAlreadyPosted = !widget->d_func()->dirty.isEmpty();
+ widget->d_func()->dirty += rgn;
+ if (!eventAlreadyPosted || updateImmediately)
+ sendUpdateRequest(widget, updateImmediately);
+ return;
+ }
+
+ if (fullUpdatePending) {
+ if (updateImmediately)
+ sendUpdateRequest(tlw, updateImmediately);
+ return;
+ }
+
+ if (!windowSurface->hasFeature(QWindowSurface::PartialUpdates)) {
+ fullUpdatePending = true;
+ sendUpdateRequest(tlw, updateImmediately);
+ return;
+ }
+
+ const QPoint offset = widget->mapTo(tlw, QPoint());
+ const QRect widgetRect = widget->d_func()->effectiveRectFor(widget->rect());
+ if (qt_region_strictContains(dirty, widgetRect.translated(offset))) {
+ if (updateImmediately)
+ sendUpdateRequest(tlw, updateImmediately);
+ return; // Already dirty.
+ }
+
+ if (invalidateBuffer) {
+ const bool eventAlreadyPosted = !dirty.isEmpty();
+#ifndef QT_NO_GRAPHICSEFFECT
+ if (widget->d_func()->graphicsEffect)
+ dirty += widget->d_func()->effectiveRectFor(rgn.boundingRect()).translated(offset);
+ else
+#endif //QT_NO_GRAPHICSEFFECT
+ dirty += rgn.translated(offset);
+ if (!eventAlreadyPosted || updateImmediately)
+ sendUpdateRequest(tlw, updateImmediately);
+ return;
+ }
+
+ if (dirtyWidgets.isEmpty()) {
+ addDirtyWidget(widget, rgn);
+ sendUpdateRequest(tlw, updateImmediately);
+ return;
+ }
+
+ if (widget->d_func()->inDirtyList) {
+ if (!qt_region_strictContains(widget->d_func()->dirty, widgetRect)) {
+#ifndef QT_NO_GRAPHICSEFFECT
+ if (widget->d_func()->graphicsEffect)
+ widget->d_func()->dirty += widget->d_func()->effectiveRectFor(rgn.boundingRect());
+ else
+#endif //QT_NO_GRAPHICSEFFECT
+ widget->d_func()->dirty += rgn;
+ }
+ } else {
+ addDirtyWidget(widget, rgn);
+ }
+
+ if (updateImmediately)
+ sendUpdateRequest(tlw, updateImmediately);
+}
+
+/*!
+ This function is equivalent to calling markDirty(QRegion(rect), ...), but
+ is more efficient as it eliminates QRegion operations/allocations and can
+ use the rect more precisely for additional cut-offs.
+
+ ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore).
+*/
+void QWidgetBackingStore::markDirty(const QRect &rect, QWidget *widget, bool updateImmediately,
+ bool invalidateBuffer)
+{
+ Q_ASSERT(tlw->d_func()->extra);
+ Q_ASSERT(tlw->d_func()->extra->topextra);
+ Q_ASSERT(!tlw->d_func()->extra->topextra->inTopLevelResize);
+ Q_ASSERT(widget->isVisible() && widget->updatesEnabled());
+ Q_ASSERT(widget->window() == tlw);
+ Q_ASSERT(!rect.isEmpty());
+
+#ifndef QT_NO_GRAPHICSEFFECT
+ widget->d_func()->invalidateGraphicsEffectsRecursively();
+#endif //QT_NO_GRAPHICSEFFECT
+
+ if (widget->d_func()->paintOnScreen()) {
+ if (widget->d_func()->dirty.isEmpty()) {
+ widget->d_func()->dirty = QRegion(rect);
+ sendUpdateRequest(widget, updateImmediately);
+ return;
+ } else if (qt_region_strictContains(widget->d_func()->dirty, rect)) {
+ if (updateImmediately)
+ sendUpdateRequest(widget, updateImmediately);
+ return; // Already dirty.
+ }
+
+ const bool eventAlreadyPosted = !widget->d_func()->dirty.isEmpty();
+ widget->d_func()->dirty += rect;
+ if (!eventAlreadyPosted || updateImmediately)
+ sendUpdateRequest(widget, updateImmediately);
+ return;
+ }
+
+ if (fullUpdatePending) {
+ if (updateImmediately)
+ sendUpdateRequest(tlw, updateImmediately);
+ return;
+ }
+
+ if (!windowSurface->hasFeature(QWindowSurface::PartialUpdates)) {
+ fullUpdatePending = true;
+ sendUpdateRequest(tlw, updateImmediately);
+ return;
+ }
+
+ const QRect widgetRect = widget->d_func()->effectiveRectFor(rect);
+ const QRect translatedRect(widgetRect.translated(widget->mapTo(tlw, QPoint())));
+ if (qt_region_strictContains(dirty, translatedRect)) {
+ if (updateImmediately)
+ sendUpdateRequest(tlw, updateImmediately);
+ return; // Already dirty
+ }
+
+ if (invalidateBuffer) {
+ const bool eventAlreadyPosted = !dirty.isEmpty();
+ dirty += translatedRect;
+ if (!eventAlreadyPosted || updateImmediately)
+ sendUpdateRequest(tlw, updateImmediately);
+ return;
+ }
+
+ if (dirtyWidgets.isEmpty()) {
+ addDirtyWidget(widget, rect);
+ sendUpdateRequest(tlw, updateImmediately);
+ return;
+ }
+
+ if (widget->d_func()->inDirtyList) {
+ if (!qt_region_strictContains(widget->d_func()->dirty, widgetRect))
+ widget->d_func()->dirty += widgetRect;
+ } else {
+ addDirtyWidget(widget, rect);
+ }
+
+ if (updateImmediately)
+ sendUpdateRequest(tlw, updateImmediately);
+}
+
+/*!
+ Marks the \a region of the \a widget as dirty on screen. The \a region will be copied from
+ the backing store to the \a widget's native parent next time flush() is called.
+
+ Paint on screen widgets are ignored.
+*/
+void QWidgetBackingStore::markDirtyOnScreen(const QRegion &region, QWidget *widget, const QPoint &topLevelOffset)
+{
+ if (!widget || widget->d_func()->paintOnScreen() || region.isEmpty())
+ return;
+
+#if defined(Q_WS_QWS) || defined(Q_WS_MAC)
+ if (!widget->testAttribute(Qt::WA_WState_InPaintEvent))
+ dirtyOnScreen += region.translated(topLevelOffset);
+ return;
+#endif
+
+ // Top-level.
+ if (widget == tlw) {
+ if (!widget->testAttribute(Qt::WA_WState_InPaintEvent))
+ dirtyOnScreen += region;
+ return;
+ }
+
+ // Alien widgets.
+ if (!widget->internalWinId() && !widget->isWindow()) {
+ QWidget *nativeParent = widget->nativeParentWidget(); // Alien widgets with the top-level as the native parent (common case).
+ if (nativeParent == tlw) {
+ if (!widget->testAttribute(Qt::WA_WState_InPaintEvent))
+ dirtyOnScreen += region.translated(topLevelOffset);
+ return;
+ }
+
+ // Alien widgets with native parent != tlw.
+ QWidgetPrivate *nativeParentPrivate = nativeParent->d_func();
+ if (!nativeParentPrivate->needsFlush)
+ nativeParentPrivate->needsFlush = new QRegion;
+ const QPoint nativeParentOffset = widget->mapTo(nativeParent, QPoint());
+ *nativeParentPrivate->needsFlush += region.translated(nativeParentOffset);
+ appendDirtyOnScreenWidget(nativeParent);
+ return;
+ }
+
+ // Native child widgets.
+ QWidgetPrivate *widgetPrivate = widget->d_func();
+ if (!widgetPrivate->needsFlush)
+ widgetPrivate->needsFlush = new QRegion;
+ *widgetPrivate->needsFlush += region;
+ appendDirtyOnScreenWidget(widget);
+}
+
+void QWidgetBackingStore::removeDirtyWidget(QWidget *w)
+{
+ if (!w)
+ return;
+
+ dirtyWidgetsRemoveAll(w);
+ dirtyOnScreenWidgetsRemoveAll(w);
+ resetWidget(w);
+
+ QWidgetPrivate *wd = w->d_func();
+ const int n = wd->children.count();
+ for (int i = 0; i < n; ++i) {
+ if (QWidget *child = qobject_cast<QWidget*>(wd->children.at(i)))
+ removeDirtyWidget(child);
+ }
+}
+
+#if defined(Q_WS_QWS) && !defined(QT_NO_QWS_MANAGER)
+bool QWidgetBackingStore::hasDirtyWindowDecoration() const
+{
+ QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData();
+ if (tlwExtra && tlwExtra->qwsManager)
+ return !tlwExtra->qwsManager->d_func()->dirtyRegions.isEmpty();
+ return false;
+}
+
+void QWidgetBackingStore::paintWindowDecoration()
+{
+ if (!hasDirtyWindowDecoration())
+ return;
+
+ QDecoration &decoration = QApplication::qwsDecoration();
+ const QRect decorationRect = tlw->rect();
+ QRegion decorationRegion = decoration.region(tlw, decorationRect);
+
+ QWSManagerPrivate *managerPrivate = tlw->d_func()->topData()->qwsManager->d_func();
+ const bool doClipping = !managerPrivate->entireDecorationNeedsRepaint
+ && !managerPrivate->dirtyClip.isEmpty();
+
+ if (doClipping) {
+ decorationRegion &= static_cast<QWSWindowSurface *>(windowSurface)->clipRegion();
+ decorationRegion &= managerPrivate->dirtyClip;
+ }
+
+ if (decorationRegion.isEmpty())
+ return;
+
+ //### The QWS decorations do not always paint the pixels they promise to paint.
+ // This causes painting problems with QWSMemorySurface. Since none of the other
+ // window surfaces actually use the region, passing an empty region is a safe
+ // workaround.
+
+ windowSurface->beginPaint(QRegion());
+
+ QPaintEngine *engine = windowSurface->paintDevice()->paintEngine();
+ Q_ASSERT(engine);
+ const QRegion oldSystemClip(engine->systemClip());
+ engine->setSystemClip(decorationRegion.translated(tlwOffset));
+
+ QPainter painter(windowSurface->paintDevice());
+ painter.setFont(QApplication::font());
+ painter.translate(tlwOffset);
+
+ const int numDirty = managerPrivate->dirtyRegions.size();
+ for (int i = 0; i < numDirty; ++i) {
+ const int area = managerPrivate->dirtyRegions.at(i);
+
+ QRegion clipRegion = decoration.region(tlw, decorationRect, area);
+ if (!clipRegion.isEmpty()) {
+ // Decoration styles changes the clip and assumes the old clip is non-empty,
+ // so we have to set it, but in theory it shouldn't be required.
+ painter.setClipRegion(clipRegion);
+ decoration.paint(&painter, tlw, area, managerPrivate->dirtyStates.at(i));
+ }
+ }
+ markDirtyOnScreen(decorationRegion, tlw, QPoint());
+
+ painter.end();
+ windowSurface->endPaint(decorationRegion);
+ managerPrivate->clearDirtyRegions();
+ engine->setSystemClip(oldSystemClip);
+}
+#endif
+
+void QWidgetBackingStore::updateLists(QWidget *cur)
+{
+ if (!cur)
+ return;
+
+ QList<QObject*> children = cur->children();
+ for (int i = 0; i < children.size(); ++i) {
+ QWidget *child = qobject_cast<QWidget*>(children.at(i));
+ if (!child)
+ continue;
+
+ updateLists(child);
+ }
+
+ if (cur->testAttribute(Qt::WA_StaticContents))
+ addStaticWidget(cur);
+
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+ QTLWExtra *extra = cur->d_func()->maybeTopData();
+ if (extra && extra->windowSurface && cur != tlw)
+ subSurfaces.append(extra->windowSurface);
+#endif
+}
+
+QWidgetBackingStore::QWidgetBackingStore(QWidget *topLevel)
+ : tlw(topLevel), dirtyOnScreenWidgets(0), hasDirtyFromPreviousSync(false)
+ , fullUpdatePending(0)
+{
+ windowSurface = tlw->windowSurface();
+ if (!windowSurface)
+ windowSurface = topLevel->d_func()->createDefaultWindowSurface();
+
+ // The QWindowSurface constructor will call QWidget::setWindowSurface(),
+ // but automatically created surfaces should not be added to the topdata.
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+ Q_ASSERT(topLevel->d_func()->topData()->windowSurface == windowSurface);
+#endif
+ topLevel->d_func()->topData()->windowSurface = 0;
+
+ // Ensure all existing subsurfaces and static widgets are added to their respective lists.
+ updateLists(topLevel);
+}
+
+QWidgetBackingStore::~QWidgetBackingStore()
+{
+ for (int c = 0; c < dirtyWidgets.size(); ++c) {
+ resetWidget(dirtyWidgets.at(c));
+ }
+
+ delete windowSurface;
+ windowSurface = 0;
+ delete dirtyOnScreenWidgets;
+ dirtyOnScreenWidgets = 0;
+}
+
+//parent's coordinates; move whole rect; update parent and widget
+//assume the screen blt has already been done, so we don't need to refresh that part
+void QWidgetPrivate::moveRect(const QRect &rect, int dx, int dy)
+{
+ Q_Q(QWidget);
+ if (!q->isVisible() || (dx == 0 && dy == 0))
+ return;
+
+ QWidget *tlw = q->window();
+ QTLWExtra* x = tlw->d_func()->topData();
+ if (x->inTopLevelResize)
+ return;
+
+ static int accelEnv = -1;
+ if (accelEnv == -1) {
+ accelEnv = qgetenv("QT_NO_FAST_MOVE").toInt() == 0;
+ }
+
+ QWidget *pw = q->parentWidget();
+ QPoint toplevelOffset = pw->mapTo(tlw, QPoint());
+ QWidgetPrivate *pd = pw->d_func();
+ QRect clipR(pd->clipRect());
+#ifdef Q_WS_QWS
+ QWidgetBackingStore *wbs = x->backingStore.data();
+ QWSWindowSurface *surface = static_cast<QWSWindowSurface*>(wbs->windowSurface);
+ clipR = clipR.intersected(surface->clipRegion().translated(-toplevelOffset).boundingRect());
+#endif
+ const QRect newRect(rect.translated(dx, dy));
+ QRect destRect = rect.intersected(clipR);
+ if (destRect.isValid())
+ destRect = destRect.translated(dx, dy).intersected(clipR);
+ const QRect sourceRect(destRect.translated(-dx, -dy));
+ const QRect parentRect(rect & clipR);
+
+ bool accelerateMove = accelEnv && isOpaque
+#ifndef QT_NO_GRAPHICSVIEW
+ // No accelerate move for proxy widgets.
+ && !tlw->d_func()->extra->proxyWidget
+#endif
+ && !isOverlapped(sourceRect) && !isOverlapped(destRect);
+
+ if (!accelerateMove) {
+ QRegion parentR(effectiveRectFor(parentRect));
+ if (!extra || !extra->hasMask) {
+ parentR -= newRect;
+ } else {
+ // invalidateBuffer() excludes anything outside the mask
+ parentR += newRect & clipR;
+ }
+ pd->invalidateBuffer(parentR);
+ invalidateBuffer((newRect & clipR).translated(-data.crect.topLeft()));
+ } else {
+
+ QWidgetBackingStore *wbs = x->backingStore.data();
+ QRegion childExpose(newRect & clipR);
+
+ if (sourceRect.isValid() && wbs->bltRect(sourceRect, dx, dy, pw))
+ childExpose -= destRect;
+
+ if (!pw->updatesEnabled())
+ return;
+
+ const bool childUpdatesEnabled = q->updatesEnabled();
+ if (childUpdatesEnabled && !childExpose.isEmpty()) {
+ childExpose.translate(-data.crect.topLeft());
+ wbs->markDirty(childExpose, q);
+ isMoved = true;
+ }
+
+ QRegion parentExpose(parentRect);
+ parentExpose -= newRect;
+ if (extra && extra->hasMask)
+ parentExpose += QRegion(newRect) - extra->mask.translated(data.crect.topLeft());
+
+ if (!parentExpose.isEmpty()) {
+ wbs->markDirty(parentExpose, pw);
+ pd->isMoved = true;
+ }
+
+ if (childUpdatesEnabled) {
+ QRegion needsFlush(sourceRect);
+ needsFlush += destRect;
+ wbs->markDirtyOnScreen(needsFlush, pw, toplevelOffset);
+ }
+ }
+}
+
+//widget's coordinates; scroll within rect; only update widget
+void QWidgetPrivate::scrollRect(const QRect &rect, int dx, int dy)
+{
+ Q_Q(QWidget);
+ QWidget *tlw = q->window();
+ QTLWExtra* x = tlw->d_func()->topData();
+ if (x->inTopLevelResize)
+ return;
+
+ QWidgetBackingStore *wbs = x->backingStore.data();
+ if (!wbs)
+ return;
+
+ static int accelEnv = -1;
+ if (accelEnv == -1) {
+ accelEnv = qgetenv("QT_NO_FAST_SCROLL").toInt() == 0;
+ }
+
+ QRect scrollRect = rect & clipRect();
+ bool overlapped = false;
+ bool accelerateScroll = accelEnv && isOpaque
+ && !(overlapped = isOverlapped(scrollRect.translated(data.crect.topLeft())));
+
+#if defined(Q_WS_QWS)
+ QWSWindowSurface *surface;
+ surface = static_cast<QWSWindowSurface*>(wbs->windowSurface);
+
+ if (accelerateScroll && !surface->isBuffered()) {
+ const QRegion surfaceClip = surface->clipRegion();
+ const QRegion outsideClip = QRegion(rect) - surfaceClip;
+ if (!outsideClip.isEmpty()) {
+ const QVector<QRect> clipped = (surfaceClip & rect).rects();
+ if (clipped.size() < 8) {
+ for (int i = 0; i < clipped.size(); ++i)
+ this->scrollRect(clipped.at(i), dx, dy);
+ return;
+ } else {
+ accelerateScroll = false;
+ }
+ }
+ }
+#endif // Q_WS_QWS
+
+ if (!accelerateScroll) {
+ if (overlapped) {
+ QRegion region(scrollRect);
+ subtractOpaqueSiblings(region);
+ invalidateBuffer(region);
+ }else {
+ invalidateBuffer(scrollRect);
+ }
+ } else {
+ const QPoint toplevelOffset = q->mapTo(tlw, QPoint());
+#ifdef Q_WS_QWS
+ QWSWindowSurface *surface = static_cast<QWSWindowSurface*>(wbs->windowSurface);
+ const QRegion clip = surface->clipRegion().translated(-toplevelOffset) & scrollRect;
+ const QRect clipBoundingRect = clip.boundingRect();
+ scrollRect &= clipBoundingRect;
+#endif
+ const QRect destRect = scrollRect.translated(dx, dy) & scrollRect;
+ const QRect sourceRect = destRect.translated(-dx, -dy);
+
+ QRegion childExpose(scrollRect);
+ if (sourceRect.isValid()) {
+ if (wbs->bltRect(sourceRect, dx, dy, q))
+ childExpose -= destRect;
+ }
+
+ if (inDirtyList) {
+ if (rect == q->rect()) {
+ dirty.translate(dx, dy);
+ } else {
+ QRegion dirtyScrollRegion = dirty.intersected(scrollRect);
+ if (!dirtyScrollRegion.isEmpty()) {
+ dirty -= dirtyScrollRegion;
+ dirtyScrollRegion.translate(dx, dy);
+ dirty += dirtyScrollRegion;
+ }
+ }
+ }
+
+ if (!q->updatesEnabled())
+ return;
+
+ if (!childExpose.isEmpty()) {
+ wbs->markDirty(childExpose, q);
+ isScrolled = true;
+ }
+
+ // Instead of using native scroll-on-screen, we copy from
+ // backingstore, giving only one screen update for each
+ // scroll, and a solid appearance
+ wbs->markDirtyOnScreen(destRect, q, toplevelOffset);
+ }
+}
+
+static inline bool discardSyncRequest(QWidget *tlw, QTLWExtra *tlwExtra)
+{
+ if (!tlw || !tlwExtra)
+ return true;
+
+#ifdef Q_WS_X11
+ // Delay the sync until we get an Expose event from X11 (initial show).
+ // Qt::WA_Mapped is set to true, but the actual mapping has not yet occurred.
+ // However, we must repaint immediately regardless of the state if someone calls repaint().
+ if (tlwExtra->waitingForMapNotify && !tlwExtra->inRepaint)
+ return true;
+#endif
+
+ if (!tlw->testAttribute(Qt::WA_Mapped))
+ return true;
+
+ if (!tlw->isVisible()
+#ifndef Q_WS_X11
+ // If we're minimized on X11, WA_Mapped will be false and we
+ // will return in the case above. Some window managers on X11
+ // sends us the PropertyNotify to change the minimized state
+ // *AFTER* we've received the expose event, which is baaad.
+ || tlw->isMinimized()
+#endif
+ )
+ return true;
+
+ return false;
+}
+
+/*!
+ Synchronizes the \a exposedRegion of the \a exposedWidget with the backing store.
+
+ If there's nothing to repaint, the area is flushed and painting does not occur;
+ otherwise the area is marked as dirty on screen and will be flushed right after
+ we are done with all painting.
+*/
+void QWidgetBackingStore::sync(QWidget *exposedWidget, const QRegion &exposedRegion)
+{
+ QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData();
+ if (discardSyncRequest(tlw, tlwExtra) || tlwExtra->inTopLevelResize)
+ return;
+
+ if (!exposedWidget || !exposedWidget->internalWinId() || !exposedWidget->isVisible()
+ || !exposedWidget->updatesEnabled() || exposedRegion.isEmpty()) {
+ return;
+ }
+
+ // If there's no preserved contents support we always need
+ // to do a full repaint before flushing
+ if (!windowSurface->hasFeature(QWindowSurface::PreservedContents))
+ fullUpdatePending = true;
+
+ // Nothing to repaint.
+ if (!isDirty()) {
+ qt_flush(exposedWidget, exposedRegion, windowSurface, tlw, tlwOffset);
+ return;
+ }
+
+ if (exposedWidget != tlw)
+ markDirtyOnScreen(exposedRegion, exposedWidget, exposedWidget->mapTo(tlw, QPoint()));
+ else
+ markDirtyOnScreen(exposedRegion, exposedWidget, QPoint());
+ sync();
+}
+
+/*!
+ Synchronizes the backing store, i.e. dirty areas are repainted and flushed.
+*/
+void QWidgetBackingStore::sync()
+{
+ QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData();
+ if (discardSyncRequest(tlw, tlwExtra)) {
+ // If the top-level is minimized, it's not visible on the screen so we can delay the
+ // update until it's shown again. In order to do that we must keep the dirty states.
+ // These will be cleared when we receive the first expose after showNormal().
+ // However, if the widget is not visible (isVisible() returns false), everything will
+ // be invalidated once the widget is shown again, so clear all dirty states.
+ if (!tlw->isVisible()) {
+ dirty = QRegion();
+ for (int i = 0; i < dirtyWidgets.size(); ++i)
+ resetWidget(dirtyWidgets.at(i));
+ dirtyWidgets.clear();
+ fullUpdatePending = false;
+ }
+ return;
+ }
+
+ const bool updatesDisabled = !tlw->updatesEnabled();
+ bool repaintAllWidgets = false;
+
+ const bool inTopLevelResize = tlwExtra->inTopLevelResize;
+ const QRect tlwRect(topLevelRect());
+#ifdef Q_WS_QPA
+ const QRect surfaceGeometry(tlwRect.topLeft(), windowSurface->size());
+#else
+ const QRect surfaceGeometry(windowSurface->geometry());
+#endif
+ if ((fullUpdatePending || inTopLevelResize || surfaceGeometry.size() != tlwRect.size()) && !updatesDisabled) {
+ if (hasStaticContents()) {
+ // Repaint existing dirty area and newly visible area.
+ const QRect clipRect(0, 0, surfaceGeometry.width(), surfaceGeometry.height());
+ const QRegion staticRegion(staticContents(0, clipRect));
+ QRegion newVisible(0, 0, tlwRect.width(), tlwRect.height());
+ newVisible -= staticRegion;
+ dirty += newVisible;
+ windowSurface->setStaticContents(staticRegion);
+ } else {
+ // Repaint everything.
+ dirty = QRegion(0, 0, tlwRect.width(), tlwRect.height());
+ for (int i = 0; i < dirtyWidgets.size(); ++i)
+ resetWidget(dirtyWidgets.at(i));
+ dirtyWidgets.clear();
+ repaintAllWidgets = true;
+ }
+ }
+
+#ifdef Q_WS_QPA
+ if (inTopLevelResize || surfaceGeometry.size() != tlwRect.size())
+ windowSurface->resize(tlwRect.size());
+#else
+ if (inTopLevelResize || surfaceGeometry != tlwRect)
+ windowSurface->setGeometry(tlwRect);
+#endif
+
+ if (updatesDisabled)
+ return;
+
+ if (hasDirtyFromPreviousSync)
+ dirty += dirtyFromPreviousSync;
+
+ // Contains everything that needs repaint.
+ QRegion toClean(dirty);
+
+ // Loop through all update() widgets and remove them from the list before they are
+ // painted (in case someone calls update() in paintEvent). If the widget is opaque
+ // and does not have transparent overlapping siblings, append it to the
+ // opaqueNonOverlappedWidgets list and paint it directly without composition.
+ QVarLengthArray<QWidget *, 32> opaqueNonOverlappedWidgets;
+ for (int i = 0; i < dirtyWidgets.size(); ++i) {
+ QWidget *w = dirtyWidgets.at(i);
+ QWidgetPrivate *wd = w->d_func();
+ if (wd->data.in_destructor)
+ continue;
+
+ // Clip with mask() and clipRect().
+ wd->dirty &= wd->clipRect();
+ wd->clipToEffectiveMask(wd->dirty);
+
+ // Subtract opaque siblings and children.
+ bool hasDirtySiblingsAbove = false;
+ // We know for sure that the widget isn't overlapped if 'isMoved' is true.
+ if (!wd->isMoved)
+ wd->subtractOpaqueSiblings(wd->dirty, &hasDirtySiblingsAbove);
+ // Scrolled and moved widgets must draw all children.
+ if (!wd->isScrolled && !wd->isMoved)
+ wd->subtractOpaqueChildren(wd->dirty, w->rect());
+
+ if (wd->dirty.isEmpty()) {
+ resetWidget(w);
+ continue;
+ }
+
+ const QRegion widgetDirty(w != tlw ? wd->dirty.translated(w->mapTo(tlw, QPoint()))
+ : wd->dirty);
+ toClean += widgetDirty;
+
+#ifndef QT_NO_GRAPHICSVIEW
+ if (tlw->d_func()->extra->proxyWidget) {
+ resetWidget(w);
+ continue;
+ }
+#endif
+
+ if (!hasDirtySiblingsAbove && wd->isOpaque && !dirty.intersects(widgetDirty.boundingRect())) {
+ opaqueNonOverlappedWidgets.append(w);
+ } else {
+ resetWidget(w);
+ dirty += widgetDirty;
+ }
+ }
+ dirtyWidgets.clear();
+
+ fullUpdatePending = false;
+
+ if (toClean.isEmpty()) {
+ // Nothing to repaint. However, we might have newly exposed areas on the
+ // screen if this function was called from sync(QWidget *, QRegion)), so
+ // we have to make sure those are flushed.
+ flush();
+ return;
+ }
+
+#ifndef QT_NO_GRAPHICSVIEW
+ if (tlw->d_func()->extra->proxyWidget) {
+ updateStaticContentsSize();
+ dirty = QRegion();
+ const QVector<QRect> rects(toClean.rects());
+ for (int i = 0; i < rects.size(); ++i)
+ tlw->d_func()->extra->proxyWidget->update(rects.at(i));
+ return;
+ }
+#endif
+
+#ifndef Q_BACKINGSTORE_SUBSURFACES
+ BeginPaintInfo beginPaintInfo;
+ beginPaint(toClean, tlw, windowSurface, &beginPaintInfo);
+ if (beginPaintInfo.nothingToPaint) {
+ for (int i = 0; i < opaqueNonOverlappedWidgets.size(); ++i)
+ resetWidget(opaqueNonOverlappedWidgets[i]);
+ dirty = QRegion();
+ return;
+ }
+#endif
+
+ // Must do this before sending any paint events because
+ // the size may change in the paint event.
+ updateStaticContentsSize();
+ const QRegion dirtyCopy(dirty);
+ dirty = QRegion();
+
+ // Paint opaque non overlapped widgets.
+ for (int i = 0; i < opaqueNonOverlappedWidgets.size(); ++i) {
+ QWidget *w = opaqueNonOverlappedWidgets[i];
+ QWidgetPrivate *wd = w->d_func();
+
+ int flags = QWidgetPrivate::DrawRecursive;
+ // Scrolled and moved widgets must draw all children.
+ if (!wd->isScrolled && !wd->isMoved)
+ flags |= QWidgetPrivate::DontDrawOpaqueChildren;
+ if (w == tlw)
+ flags |= QWidgetPrivate::DrawAsRoot;
+
+ QRegion toBePainted(wd->dirty);
+ resetWidget(w);
+
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+ QWindowSurface *subSurface = w->windowSurface();
+ BeginPaintInfo beginPaintInfo;
+
+ QPoint off = w->mapTo(tlw, QPoint());
+ toBePainted.translate(off);
+ beginPaint(toBePainted, w, subSurface, &beginPaintInfo, true);
+ toBePainted.translate(-off);
+
+ if (beginPaintInfo.nothingToPaint)
+ continue;
+
+ if (beginPaintInfo.windowSurfaceRecreated) {
+ // Eep the window surface has changed. The old one may have been
+ // deleted, in which case we will segfault on the call to
+ // painterOffset() below. Use the new window surface instead.
+ subSurface = w->windowSurface();
+ }
+
+ QPoint offset(tlwOffset);
+ if (subSurface == windowSurface)
+ offset += w->mapTo(tlw, QPoint());
+ else
+ offset = static_cast<QWSWindowSurface*>(subSurface)->painterOffset();
+ wd->drawWidget(subSurface->paintDevice(), toBePainted, offset, flags, 0, this);
+
+ endPaint(toBePainted, subSurface, &beginPaintInfo);
+#else
+ QPoint offset(tlwOffset);
+ if (w != tlw)
+ offset += w->mapTo(tlw, QPoint());
+ wd->drawWidget(windowSurface->paintDevice(), toBePainted, offset, flags, 0, this);
+#endif
+ }
+
+ // Paint the rest with composition.
+#ifndef Q_BACKINGSTORE_SUBSURFACES
+ if (repaintAllWidgets || !dirtyCopy.isEmpty()) {
+ const int flags = QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawRecursive;
+ tlw->d_func()->drawWidget(windowSurface->paintDevice(), dirtyCopy, tlwOffset, flags, 0, this);
+ }
+
+ endPaint(toClean, windowSurface, &beginPaintInfo);
+#else
+ if (!repaintAllWidgets && dirtyCopy.isEmpty())
+ return; // Nothing more to paint.
+
+ QList<QWindowSurface *> surfaceList(subSurfaces);
+ surfaceList.prepend(windowSurface);
+ const QRect dirtyBoundingRect(dirtyCopy.boundingRect());
+
+ // Loop through all window surfaces (incl. the top-level surface) and
+ // repaint those intersecting with the bounding rect of the dirty region.
+ for (int i = 0; i < surfaceList.size(); ++i) {
+ QWindowSurface *subSurface = surfaceList.at(i);
+ QWidget *w = subSurface->window();
+ QWidgetPrivate *wd = w->d_func();
+
+ const QRect clipRect = wd->clipRect().translated(w->mapTo(tlw, QPoint()));
+ if (!qRectIntersects(dirtyBoundingRect, clipRect))
+ continue;
+
+ toClean = dirtyCopy;
+ BeginPaintInfo beginPaintInfo;
+ beginPaint(toClean, w, subSurface, &beginPaintInfo);
+ if (beginPaintInfo.nothingToPaint)
+ continue;
+
+ if (beginPaintInfo.windowSurfaceRecreated) {
+ // Eep the window surface has changed. The old one may have been
+ // deleted, in which case we will segfault on the call to
+ // painterOffset() below. Use the new window surface instead.
+ subSurface = w->windowSurface();
+ }
+
+ int flags = QWidgetPrivate::DrawRecursive;
+ if (w == tlw)
+ flags |= QWidgetPrivate::DrawAsRoot;
+ const QPoint painterOffset = static_cast<QWSWindowSurface*>(subSurface)->painterOffset();
+ wd->drawWidget(subSurface->paintDevice(), toClean, painterOffset, flags, 0, this);
+
+ endPaint(toClean, subSurface, &beginPaintInfo);
+ }
+#endif
+}
+
+/*!
+ Flushes the contents of the backing store into the top-level widget.
+ If the \a widget is non-zero, the content is flushed to the \a widget.
+ If the \a surface is non-zero, the content of the \a surface is flushed.
+*/
+void QWidgetBackingStore::flush(QWidget *widget, QWindowSurface *surface)
+{
+#if defined(Q_WS_QWS) && !defined(QT_NO_QWS_MANAGER)
+ paintWindowDecoration();
+#endif
+
+ if (!dirtyOnScreen.isEmpty()) {
+ QWidget *target = widget ? widget : tlw;
+ QWindowSurface *source = surface ? surface : windowSurface;
+ qt_flush(target, dirtyOnScreen, source, tlw, tlwOffset);
+ dirtyOnScreen = QRegion();
+ }
+
+ if (!dirtyOnScreenWidgets || dirtyOnScreenWidgets->isEmpty())
+ return;
+
+ for (int i = 0; i < dirtyOnScreenWidgets->size(); ++i) {
+ QWidget *w = dirtyOnScreenWidgets->at(i);
+ QWidgetPrivate *wd = w->d_func();
+ Q_ASSERT(wd->needsFlush);
+ qt_flush(w, *wd->needsFlush, windowSurface, tlw, tlwOffset);
+ *wd->needsFlush = QRegion();
+ }
+ dirtyOnScreenWidgets->clear();
+}
+
+static inline bool discardInvalidateBufferRequest(QWidget *widget, QTLWExtra *tlwExtra)
+{
+ Q_ASSERT(widget);
+ if (QApplication::closingDown())
+ return true;
+
+ if (!tlwExtra || tlwExtra->inTopLevelResize || !tlwExtra->backingStore)
+ return true;
+
+ if (!widget->isVisible() || !widget->updatesEnabled())
+ return true;
+
+ return false;
+}
+
+/*!
+ Invalidates the buffer when the widget is resized.
+ Static areas are never invalidated unless absolutely needed.
+*/
+void QWidgetPrivate::invalidateBuffer_resizeHelper(const QPoint &oldPos, const QSize &oldSize)
+{
+ Q_Q(QWidget);
+ Q_ASSERT(!q->isWindow());
+ Q_ASSERT(q->parentWidget());
+
+ const bool staticContents = q->testAttribute(Qt::WA_StaticContents);
+ const bool sizeDecreased = (data.crect.width() < oldSize.width())
+ || (data.crect.height() < oldSize.height());
+
+ const QPoint offset(data.crect.x() - oldPos.x(), data.crect.y() - oldPos.y());
+ const bool parentAreaExposed = !offset.isNull() || sizeDecreased;
+ const QRect newWidgetRect(q->rect());
+ const QRect oldWidgetRect(0, 0, oldSize.width(), oldSize.height());
+
+ if (!staticContents || graphicsEffect) {
+ QRegion staticChildren;
+ QWidgetBackingStore *bs = 0;
+ if (offset.isNull() && (bs = maybeBackingStore()))
+ staticChildren = bs->staticContents(q, oldWidgetRect);
+ const bool hasStaticChildren = !staticChildren.isEmpty();
+
+ if (hasStaticChildren) {
+ QRegion dirty(newWidgetRect);
+ dirty -= staticChildren;
+ invalidateBuffer(dirty);
+ } else {
+ // Entire widget needs repaint.
+ invalidateBuffer(newWidgetRect);
+ }
+
+ if (!parentAreaExposed)
+ return;
+
+ // Invalidate newly exposed area of the parent.
+ if (!graphicsEffect && extra && extra->hasMask) {
+ QRegion parentExpose(extra->mask.translated(oldPos));
+ parentExpose &= QRect(oldPos, oldSize);
+ if (hasStaticChildren)
+ parentExpose -= data.crect; // Offset is unchanged, safe to do this.
+ q->parentWidget()->d_func()->invalidateBuffer(parentExpose);
+ } else {
+ if (hasStaticChildren && !graphicsEffect) {
+ QRegion parentExpose(QRect(oldPos, oldSize));
+ parentExpose -= data.crect; // Offset is unchanged, safe to do this.
+ q->parentWidget()->d_func()->invalidateBuffer(parentExpose);
+ } else {
+ q->parentWidget()->d_func()->invalidateBuffer(effectiveRectFor(QRect(oldPos, oldSize)));
+ }
+ }
+ return;
+ }
+
+ // Move static content to its new position.
+ if (!offset.isNull()) {
+ if (sizeDecreased) {
+ const QSize minSize(qMin(oldSize.width(), data.crect.width()),
+ qMin(oldSize.height(), data.crect.height()));
+ moveRect(QRect(oldPos, minSize), offset.x(), offset.y());
+ } else {
+ moveRect(QRect(oldPos, oldSize), offset.x(), offset.y());
+ }
+ }
+
+ // Invalidate newly visible area of the widget.
+ if (!sizeDecreased || !oldWidgetRect.contains(newWidgetRect)) {
+ QRegion newVisible(newWidgetRect);
+ newVisible -= oldWidgetRect;
+ invalidateBuffer(newVisible);
+ }
+
+ if (!parentAreaExposed)
+ return;
+
+ // Invalidate newly exposed area of the parent.
+ const QRect oldRect(oldPos, oldSize);
+ if (extra && extra->hasMask) {
+ QRegion parentExpose(oldRect);
+ parentExpose &= extra->mask.translated(oldPos);
+ parentExpose -= (extra->mask.translated(data.crect.topLeft()) & data.crect);
+ q->parentWidget()->d_func()->invalidateBuffer(parentExpose);
+ } else {
+ QRegion parentExpose(oldRect);
+ parentExpose -= data.crect;
+ q->parentWidget()->d_func()->invalidateBuffer(parentExpose);
+ }
+}
+
+/*!
+ Invalidates the \a rgn (in widget's coordinates) of the backing store, i.e.
+ all widgets intersecting with the region will be repainted when the backing store
+ is synced.
+
+ ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore).
+*/
+void QWidgetPrivate::invalidateBuffer(const QRegion &rgn)
+{
+ Q_Q(QWidget);
+
+ QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData();
+ if (discardInvalidateBufferRequest(q, tlwExtra) || rgn.isEmpty())
+ return;
+
+ QRegion wrgn(rgn);
+ wrgn &= clipRect();
+ if (!graphicsEffect && extra && extra->hasMask)
+ wrgn &= extra->mask;
+ if (wrgn.isEmpty())
+ return;
+
+ tlwExtra->backingStore->markDirty(wrgn, q, false, true);
+}
+
+/*!
+ This function is equivalent to calling invalidateBuffer(QRegion(rect), ...), but
+ is more efficient as it eliminates QRegion operations/allocations and can
+ use the rect more precisely for additional cut-offs.
+
+ ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore).
+*/
+void QWidgetPrivate::invalidateBuffer(const QRect &rect)
+{
+ Q_Q(QWidget);
+
+ QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData();
+ if (discardInvalidateBufferRequest(q, tlwExtra) || rect.isEmpty())
+ return;
+
+ QRect wRect(rect);
+ wRect &= clipRect();
+ if (wRect.isEmpty())
+ return;
+
+ if (graphicsEffect || !extra || !extra->hasMask) {
+ tlwExtra->backingStore->markDirty(wRect, q, false, true);
+ return;
+ }
+
+ QRegion wRgn(extra->mask);
+ wRgn &= wRect;
+ if (wRgn.isEmpty())
+ return;
+
+ tlwExtra->backingStore->markDirty(wRgn, q, false, true);
+}
+
+void QWidgetPrivate::repaint_sys(const QRegion &rgn)
+{
+ if (data.in_destructor)
+ return;
+
+ Q_Q(QWidget);
+ if (q->testAttribute(Qt::WA_StaticContents)) {
+ if (!extra)
+ createExtra();
+ extra->staticContentsSize = data.crect.size();
+ }
+
+#ifdef Q_WS_QPA //Dont even call q->p
+ QPaintEngine *engine = 0;
+#else
+ QPaintEngine *engine = q->paintEngine();
+#endif
+ // QGLWidget does not support partial updates if:
+ // 1) The context is double buffered
+ // 2) The context is single buffered and auto-fill background is enabled.
+ const bool noPartialUpdateSupport = (engine && (engine->type() == QPaintEngine::OpenGL
+ || engine->type() == QPaintEngine::OpenGL2))
+ && (usesDoubleBufferedGLContext || q->autoFillBackground());
+ QRegion toBePainted(noPartialUpdateSupport ? q->rect() : rgn);
+
+#ifdef Q_WS_MAC
+ // No difference between update() and repaint() on the Mac.
+ update_sys(toBePainted);
+ return;
+#endif
+
+ toBePainted &= clipRect();
+ clipToEffectiveMask(toBePainted);
+ if (toBePainted.isEmpty())
+ return; // Nothing to repaint.
+
+#ifndef QT_NO_PAINT_DEBUG
+ bool flushed = QWidgetBackingStore::flushPaint(q, toBePainted);
+#endif
+
+ drawWidget(q, toBePainted, QPoint(), QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawPaintOnScreen, 0);
+
+#ifndef QT_NO_PAINT_DEBUG
+ if (flushed)
+ QWidgetBackingStore::unflushPaint(q, toBePainted);
+#endif
+
+ if (!q->testAttribute(Qt::WA_PaintOutsidePaintEvent) && q->paintingActive())
+ qWarning("QWidget::repaint: It is dangerous to leave painters active on a widget outside of the PaintEvent");
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qbackingstore_p.h b/src/gui/painting/qbackingstore_p.h
new file mode 100644
index 0000000000..05f4bfcb6c
--- /dev/null
+++ b/src/gui/painting/qbackingstore_p.h
@@ -0,0 +1,278 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QBACKINGSTORE_P_H
+#define QBACKINGSTORE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QDebug>
+#include <QtGui/qwidget.h>
+#include <private/qwidget_p.h>
+#include <private/qwindowsurface_p.h>
+#ifdef Q_WS_QWS
+#include <private/qwindowsurface_qws_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QWindowSurface;
+
+struct BeginPaintInfo {
+ inline BeginPaintInfo() : wasFlushed(0), nothingToPaint(0), windowSurfaceRecreated(0) {}
+ uint wasFlushed : 1;
+ uint nothingToPaint : 1;
+ uint windowSurfaceRecreated : 1;
+};
+
+class Q_AUTOTEST_EXPORT QWidgetBackingStore
+{
+public:
+ QWidgetBackingStore(QWidget *t);
+ ~QWidgetBackingStore();
+
+ static void showYellowThing(QWidget *widget, const QRegion &rgn, int msec, bool);
+
+ void sync(QWidget *exposedWidget, const QRegion &exposedRegion);
+ void sync();
+ void flush(QWidget *widget = 0, QWindowSurface *surface = 0);
+
+ inline QPoint topLevelOffset() const { return tlwOffset; }
+
+ QWindowSurface *surface() const { return windowSurface; }
+
+ inline bool isDirty() const
+ {
+ return !(dirtyWidgets.isEmpty() && dirty.isEmpty() && !hasDirtyFromPreviousSync
+ && !fullUpdatePending
+#if defined(Q_WS_QWS) && !defined(QT_NO_QWS_MANAGER)
+ && !hasDirtyWindowDecoration()
+#endif
+ );
+ }
+
+ // ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore).
+ void markDirty(const QRegion &rgn, QWidget *widget, bool updateImmediately = false,
+ bool invalidateBuffer = false);
+ void markDirty(const QRect &rect, QWidget *widget, bool updateImmediately = false,
+ bool invalidateBuffer = false);
+
+private:
+ QWidget *tlw;
+ QRegion dirtyOnScreen; // needsFlush
+ QRegion dirty; // needsRepaint
+ QRegion dirtyFromPreviousSync;
+ QVector<QWidget *> dirtyWidgets;
+ QVector<QWidget *> *dirtyOnScreenWidgets;
+ QList<QWidget *> staticWidgets;
+ QWindowSurface *windowSurface;
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+ QList<QWindowSurface*> subSurfaces;
+#endif
+ uint hasDirtyFromPreviousSync : 1;
+ uint fullUpdatePending : 1;
+
+ QPoint tlwOffset;
+
+ static bool flushPaint(QWidget *widget, const QRegion &rgn);
+ static void unflushPaint(QWidget *widget, const QRegion &rgn);
+
+ bool bltRect(const QRect &rect, int dx, int dy, QWidget *widget);
+ void releaseBuffer();
+
+ void beginPaint(QRegion &toClean, QWidget *widget, QWindowSurface *windowSurface,
+ BeginPaintInfo *returnInfo, bool toCleanIsInTopLevelCoordinates = true);
+ void endPaint(const QRegion &cleaned, QWindowSurface *windowSurface, BeginPaintInfo *beginPaintInfo);
+
+ QRegion dirtyRegion(QWidget *widget = 0) const;
+ QRegion staticContents(QWidget *widget = 0, const QRect &withinClipRect = QRect()) const;
+
+ void markDirtyOnScreen(const QRegion &dirtyOnScreen, QWidget *widget, const QPoint &topLevelOffset);
+
+ void removeDirtyWidget(QWidget *w);
+
+#if defined(Q_WS_QWS) && !defined(QT_NO_QWS_MANAGER)
+ bool hasDirtyWindowDecoration() const;
+ void paintWindowDecoration();
+#endif
+ void updateLists(QWidget *widget);
+
+ inline void addDirtyWidget(QWidget *widget, const QRegion &rgn)
+ {
+ if (widget && !widget->d_func()->inDirtyList && !widget->data->in_destructor) {
+ QWidgetPrivate *widgetPrivate = widget->d_func();
+#ifndef QT_NO_GRAPHICSEFFECT
+ if (widgetPrivate->graphicsEffect)
+ widgetPrivate->dirty = widgetPrivate->effectiveRectFor(rgn.boundingRect());
+ else
+#endif //QT_NO_GRAPHICSEFFECT
+ widgetPrivate->dirty = rgn;
+ dirtyWidgets.append(widget);
+ widgetPrivate->inDirtyList = true;
+ }
+ }
+
+ inline void dirtyWidgetsRemoveAll(QWidget *widget)
+ {
+ int i = 0;
+ while (i < dirtyWidgets.size()) {
+ if (dirtyWidgets.at(i) == widget)
+ dirtyWidgets.remove(i);
+ else
+ ++i;
+ }
+ }
+
+ inline void addStaticWidget(QWidget *widget)
+ {
+ if (!widget)
+ return;
+
+ Q_ASSERT(widget->testAttribute(Qt::WA_StaticContents));
+ if (!staticWidgets.contains(widget))
+ staticWidgets.append(widget);
+ }
+
+ inline void removeStaticWidget(QWidget *widget)
+ { staticWidgets.removeAll(widget); }
+
+ // Move the reparented widget and all its static children from this backing store
+ // to the new backing store if reparented into another top-level / backing store.
+ inline void moveStaticWidgets(QWidget *reparented)
+ {
+ Q_ASSERT(reparented);
+ QWidgetBackingStore *newBs = reparented->d_func()->maybeBackingStore();
+ if (newBs == this)
+ return;
+
+ int i = 0;
+ while (i < staticWidgets.size()) {
+ QWidget *w = staticWidgets.at(i);
+ if (reparented == w || reparented->isAncestorOf(w)) {
+ staticWidgets.removeAt(i);
+ if (newBs)
+ newBs->addStaticWidget(w);
+ } else {
+ ++i;
+ }
+ }
+ }
+
+ inline QRect topLevelRect() const
+ {
+#ifdef Q_WS_QWS
+ return tlw->frameGeometry();
+#else
+ return tlw->data->crect;
+#endif
+ }
+
+ inline void appendDirtyOnScreenWidget(QWidget *widget)
+ {
+ if (!widget)
+ return;
+
+ if (!dirtyOnScreenWidgets) {
+ dirtyOnScreenWidgets = new QVector<QWidget *>;
+ dirtyOnScreenWidgets->append(widget);
+ } else if (!dirtyOnScreenWidgets->contains(widget)) {
+ dirtyOnScreenWidgets->append(widget);
+ }
+ }
+
+ inline void dirtyOnScreenWidgetsRemoveAll(QWidget *widget)
+ {
+ if (!widget || !dirtyOnScreenWidgets)
+ return;
+
+ int i = 0;
+ while (i < dirtyOnScreenWidgets->size()) {
+ if (dirtyOnScreenWidgets->at(i) == widget)
+ dirtyOnScreenWidgets->remove(i);
+ else
+ ++i;
+ }
+ }
+
+ inline void resetWidget(QWidget *widget)
+ {
+ if (widget) {
+ widget->d_func()->inDirtyList = false;
+ widget->d_func()->isScrolled = false;
+ widget->d_func()->isMoved = false;
+ widget->d_func()->dirty = QRegion();
+ }
+ }
+
+ inline void updateStaticContentsSize()
+ {
+ for (int i = 0; i < staticWidgets.size(); ++i) {
+ QWidgetPrivate *wd = staticWidgets.at(i)->d_func();
+ if (!wd->extra)
+ wd->createExtra();
+ wd->extra->staticContentsSize = wd->data.crect.size();
+ }
+ }
+
+ inline bool hasStaticContents() const
+ { return !staticWidgets.isEmpty() && windowSurface->hasFeature(QWindowSurface::StaticContents); }
+
+ friend QRegion qt_dirtyRegion(QWidget *);
+ friend class QWidgetPrivate;
+ friend class QWidget;
+ friend class QWSManagerPrivate;
+ friend class QETWidget;
+ friend class QWindowSurface;
+ friend class QWSWindowSurface;
+};
+
+QT_END_NAMESPACE
+
+#endif // QBACKINGSTORE_P_H
diff --git a/src/gui/painting/qbezier.cpp b/src/gui/painting/qbezier.cpp
new file mode 100644
index 0000000000..a2f0edd397
--- /dev/null
+++ b/src/gui/painting/qbezier.cpp
@@ -0,0 +1,702 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbezier_p.h"
+#include <qdebug.h>
+#include <qline.h>
+#include <qpolygon.h>
+#include <qvector.h>
+#include <qlist.h>
+#include <qmath.h>
+
+#include <private/qnumeric_p.h>
+#include <private/qmath_p.h>
+
+QT_BEGIN_NAMESPACE
+
+//#define QDEBUG_BEZIER
+
+#ifdef FLOAT_ACCURACY
+#define INV_EPS (1L<<23)
+#else
+/* The value of 1.0 / (1L<<14) is enough for most applications */
+#define INV_EPS (1L<<14)
+#endif
+
+#ifndef M_SQRT2
+#define M_SQRT2 1.41421356237309504880
+#endif
+
+/*!
+ \internal
+*/
+QBezier QBezier::fromPoints(const QPointF &p1, const QPointF &p2,
+ const QPointF &p3, const QPointF &p4)
+{
+ QBezier b;
+ b.x1 = p1.x();
+ b.y1 = p1.y();
+ b.x2 = p2.x();
+ b.y2 = p2.y();
+ b.x3 = p3.x();
+ b.y3 = p3.y();
+ b.x4 = p4.x();
+ b.y4 = p4.y();
+ return b;
+}
+
+/*!
+ \internal
+*/
+QPolygonF QBezier::toPolygon(qreal bezier_flattening_threshold) const
+{
+ // flattening is done by splitting the bezier until we can replace the segment by a straight
+ // line. We split further until the control points are close enough to the line connecting the
+ // boundary points.
+ //
+ // the Distance of a point p from a line given by the points (a,b) is given by:
+ //
+ // d = abs( (bx - ax)(ay - py) - (by - ay)(ax - px) ) / line_length
+ //
+ // We can stop splitting if both control points are close enough to the line.
+ // To make the algorithm faster we use the manhattan length of the line.
+
+ QPolygonF polygon;
+ polygon.append(QPointF(x1, y1));
+ addToPolygon(&polygon, bezier_flattening_threshold);
+ return polygon;
+}
+
+QBezier QBezier::mapBy(const QTransform &transform) const
+{
+ return QBezier::fromPoints(transform.map(pt1()), transform.map(pt2()), transform.map(pt3()), transform.map(pt4()));
+}
+
+QBezier QBezier::getSubRange(qreal t0, qreal t1) const
+{
+ QBezier result;
+ QBezier temp;
+
+ // cut at t1
+ if (qFuzzyIsNull(t1 - qreal(1.))) {
+ result = *this;
+ } else {
+ temp = *this;
+ temp.parameterSplitLeft(t1, &result);
+ }
+
+ // cut at t0
+ if (!qFuzzyIsNull(t0))
+ result.parameterSplitLeft(t0 / t1, &temp);
+
+ return result;
+}
+
+static inline int quadraticRoots(qreal a, qreal b, qreal c,
+ qreal *x1, qreal *x2)
+{
+ if (qFuzzyIsNull(a)) {
+ if (qFuzzyIsNull(b))
+ return 0;
+ *x1 = *x2 = (-c / b);
+ return 1;
+ } else {
+ const qreal det = b * b - 4 * a * c;
+ if (qFuzzyIsNull(det)) {
+ *x1 = *x2 = -b / (2 * a);
+ return 1;
+ }
+ if (det > 0) {
+ if (qFuzzyIsNull(b)) {
+ *x2 = qSqrt(-c / a);
+ *x1 = -(*x2);
+ return 2;
+ }
+ const qreal stableA = b / (2 * a);
+ const qreal stableB = c / (a * stableA * stableA);
+ const qreal stableC = -1 - qSqrt(1 - stableB);
+ *x2 = stableA * stableC;
+ *x1 = (stableA * stableB) / stableC;
+ return 2;
+ } else
+ return 0;
+ }
+}
+
+static inline bool findInflections(qreal a, qreal b, qreal c,
+ qreal *t1 , qreal *t2, qreal *tCups)
+{
+ qreal r1 = 0, r2 = 0;
+
+ short rootsCount = quadraticRoots(a, b, c, &r1, &r2);
+
+ if (rootsCount >= 1) {
+ if (r1 < r2) {
+ *t1 = r1;
+ *t2 = r2;
+ } else {
+ *t1 = r2;
+ *t2 = r1;
+ }
+ if (!qFuzzyIsNull(a))
+ *tCups = qreal(0.5) * (-b / a);
+ else
+ *tCups = 2;
+
+ return true;
+ }
+
+ return false;
+}
+
+
+void QBezier::addToPolygon(QPolygonF *polygon, qreal bezier_flattening_threshold) const
+{
+ QBezier beziers[32];
+ beziers[0] = *this;
+ QBezier *b = beziers;
+
+ while (b >= beziers) {
+ // check if we can pop the top bezier curve from the stack
+ qreal y4y1 = b->y4 - b->y1;
+ qreal x4x1 = b->x4 - b->x1;
+ qreal l = qAbs(x4x1) + qAbs(y4y1);
+ qreal d;
+ if (l > 1.) {
+ d = qAbs( (x4x1)*(b->y1 - b->y2) - (y4y1)*(b->x1 - b->x2) )
+ + qAbs( (x4x1)*(b->y1 - b->y3) - (y4y1)*(b->x1 - b->x3) );
+ } else {
+ d = qAbs(b->x1 - b->x2) + qAbs(b->y1 - b->y2) +
+ qAbs(b->x1 - b->x3) + qAbs(b->y1 - b->y3);
+ l = 1.;
+ }
+ if (d < bezier_flattening_threshold*l || b == beziers + 31) {
+ // good enough, we pop it off and add the endpoint
+ polygon->append(QPointF(b->x4, b->y4));
+ --b;
+ } else {
+ // split, second half of the polygon goes lower into the stack
+ b->split(b+1, b);
+ ++b;
+ }
+ }
+}
+
+QRectF QBezier::bounds() const
+{
+ qreal xmin = x1;
+ qreal xmax = x1;
+ if (x2 < xmin)
+ xmin = x2;
+ else if (x2 > xmax)
+ xmax = x2;
+ if (x3 < xmin)
+ xmin = x3;
+ else if (x3 > xmax)
+ xmax = x3;
+ if (x4 < xmin)
+ xmin = x4;
+ else if (x4 > xmax)
+ xmax = x4;
+
+ qreal ymin = y1;
+ qreal ymax = y1;
+ if (y2 < ymin)
+ ymin = y2;
+ else if (y2 > ymax)
+ ymax = y2;
+ if (y3 < ymin)
+ ymin = y3;
+ else if (y3 > ymax)
+ ymax = y3;
+ if (y4 < ymin)
+ ymin = y4;
+ else if (y4 > ymax)
+ ymax = y4;
+ return QRectF(xmin, ymin, xmax-xmin, ymax-ymin);
+}
+
+
+enum ShiftResult {
+ Ok,
+ Discard,
+ Split,
+ Circle
+};
+
+static ShiftResult good_offset(const QBezier *b1, const QBezier *b2, qreal offset, qreal threshold)
+{
+ const qreal o2 = offset*offset;
+ const qreal max_dist_line = threshold*offset*offset;
+ const qreal max_dist_normal = threshold*offset;
+ const qreal spacing = qreal(0.25);
+ for (qreal i = spacing; i < qreal(0.99); i += spacing) {
+ QPointF p1 = b1->pointAt(i);
+ QPointF p2 = b2->pointAt(i);
+ qreal d = (p1.x() - p2.x())*(p1.x() - p2.x()) + (p1.y() - p2.y())*(p1.y() - p2.y());
+ if (qAbs(d - o2) > max_dist_line)
+ return Split;
+
+ QPointF normalPoint = b1->normalVector(i);
+ qreal l = qAbs(normalPoint.x()) + qAbs(normalPoint.y());
+ if (l != qreal(0.0)) {
+ d = qAbs( normalPoint.x()*(p1.y() - p2.y()) - normalPoint.y()*(p1.x() - p2.x()) ) / l;
+ if (d > max_dist_normal)
+ return Split;
+ }
+ }
+ return Ok;
+}
+
+static ShiftResult shift(const QBezier *orig, QBezier *shifted, qreal offset, qreal threshold)
+{
+ int map[4];
+ bool p1_p2_equal = (orig->x1 == orig->x2 && orig->y1 == orig->y2);
+ bool p2_p3_equal = (orig->x2 == orig->x3 && orig->y2 == orig->y3);
+ bool p3_p4_equal = (orig->x3 == orig->x4 && orig->y3 == orig->y4);
+
+ QPointF points[4];
+ int np = 0;
+ points[np] = QPointF(orig->x1, orig->y1);
+ map[0] = 0;
+ ++np;
+ if (!p1_p2_equal) {
+ points[np] = QPointF(orig->x2, orig->y2);
+ ++np;
+ }
+ map[1] = np - 1;
+ if (!p2_p3_equal) {
+ points[np] = QPointF(orig->x3, orig->y3);
+ ++np;
+ }
+ map[2] = np - 1;
+ if (!p3_p4_equal) {
+ points[np] = QPointF(orig->x4, orig->y4);
+ ++np;
+ }
+ map[3] = np - 1;
+ if (np == 1)
+ return Discard;
+
+ QRectF b = orig->bounds();
+ if (np == 4 && b.width() < .1*offset && b.height() < .1*offset) {
+ qreal l = (orig->x1 - orig->x2)*(orig->x1 - orig->x2) +
+ (orig->y1 - orig->y2)*(orig->y1 - orig->y1) *
+ (orig->x3 - orig->x4)*(orig->x3 - orig->x4) +
+ (orig->y3 - orig->y4)*(orig->y3 - orig->y4);
+ qreal dot = (orig->x1 - orig->x2)*(orig->x3 - orig->x4) +
+ (orig->y1 - orig->y2)*(orig->y3 - orig->y4);
+ if (dot < 0 && dot*dot < 0.8*l)
+ // the points are close and reverse dirction. Approximate the whole
+ // thing by a semi circle
+ return Circle;
+ }
+
+ QPointF points_shifted[4];
+
+ QLineF prev = QLineF(QPointF(), points[1] - points[0]);
+ QPointF prev_normal = prev.normalVector().unitVector().p2();
+
+ points_shifted[0] = points[0] + offset * prev_normal;
+
+ for (int i = 1; i < np - 1; ++i) {
+ QLineF next = QLineF(QPointF(), points[i + 1] - points[i]);
+ QPointF next_normal = next.normalVector().unitVector().p2();
+
+ QPointF normal_sum = prev_normal + next_normal;
+
+ qreal r = qreal(1.0) + prev_normal.x() * next_normal.x()
+ + prev_normal.y() * next_normal.y();
+
+ if (qFuzzyIsNull(r)) {
+ points_shifted[i] = points[i] + offset * prev_normal;
+ } else {
+ qreal k = offset / r;
+ points_shifted[i] = points[i] + k * normal_sum;
+ }
+
+ prev_normal = next_normal;
+ }
+
+ points_shifted[np - 1] = points[np - 1] + offset * prev_normal;
+
+ *shifted = QBezier::fromPoints(points_shifted[map[0]], points_shifted[map[1]],
+ points_shifted[map[2]], points_shifted[map[3]]);
+
+ return good_offset(orig, shifted, offset, threshold);
+}
+
+// This value is used to determine the length of control point vectors
+// when approximating arc segments as curves. The factor is multiplied
+// with the radius of the circle.
+#define KAPPA qreal(0.5522847498)
+
+
+static bool addCircle(const QBezier *b, qreal offset, QBezier *o)
+{
+ QPointF normals[3];
+
+ normals[0] = QPointF(b->y2 - b->y1, b->x1 - b->x2);
+ qreal dist = qSqrt(normals[0].x()*normals[0].x() + normals[0].y()*normals[0].y());
+ if (qFuzzyIsNull(dist))
+ return false;
+ normals[0] /= dist;
+ normals[2] = QPointF(b->y4 - b->y3, b->x3 - b->x4);
+ dist = qSqrt(normals[2].x()*normals[2].x() + normals[2].y()*normals[2].y());
+ if (qFuzzyIsNull(dist))
+ return false;
+ normals[2] /= dist;
+
+ normals[1] = QPointF(b->x1 - b->x2 - b->x3 + b->x4, b->y1 - b->y2 - b->y3 + b->y4);
+ normals[1] /= -1*qSqrt(normals[1].x()*normals[1].x() + normals[1].y()*normals[1].y());
+
+ qreal angles[2];
+ qreal sign = 1.;
+ for (int i = 0; i < 2; ++i) {
+ qreal cos_a = normals[i].x()*normals[i+1].x() + normals[i].y()*normals[i+1].y();
+ if (cos_a > 1.)
+ cos_a = 1.;
+ if (cos_a < -1.)
+ cos_a = -1;
+ angles[i] = qAcos(cos_a)/Q_PI;
+ }
+
+ if (angles[0] + angles[1] > 1.) {
+ // more than 180 degrees
+ normals[1] = -normals[1];
+ angles[0] = 1. - angles[0];
+ angles[1] = 1. - angles[1];
+ sign = -1.;
+
+ }
+
+ QPointF circle[3];
+ circle[0] = QPointF(b->x1, b->y1) + normals[0]*offset;
+ circle[1] = QPointF(qreal(0.5)*(b->x1 + b->x4), qreal(0.5)*(b->y1 + b->y4)) + normals[1]*offset;
+ circle[2] = QPointF(b->x4, b->y4) + normals[2]*offset;
+
+ for (int i = 0; i < 2; ++i) {
+ qreal kappa = qreal(2.0) * KAPPA * sign * offset * angles[i];
+
+ o->x1 = circle[i].x();
+ o->y1 = circle[i].y();
+ o->x2 = circle[i].x() - normals[i].y()*kappa;
+ o->y2 = circle[i].y() + normals[i].x()*kappa;
+ o->x3 = circle[i+1].x() + normals[i+1].y()*kappa;
+ o->y3 = circle[i+1].y() - normals[i+1].x()*kappa;
+ o->x4 = circle[i+1].x();
+ o->y4 = circle[i+1].y();
+
+ ++o;
+ }
+ return true;
+}
+
+int QBezier::shifted(QBezier *curveSegments, int maxSegments, qreal offset, float threshold) const
+{
+ Q_ASSERT(curveSegments);
+ Q_ASSERT(maxSegments > 0);
+
+ if (x1 == x2 && x1 == x3 && x1 == x4 &&
+ y1 == y2 && y1 == y3 && y1 == y4)
+ return 0;
+
+ --maxSegments;
+ QBezier beziers[10];
+redo:
+ beziers[0] = *this;
+ QBezier *b = beziers;
+ QBezier *o = curveSegments;
+
+ while (b >= beziers) {
+ int stack_segments = b - beziers + 1;
+ if ((stack_segments == 10) || (o - curveSegments == maxSegments - stack_segments)) {
+ threshold *= qreal(1.5);
+ if (threshold > qreal(2.0))
+ goto give_up;
+ goto redo;
+ }
+ ShiftResult res = shift(b, o, offset, threshold);
+ if (res == Discard) {
+ --b;
+ } else if (res == Ok) {
+ ++o;
+ --b;
+ continue;
+ } else if (res == Circle && maxSegments - (o - curveSegments) >= 2) {
+ // add semi circle
+ if (addCircle(b, offset, o))
+ o += 2;
+ --b;
+ } else {
+ b->split(b+1, b);
+ ++b;
+ }
+ }
+
+give_up:
+ while (b >= beziers) {
+ ShiftResult res = shift(b, o, offset, threshold);
+
+ // if res isn't Ok or Split then *o is undefined
+ if (res == Ok || res == Split)
+ ++o;
+
+ --b;
+ }
+
+ Q_ASSERT(o - curveSegments <= maxSegments);
+ return o - curveSegments;
+}
+
+#ifdef QDEBUG_BEZIER
+static QDebug operator<<(QDebug dbg, const QBezier &bz)
+{
+ dbg << '[' << bz.x1<< ", " << bz.y1 << "], "
+ << '[' << bz.x2 <<", " << bz.y2 << "], "
+ << '[' << bz.x3 <<", " << bz.y3 << "], "
+ << '[' << bz.x4 <<", " << bz.y4 << ']';
+ return dbg;
+}
+#endif
+
+static inline void splitBezierAt(const QBezier &bez, qreal t,
+ QBezier *left, QBezier *right)
+{
+ left->x1 = bez.x1;
+ left->y1 = bez.y1;
+
+ left->x2 = bez.x1 + t * ( bez.x2 - bez.x1 );
+ left->y2 = bez.y1 + t * ( bez.y2 - bez.y1 );
+
+ left->x3 = bez.x2 + t * ( bez.x3 - bez.x2 ); // temporary holding spot
+ left->y3 = bez.y2 + t * ( bez.y3 - bez.y2 ); // temporary holding spot
+
+ right->x3 = bez.x3 + t * ( bez.x4 - bez.x3 );
+ right->y3 = bez.y3 + t * ( bez.y4 - bez.y3 );
+
+ right->x2 = left->x3 + t * ( right->x3 - left->x3);
+ right->y2 = left->y3 + t * ( right->y3 - left->y3);
+
+ left->x3 = left->x2 + t * ( left->x3 - left->x2 );
+ left->y3 = left->y2 + t * ( left->y3 - left->y2 );
+
+ left->x4 = right->x1 = left->x3 + t * (right->x2 - left->x3);
+ left->y4 = right->y1 = left->y3 + t * (right->y2 - left->y3);
+
+ right->x4 = bez.x4;
+ right->y4 = bez.y4;
+}
+
+qreal QBezier::length(qreal error) const
+{
+ qreal length = qreal(0.0);
+
+ addIfClose(&length, error);
+
+ return length;
+}
+
+void QBezier::addIfClose(qreal *length, qreal error) const
+{
+ QBezier left, right; /* bez poly splits */
+
+ qreal len = qreal(0.0); /* arc length */
+ qreal chord; /* chord length */
+
+ len = len + QLineF(QPointF(x1, y1),QPointF(x2, y2)).length();
+ len = len + QLineF(QPointF(x2, y2),QPointF(x3, y3)).length();
+ len = len + QLineF(QPointF(x3, y3),QPointF(x4, y4)).length();
+
+ chord = QLineF(QPointF(x1, y1),QPointF(x4, y4)).length();
+
+ if((len-chord) > error) {
+ split(&left, &right); /* split in two */
+ left.addIfClose(length, error); /* try left side */
+ right.addIfClose(length, error); /* try right side */
+ return;
+ }
+
+ *length = *length + len;
+
+ return;
+}
+
+qreal QBezier::tForY(qreal t0, qreal t1, qreal y) const
+{
+ qreal py0 = pointAt(t0).y();
+ qreal py1 = pointAt(t1).y();
+
+ if (py0 > py1) {
+ qSwap(py0, py1);
+ qSwap(t0, t1);
+ }
+
+ Q_ASSERT(py0 <= py1);
+
+ if (py0 >= y)
+ return t0;
+ else if (py1 <= y)
+ return t1;
+
+ Q_ASSERT(py0 < y && y < py1);
+
+ qreal lt = t0;
+ qreal dt;
+ do {
+ qreal t = qreal(0.5) * (t0 + t1);
+
+ qreal a, b, c, d;
+ QBezier::coefficients(t, a, b, c, d);
+ qreal yt = a * y1 + b * y2 + c * y3 + d * y4;
+
+ if (yt < y) {
+ t0 = t;
+ py0 = yt;
+ } else {
+ t1 = t;
+ py1 = yt;
+ }
+ dt = lt - t;
+ lt = t;
+ } while (qAbs(dt) > qreal(1e-7));
+
+ return t0;
+}
+
+int QBezier::stationaryYPoints(qreal &t0, qreal &t1) const
+{
+ // y(t) = (1 - t)^3 * y1 + 3 * (1 - t)^2 * t * y2 + 3 * (1 - t) * t^2 * y3 + t^3 * y4
+ // y'(t) = 3 * (-(1-2t+t^2) * y1 + (1 - 4 * t + 3 * t^2) * y2 + (2 * t - 3 * t^2) * y3 + t^2 * y4)
+ // y'(t) = 3 * ((-y1 + 3 * y2 - 3 * y3 + y4)t^2 + (2 * y1 - 4 * y2 + 2 * y3)t + (-y1 + y2))
+
+ const qreal a = -y1 + 3 * y2 - 3 * y3 + y4;
+ const qreal b = 2 * y1 - 4 * y2 + 2 * y3;
+ const qreal c = -y1 + y2;
+
+ if (qFuzzyIsNull(a)) {
+ if (qFuzzyIsNull(b))
+ return 0;
+
+ t0 = -c / b;
+ return t0 > 0 && t0 < 1;
+ }
+
+ qreal reciprocal = b * b - 4 * a * c;
+
+ if (qFuzzyIsNull(reciprocal)) {
+ t0 = -b / (2 * a);
+ return t0 > 0 && t0 < 1;
+ } else if (reciprocal > 0) {
+ qreal temp = qSqrt(reciprocal);
+
+ t0 = (-b - temp)/(2*a);
+ t1 = (-b + temp)/(2*a);
+
+ if (t1 < t0)
+ qSwap(t0, t1);
+
+ int count = 0;
+ qreal t[2] = { 0, 1 };
+
+ if (t0 > 0 && t0 < 1)
+ t[count++] = t0;
+ if (t1 > 0 && t1 < 1)
+ t[count++] = t1;
+
+ t0 = t[0];
+ t1 = t[1];
+
+ return count;
+ }
+
+ return 0;
+}
+
+qreal QBezier::tAtLength(qreal l) const
+{
+ qreal len = length();
+ qreal t = qreal(1.0);
+ const qreal error = qreal(0.01);
+ if (l > len || qFuzzyCompare(l, len))
+ return t;
+
+ t *= qreal(0.5);
+ //int iters = 0;
+ //qDebug()<<"LEN is "<<l<<len;
+ qreal lastBigger = qreal(1.0);
+ while (1) {
+ //qDebug()<<"\tt is "<<t;
+ QBezier right = *this;
+ QBezier left;
+ right.parameterSplitLeft(t, &left);
+ qreal lLen = left.length();
+ if (qAbs(lLen - l) < error)
+ break;
+
+ if (lLen < l) {
+ t += (lastBigger - t) * qreal(0.5);
+ } else {
+ lastBigger = t;
+ t -= t * qreal(0.5);
+ }
+ //++iters;
+ }
+ //qDebug()<<"number of iters is "<<iters;
+ return t;
+}
+
+QBezier QBezier::bezierOnInterval(qreal t0, qreal t1) const
+{
+ if (t0 == 0 && t1 == 1)
+ return *this;
+
+ QBezier bezier = *this;
+
+ QBezier result;
+ bezier.parameterSplitLeft(t0, &result);
+ qreal trueT = (t1-t0)/(1-t0);
+ bezier.parameterSplitLeft(trueT, &result);
+
+ return result;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qbezier_p.h b/src/gui/painting/qbezier_p.h
new file mode 100644
index 0000000000..8d37e9a653
--- /dev/null
+++ b/src/gui/painting/qbezier_p.h
@@ -0,0 +1,274 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QBEZIER_P_H
+#define QBEZIER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtCore/qpoint.h"
+#include "QtCore/qline.h"
+#include "QtCore/qrect.h"
+#include "QtCore/qvector.h"
+#include "QtCore/qlist.h"
+#include "QtCore/qpair.h"
+#include "QtGui/qtransform.h"
+
+QT_BEGIN_NAMESPACE
+
+class QPolygonF;
+
+class Q_GUI_EXPORT QBezier
+{
+public:
+ static QBezier fromPoints(const QPointF &p1, const QPointF &p2,
+ const QPointF &p3, const QPointF &p4);
+
+ static void coefficients(qreal t, qreal &a, qreal &b, qreal &c, qreal &d);
+
+ inline QPointF pointAt(qreal t) const;
+ inline QPointF normalVector(qreal t) const;
+
+ inline QPointF derivedAt(qreal t) const;
+ inline QPointF secondDerivedAt(qreal t) const;
+
+ QPolygonF toPolygon(qreal bezier_flattening_threshold = 0.5) const;
+ void addToPolygon(QPolygonF *p, qreal bezier_flattening_threshold = 0.5) const;
+
+ QRectF bounds() const;
+ qreal length(qreal error = 0.01) const;
+ void addIfClose(qreal *length, qreal error) const;
+
+ qreal tAtLength(qreal len) const;
+
+ int stationaryYPoints(qreal &t0, qreal &t1) const;
+ qreal tForY(qreal t0, qreal t1, qreal y) const;
+
+ QPointF pt1() const { return QPointF(x1, y1); }
+ QPointF pt2() const { return QPointF(x2, y2); }
+ QPointF pt3() const { return QPointF(x3, y3); }
+ QPointF pt4() const { return QPointF(x4, y4); }
+
+ QBezier mapBy(const QTransform &transform) const;
+
+ inline QPointF midPoint() const;
+ inline QLineF midTangent() const;
+
+ inline QLineF startTangent() const;
+ inline QLineF endTangent() const;
+
+ inline void parameterSplitLeft(qreal t, QBezier *left);
+ inline void split(QBezier *firstHalf, QBezier *secondHalf) const;
+
+ int shifted(QBezier *curveSegments, int maxSegmets,
+ qreal offset, float threshold) const;
+
+ QBezier bezierOnInterval(qreal t0, qreal t1) const;
+ QBezier getSubRange(qreal t0, qreal t1) const;
+
+ qreal x1, y1, x2, y2, x3, y3, x4, y4;
+};
+
+inline QPointF QBezier::midPoint() const
+{
+ return QPointF((x1 + x4 + 3*(x2 + x3))/8., (y1 + y4 + 3*(y2 + y3))/8.);
+}
+
+inline QLineF QBezier::midTangent() const
+{
+ QPointF mid = midPoint();
+ QLineF dir(QLineF(x1, y1, x2, y2).pointAt(0.5), QLineF(x3, y3, x4, y4).pointAt(0.5));
+ return QLineF(mid.x() - dir.dx(), mid.y() - dir.dy(),
+ mid.x() + dir.dx(), mid.y() + dir.dy());
+}
+
+inline QLineF QBezier::startTangent() const
+{
+ QLineF tangent(pt1(), pt2());
+ if (tangent.isNull())
+ tangent = QLineF(pt1(), pt3());
+ if (tangent.isNull())
+ tangent = QLineF(pt1(), pt4());
+ return tangent;
+}
+
+inline QLineF QBezier::endTangent() const
+{
+ QLineF tangent(pt4(), pt3());
+ if (tangent.isNull())
+ tangent = QLineF(pt4(), pt2());
+ if (tangent.isNull())
+ tangent = QLineF(pt4(), pt1());
+ return tangent;
+}
+
+inline void QBezier::coefficients(qreal t, qreal &a, qreal &b, qreal &c, qreal &d)
+{
+ qreal m_t = 1. - t;
+ b = m_t * m_t;
+ c = t * t;
+ d = c * t;
+ a = b * m_t;
+ b *= 3. * t;
+ c *= 3. * m_t;
+}
+
+inline QPointF QBezier::pointAt(qreal t) const
+{
+#if 1
+ qreal a, b, c, d;
+ coefficients(t, a, b, c, d);
+ return QPointF(a*x1 + b*x2 + c*x3 + d*x4, a*y1 + b*y2 + c*y3 + d*y4);
+#else
+ // numerically more stable:
+ qreal m_t = 1. - t;
+ qreal a = x1*m_t + x2*t;
+ qreal b = x2*m_t + x3*t;
+ qreal c = x3*m_t + x4*t;
+ a = a*m_t + b*t;
+ b = b*m_t + c*t;
+ qreal x = a*m_t + b*t;
+ qreal a = y1*m_t + y2*t;
+ qreal b = y2*m_t + y3*t;
+ qreal c = y3*m_t + y4*t;
+ a = a*m_t + b*t;
+ b = b*m_t + c*t;
+ qreal y = a*m_t + b*t;
+ return QPointF(x, y);
+#endif
+}
+
+inline QPointF QBezier::normalVector(qreal t) const
+{
+ qreal m_t = 1. - t;
+ qreal a = m_t * m_t;
+ qreal b = t * m_t;
+ qreal c = t * t;
+
+ return QPointF((y2-y1) * a + (y3-y2) * b + (y4-y3) * c, -(x2-x1) * a - (x3-x2) * b - (x4-x3) * c);
+}
+
+inline QPointF QBezier::derivedAt(qreal t) const
+{
+ // p'(t) = 3 * (-(1-2t+t^2) * p0 + (1 - 4 * t + 3 * t^2) * p1 + (2 * t - 3 * t^2) * p2 + t^2 * p3)
+
+ qreal m_t = 1. - t;
+
+ qreal d = t * t;
+ qreal a = -m_t * m_t;
+ qreal b = 1 - 4 * t + 3 * d;
+ qreal c = 2 * t - 3 * d;
+
+ return 3 * QPointF(a * x1 + b * x2 + c * x3 + d * x4,
+ a * y1 + b * y2 + c * y3 + d * y4);
+}
+
+inline QPointF QBezier::secondDerivedAt(qreal t) const
+{
+ qreal a = 2. - 2. * t;
+ qreal b = -4 + 6 * t;
+ qreal c = 2 - 6 * t;
+ qreal d = 2 * t;
+
+ return 3 * QPointF(a * x1 + b * x2 + c * x3 + d * x4,
+ a * y1 + b * y2 + c * y3 + d * y4);
+}
+
+inline void QBezier::split(QBezier *firstHalf, QBezier *secondHalf) const
+{
+ Q_ASSERT(firstHalf);
+ Q_ASSERT(secondHalf);
+
+ qreal c = (x2 + x3)*.5;
+ firstHalf->x2 = (x1 + x2)*.5;
+ secondHalf->x3 = (x3 + x4)*.5;
+ firstHalf->x1 = x1;
+ secondHalf->x4 = x4;
+ firstHalf->x3 = (firstHalf->x2 + c)*.5;
+ secondHalf->x2 = (secondHalf->x3 + c)*.5;
+ firstHalf->x4 = secondHalf->x1 = (firstHalf->x3 + secondHalf->x2)*.5;
+
+ c = (y2 + y3)/2;
+ firstHalf->y2 = (y1 + y2)*.5;
+ secondHalf->y3 = (y3 + y4)*.5;
+ firstHalf->y1 = y1;
+ secondHalf->y4 = y4;
+ firstHalf->y3 = (firstHalf->y2 + c)*.5;
+ secondHalf->y2 = (secondHalf->y3 + c)*.5;
+ firstHalf->y4 = secondHalf->y1 = (firstHalf->y3 + secondHalf->y2)*.5;
+}
+
+inline void QBezier::parameterSplitLeft(qreal t, QBezier *left)
+{
+ left->x1 = x1;
+ left->y1 = y1;
+
+ left->x2 = x1 + t * ( x2 - x1 );
+ left->y2 = y1 + t * ( y2 - y1 );
+
+ left->x3 = x2 + t * ( x3 - x2 ); // temporary holding spot
+ left->y3 = y2 + t * ( y3 - y2 ); // temporary holding spot
+
+ x3 = x3 + t * ( x4 - x3 );
+ y3 = y3 + t * ( y4 - y3 );
+
+ x2 = left->x3 + t * ( x3 - left->x3);
+ y2 = left->y3 + t * ( y3 - left->y3);
+
+ left->x3 = left->x2 + t * ( left->x3 - left->x2 );
+ left->y3 = left->y2 + t * ( left->y3 - left->y2 );
+
+ left->x4 = x1 = left->x3 + t * (x2 - left->x3);
+ left->y4 = y1 = left->y3 + t * (y2 - left->y3);
+}
+
+QT_END_NAMESPACE
+
+#endif // QBEZIER_P_H
diff --git a/src/gui/painting/qblendfunctions.cpp b/src/gui/painting/qblendfunctions.cpp
new file mode 100644
index 0000000000..10674f8ef5
--- /dev/null
+++ b/src/gui/painting/qblendfunctions.cpp
@@ -0,0 +1,1644 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qmath.h>
+#include "qblendfunctions_p.h"
+
+QT_BEGIN_NAMESPACE
+
+struct SourceOnlyAlpha
+{
+ inline uchar alpha(uchar src) const { return src; }
+ inline quint16 bytemul(quint16 spix) const { return spix; }
+};
+
+
+struct SourceAndConstAlpha
+{
+ SourceAndConstAlpha(int a) : m_alpha256(a) {
+ m_alpha255 = (m_alpha256 * 255) >> 8;
+ };
+ inline uchar alpha(uchar src) const { return (src * m_alpha256) >> 8; }
+ inline quint16 bytemul(quint16 x) const {
+ uint t = (((x & 0x07e0)*m_alpha255) >> 8) & 0x07e0;
+ t |= (((x & 0xf81f)*(m_alpha255>>2)) >> 6) & 0xf81f;
+ return t;
+ }
+ int m_alpha255;
+ int m_alpha256;
+};
+
+
+/************************************************************************
+ RGB16 (565) format target format
+ ************************************************************************/
+
+static inline quint16 convert_argb32_to_rgb16(quint32 spix)
+{
+ quint32 b = spix;
+ quint32 g = spix;
+ b >>= 8;
+ g >>= 5;
+ b &= 0x0000f800;
+ g &= 0x000007e0;
+ spix >>= 3;
+ b |= g;
+ spix &= 0x0000001f;
+ b |= spix;
+ return b;
+}
+
+struct Blend_RGB16_on_RGB16_NoAlpha {
+ inline void write(quint16 *dst, quint16 src) { *dst = src; }
+
+ inline void flush(void *) {}
+};
+
+struct Blend_RGB16_on_RGB16_ConstAlpha {
+ inline Blend_RGB16_on_RGB16_ConstAlpha(quint32 alpha) {
+ m_alpha = (alpha * 255) >> 8;
+ m_ialpha = 255 - m_alpha;
+ }
+
+ inline void write(quint16 *dst, quint16 src) {
+ *dst = BYTE_MUL_RGB16(src, m_alpha) + BYTE_MUL_RGB16(*dst, m_ialpha);
+ }
+
+ inline void flush(void *) {}
+
+ quint32 m_alpha;
+ quint32 m_ialpha;
+};
+
+struct Blend_ARGB24_on_RGB16_SourceAlpha {
+ inline void write(quint16 *dst, const qargb8565 &src) {
+ const uint alpha = src.alpha();
+ if (alpha) {
+ quint16 s = src.rawValue16();
+ if (alpha < 255)
+ s += BYTE_MUL_RGB16(*dst, 255 - alpha);
+ *dst = s;
+ }
+ }
+
+ inline void flush(void *) {}
+};
+
+struct Blend_ARGB24_on_RGB16_SourceAndConstAlpha {
+ inline Blend_ARGB24_on_RGB16_SourceAndConstAlpha(quint32 alpha) {
+ m_alpha = (alpha * 255) >> 8;
+ }
+
+ inline void write(quint16 *dst, qargb8565 src) {
+ src = src.byte_mul(src.alpha(m_alpha));
+ const uint alpha = src.alpha();
+ if (alpha) {
+ quint16 s = src.rawValue16();
+ if (alpha < 255)
+ s += BYTE_MUL_RGB16(*dst, 255 - alpha);
+ *dst = s;
+ }
+ }
+
+ inline void flush(void *) {}
+
+ quint32 m_alpha;
+};
+
+struct Blend_ARGB32_on_RGB16_SourceAlpha {
+ inline void write(quint16 *dst, quint32 src) {
+ const quint8 alpha = qAlpha(src);
+ if(alpha) {
+ quint16 s = convert_argb32_to_rgb16(src);
+ if(alpha < 255)
+ s += BYTE_MUL_RGB16(*dst, 255 - alpha);
+ *dst = s;
+ }
+ }
+
+ inline void flush(void *) {}
+};
+
+struct Blend_ARGB32_on_RGB16_SourceAndConstAlpha {
+ inline Blend_ARGB32_on_RGB16_SourceAndConstAlpha(quint32 alpha) {
+ m_alpha = (alpha * 255) >> 8;
+ }
+
+ inline void write(quint16 *dst, quint32 src) {
+ src = BYTE_MUL(src, m_alpha);
+ const quint8 alpha = qAlpha(src);
+ if(alpha) {
+ quint16 s = convert_argb32_to_rgb16(src);
+ if(alpha < 255)
+ s += BYTE_MUL_RGB16(*dst, 255 - alpha);
+ *dst = s;
+ }
+ }
+
+ inline void flush(void *) {}
+
+ quint32 m_alpha;
+};
+
+void qt_scale_image_rgb16_on_rgb16(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ const QRectF &targetRect,
+ const QRectF &sourceRect,
+ const QRect &clip,
+ int const_alpha)
+{
+#ifdef QT_DEBUG_DRAW
+ printf("qt_scale_rgb16_on_rgb16: dst=(%p, %d), src=(%p, %d), target=(%d, %d), [%d x %d], src=(%d, %d) [%d x %d] alpha=%d\n",
+ destPixels, dbpl, srcPixels, sbpl,
+ targetRect.x(), targetRect.y(), targetRect.width(), targetRect.height(),
+ sourceRect.x(), sourceRect.y(), sourceRect.width(), sourceRect.height(),
+ const_alpha);
+#endif
+ if (const_alpha == 256) {
+ Blend_RGB16_on_RGB16_NoAlpha noAlpha;
+ qt_scale_image_16bit<quint16>(destPixels, dbpl, srcPixels, sbpl,
+ targetRect, sourceRect, clip, noAlpha);
+ } else {
+ Blend_RGB16_on_RGB16_ConstAlpha constAlpha(const_alpha);
+ qt_scale_image_16bit<quint16>(destPixels, dbpl, srcPixels, sbpl,
+ targetRect, sourceRect, clip, constAlpha);
+ }
+}
+
+void qt_scale_image_argb24_on_rgb16(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ const QRectF &targetRect,
+ const QRectF &sourceRect,
+ const QRect &clip,
+ int const_alpha)
+{
+#ifdef QT_DEBUG_DRAW
+ printf("qt_scale_argb24_on_rgb16: dst=(%p, %d), src=(%p, %d), target=(%d, %d), [%d x %d], src=(%d, %d) [%d x %d] alpha=%d\n",
+ destPixels, dbpl, srcPixels, sbpl,
+ targetRect.x(), targetRect.y(), targetRect.width(), targetRect.height(),
+ sourceRect.x(), sourceRect.y(), sourceRect.width(), sourceRect.height(),
+ const_alpha);
+#endif
+ if (const_alpha == 256) {
+ Blend_ARGB24_on_RGB16_SourceAlpha noAlpha;
+ qt_scale_image_16bit<qargb8565>(destPixels, dbpl, srcPixels, sbpl,
+ targetRect, sourceRect, clip, noAlpha);
+ } else {
+ Blend_ARGB24_on_RGB16_SourceAndConstAlpha constAlpha(const_alpha);
+ qt_scale_image_16bit<qargb8565>(destPixels, dbpl, srcPixels, sbpl,
+ targetRect, sourceRect, clip, constAlpha);
+ }
+}
+
+
+void qt_scale_image_argb32_on_rgb16(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ const QRectF &targetRect,
+ const QRectF &sourceRect,
+ const QRect &clip,
+ int const_alpha)
+{
+#ifdef QT_DEBUG_DRAW
+ printf("qt_scale_argb32_on_rgb16: dst=(%p, %d), src=(%p, %d), target=(%d, %d), [%d x %d], src=(%d, %d) [%d x %d] alpha=%d\n",
+ destPixels, dbpl, srcPixels, sbpl,
+ targetRect.x(), targetRect.y(), targetRect.width(), targetRect.height(),
+ sourceRect.x(), sourceRect.y(), sourceRect.width(), sourceRect.height(),
+ const_alpha);
+#endif
+ if (const_alpha == 256) {
+ Blend_ARGB32_on_RGB16_SourceAlpha noAlpha;
+ qt_scale_image_16bit<quint32>(destPixels, dbpl, srcPixels, sbpl,
+ targetRect, sourceRect, clip, noAlpha);
+ } else {
+ Blend_ARGB32_on_RGB16_SourceAndConstAlpha constAlpha(const_alpha);
+ qt_scale_image_16bit<quint32>(destPixels, dbpl, srcPixels, sbpl,
+ targetRect, sourceRect, clip, constAlpha);
+ }
+}
+
+void qt_blend_rgb16_on_rgb16(uchar *dst, int dbpl,
+ const uchar *src, int sbpl,
+ int w, int h,
+ int const_alpha)
+{
+#ifdef QT_DEBUG_DRAW
+ printf("qt_blend_rgb16_on_rgb16: dst=(%p, %d), src=(%p, %d), dim=(%d, %d) alpha=%d\n",
+ dst, dbpl, src, sbpl, w, h, const_alpha);
+#endif
+
+ if (const_alpha == 256) {
+ if (w <= 64) {
+ while (h--) {
+ QT_MEMCPY_USHORT(dst, src, w);
+ dst += dbpl;
+ src += sbpl;
+ }
+ } else {
+ int length = w << 1;
+ while (h--) {
+ memcpy(dst, src, length);
+ dst += dbpl;
+ src += sbpl;
+ }
+ }
+ } else if (const_alpha != 0) {
+ SourceAndConstAlpha alpha(const_alpha); // expects the 0-256 range
+ quint16 *d = (quint16 *) dst;
+ const quint16 *s = (const quint16 *) src;
+ quint8 a = (255 * const_alpha) >> 8;
+ quint8 ia = 255 - a;
+ while (h--) {
+ for (int x=0; x<w; ++x) {
+ d[x] = BYTE_MUL_RGB16(s[x], a) + BYTE_MUL_RGB16(d[x], ia);
+ }
+ d = (quint16 *)(((uchar *) d) + dbpl);
+ s = (const quint16 *)(((const uchar *) s) + sbpl);
+ }
+ }
+}
+
+
+template <typename T> void qt_blend_argb24_on_rgb16(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h, const T &alphaFunc)
+{
+ int srcOffset = w*3;
+ int dstJPL = dbpl / 2;
+ quint16 *dst = (quint16 *) destPixels;
+ int dstExtraStride = dstJPL - w;
+
+ for (int y=0; y<h; ++y) {
+ const uchar *src = srcPixels + y * sbpl;
+ const uchar *srcEnd = src + srcOffset;
+ while (src < srcEnd) {
+#if defined(QT_ARCH_ARMV5) || defined(QT_ARCH_POWERPC) || defined(QT_ARCH_SH) || defined(QT_ARCH_AVR32) || (defined(QT_ARCH_WINDOWSCE) && !defined(_X86_)) || (defined(QT_ARCH_SPARC) && defined(Q_CC_GNU)) || (defined(QT_ARCH_INTEGRITY) && !defined(_X86_))
+ // non-16-bit aligned memory access is not possible on PowerPC,
+ // ARM <v6 (QT_ARCH_ARMV5) & SH & AVR32 & SPARC w/GCC
+ quint16 spix = (quint16(src[2])<<8) + src[1];
+#else
+ quint16 spix = *(quint16 *) (src + 1);
+#endif
+ uchar alpha = alphaFunc.alpha(*src);
+
+ if (alpha == 255) {
+ *dst = spix;
+ } else if (alpha != 0) {
+ quint16 dpix = *dst;
+ quint32 sia = 255 - alpha;
+
+ quint16 dr = (dpix & 0x0000f800);
+ quint16 dg = (dpix & 0x000007e0);
+ quint16 db = (dpix & 0x0000001f);
+
+ quint32 siar = dr * sia;
+ quint32 siag = dg * sia;
+ quint32 siab = db * sia;
+
+ quint32 rr = ((siar + (siar>>8) + (0x80 << 8)) >> 8) & 0xf800;
+ quint32 rg = ((siag + (siag>>8) + (0x80 << 3)) >> 8) & 0x07e0;
+ quint32 rb = ((siab + (siab>>8) + (0x80 >> 3)) >> 8) & 0x001f;
+
+ *dst = alphaFunc.bytemul(spix) + rr + rg + rb;
+ }
+
+ ++dst;
+ src += 3;
+ }
+ dst += dstExtraStride;
+ }
+
+}
+
+static void qt_blend_argb24_on_rgb16(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha)
+{
+#ifdef QT_DEBUG_DRAW
+ printf("qt_blend_argb24_on_rgb16: dst=(%p, %d), src=(%p, %d), dim=(%d, %d) alpha=%d\n",
+ destPixels, dbpl, srcPixels, sbpl, w, h, const_alpha);
+#endif
+
+ if (const_alpha != 256) {
+ SourceAndConstAlpha alphaFunc(const_alpha);
+ qt_blend_argb24_on_rgb16(destPixels, dbpl, srcPixels, sbpl, w, h, alphaFunc);
+ } else {
+ SourceOnlyAlpha alphaFunc;
+ qt_blend_argb24_on_rgb16(destPixels, dbpl, srcPixels, sbpl, w, h, alphaFunc);
+ }
+}
+
+
+
+
+void qt_blend_argb32_on_rgb16_const_alpha(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha)
+{
+ quint16 *dst = (quint16 *) destPixels;
+ const quint32 *src = (const quint32 *) srcPixels;
+
+ const_alpha = (const_alpha * 255) >> 8;
+ for (int y=0; y<h; ++y) {
+ for (int i = 0; i < w; ++i) {
+ uint s = src[i];
+ s = BYTE_MUL(s, const_alpha);
+ int alpha = qAlpha(s);
+ s = convert_argb32_to_rgb16(s);
+ s += BYTE_MUL_RGB16(dst[i], 255 - alpha);
+ dst[i] = s;
+ }
+ dst = (quint16 *)(((uchar *) dst) + dbpl);
+ src = (const quint32 *)(((const uchar *) src) + sbpl);
+ }
+}
+
+static void qt_blend_argb32_on_rgb16(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha)
+{
+ if (const_alpha != 256) {
+ qt_blend_argb32_on_rgb16_const_alpha(destPixels, dbpl, srcPixels, sbpl, w, h, const_alpha);
+ return;
+ }
+
+ quint16 *dst = (quint16 *) destPixels;
+ quint32 *src = (quint32 *) srcPixels;
+
+ for (int y=0; y<h; ++y) {
+ for (int x=0; x<w; ++x) {
+
+ quint32 spix = src[x];
+ quint32 alpha = spix >> 24;
+
+ if (alpha == 255) {
+ dst[x] = convert_argb32_to_rgb16(spix);
+ } else if (alpha != 0) {
+ quint32 dpix = dst[x];
+
+ quint32 sia = 255 - alpha;
+
+ quint32 sr = (spix >> 8) & 0xf800;
+ quint32 sg = (spix >> 5) & 0x07e0;
+ quint32 sb = (spix >> 3) & 0x001f;
+
+ quint32 dr = (dpix & 0x0000f800);
+ quint32 dg = (dpix & 0x000007e0);
+ quint32 db = (dpix & 0x0000001f);
+
+ quint32 siar = dr * sia;
+ quint32 siag = dg * sia;
+ quint32 siab = db * sia;
+
+ quint32 rr = sr + ((siar + (siar>>8) + (0x80 << 8)) >> 8);
+ quint32 rg = sg + ((siag + (siag>>8) + (0x80 << 3)) >> 8);
+ quint32 rb = sb + ((siab + (siab>>8) + (0x80 >> 3)) >> 8);
+
+ dst[x] = (rr & 0xf800)
+ | (rg & 0x07e0)
+ | (rb);
+ }
+ }
+ dst = (quint16 *) (((uchar *) dst) + dbpl);
+ src = (quint32 *) (((uchar *) src) + sbpl);
+ }
+}
+
+
+static void qt_blend_rgb32_on_rgb16(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha)
+{
+#ifdef QT_DEBUG_DRAW
+ printf("qt_blend_rgb32_on_rgb16: dst=(%p, %d), src=(%p, %d), dim=(%d, %d) alpha=%d\n",
+ destPixels, dbpl, srcPixels, sbpl, w, h, const_alpha);
+#endif
+
+ if (const_alpha != 256) {
+ qt_blend_argb32_on_rgb16(destPixels, dbpl, srcPixels, sbpl, w, h, const_alpha);
+ return;
+ }
+
+ const quint32 *src = (const quint32 *) srcPixels;
+ int srcExtraStride = (sbpl >> 2) - w;
+
+ int dstJPL = dbpl / 2;
+
+ quint16 *dst = (quint16 *) destPixels;
+ quint16 *dstEnd = dst + dstJPL * h;
+
+ int dstExtraStride = dstJPL - w;
+
+ while (dst < dstEnd) {
+ const quint32 *srcEnd = src + w;
+ while (src < srcEnd) {
+ *dst = convert_argb32_to_rgb16(*src);
+ ++dst;
+ ++src;
+ }
+ dst += dstExtraStride;
+ src += srcExtraStride;
+ }
+}
+
+
+
+/************************************************************************
+ RGB32 (-888) format target format
+ ************************************************************************/
+
+static void qt_blend_argb32_on_argb32(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha)
+{
+#ifdef QT_DEBUG_DRAW
+ fprintf(stdout, "qt_blend_argb32_on_argb32: dst=(%p, %d), src=(%p, %d), dim=(%d, %d) alpha=%d\n",
+ destPixels, dbpl, srcPixels, sbpl, w, h, const_alpha);
+ fflush(stdout);
+#endif
+
+ const uint *src = (const uint *) srcPixels;
+ uint *dst = (uint *) destPixels;
+ if (const_alpha == 256) {
+ for (int y=0; y<h; ++y) {
+ for (int x=0; x<w; ++x) {
+ uint s = src[x];
+ if (s >= 0xff000000)
+ dst[x] = s;
+ else if (s != 0)
+ dst[x] = s + BYTE_MUL(dst[x], qAlpha(~s));
+ }
+ dst = (quint32 *)(((uchar *) dst) + dbpl);
+ src = (const quint32 *)(((const uchar *) src) + sbpl);
+ }
+ } else if (const_alpha != 0) {
+ const_alpha = (const_alpha * 255) >> 8;
+ for (int y=0; y<h; ++y) {
+ for (int x=0; x<w; ++x) {
+ uint s = BYTE_MUL(src[x], const_alpha);
+ dst[x] = s + BYTE_MUL(dst[x], qAlpha(~s));
+ }
+ dst = (quint32 *)(((uchar *) dst) + dbpl);
+ src = (const quint32 *)(((const uchar *) src) + sbpl);
+ }
+ }
+}
+
+
+void qt_blend_rgb32_on_rgb32(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha)
+{
+#ifdef QT_DEBUG_DRAW
+ fprintf(stdout, "qt_blend_rgb32_on_rgb32: dst=(%p, %d), src=(%p, %d), dim=(%d, %d) alpha=%d\n",
+ destPixels, dbpl, srcPixels, sbpl, w, h, const_alpha);
+ fflush(stdout);
+#endif
+
+ if (const_alpha != 256) {
+ qt_blend_argb32_on_argb32(destPixels, dbpl, srcPixels, sbpl, w, h, const_alpha);
+ return;
+ }
+
+ const uint *src = (const uint *) srcPixels;
+ uint *dst = (uint *) destPixels;
+ if (w <= 64) {
+ for (int y=0; y<h; ++y) {
+ qt_memconvert(dst, src, w);
+ dst = (quint32 *)(((uchar *) dst) + dbpl);
+ src = (const quint32 *)(((const uchar *) src) + sbpl);
+ }
+ } else {
+ int len = w * 4;
+ for (int y=0; y<h; ++y) {
+ memcpy(dst, src, len);
+ dst = (quint32 *)(((uchar *) dst) + dbpl);
+ src = (const quint32 *)(((const uchar *) src) + sbpl);
+ }
+ }
+}
+
+
+
+struct Blend_RGB32_on_RGB32_NoAlpha {
+ inline void write(quint32 *dst, quint32 src) { *dst = src; }
+
+ inline void flush(void *) {}
+};
+
+struct Blend_RGB32_on_RGB32_ConstAlpha {
+ inline Blend_RGB32_on_RGB32_ConstAlpha(quint32 alpha) {
+ m_alpha = (alpha * 255) >> 8;
+ m_ialpha = 255 - m_alpha;
+ }
+
+ inline void write(quint32 *dst, quint32 src) {
+ *dst = BYTE_MUL(src, m_alpha) + BYTE_MUL(*dst, m_ialpha);
+ }
+
+ inline void flush(void *) {}
+
+ quint32 m_alpha;
+ quint32 m_ialpha;
+};
+
+struct Blend_ARGB32_on_ARGB32_SourceAlpha {
+ inline void write(quint32 *dst, quint32 src) {
+ *dst = src + BYTE_MUL(*dst, qAlpha(~src));
+ }
+
+ inline void flush(void *) {}
+};
+
+struct Blend_ARGB32_on_ARGB32_SourceAndConstAlpha {
+ inline Blend_ARGB32_on_ARGB32_SourceAndConstAlpha(quint32 alpha) {
+ m_alpha = (alpha * 255) >> 8;
+ m_ialpha = 255 - m_alpha;
+ }
+
+ inline void write(quint32 *dst, quint32 src) {
+ src = BYTE_MUL(src, m_alpha);
+ *dst = src + BYTE_MUL(*dst, qAlpha(~src));
+ }
+
+ inline void flush(void *) {}
+
+ quint32 m_alpha;
+ quint32 m_ialpha;
+};
+
+void qt_scale_image_rgb32_on_rgb32(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ const QRectF &targetRect,
+ const QRectF &sourceRect,
+ const QRect &clip,
+ int const_alpha)
+{
+#ifdef QT_DEBUG_DRAW
+ printf("qt_scale_rgb32_on_rgb32: dst=(%p, %d), src=(%p, %d), target=(%d, %d), [%d x %d], src=(%d, %d) [%d x %d] alpha=%d\n",
+ destPixels, dbpl, srcPixels, sbpl,
+ targetRect.x(), targetRect.y(), targetRect.width(), targetRect.height(),
+ sourceRect.x(), sourceRect.y(), sourceRect.width(), sourceRect.height(),
+ const_alpha);
+#endif
+ if (const_alpha == 256) {
+ Blend_RGB32_on_RGB32_NoAlpha noAlpha;
+ qt_scale_image_32bit(destPixels, dbpl, srcPixels, sbpl,
+ targetRect, sourceRect, clip, noAlpha);
+ } else {
+ Blend_RGB32_on_RGB32_ConstAlpha constAlpha(const_alpha);
+ qt_scale_image_32bit(destPixels, dbpl, srcPixels, sbpl,
+ targetRect, sourceRect, clip, constAlpha);
+ }
+}
+
+void qt_scale_image_argb32_on_argb32(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ const QRectF &targetRect,
+ const QRectF &sourceRect,
+ const QRect &clip,
+ int const_alpha)
+{
+#ifdef QT_DEBUG_DRAW
+ printf("qt_scale_argb32_on_argb32: dst=(%p, %d), src=(%p, %d), target=(%d, %d), [%d x %d], src=(%d, %d) [%d x %d] alpha=%d\n",
+ destPixels, dbpl, srcPixels, sbpl,
+ targetRect.x(), targetRect.y(), targetRect.width(), targetRect.height(),
+ sourceRect.x(), sourceRect.y(), sourceRect.width(), sourceRect.height(),
+ const_alpha);
+#endif
+ if (const_alpha == 256) {
+ Blend_ARGB32_on_ARGB32_SourceAlpha sourceAlpha;
+ qt_scale_image_32bit(destPixels, dbpl, srcPixels, sbpl,
+ targetRect, sourceRect, clip, sourceAlpha);
+ } else {
+ Blend_ARGB32_on_ARGB32_SourceAndConstAlpha constAlpha(const_alpha);
+ qt_scale_image_32bit(destPixels, dbpl, srcPixels, sbpl,
+ targetRect, sourceRect, clip, constAlpha);
+ }
+}
+
+void qt_transform_image_rgb16_on_rgb16(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ const QRectF &targetRect,
+ const QRectF &sourceRect,
+ const QRect &clip,
+ const QTransform &targetRectTransform,
+ int const_alpha)
+{
+ if (const_alpha == 256) {
+ Blend_RGB16_on_RGB16_NoAlpha noAlpha;
+ qt_transform_image(reinterpret_cast<quint16 *>(destPixels), dbpl,
+ reinterpret_cast<const quint16 *>(srcPixels), sbpl,
+ targetRect, sourceRect, clip, targetRectTransform, noAlpha);
+ } else {
+ Blend_RGB16_on_RGB16_ConstAlpha constAlpha(const_alpha);
+ qt_transform_image(reinterpret_cast<quint16 *>(destPixels), dbpl,
+ reinterpret_cast<const quint16 *>(srcPixels), sbpl,
+ targetRect, sourceRect, clip, targetRectTransform, constAlpha);
+ }
+}
+
+void qt_transform_image_argb24_on_rgb16(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ const QRectF &targetRect,
+ const QRectF &sourceRect,
+ const QRect &clip,
+ const QTransform &targetRectTransform,
+ int const_alpha)
+{
+ if (const_alpha == 256) {
+ Blend_ARGB24_on_RGB16_SourceAlpha noAlpha;
+ qt_transform_image(reinterpret_cast<quint16 *>(destPixels), dbpl,
+ reinterpret_cast<const qargb8565 *>(srcPixels), sbpl,
+ targetRect, sourceRect, clip, targetRectTransform, noAlpha);
+ } else {
+ Blend_ARGB24_on_RGB16_SourceAndConstAlpha constAlpha(const_alpha);
+ qt_transform_image(reinterpret_cast<quint16 *>(destPixels), dbpl,
+ reinterpret_cast<const qargb8565 *>(srcPixels), sbpl,
+ targetRect, sourceRect, clip, targetRectTransform, constAlpha);
+ }
+}
+
+
+void qt_transform_image_argb32_on_rgb16(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ const QRectF &targetRect,
+ const QRectF &sourceRect,
+ const QRect &clip,
+ const QTransform &targetRectTransform,
+ int const_alpha)
+{
+ if (const_alpha == 256) {
+ Blend_ARGB32_on_RGB16_SourceAlpha noAlpha;
+ qt_transform_image(reinterpret_cast<quint16 *>(destPixels), dbpl,
+ reinterpret_cast<const quint32 *>(srcPixels), sbpl,
+ targetRect, sourceRect, clip, targetRectTransform, noAlpha);
+ } else {
+ Blend_ARGB32_on_RGB16_SourceAndConstAlpha constAlpha(const_alpha);
+ qt_transform_image(reinterpret_cast<quint16 *>(destPixels), dbpl,
+ reinterpret_cast<const quint32 *>(srcPixels), sbpl,
+ targetRect, sourceRect, clip, targetRectTransform, constAlpha);
+ }
+}
+
+
+void qt_transform_image_rgb32_on_rgb32(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ const QRectF &targetRect,
+ const QRectF &sourceRect,
+ const QRect &clip,
+ const QTransform &targetRectTransform,
+ int const_alpha)
+{
+ if (const_alpha == 256) {
+ Blend_RGB32_on_RGB32_NoAlpha noAlpha;
+ qt_transform_image(reinterpret_cast<quint32 *>(destPixels), dbpl,
+ reinterpret_cast<const quint32 *>(srcPixels), sbpl,
+ targetRect, sourceRect, clip, targetRectTransform, noAlpha);
+ } else {
+ Blend_RGB32_on_RGB32_ConstAlpha constAlpha(const_alpha);
+ qt_transform_image(reinterpret_cast<quint32 *>(destPixels), dbpl,
+ reinterpret_cast<const quint32 *>(srcPixels), sbpl,
+ targetRect, sourceRect, clip, targetRectTransform, constAlpha);
+ }
+}
+
+void qt_transform_image_argb32_on_argb32(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ const QRectF &targetRect,
+ const QRectF &sourceRect,
+ const QRect &clip,
+ const QTransform &targetRectTransform,
+ int const_alpha)
+{
+ if (const_alpha == 256) {
+ Blend_ARGB32_on_ARGB32_SourceAlpha sourceAlpha;
+ qt_transform_image(reinterpret_cast<quint32 *>(destPixels), dbpl,
+ reinterpret_cast<const quint32 *>(srcPixels), sbpl,
+ targetRect, sourceRect, clip, targetRectTransform, sourceAlpha);
+ } else {
+ Blend_ARGB32_on_ARGB32_SourceAndConstAlpha constAlpha(const_alpha);
+ qt_transform_image(reinterpret_cast<quint32 *>(destPixels), dbpl,
+ reinterpret_cast<const quint32 *>(srcPixels), sbpl,
+ targetRect, sourceRect, clip, targetRectTransform, constAlpha);
+ }
+}
+
+SrcOverScaleFunc qScaleFunctions[QImage::NImageFormats][QImage::NImageFormats] = {
+ { // Format_Invalid
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_Mono
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_MonoLSB
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_Indexed8
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_RGB32
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ qt_scale_image_rgb32_on_rgb32, // Format_RGB32,
+ 0, // Format_ARGB32,
+ qt_scale_image_argb32_on_argb32, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_ARGB32
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_ARGB32_Premultiplied
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ qt_scale_image_rgb32_on_rgb32, // Format_RGB32,
+ 0, // Format_ARGB32,
+ qt_scale_image_argb32_on_argb32, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_RGB16
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ qt_scale_image_argb32_on_rgb16, // Format_ARGB32_Premultiplied,
+ qt_scale_image_rgb16_on_rgb16, // Format_RGB16,
+ qt_scale_image_argb24_on_rgb16, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_ARGB8565_Premultiplied
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_RGB666
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_ARGB6666_Premultiplied
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_RGB555
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_ARGB8555_Premultiplied
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_RGB888
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_RGB444
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_ARGB4444_Premultiplied
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ }
+};
+
+
+SrcOverBlendFunc qBlendFunctions[QImage::NImageFormats][QImage::NImageFormats] = {
+ { // Format_Invalid
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_Mono
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_MonoLSB
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_Indexed8
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_RGB32
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ qt_blend_rgb32_on_rgb32, // Format_RGB32,
+ 0, // Format_ARGB32,
+ qt_blend_argb32_on_argb32, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_ARGB32
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_ARGB32_Premultiplied
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ qt_blend_rgb32_on_rgb32, // Format_RGB32,
+ 0, // Format_ARGB32,
+ qt_blend_argb32_on_argb32, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_RGB16
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ qt_blend_rgb32_on_rgb16, // Format_RGB32,
+ 0, // Format_ARGB32,
+ qt_blend_argb32_on_rgb16, // Format_ARGB32_Premultiplied,
+ qt_blend_rgb16_on_rgb16, // Format_RGB16,
+ qt_blend_argb24_on_rgb16, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_ARGB8565_Premultiplied
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_RGB666
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_ARGB6666_Premultiplied
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_RGB555
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_ARGB8555_Premultiplied
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_RGB888
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_RGB444
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_ARGB4444_Premultiplied
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ }
+};
+
+SrcOverTransformFunc qTransformFunctions[QImage::NImageFormats][QImage::NImageFormats] = {
+ { // Format_Invalid
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_Mono
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_MonoLSB
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_Indexed8
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_RGB32
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ qt_transform_image_rgb32_on_rgb32, // Format_RGB32,
+ 0, // Format_ARGB32,
+ qt_transform_image_argb32_on_argb32, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_ARGB32
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_ARGB32_Premultiplied
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ qt_transform_image_rgb32_on_rgb32, // Format_RGB32,
+ 0, // Format_ARGB32,
+ qt_transform_image_argb32_on_argb32, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_RGB16
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ qt_transform_image_argb32_on_rgb16, // Format_ARGB32_Premultiplied,
+ qt_transform_image_rgb16_on_rgb16, // Format_RGB16,
+ qt_transform_image_argb24_on_rgb16, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_ARGB8565_Premultiplied
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_RGB666
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_ARGB6666_Premultiplied
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_RGB555
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_ARGB8555_Premultiplied
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_RGB888
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_RGB444
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ },
+ { // Format_ARGB4444_Premultiplied
+ 0, // Format_Invalid,
+ 0, // Format_Mono,
+ 0, // Format_MonoLSB,
+ 0, // Format_Indexed8,
+ 0, // Format_RGB32,
+ 0, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied,
+ 0, // Format_RGB16,
+ 0, // Format_ARGB8565_Premultiplied,
+ 0, // Format_RGB666,
+ 0, // Format_ARGB6666_Premultiplied,
+ 0, // Format_RGB555,
+ 0, // Format_ARGB8555_Premultiplied,
+ 0, // Format_RGB888,
+ 0, // Format_RGB444,
+ 0 // Format_ARGB4444_Premultiplied,
+ }
+};
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qblendfunctions_p.h b/src/gui/painting/qblendfunctions_p.h
new file mode 100644
index 0000000000..81f8b70caa
--- /dev/null
+++ b/src/gui/painting/qblendfunctions_p.h
@@ -0,0 +1,497 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QBLENDFUNCTIONS_P_H
+#define QBLENDFUNCTIONS_P_H
+
+#include <qmath.h>
+#include "qdrawhelper_p.h"
+
+QT_BEGIN_NAMESPACE
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+template <typename SRC, typename T>
+void qt_scale_image_16bit(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ const QRectF &targetRect,
+ const QRectF &srcRect,
+ const QRect &clip,
+ T blender)
+{
+ qreal sx = targetRect.width() / (qreal) srcRect.width();
+ qreal sy = targetRect.height() / (qreal) srcRect.height();
+
+ int ix = 0x00010000 / sx;
+ int iy = 0x00010000 / sy;
+
+// qDebug() << "scale:" << endl
+// << " - target" << targetRect << endl
+// << " - source" << srcRect << endl
+// << " - clip" << clip << endl
+// << " - sx=" << sx << " sy=" << sy << " ix=" << ix << " iy=" << iy;
+
+ int cx1 = clip.x();
+ int cx2 = clip.x() + clip.width();
+ int cy1 = clip.top();
+ int cy2 = clip.y() + clip.height();
+
+ int tx1 = qRound(targetRect.left());
+ int tx2 = qRound(targetRect.right());
+ int ty1 = qRound(targetRect.top());
+ int ty2 = qRound(targetRect.bottom());
+
+ if (tx2 < tx1)
+ qSwap(tx2, tx1);
+
+ if (ty2 < ty1)
+ qSwap(ty2, ty1);
+
+ if (tx1 < cx1)
+ tx1 = cx1;
+
+ if (tx2 >= cx2)
+ tx2 = cx2;
+
+ if (tx1 >= tx2)
+ return;
+
+ if (ty1 < cy1)
+ ty1 = cy1;
+
+ if (ty2 >= cy2)
+ ty2 = cy2;
+
+ if (ty1 >= ty2)
+ return;
+
+ int h = ty2 - ty1;
+ int w = tx2 - tx1;
+
+
+ quint32 basex;
+ quint32 srcy;
+
+ if (sx < 0) {
+ int dstx = qFloor((tx1 + qreal(0.5) - targetRect.right()) * ix) + 1;
+ basex = quint32(srcRect.right() * 65536) + dstx;
+ } else {
+ int dstx = qCeil((tx1 + qreal(0.5) - targetRect.left()) * ix) - 1;
+ basex = quint32(srcRect.left() * 65536) + dstx;
+ }
+ if (sy < 0) {
+ int dsty = qFloor((ty1 + qreal(0.5) - targetRect.bottom()) * iy) + 1;
+ srcy = quint32(srcRect.bottom() * 65536) + dsty;
+ } else {
+ int dsty = qCeil((ty1 + qreal(0.5) - targetRect.top()) * iy) - 1;
+ srcy = quint32(srcRect.top() * 65536) + dsty;
+ }
+
+ quint16 *dst = ((quint16 *) (destPixels + ty1 * dbpl)) + tx1;
+
+ while (h--) {
+ const SRC *src = (const SRC *) (srcPixels + (srcy >> 16) * sbpl);
+ int srcx = basex;
+ int x = 0;
+ for (; x<w-7; x+=8) {
+ blender.write(&dst[x], src[srcx >> 16]); srcx += ix;
+ blender.write(&dst[x+1], src[srcx >> 16]); srcx += ix;
+ blender.write(&dst[x+2], src[srcx >> 16]); srcx += ix;
+ blender.write(&dst[x+3], src[srcx >> 16]); srcx += ix;
+ blender.write(&dst[x+4], src[srcx >> 16]); srcx += ix;
+ blender.write(&dst[x+5], src[srcx >> 16]); srcx += ix;
+ blender.write(&dst[x+6], src[srcx >> 16]); srcx += ix;
+ blender.write(&dst[x+7], src[srcx >> 16]); srcx += ix;
+ }
+ for (; x<w; ++x) {
+ blender.write(&dst[x], src[srcx >> 16]);
+ srcx += ix;
+ }
+ blender.flush(&dst[x]);
+ dst = (quint16 *)(((uchar *) dst) + dbpl);
+ srcy += iy;
+ }
+}
+
+template <typename T> void qt_scale_image_32bit(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ const QRectF &targetRect,
+ const QRectF &srcRect,
+ const QRect &clip,
+ T blender)
+{
+ qreal sx = targetRect.width() / (qreal) srcRect.width();
+ qreal sy = targetRect.height() / (qreal) srcRect.height();
+
+ int ix = 0x00010000 / sx;
+ int iy = 0x00010000 / sy;
+
+// qDebug() << "scale:" << endl
+// << " - target" << targetRect << endl
+// << " - source" << srcRect << endl
+// << " - clip" << clip << endl
+// << " - sx=" << sx << " sy=" << sy << " ix=" << ix << " iy=" << iy;
+
+ int cx1 = clip.x();
+ int cx2 = clip.x() + clip.width();
+ int cy1 = clip.top();
+ int cy2 = clip.y() + clip.height();
+
+ int tx1 = qRound(targetRect.left());
+ int tx2 = qRound(targetRect.right());
+ int ty1 = qRound(targetRect.top());
+ int ty2 = qRound(targetRect.bottom());
+
+ if (tx2 < tx1)
+ qSwap(tx2, tx1);
+
+ if (ty2 < ty1)
+ qSwap(ty2, ty1);
+
+ if (tx1 < cx1)
+ tx1 = cx1;
+
+ if (tx2 >= cx2)
+ tx2 = cx2;
+
+ if (tx1 >= tx2)
+ return;
+
+ if (ty1 < cy1)
+ ty1 = cy1;
+
+ if (ty2 >= cy2)
+ ty2 = cy2;
+
+ if (ty1 >= ty2)
+ return;
+
+ int h = ty2 - ty1;
+ int w = tx2 - tx1;
+
+ quint32 basex;
+ quint32 srcy;
+
+ if (sx < 0) {
+ int dstx = qFloor((tx1 + qreal(0.5) - targetRect.right()) * ix) + 1;
+ basex = quint32(srcRect.right() * 65536) + dstx;
+ } else {
+ int dstx = qCeil((tx1 + qreal(0.5) - targetRect.left()) * ix) - 1;
+ basex = quint32(srcRect.left() * 65536) + dstx;
+ }
+ if (sy < 0) {
+ int dsty = qFloor((ty1 + qreal(0.5) - targetRect.bottom()) * iy) + 1;
+ srcy = quint32(srcRect.bottom() * 65536) + dsty;
+ } else {
+ int dsty = qCeil((ty1 + qreal(0.5) - targetRect.top()) * iy) - 1;
+ srcy = quint32(srcRect.top() * 65536) + dsty;
+ }
+
+ quint32 *dst = ((quint32 *) (destPixels + ty1 * dbpl)) + tx1;
+
+ while (h--) {
+ const uint *src = (const quint32 *) (srcPixels + (srcy >> 16) * sbpl);
+ int srcx = basex;
+ int x = 0;
+ for (; x<w; ++x) {
+ blender.write(&dst[x], src[srcx >> 16]);
+ srcx += ix;
+ }
+ blender.flush(&dst[x]);
+ dst = (quint32 *)(((uchar *) dst) + dbpl);
+ srcy += iy;
+ }
+}
+
+struct QTransformImageVertex
+{
+ qreal x, y, u, v; // destination coordinates (x, y) and source coordinates (u, v)
+};
+
+template <class SrcT, class DestT, class Blender>
+void qt_transform_image_rasterize(DestT *destPixels, int dbpl,
+ const SrcT *srcPixels, int sbpl,
+ const QTransformImageVertex &topLeft, const QTransformImageVertex &bottomLeft,
+ const QTransformImageVertex &topRight, const QTransformImageVertex &bottomRight,
+ const QRect &sourceRect,
+ const QRect &clip,
+ qreal topY, qreal bottomY,
+ int dudx, int dvdx, int dudy, int dvdy, int u0, int v0,
+ Blender blender)
+{
+ int fromY = qMax(qRound(topY), clip.top());
+ int toY = qMin(qRound(bottomY), clip.top() + clip.height());
+ if (fromY >= toY)
+ return;
+
+ qreal leftSlope = (bottomLeft.x - topLeft.x) / (bottomLeft.y - topLeft.y);
+ qreal rightSlope = (bottomRight.x - topRight.x) / (bottomRight.y - topRight.y);
+ int dx_l = int(leftSlope * 0x10000);
+ int dx_r = int(rightSlope * 0x10000);
+ int x_l = int((topLeft.x + (qreal(0.5) + fromY - topLeft.y) * leftSlope + qreal(0.5)) * 0x10000);
+ int x_r = int((topRight.x + (qreal(0.5) + fromY - topRight.y) * rightSlope + qreal(0.5)) * 0x10000);
+
+ int fromX, toX, x1, x2, u, v, i, ii;
+ DestT *line;
+ for (int y = fromY; y < toY; ++y) {
+ line = reinterpret_cast<DestT *>(reinterpret_cast<uchar *>(destPixels) + y * dbpl);
+
+ fromX = qMax(x_l >> 16, clip.left());
+ toX = qMin(x_r >> 16, clip.left() + clip.width());
+ if (fromX < toX) {
+ // Because of rounding, we can get source coordinates outside the source image.
+ // Clamp these coordinates to the source rect to avoid segmentation fault and
+ // garbage on the screen.
+
+ // Find the first pixel on the current scan line where the source coordinates are within the source rect.
+ x1 = fromX;
+ u = x1 * dudx + y * dudy + u0;
+ v = x1 * dvdx + y * dvdy + v0;
+ for (; x1 < toX; ++x1) {
+ int uu = u >> 16;
+ int vv = v >> 16;
+ if (uu >= sourceRect.left() && uu < sourceRect.left() + sourceRect.width()
+ && vv >= sourceRect.top() && vv < sourceRect.top() + sourceRect.height()) {
+ break;
+ }
+ u += dudx;
+ v += dvdx;
+ }
+
+ // Find the last pixel on the current scan line where the source coordinates are within the source rect.
+ x2 = toX;
+ u = (x2 - 1) * dudx + y * dudy + u0;
+ v = (x2 - 1) * dvdx + y * dvdy + v0;
+ for (; x2 > x1; --x2) {
+ int uu = u >> 16;
+ int vv = v >> 16;
+ if (uu >= sourceRect.left() && uu < sourceRect.left() + sourceRect.width()
+ && vv >= sourceRect.top() && vv < sourceRect.top() + sourceRect.height()) {
+ break;
+ }
+ u -= dudx;
+ v -= dvdx;
+ }
+
+ // Set up values at the beginning of the scan line.
+ u = fromX * dudx + y * dudy + u0;
+ v = fromX * dvdx + y * dvdy + v0;
+ line += fromX;
+
+ // Beginning of the scan line, with per-pixel checks.
+ i = x1 - fromX;
+ while (i) {
+ int uu = qBound(sourceRect.left(), u >> 16, sourceRect.left() + sourceRect.width() - 1);
+ int vv = qBound(sourceRect.top(), v >> 16, sourceRect.top() + sourceRect.height() - 1);
+ blender.write(line, reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + vv * sbpl)[uu]);
+ u += dudx;
+ v += dvdx;
+ ++line;
+ --i;
+ }
+
+ // Middle of the scan line, without checks.
+ // Manual loop unrolling.
+ i = x2 - x1;
+ ii = i >> 3;
+ while (ii) {
+ blender.write(&line[0], reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx;
+ blender.write(&line[1], reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx;
+ blender.write(&line[2], reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx;
+ blender.write(&line[3], reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx;
+ blender.write(&line[4], reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx;
+ blender.write(&line[5], reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx;
+ blender.write(&line[6], reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx;
+ blender.write(&line[7], reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx;
+
+ line += 8;
+
+ --ii;
+ }
+ switch (i & 7) {
+ case 7: blender.write(line, reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; ++line;
+ case 6: blender.write(line, reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; ++line;
+ case 5: blender.write(line, reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; ++line;
+ case 4: blender.write(line, reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; ++line;
+ case 3: blender.write(line, reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; ++line;
+ case 2: blender.write(line, reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; ++line;
+ case 1: blender.write(line, reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + (v >> 16) * sbpl)[u >> 16]); u += dudx; v += dvdx; ++line;
+ }
+
+ // End of the scan line, with per-pixel checks.
+ i = toX - x2;
+ while (i) {
+ int uu = qBound(sourceRect.left(), u >> 16, sourceRect.left() + sourceRect.width() - 1);
+ int vv = qBound(sourceRect.top(), v >> 16, sourceRect.top() + sourceRect.height() - 1);
+ blender.write(line, reinterpret_cast<const SrcT *>(reinterpret_cast<const uchar *>(srcPixels) + vv * sbpl)[uu]);
+ u += dudx;
+ v += dvdx;
+ ++line;
+ --i;
+ }
+
+ blender.flush(line);
+ }
+ x_l += dx_l;
+ x_r += dx_r;
+ }
+}
+
+template <class SrcT, class DestT, class Blender>
+void qt_transform_image(DestT *destPixels, int dbpl,
+ const SrcT *srcPixels, int sbpl,
+ const QRectF &targetRect,
+ const QRectF &sourceRect,
+ const QRect &clip,
+ const QTransform &targetRectTransform,
+ Blender blender)
+{
+ enum Corner
+ {
+ TopLeft,
+ TopRight,
+ BottomRight,
+ BottomLeft
+ };
+
+ // map source rectangle to destination.
+ QTransformImageVertex v[4];
+ v[TopLeft].u = v[BottomLeft].u = sourceRect.left();
+ v[TopLeft].v = v[TopRight].v = sourceRect.top();
+ v[TopRight].u = v[BottomRight].u = sourceRect.right();
+ v[BottomLeft].v = v[BottomRight].v = sourceRect.bottom();
+ targetRectTransform.map(targetRect.left(), targetRect.top(), &v[TopLeft].x, &v[TopLeft].y);
+ targetRectTransform.map(targetRect.right(), targetRect.top(), &v[TopRight].x, &v[TopRight].y);
+ targetRectTransform.map(targetRect.left(), targetRect.bottom(), &v[BottomLeft].x, &v[BottomLeft].y);
+ targetRectTransform.map(targetRect.right(), targetRect.bottom(), &v[BottomRight].x, &v[BottomRight].y);
+
+ // find topmost vertex.
+ int topmost = 0;
+ for (int i = 1; i < 4; ++i) {
+ if (v[i].y < v[topmost].y)
+ topmost = i;
+ }
+ // rearrange array such that topmost vertex is at index 0.
+ switch (topmost) {
+ case 1:
+ {
+ QTransformImageVertex t = v[0];
+ for (int i = 0; i < 3; ++i)
+ v[i] = v[i+1];
+ v[3] = t;
+ }
+ break;
+ case 2:
+ qSwap(v[0], v[2]);
+ qSwap(v[1], v[3]);
+ break;
+ case 3:
+ {
+ QTransformImageVertex t = v[3];
+ for (int i = 3; i > 0; --i)
+ v[i] = v[i-1];
+ v[0] = t;
+ }
+ break;
+ }
+
+ // if necessary, swap vertex 1 and 3 such that 1 is to the left of 3.
+ qreal dx1 = v[1].x - v[0].x;
+ qreal dy1 = v[1].y - v[0].y;
+ qreal dx2 = v[3].x - v[0].x;
+ qreal dy2 = v[3].y - v[0].y;
+ if (dx1 * dy2 - dx2 * dy1 > 0)
+ qSwap(v[1], v[3]);
+
+ QTransformImageVertex u = {v[1].x - v[0].x, v[1].y - v[0].y, v[1].u - v[0].u, v[1].v - v[0].v};
+ QTransformImageVertex w = {v[2].x - v[0].x, v[2].y - v[0].y, v[2].u - v[0].u, v[2].v - v[0].v};
+
+ qreal det = u.x * w.y - u.y * w.x;
+ if (det == 0)
+ return;
+
+ qreal invDet = 1.0 / det;
+ qreal m11, m12, m21, m22, mdx, mdy;
+
+ m11 = (u.u * w.y - u.y * w.u) * invDet;
+ m12 = (u.x * w.u - u.u * w.x) * invDet;
+ m21 = (u.v * w.y - u.y * w.v) * invDet;
+ m22 = (u.x * w.v - u.v * w.x) * invDet;
+ mdx = v[0].u - m11 * v[0].x - m12 * v[0].y;
+ mdy = v[0].v - m21 * v[0].x - m22 * v[0].y;
+
+ int dudx = int(m11 * 0x10000);
+ int dvdx = int(m21 * 0x10000);
+ int dudy = int(m12 * 0x10000);
+ int dvdy = int(m22 * 0x10000);
+ int u0 = qCeil((qreal(0.5) * m11 + qreal(0.5) * m12 + mdx) * 0x10000) - 1;
+ int v0 = qCeil((qreal(0.5) * m21 + qreal(0.5) * m22 + mdy) * 0x10000) - 1;
+
+ int x1 = qFloor(sourceRect.left());
+ int y1 = qFloor(sourceRect.top());
+ int x2 = qCeil(sourceRect.right());
+ int y2 = qCeil(sourceRect.bottom());
+ QRect sourceRectI(x1, y1, x2 - x1, y2 - y1);
+
+ // rasterize trapezoids.
+ if (v[1].y < v[3].y) {
+ qt_transform_image_rasterize(destPixels, dbpl, srcPixels, sbpl, v[0], v[1], v[0], v[3], sourceRectI, clip, v[0].y, v[1].y, dudx, dvdx, dudy, dvdy, u0, v0, blender);
+ qt_transform_image_rasterize(destPixels, dbpl, srcPixels, sbpl, v[1], v[2], v[0], v[3], sourceRectI, clip, v[1].y, v[3].y, dudx, dvdx, dudy, dvdy, u0, v0, blender);
+ qt_transform_image_rasterize(destPixels, dbpl, srcPixels, sbpl, v[1], v[2], v[3], v[2], sourceRectI, clip, v[3].y, v[2].y, dudx, dvdx, dudy, dvdy, u0, v0, blender);
+ } else {
+ qt_transform_image_rasterize(destPixels, dbpl, srcPixels, sbpl, v[0], v[1], v[0], v[3], sourceRectI, clip, v[0].y, v[3].y, dudx, dvdx, dudy, dvdy, u0, v0, blender);
+ qt_transform_image_rasterize(destPixels, dbpl, srcPixels, sbpl, v[0], v[1], v[3], v[2], sourceRectI, clip, v[3].y, v[1].y, dudx, dvdx, dudy, dvdy, u0, v0, blender);
+ qt_transform_image_rasterize(destPixels, dbpl, srcPixels, sbpl, v[1], v[2], v[3], v[2], sourceRectI, clip, v[1].y, v[2].y, dudx, dvdx, dudy, dvdy, u0, v0, blender);
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif // QBLENDFUNCTIONS_P_H
diff --git a/src/gui/painting/qblittable.cpp b/src/gui/painting/qblittable.cpp
new file mode 100644
index 0000000000..58025312c7
--- /dev/null
+++ b/src/gui/painting/qblittable.cpp
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qblittable_p.h"
+
+#ifndef QT_NO_BLITTABLE
+QT_BEGIN_NAMESPACE
+
+class QBlittablePrivate
+{
+public:
+ QBlittablePrivate(const QSize &size, QBlittable::Capabilities caps)
+ : caps(caps), m_size(size), locked(false), cachedImg(0)
+ {}
+ QBlittable::Capabilities caps;
+ QSize m_size;
+ bool locked;
+ QImage *cachedImg;
+};
+
+
+QBlittable::QBlittable(const QSize &size, Capabilities caps)
+ : d_ptr(new QBlittablePrivate(size,caps))
+{
+}
+
+QBlittable::~QBlittable()
+{
+ delete d_ptr;
+}
+
+
+QBlittable::Capabilities QBlittable::capabilities() const
+{
+ Q_D(const QBlittable);
+ return d->caps;
+}
+
+QSize QBlittable::size() const
+{
+ Q_D(const QBlittable);
+ return d->m_size;
+}
+
+QImage *QBlittable::lock()
+{
+ Q_D(QBlittable);
+ if (!d->locked) {
+ d->cachedImg = doLock();
+ d->locked = true;
+ }
+
+ return d->cachedImg;
+}
+
+void QBlittable::unlock()
+{
+ Q_D(QBlittable);
+ if (d->locked) {
+ doUnlock();
+ d->locked = false;
+ }
+}
+
+QT_END_NAMESPACE
+#endif //QT_NO_BLITTABLE
+
diff --git a/src/gui/painting/qblittable_p.h b/src/gui/painting/qblittable_p.h
new file mode 100644
index 0000000000..9d0e8225b8
--- /dev/null
+++ b/src/gui/painting/qblittable_p.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QBLITTABLE_P_H
+#define QBLITTABLE_P_H
+
+#include <QtCore/qsize.h>
+#include <QtGui/private/qpixmap_blitter_p.h>
+
+
+#ifndef QT_NO_BLITTABLE
+QT_BEGIN_NAMESPACE
+
+class QImage;
+class QBlittablePrivate;
+
+class Q_GUI_EXPORT QBlittable
+{
+ Q_DECLARE_PRIVATE(QBlittable);
+public:
+ enum Capability {
+
+ SolidRectCapability = 0x0001,
+ SourcePixmapCapability = 0x0002,
+ SourceOverPixmapCapability = 0x0004,
+ SourceOverScaledPixmapCapability = 0x0008,
+
+ // Internal ones
+ OutlineCapability = 0x0001000,
+ };
+ Q_DECLARE_FLAGS (Capabilities, Capability);
+
+ QBlittable(const QSize &size, Capabilities caps);
+ virtual ~QBlittable();
+
+ Capabilities capabilities() const;
+ QSize size() const;
+
+ virtual void fillRect(const QRectF &rect, const QColor &color) = 0;
+ virtual void drawPixmap(const QRectF &rect, const QPixmap &pixmap, const QRectF &subrect) = 0;
+
+ QImage *lock();
+ void unlock();
+
+protected:
+ virtual QImage *doLock() = 0;
+ virtual void doUnlock() = 0;
+ QBlittablePrivate *d_ptr;
+};
+
+QT_END_NAMESPACE
+#endif //QT_NO_BLITTABLE
+#endif //QBLITTABLE_P_H
diff --git a/src/gui/painting/qbrush.cpp b/src/gui/painting/qbrush.cpp
new file mode 100644
index 0000000000..bf5764a609
--- /dev/null
+++ b/src/gui/painting/qbrush.cpp
@@ -0,0 +1,2196 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbrush.h"
+#include "qpixmap.h"
+#include "qbitmap.h"
+#include "qpixmapcache.h"
+#include "qdatastream.h"
+#include "qvariant.h"
+#include "qline.h"
+#include "qdebug.h"
+#include <QtCore/qcoreapplication.h>
+#include "private/qstylehelper_p.h"
+#include <QtCore/qnumeric.h>
+
+QT_BEGIN_NAMESPACE
+
+const uchar *qt_patternForBrush(int brushStyle, bool invert)
+{
+ Q_ASSERT(brushStyle > Qt::SolidPattern && brushStyle < Qt::LinearGradientPattern);
+ if(invert) {
+ static const uchar dense1_pat[] = { 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff };
+ static const uchar dense2_pat[] = { 0x77, 0xff, 0xdd, 0xff, 0x77, 0xff, 0xdd, 0xff };
+ static const uchar dense3_pat[] = { 0x55, 0xbb, 0x55, 0xee, 0x55, 0xbb, 0x55, 0xee };
+ static const uchar dense4_pat[] = { 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55 };
+ static const uchar dense5_pat[] = { 0xaa, 0x44, 0xaa, 0x11, 0xaa, 0x44, 0xaa, 0x11 };
+ static const uchar dense6_pat[] = { 0x88, 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00 };
+ static const uchar dense7_pat[] = { 0x00, 0x44, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00 };
+ static const uchar hor_pat[] = { 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00 };
+ static const uchar ver_pat[] = { 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 };
+ static const uchar cross_pat[] = { 0x10, 0x10, 0x10, 0xff, 0x10, 0x10, 0x10, 0x10 };
+ static const uchar bdiag_pat[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
+ static const uchar fdiag_pat[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
+ static const uchar dcross_pat[] = { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 };
+ static const uchar *const pat_tbl[] = {
+ dense1_pat, dense2_pat, dense3_pat, dense4_pat, dense5_pat,
+ dense6_pat, dense7_pat,
+ hor_pat, ver_pat, cross_pat, bdiag_pat, fdiag_pat, dcross_pat };
+ return pat_tbl[brushStyle - Qt::Dense1Pattern];
+ }
+ static const uchar dense1_pat[] = { 0x00, 0x44, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00 };
+ static const uchar dense2_pat[] = { 0x88, 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00 };
+ static const uchar dense3_pat[] = { 0xaa, 0x44, 0xaa, 0x11, 0xaa, 0x44, 0xaa, 0x11 };
+ static const uchar dense4_pat[] = { 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa };
+ static const uchar dense5_pat[] = { 0x55, 0xbb, 0x55, 0xee, 0x55, 0xbb, 0x55, 0xee };
+ static const uchar dense6_pat[] = { 0x77, 0xff, 0xdd, 0xff, 0x77, 0xff, 0xdd, 0xff };
+ static const uchar dense7_pat[] = { 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff };
+ static const uchar hor_pat[] = { 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff };
+ static const uchar ver_pat[] = { 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef };
+ static const uchar cross_pat[] = { 0xef, 0xef, 0xef, 0x00, 0xef, 0xef, 0xef, 0xef };
+ static const uchar bdiag_pat[] = { 0x7f, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, 0xfe };
+ static const uchar fdiag_pat[] = { 0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f };
+ static const uchar dcross_pat[] = { 0x7e, 0xbd, 0xdb, 0xe7, 0xe7, 0xdb, 0xbd, 0x7e };
+ static const uchar *const pat_tbl[] = {
+ dense1_pat, dense2_pat, dense3_pat, dense4_pat, dense5_pat,
+ dense6_pat, dense7_pat,
+ hor_pat, ver_pat, cross_pat, bdiag_pat, fdiag_pat, dcross_pat };
+ return pat_tbl[brushStyle - Qt::Dense1Pattern];
+}
+
+QPixmap qt_pixmapForBrush(int brushStyle, bool invert)
+{
+
+ QPixmap pm;
+ QString key = QLatin1Literal("$qt-brush$")
+ % HexString<uint>(brushStyle)
+ % QLatin1Char(invert ? '1' : '0');
+ if (!QPixmapCache::find(key, pm)) {
+ pm = QBitmap::fromData(QSize(8, 8), qt_patternForBrush(brushStyle, invert),
+ QImage::Format_MonoLSB);
+ QPixmapCache::insert(key, pm);
+ }
+
+ return pm;
+}
+
+class QBrushPatternImageCache
+{
+public:
+ QBrushPatternImageCache()
+ : m_initialized(false)
+ {
+ init();
+ }
+
+ void init()
+ {
+ for (int style = Qt::Dense1Pattern; style <= Qt::DiagCrossPattern; ++style) {
+ int i = style - Qt::Dense1Pattern;
+ m_images[i][0] = QImage(qt_patternForBrush(style, 0), 8, 8, 1, QImage::Format_MonoLSB);
+ m_images[i][1] = QImage(qt_patternForBrush(style, 1), 8, 8, 1, QImage::Format_MonoLSB);
+ }
+ m_initialized = true;
+ }
+
+ QImage getImage(int brushStyle, bool invert) const
+ {
+ Q_ASSERT(brushStyle >= Qt::Dense1Pattern && brushStyle <= Qt::DiagCrossPattern);
+ if (!m_initialized)
+ const_cast<QBrushPatternImageCache*>(this)->init();
+ return m_images[brushStyle - Qt::Dense1Pattern][invert];
+ }
+
+ void cleanup() {
+ for (int style = Qt::Dense1Pattern; style <= Qt::DiagCrossPattern; ++style) {
+ int i = style - Qt::Dense1Pattern;
+ m_images[i][0] = QImage();
+ m_images[i][1] = QImage();
+ }
+ m_initialized = false;
+ }
+
+private:
+ QImage m_images[Qt::DiagCrossPattern - Qt::Dense1Pattern + 1][2];
+ bool m_initialized;
+};
+
+static void qt_cleanup_brush_pattern_image_cache();
+Q_GLOBAL_STATIC_WITH_INITIALIZER(QBrushPatternImageCache, qt_brushPatternImageCache,
+ {
+ qAddPostRoutine(qt_cleanup_brush_pattern_image_cache);
+ })
+
+static void qt_cleanup_brush_pattern_image_cache()
+{
+ qt_brushPatternImageCache()->cleanup();
+}
+
+Q_GUI_EXPORT QImage qt_imageForBrush(int brushStyle, bool invert)
+{
+ return qt_brushPatternImageCache()->getImage(brushStyle, invert);
+}
+
+struct QTexturedBrushData : public QBrushData
+{
+ QTexturedBrushData() {
+ m_has_pixmap_texture = false;
+ m_pixmap = 0;
+ }
+ ~QTexturedBrushData() {
+ delete m_pixmap;
+ }
+
+ void setPixmap(const QPixmap &pm) {
+ delete m_pixmap;
+
+ if (pm.isNull()) {
+ m_pixmap = 0;
+ m_has_pixmap_texture = false;
+ } else {
+ m_pixmap = new QPixmap(pm);
+ m_has_pixmap_texture = true;
+ }
+
+ m_image = QImage();
+ }
+
+ void setImage(const QImage &image) {
+ m_image = image;
+ delete m_pixmap;
+ m_pixmap = 0;
+ m_has_pixmap_texture = false;
+ }
+
+ QPixmap &pixmap() {
+ if (!m_pixmap) {
+ m_pixmap = new QPixmap(QPixmap::fromImage(m_image));
+ }
+ return *m_pixmap;
+ }
+
+ QImage &image() {
+ if (m_image.isNull() && m_pixmap)
+ m_image = m_pixmap->toImage();
+ return m_image;
+ }
+
+ QPixmap *m_pixmap;
+ QImage m_image;
+ bool m_has_pixmap_texture;
+};
+
+// returns true if the brush has a pixmap (or bitmap) set as the
+// brush texture, false otherwise
+bool Q_GUI_EXPORT qHasPixmapTexture(const QBrush& brush)
+{
+ if (brush.style() != Qt::TexturePattern)
+ return false;
+ QTexturedBrushData *tx_data = static_cast<QTexturedBrushData *>(brush.d.data());
+ return tx_data->m_has_pixmap_texture;
+}
+
+struct QGradientBrushData : public QBrushData
+{
+ QGradient gradient;
+};
+
+struct QBrushDataPointerDeleter
+{
+ static inline void deleteData(QBrushData *d)
+ {
+ switch (d->style) {
+ case Qt::TexturePattern:
+ delete static_cast<QTexturedBrushData*>(d);
+ break;
+ case Qt::LinearGradientPattern:
+ case Qt::RadialGradientPattern:
+ case Qt::ConicalGradientPattern:
+ delete static_cast<QGradientBrushData*>(d);
+ break;
+ default:
+ delete d;
+ }
+ }
+
+ static inline void cleanup(QBrushData *d)
+ {
+ if (d && !d->ref.deref()) {
+ deleteData(d);
+ }
+ }
+};
+
+/*!
+ \class QBrush
+ \ingroup painting
+ \ingroup shared
+
+ \brief The QBrush class defines the fill pattern of shapes drawn
+ by QPainter.
+
+ A brush has a style, a color, a gradient and a texture.
+
+ The brush style() defines the fill pattern using the
+ Qt::BrushStyle enum. The default brush style is Qt::NoBrush
+ (depending on how you construct a brush). This style tells the
+ painter to not fill shapes. The standard style for filling is
+ Qt::SolidPattern. The style can be set when the brush is created
+ using the appropriate constructor, and in addition the setStyle()
+ function provides means for altering the style once the brush is
+ constructed.
+
+ \image brush-styles.png Brush Styles
+
+ The brush color() defines the color of the fill pattern. The color
+ can either be one of Qt's predefined colors, Qt::GlobalColor, or
+ any other custom QColor. The currently set color can be retrieved
+ and altered using the color() and setColor() functions,
+ respectively.
+
+ The gradient() defines the gradient fill used when the current
+ style is either Qt::LinearGradientPattern,
+ Qt::RadialGradientPattern or Qt::ConicalGradientPattern. Gradient
+ brushes are created by giving a QGradient as a constructor
+ argument when creating the QBrush. Qt provides three different
+ gradients: QLinearGradient, QConicalGradient, and QRadialGradient
+ - all of which inherit QGradient.
+
+ \snippet doc/src/snippets/brush/gradientcreationsnippet.cpp 0
+
+ The texture() defines the pixmap used when the current style is
+ Qt::TexturePattern. You can create a brush with a texture by
+ providing the pixmap when the brush is created or by using
+ setTexture().
+
+ Note that applying setTexture() makes style() ==
+ Qt::TexturePattern, regardless of previous style
+ settings. Also, calling setColor() will not make a difference if
+ the style is a gradient. The same is the case if the style is
+ Qt::TexturePattern style unless the current texture is a QBitmap.
+
+ The isOpaque() function returns true if the brush is fully opaque
+ otherwise false. A brush is considered opaque if:
+
+ \list
+ \o The alpha component of the color() is 255.
+ \o Its texture() does not have an alpha channel and is not a QBitmap.
+ \o The colors in the gradient() all have an alpha component that is 255.
+ \endlist
+
+ \table 100%
+ \row
+ \o \inlineimage brush-outline.png Outlines
+ \o
+
+ To specify the style and color of lines and outlines, use the
+ QPainter's \l {QPen}{pen} combined with Qt::PenStyle and
+ Qt::GlobalColor:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qbrush.cpp 0
+
+ Note that, by default, QPainter renders the outline (using the
+ currently set pen) when drawing shapes. Use \l {Qt::NoPen}{\c
+ painter.setPen(Qt::NoPen)} to disable this behavior.
+
+ \endtable
+
+ For more information about painting in general, see the \l{Paint
+ System}.
+
+ \sa Qt::BrushStyle, QPainter, QColor
+*/
+
+#ifndef QT_NO_THREAD
+// Special deleter that only deletes if the ref-count goes to zero
+template <>
+class QGlobalStaticDeleter<QBrushData>
+{
+public:
+ QGlobalStatic<QBrushData> &globalStatic;
+ QGlobalStaticDeleter(QGlobalStatic<QBrushData> &_globalStatic)
+ : globalStatic(_globalStatic)
+ { }
+
+ inline ~QGlobalStaticDeleter()
+ {
+ if (!globalStatic.pointer->ref.deref())
+ delete globalStatic.pointer;
+ globalStatic.pointer = 0;
+ globalStatic.destroyed = true;
+ }
+};
+#endif
+
+Q_GLOBAL_STATIC_WITH_INITIALIZER(QBrushData, nullBrushInstance,
+ {
+ x->ref = 1;
+ x->style = Qt::BrushStyle(0);
+ x->color = Qt::black;
+ })
+
+static bool qbrush_check_type(Qt::BrushStyle style) {
+ switch (style) {
+ case Qt::TexturePattern:
+ qWarning("QBrush: Incorrect use of TexturePattern");
+ break;
+ case Qt::LinearGradientPattern:
+ case Qt::RadialGradientPattern:
+ case Qt::ConicalGradientPattern:
+ qWarning("QBrush: Wrong use of a gradient pattern");
+ break;
+ default:
+ return true;
+ }
+ return false;
+}
+
+/*!
+ \internal
+ Initializes the brush.
+*/
+
+void QBrush::init(const QColor &color, Qt::BrushStyle style)
+{
+ switch(style) {
+ case Qt::NoBrush:
+ d.reset(nullBrushInstance());
+ d->ref.ref();
+ if (d->color != color) setColor(color);
+ return;
+ case Qt::TexturePattern:
+ d.reset(new QTexturedBrushData);
+ break;
+ case Qt::LinearGradientPattern:
+ case Qt::RadialGradientPattern:
+ case Qt::ConicalGradientPattern:
+ d.reset(new QGradientBrushData);
+ break;
+ default:
+ d.reset(new QBrushData);
+ break;
+ }
+ d->ref = 1;
+ d->style = style;
+ d->color = color;
+}
+
+/*!
+ Constructs a default black brush with the style Qt::NoBrush
+ (i.e. this brush will not fill shapes).
+*/
+
+QBrush::QBrush()
+ : d(nullBrushInstance())
+{
+ Q_ASSERT(d);
+ d->ref.ref();
+}
+
+/*!
+ Constructs a brush with a black color and a texture set to the
+ given \a pixmap. The style is set to Qt::TexturePattern.
+
+ \sa setTexture()
+*/
+
+QBrush::QBrush(const QPixmap &pixmap)
+{
+ init(Qt::black, Qt::TexturePattern);
+ setTexture(pixmap);
+}
+
+
+/*!
+ Constructs a brush with a black color and a texture set to the
+ given \a image. The style is set to Qt::TexturePattern.
+
+ \sa setTextureImage()
+*/
+
+QBrush::QBrush(const QImage &image)
+{
+ init(Qt::black, Qt::TexturePattern);
+ setTextureImage(image);
+}
+
+/*!
+ Constructs a black brush with the given \a style.
+
+ \sa setStyle()
+*/
+
+QBrush::QBrush(Qt::BrushStyle style)
+{
+ if (qbrush_check_type(style))
+ init(Qt::black, style);
+ else {
+ d.reset(nullBrushInstance());
+ d->ref.ref();
+ }
+}
+
+/*!
+ Constructs a brush with the given \a color and \a style.
+
+ \sa setColor(), setStyle()
+*/
+
+QBrush::QBrush(const QColor &color, Qt::BrushStyle style)
+{
+ if (qbrush_check_type(style))
+ init(color, style);
+ else {
+ d.reset(nullBrushInstance());
+ d->ref.ref();
+ }
+}
+
+/*!
+ \fn QBrush::QBrush(Qt::GlobalColor color, Qt::BrushStyle style)
+
+ Constructs a brush with the given \a color and \a style.
+
+ \sa setColor(), setStyle()
+*/
+QBrush::QBrush(Qt::GlobalColor color, Qt::BrushStyle style)
+{
+ if (qbrush_check_type(style))
+ init(color, style);
+ else {
+ d.reset(nullBrushInstance());
+ d->ref.ref();
+ }
+}
+
+/*!
+ Constructs a brush with the given \a color and the custom pattern
+ stored in \a pixmap.
+
+ The style is set to Qt::TexturePattern. The color will only have
+ an effect for QBitmaps.
+
+ \sa setColor(), setPixmap()
+*/
+
+QBrush::QBrush(const QColor &color, const QPixmap &pixmap)
+{
+ init(color, Qt::TexturePattern);
+ setTexture(pixmap);
+}
+
+/*!
+
+ Constructs a brush with the given \a color and the custom pattern
+ stored in \a pixmap.
+
+ The style is set to Qt::TexturePattern. The color will only have
+ an effect for QBitmaps.
+
+ \sa setColor(), setPixmap()
+*/
+QBrush::QBrush(Qt::GlobalColor color, const QPixmap &pixmap)
+{
+ init(color, Qt::TexturePattern);
+ setTexture(pixmap);
+}
+
+/*!
+ Constructs a copy of \a other.
+*/
+
+QBrush::QBrush(const QBrush &other)
+ : d(other.d.data())
+{
+ d->ref.ref();
+}
+
+/*!
+ Constructs a brush based on the given \a gradient.
+
+ The brush style is set to the corresponding gradient style (either
+ Qt::LinearGradientPattern, Qt::RadialGradientPattern or
+ Qt::ConicalGradientPattern).
+*/
+QBrush::QBrush(const QGradient &gradient)
+{
+ Q_ASSERT_X(gradient.type() != QGradient::NoGradient, "QBrush::QBrush",
+ "QGradient should not be used directly, use the linear, radial\n"
+ "or conical gradients instead");
+
+ const Qt::BrushStyle enum_table[] = {
+ Qt::LinearGradientPattern,
+ Qt::RadialGradientPattern,
+ Qt::ConicalGradientPattern
+ };
+
+ init(QColor(), enum_table[gradient.type()]);
+ QGradientBrushData *grad = static_cast<QGradientBrushData *>(d.data());
+ grad->gradient = gradient;
+}
+
+/*!
+ Destroys the brush.
+*/
+
+QBrush::~QBrush()
+{
+}
+
+void QBrush::cleanUp(QBrushData *x)
+{
+ QBrushDataPointerDeleter::deleteData(x);
+}
+
+
+void QBrush::detach(Qt::BrushStyle newStyle)
+{
+ if (newStyle == d->style && d->ref == 1)
+ return;
+
+ QScopedPointer<QBrushData> x;
+ switch(newStyle) {
+ case Qt::TexturePattern: {
+ QTexturedBrushData *tbd = new QTexturedBrushData;
+ if (d->style == Qt::TexturePattern) {
+ QTexturedBrushData *data = static_cast<QTexturedBrushData *>(d.data());
+ if (data->m_has_pixmap_texture)
+ tbd->setPixmap(data->pixmap());
+ else
+ tbd->setImage(data->image());
+ }
+ x.reset(tbd);
+ break;
+ }
+ case Qt::LinearGradientPattern:
+ case Qt::RadialGradientPattern:
+ case Qt::ConicalGradientPattern:
+ x.reset(new QGradientBrushData);
+ static_cast<QGradientBrushData *>(x.data())->gradient =
+ static_cast<QGradientBrushData *>(d.data())->gradient;
+ break;
+ default:
+ x.reset(new QBrushData);
+ break;
+ }
+ x->ref = 1;
+ x->style = newStyle;
+ x->color = d->color;
+ x->transform = d->transform;
+ d.reset(x.take());
+}
+
+
+/*!
+ \fn QBrush &QBrush::operator=(const QBrush &brush)
+
+ Assigns the given \a brush to \e this brush and returns a
+ reference to \e this brush.
+*/
+
+QBrush &QBrush::operator=(const QBrush &b)
+{
+ if (d == b.d)
+ return *this;
+
+ b.d->ref.ref();
+ d.reset(b.d.data());
+ return *this;
+}
+
+
+/*!
+ \fn void QBrush::swap(QBrush &other)
+ \since 4.8
+
+ Swaps brush \a other with this brush. This operation is very
+ fast and never fails.
+*/
+
+/*!
+ Returns the brush as a QVariant
+*/
+QBrush::operator QVariant() const
+{
+ return QVariant(QVariant::Brush, this);
+}
+
+/*!
+ \fn Qt::BrushStyle QBrush::style() const
+
+ Returns the brush style.
+
+ \sa setStyle()
+*/
+
+/*!
+ Sets the brush style to \a style.
+
+ \sa style()
+*/
+
+void QBrush::setStyle(Qt::BrushStyle style)
+{
+ if (d->style == style)
+ return;
+
+ if (qbrush_check_type(style)) {
+ detach(style);
+ d->style = style;
+ }
+}
+
+
+/*!
+ \fn const QColor &QBrush::color() const
+
+ Returns the brush color.
+
+ \sa setColor()
+*/
+
+/*!
+ \fn void QBrush::setColor(const QColor &color)
+
+ Sets the brush color to the given \a color.
+
+ Note that calling setColor() will not make a difference if the
+ style is a gradient. The same is the case if the style is
+ Qt::TexturePattern style unless the current texture is a QBitmap.
+
+ \sa color()
+*/
+
+void QBrush::setColor(const QColor &c)
+{
+ detach(d->style);
+ d->color = c;
+}
+
+/*!
+ \fn void QBrush::setColor(Qt::GlobalColor color)
+ \overload
+
+ Sets the brush color to the given \a color.
+*/
+
+
+#ifdef QT3_SUPPORT
+
+/*!
+ \fn void QBrush::setPixmap(const QPixmap &pixmap)
+
+ \compat
+
+ Sets a custom pattern for this brush.
+
+ Use setTexture() instead.
+*/
+
+/*!
+ \fn QPixmap *QBrush::pixmap() const
+
+ Returns a pointer to the custom brush pattern.
+
+ Use texture() instead.
+*/
+QPixmap *QBrush::pixmap() const
+{
+ if (d->style != Qt::TexturePattern)
+ return 0;
+ QTexturedBrushData *data = static_cast<QTexturedBrushData*>(d.data());
+ QPixmap &pixmap = data->pixmap();
+ return pixmap.isNull() ? 0 : &pixmap;
+}
+#endif
+
+/*!
+ \fn QPixmap QBrush::texture() const
+
+ Returns the custom brush pattern, or a null pixmap if no custom brush pattern
+ has been set.
+
+ \sa setTexture()
+*/
+QPixmap QBrush::texture() const
+{
+ return d->style == Qt::TexturePattern
+ ? (static_cast<QTexturedBrushData *>(d.data()))->pixmap()
+ : QPixmap();
+}
+
+/*!
+ Sets the brush pixmap to \a pixmap. The style is set to
+ Qt::TexturePattern.
+
+ The current brush color will only have an effect for monochrome
+ pixmaps, i.e. for QPixmap::depth() == 1 (\l {QBitmap}{QBitmaps}).
+
+ \sa texture()
+*/
+
+void QBrush::setTexture(const QPixmap &pixmap)
+{
+ if (!pixmap.isNull()) {
+ detach(Qt::TexturePattern);
+ QTexturedBrushData *data = static_cast<QTexturedBrushData *>(d.data());
+ data->setPixmap(pixmap);
+ } else {
+ detach(Qt::NoBrush);
+ }
+}
+
+
+/*!
+ \since 4.2
+
+ Returns the custom brush pattern, or a null image if no custom
+ brush pattern has been set.
+
+ If the texture was set as a QPixmap it will be converted to a
+ QImage.
+
+ \sa setTextureImage()
+*/
+
+QImage QBrush::textureImage() const
+{
+ return d->style == Qt::TexturePattern
+ ? (static_cast<QTexturedBrushData *>(d.data()))->image()
+ : QImage();
+}
+
+
+/*!
+ \since 4.2
+
+ Sets the brush image to \a image. The style is set to
+ Qt::TexturePattern.
+
+ Note the current brush color will \e not have any affect on
+ monochrome images, as opposed to calling setTexture() with a
+ QBitmap. If you want to change the color of monochrome image
+ brushes, either convert the image to QBitmap with \c
+ QBitmap::fromImage() and set the resulting QBitmap as a texture,
+ or change the entries in the color table for the image.
+
+ \sa textureImage(), setTexture()
+*/
+
+void QBrush::setTextureImage(const QImage &image)
+{
+ if (!image.isNull()) {
+ detach(Qt::TexturePattern);
+ QTexturedBrushData *data = static_cast<QTexturedBrushData *>(d.data());
+ data->setImage(image);
+ } else {
+ detach(Qt::NoBrush);
+ }
+}
+
+
+/*!
+ Returns the gradient describing this brush.
+*/
+const QGradient *QBrush::gradient() const
+{
+ if (d->style == Qt::LinearGradientPattern
+ || d->style == Qt::RadialGradientPattern
+ || d->style == Qt::ConicalGradientPattern) {
+ return &static_cast<const QGradientBrushData *>(d.data())->gradient;
+ }
+ return 0;
+}
+
+
+/*!
+ Returns true if the brush is fully opaque otherwise false. A brush
+ is considered opaque if:
+
+ \list
+ \i The alpha component of the color() is 255.
+ \i Its texture() does not have an alpha channel and is not a QBitmap.
+ \i The colors in the gradient() all have an alpha component that is 255.
+ \endlist
+*/
+
+bool QBrush::isOpaque() const
+{
+ bool opaqueColor = d->color.alpha() == 255;
+
+ // Test awfully simple case first
+ if (d->style == Qt::SolidPattern)
+ return opaqueColor;
+
+ if (d->style == Qt::LinearGradientPattern
+ || d->style == Qt::RadialGradientPattern
+ || d->style == Qt::ConicalGradientPattern) {
+ QGradientStops stops = gradient()->stops();
+ for (int i=0; i<stops.size(); ++i)
+ if (stops.at(i).second.alpha() != 255)
+ return false;
+ return true;
+ } else if (d->style == Qt::TexturePattern) {
+ return qHasPixmapTexture(*this)
+ ? !texture().hasAlphaChannel() && !texture().isQBitmap()
+ : !textureImage().hasAlphaChannel();
+ }
+
+ return false;
+}
+
+
+/*!
+ \since 4.2
+
+ Sets \a matrix as an explicit transformation matrix on the
+ current brush. The brush transformation matrix is merged with
+ QPainter transformation matrix to produce the final result.
+
+ \sa matrix()
+*/
+void QBrush::setMatrix(const QMatrix &matrix)
+{
+ setTransform(QTransform(matrix));
+}
+
+/*!
+ \since 4.3
+
+ Sets \a matrix as an explicit transformation matrix on the
+ current brush. The brush transformation matrix is merged with
+ QPainter transformation matrix to produce the final result.
+
+ \sa transform()
+*/
+void QBrush::setTransform(const QTransform &matrix)
+{
+ detach(d->style);
+ d->transform = matrix;
+}
+
+
+/*!
+ \fn void QBrush::matrix() const
+ \since 4.2
+
+ Returns the current transformation matrix for the brush.
+
+ \sa setMatrix()
+*/
+
+/*!
+ \fn bool QBrush::operator!=(const QBrush &brush) const
+
+ Returns true if the brush is different from the given \a brush;
+ otherwise returns false.
+
+ Two brushes are different if they have different styles, colors or
+ transforms or different pixmaps or gradients depending on the style.
+
+ \sa operator==()
+*/
+
+/*!
+ \fn bool QBrush::operator==(const QBrush &brush) const
+
+ Returns true if the brush is equal to the given \a brush;
+ otherwise returns false.
+
+ Two brushes are equal if they have equal styles, colors and
+ transforms and equal pixmaps or gradients depending on the style.
+
+ \sa operator!=()
+*/
+
+bool QBrush::operator==(const QBrush &b) const
+{
+ if (b.d == d)
+ return true;
+ if (b.d->style != d->style || b.d->color != d->color || b.d->transform != d->transform)
+ return false;
+ switch (d->style) {
+ case Qt::TexturePattern:
+ {
+ const QPixmap &us = (static_cast<QTexturedBrushData *>(d.data()))->pixmap();
+ const QPixmap &them = (static_cast<QTexturedBrushData *>(b.d.data()))->pixmap();
+ return ((us.isNull() && them.isNull()) || us.cacheKey() == them.cacheKey());
+ }
+ case Qt::LinearGradientPattern:
+ case Qt::RadialGradientPattern:
+ case Qt::ConicalGradientPattern:
+ {
+ const QGradientBrushData *d1 = static_cast<QGradientBrushData *>(d.data());
+ const QGradientBrushData *d2 = static_cast<QGradientBrushData *>(b.d.data());
+ return d1->gradient == d2->gradient;
+ }
+ default:
+ return true;
+ }
+}
+
+/*!
+ \fn QBrush::operator const QColor&() const
+
+ Returns the brush's color.
+
+ Use color() instead.
+*/
+
+#ifndef QT_NO_DEBUG_STREAM
+/*!
+ \internal
+*/
+QDebug operator<<(QDebug dbg, const QBrush &b)
+{
+#ifndef Q_BROKEN_DEBUG_STREAM
+ static const char *BRUSH_STYLES[] = {
+ "NoBrush",
+ "SolidPattern",
+ "Dense1Pattern",
+ "Dense2Pattern",
+ "Dense3Pattern",
+ "Dense4Pattern",
+ "Dense5Pattern",
+ "Dense6Pattern",
+ "Dense7Pattern",
+ "HorPattern",
+ "VerPattern",
+ "CrossPattern",
+ "BDiagPattern",
+ "FDiagPattern",
+ "DiagCrossPattern",
+ "LinearGradientPattern",
+ "RadialGradientPattern",
+ "ConicalGradientPattern",
+ 0, 0, 0, 0, 0, 0,
+ "TexturePattern" // 24
+ };
+
+ dbg.nospace() << "QBrush(" << b.color() << ',' << BRUSH_STYLES[b.style()] << ')';
+ return dbg.space();
+#else
+ qWarning("This compiler doesn't support streaming QBrush to QDebug");
+ return dbg;
+ Q_UNUSED(b);
+#endif
+}
+#endif
+
+/*****************************************************************************
+ QBrush stream functions
+ *****************************************************************************/
+#ifndef QT_NO_DATASTREAM
+/*!
+ \fn QDataStream &operator<<(QDataStream &stream, const QBrush &brush)
+ \relates QBrush
+
+ Writes the given \a brush to the given \a stream and returns a
+ reference to the \a stream.
+
+ \sa {Serializing Qt Data Types}
+*/
+
+QDataStream &operator<<(QDataStream &s, const QBrush &b)
+{
+ quint8 style = (quint8) b.style();
+ bool gradient_style = false;
+
+ if (style == Qt::LinearGradientPattern || style == Qt::RadialGradientPattern
+ || style == Qt::ConicalGradientPattern)
+ gradient_style = true;
+
+ if (s.version() < QDataStream::Qt_4_0 && gradient_style)
+ style = Qt::NoBrush;
+
+ s << style << b.color();
+ if (b.style() == Qt::TexturePattern) {
+ s << b.texture();
+ } else if (s.version() >= QDataStream::Qt_4_0 && gradient_style) {
+ const QGradient *gradient = b.gradient();
+ int type_as_int = int(gradient->type());
+ s << type_as_int;
+ if (s.version() >= QDataStream::Qt_4_3) {
+ s << int(gradient->spread());
+ s << int(gradient->coordinateMode());
+ }
+
+ if (s.version() >= QDataStream::Qt_4_5)
+ s << int(gradient->interpolationMode());
+
+ if (sizeof(qreal) == sizeof(double)) {
+ s << gradient->stops();
+ } else {
+ // ensure that we write doubles here instead of streaming the stops
+ // directly; otherwise, platforms that redefine qreal might generate
+ // data that cannot be read on other platforms.
+ QVector<QGradientStop> stops = gradient->stops();
+ s << quint32(stops.size());
+ for (int i = 0; i < stops.size(); ++i) {
+ const QGradientStop &stop = stops.at(i);
+ s << QPair<double, QColor>(double(stop.first), stop.second);
+ }
+ }
+
+ if (gradient->type() == QGradient::LinearGradient) {
+ s << static_cast<const QLinearGradient *>(gradient)->start();
+ s << static_cast<const QLinearGradient *>(gradient)->finalStop();
+ } else if (gradient->type() == QGradient::RadialGradient) {
+ s << static_cast<const QRadialGradient *>(gradient)->center();
+ s << static_cast<const QRadialGradient *>(gradient)->focalPoint();
+ s << (double) static_cast<const QRadialGradient *>(gradient)->radius();
+ } else { // type == Conical
+ s << static_cast<const QConicalGradient *>(gradient)->center();
+ s << (double) static_cast<const QConicalGradient *>(gradient)->angle();
+ }
+ }
+ if (s.version() >= QDataStream::Qt_4_3)
+ s << b.transform();
+ return s;
+}
+
+/*!
+ \fn QDataStream &operator>>(QDataStream &stream, QBrush &brush)
+ \relates QBrush
+
+ Reads the given \a brush from the given \a stream and returns a
+ reference to the \a stream.
+
+ \sa {Serializing Qt Data Types}
+*/
+
+QDataStream &operator>>(QDataStream &s, QBrush &b)
+{
+ quint8 style;
+ QColor color;
+ s >> style;
+ s >> color;
+ if (style == Qt::TexturePattern) {
+ QPixmap pm;
+ s >> pm;
+ b = QBrush(color, pm);
+ } else if (style == Qt::LinearGradientPattern
+ || style == Qt::RadialGradientPattern
+ || style == Qt::ConicalGradientPattern) {
+
+ int type_as_int;
+ QGradient::Type type;
+ QGradientStops stops;
+ QGradient::CoordinateMode cmode = QGradient::LogicalMode;
+ QGradient::Spread spread = QGradient::PadSpread;
+ QGradient::InterpolationMode imode = QGradient::ColorInterpolation;
+
+ s >> type_as_int;
+ type = QGradient::Type(type_as_int);
+ if (s.version() >= QDataStream::Qt_4_3) {
+ s >> type_as_int;
+ spread = QGradient::Spread(type_as_int);
+ s >> type_as_int;
+ cmode = QGradient::CoordinateMode(type_as_int);
+ }
+
+ if (s.version() >= QDataStream::Qt_4_5) {
+ s >> type_as_int;
+ imode = QGradient::InterpolationMode(type_as_int);
+ }
+
+ if (sizeof(qreal) == sizeof(double)) {
+ s >> stops;
+ } else {
+ quint32 numStops;
+ double n;
+ QColor c;
+
+ s >> numStops;
+ for (quint32 i = 0; i < numStops; ++i) {
+ s >> n >> c;
+ stops << QPair<qreal, QColor>(n, c);
+ }
+ }
+
+ if (type == QGradient::LinearGradient) {
+ QPointF p1, p2;
+ s >> p1;
+ s >> p2;
+ QLinearGradient lg(p1, p2);
+ lg.setStops(stops);
+ lg.setSpread(spread);
+ lg.setCoordinateMode(cmode);
+ lg.setInterpolationMode(imode);
+ b = QBrush(lg);
+ } else if (type == QGradient::RadialGradient) {
+ QPointF center, focal;
+ double radius;
+ s >> center;
+ s >> focal;
+ s >> radius;
+ QRadialGradient rg(center, radius, focal);
+ rg.setStops(stops);
+ rg.setSpread(spread);
+ rg.setCoordinateMode(cmode);
+ rg.setInterpolationMode(imode);
+ b = QBrush(rg);
+ } else { // type == QGradient::ConicalGradient
+ QPointF center;
+ double angle;
+ s >> center;
+ s >> angle;
+ QConicalGradient cg(center, angle);
+ cg.setStops(stops);
+ cg.setSpread(spread);
+ cg.setCoordinateMode(cmode);
+ cg.setInterpolationMode(imode);
+ b = QBrush(cg);
+ }
+ } else {
+ b = QBrush(color, (Qt::BrushStyle)style);
+ }
+ if (s.version() >= QDataStream::Qt_4_3) {
+ QTransform transform;
+ s >> transform;
+ b.setTransform(transform);
+ }
+ return s;
+}
+#endif // QT_NO_DATASTREAM
+
+/*******************************************************************************
+ * QGradient implementations
+ */
+
+
+/*!
+ \class QGradient
+ \ingroup painting
+ \ingroup shared
+
+ \brief The QGradient class is used in combination with QBrush to
+ specify gradient fills.
+
+ Qt currently supports three types of gradient fills:
+
+ \list
+ \o \e Linear gradients interpolate colors between start and end points.
+ \o \e Radial gradients interpolate colors between a focal point and end
+ points on a circle surrounding it.
+ \o \e Conical gradients interpolate colors around a center point.
+ \endlist
+
+ A gradient's type can be retrieved using the type() function.
+ Each of the types is represented by a subclass of QGradient:
+
+ \table
+ \header
+ \o QLinearGradient
+ \o QRadialGradient
+ \o QConicalGradient
+ \row
+ \o \inlineimage qgradient-linear.png
+ \o \inlineimage qgradient-radial.png
+ \o \inlineimage qgradient-conical.png
+ \endtable
+
+ The colors in a gradient are defined using stop points of the
+ QGradientStop type; i.e., a position and a color. Use the setColorAt()
+ function to define a single stop point. Alternatively, use the
+ setStops() function to define several stop points in one go. Note that
+ the latter function \e replaces the current set of stop points.
+
+ It is the gradient's complete set of stop points (accessible
+ through the stops() function) that describes how the gradient area
+ should be filled. If no stop points have been specified, a gradient
+ of black at 0 to white at 1 is used.
+
+ A diagonal linear gradient from black at (100, 100) to white at
+ (200, 200) could be specified like this:
+
+ \snippet doc/src/snippets/brush/brush.cpp 0
+
+ A gradient can have an arbitrary number of stop points. The
+ following would create a radial gradient starting with
+ red in the center, blue and then green on the edges:
+
+ \snippet doc/src/snippets/brush/brush.cpp 1
+
+ It is possible to repeat or reflect the gradient outside its area
+ by specifiying the \l {QGradient::Spread}{spread method} using the
+ setSpread() function. The default is to pad the outside area with
+ the color at the closest stop point. The currently set \l
+ {QGradient::Spread}{spread method} can be retrieved using the
+ spread() function. The QGradient::Spread enum defines three
+ different methods:
+
+ \table
+ \row
+ \o \inlineimage qradialgradient-pad.png
+ \o \inlineimage qradialgradient-repeat.png
+ \o \inlineimage qradialgradient-reflect.png
+ \row
+ \o \l {QGradient::PadSpread}{PadSpread}
+ \o \l {QGradient::RepeatSpread}{RepeatSpread}
+ \o \l {QGradient::ReflectSpread}{ReflectSpread}
+ \endtable
+
+ Note that the setSpread() function only has effect for linear and
+ radial gradients. The reason is that the conical gradient is
+ closed by definition, i.e. the \e conical gradient fills the
+ entire circle from 0 - 360 degrees, while the boundary of a radial
+ or a linear gradient can be specified through its radius or final
+ stop points, respectively.
+
+ The gradient coordinates can be specified in logical coordinates,
+ relative to device coordinates, or relative to object bounding box coordinates.
+ The \l {QGradient::CoordinateMode}{coordinate mode} can be set using the
+ setCoordinateMode() function. The default is LogicalMode, where the
+ gradient coordinates are specified in the same way as the object
+ coordinates. To retrieve the currently set \l {QGradient::CoordinateMode}
+ {coordinate mode} use coordinateMode().
+
+
+ \sa {demos/gradients}{The Gradients Demo}, QBrush
+*/
+
+/*!
+ \internal
+*/
+QGradient::QGradient()
+ : m_type(NoGradient), dummy(0)
+{
+}
+
+
+/*!
+ \enum QGradient::Type
+
+ Specifies the type of gradient.
+
+ \value LinearGradient Interpolates colors between start and end points
+ (QLinearGradient).
+
+ \value RadialGradient Interpolate colors between a focal point and end
+ points on a circle surrounding it (QRadialGradient).
+
+ \value ConicalGradient Interpolate colors around a center point (QConicalGradient).
+ \value NoGradient No gradient is used.
+
+ \sa type()
+*/
+
+/*!
+ \enum QGradient::Spread
+
+ Specifies how the area outside the gradient area should be
+ filled.
+
+ \value PadSpread The area is filled with the closest stop
+ color. This is the default.
+
+ \value RepeatSpread The gradient is repeated outside the gradient
+ area.
+
+ \value ReflectSpread The gradient is reflected outside the
+ gradient area.
+
+ \sa spread(), setSpread()
+*/
+
+/*!
+ \fn void QGradient::setSpread(Spread method)
+
+ Specifies the spread \a method that should be used for this
+ gradient.
+
+ Note that this function only has effect for linear and radial
+ gradients.
+
+ \sa spread()
+*/
+
+/*!
+ \fn QGradient::Spread QGradient::spread() const
+
+ Returns the spread method use by this gradient. The default is
+ PadSpread.
+
+ \sa setSpread()
+*/
+
+/*!
+ \fn QGradient::Type QGradient::type() const
+
+ Returns the type of gradient.
+*/
+
+/*!
+ \fn void QGradient::setColorAt(qreal position, const QColor &color)
+
+ Creates a stop point at the given \a position with the given \a
+ color. The given \a position must be in the range 0 to 1.
+
+ \sa setStops(), stops()
+*/
+
+void QGradient::setColorAt(qreal pos, const QColor &color)
+{
+ if ((pos > 1 || pos < 0) && !qIsNaN(pos)) {
+ qWarning("QGradient::setColorAt: Color position must be specified in the range 0 to 1");
+ return;
+ }
+
+ int index = 0;
+ if (!qIsNaN(pos))
+ while (index < m_stops.size() && m_stops.at(index).first < pos) ++index;
+
+ if (index < m_stops.size() && m_stops.at(index).first == pos)
+ m_stops[index].second = color;
+ else
+ m_stops.insert(index, QGradientStop(pos, color));
+}
+
+/*!
+ \fn void QGradient::setStops(const QGradientStops &stopPoints)
+
+ Replaces the current set of stop points with the given \a
+ stopPoints. The positions of the points must be in the range 0 to
+ 1, and must be sorted with the lowest point first.
+
+ \sa setColorAt(), stops()
+*/
+void QGradient::setStops(const QGradientStops &stops)
+{
+ m_stops.clear();
+ for (int i=0; i<stops.size(); ++i)
+ setColorAt(stops.at(i).first, stops.at(i).second);
+}
+
+
+/*!
+ Returns the stop points for this gradient.
+
+ If no stop points have been specified, a gradient of black at 0 to white
+ at 1 is used.
+
+ \sa setStops(), setColorAt()
+*/
+QGradientStops QGradient::stops() const
+{
+ if (m_stops.isEmpty()) {
+ QGradientStops tmp;
+ tmp << QGradientStop(0, Qt::black) << QGradientStop(1, Qt::white);
+ return tmp;
+ }
+ return m_stops;
+}
+
+#define Q_DUMMY_ACCESSOR union {void *p; uint i;}; p = dummy;
+
+/*!
+ \enum QGradient::CoordinateMode
+ \since 4.4
+
+ This enum specifies how gradient coordinates map to the paint
+ device on which the gradient is used.
+
+ \value LogicalMode This is the default mode. The gradient coordinates
+ are specified logical space just like the object coordinates.
+ \value StretchToDeviceMode In this mode the gradient coordinates
+ are relative to the bounding rectangle of the paint device,
+ with (0,0) in the top left corner, and (1,1) in the bottom right
+ corner of the paint device.
+ \value ObjectBoundingMode In this mode the gradient coordinates are
+ relative to the bounding rectangle of the object being drawn, with
+ (0,0) in the top left corner, and (1,1) in the bottom right corner
+ of the object's bounding rectangle.
+*/
+
+/*!
+ \since 4.4
+
+ Returns the coordinate mode of this gradient. The default mode is
+ LogicalMode.
+*/
+QGradient::CoordinateMode QGradient::coordinateMode() const
+{
+ Q_DUMMY_ACCESSOR
+ return CoordinateMode(i & 0x03);
+}
+
+/*!
+ \since 4.4
+
+ Sets the coordinate mode of this gradient to \a mode. The default
+ mode is LogicalMode.
+*/
+void QGradient::setCoordinateMode(CoordinateMode mode)
+{
+ Q_DUMMY_ACCESSOR
+ i &= ~0x03;
+ i |= uint(mode);
+ dummy = p;
+}
+
+/*!
+ \enum QGradient::InterpolationMode
+ \since 4.5
+ \internal
+
+ \value ComponentInterpolation The color components and the alpha component are
+ independently linearly interpolated.
+ \value ColorInterpolation The colors are linearly interpolated in
+ premultiplied color space.
+*/
+
+/*!
+ \since 4.5
+ \internal
+
+ Returns the interpolation mode of this gradient. The default mode is
+ ColorInterpolation.
+*/
+QGradient::InterpolationMode QGradient::interpolationMode() const
+{
+ Q_DUMMY_ACCESSOR
+ return InterpolationMode((i >> 2) & 0x01);
+}
+
+/*!
+ \since 4.5
+ \internal
+
+ Sets the interpolation mode of this gradient to \a mode. The default
+ mode is ColorInterpolation.
+*/
+void QGradient::setInterpolationMode(InterpolationMode mode)
+{
+ Q_DUMMY_ACCESSOR
+ i &= ~(1 << 2);
+ i |= (uint(mode) << 2);
+ dummy = p;
+}
+
+#undef Q_DUMMY_ACCESSOR
+
+/*!
+ \fn bool QGradient::operator!=(const QGradient &gradient) const
+ \since 4.2
+
+ Returns true if the gradient is the same as the other \a gradient
+ specified; otherwise returns false.
+
+ \sa operator==()
+*/
+
+/*!
+ Returns true if the gradient is the same as the other \a gradient
+ specified; otherwise returns false.
+
+ \sa operator!=()
+*/
+bool QGradient::operator==(const QGradient &gradient) const
+{
+ if (gradient.m_type != m_type
+ || gradient.m_spread != m_spread
+ || gradient.dummy != dummy) return false;
+
+ if (m_type == LinearGradient) {
+ if (m_data.linear.x1 != gradient.m_data.linear.x1
+ || m_data.linear.y1 != gradient.m_data.linear.y1
+ || m_data.linear.x2 != gradient.m_data.linear.x2
+ || m_data.linear.y2 != gradient.m_data.linear.y2)
+ return false;
+ } else if (m_type == RadialGradient) {
+ if (m_data.radial.cx != gradient.m_data.radial.cx
+ || m_data.radial.cy != gradient.m_data.radial.cy
+ || m_data.radial.fx != gradient.m_data.radial.fx
+ || m_data.radial.fy != gradient.m_data.radial.fy
+ || m_data.radial.radius != gradient.m_data.radial.radius)
+ return false;
+ } else { // m_type == ConicalGradient
+ if (m_data.conical.cx != gradient.m_data.conical.cx
+ || m_data.conical.cy != gradient.m_data.conical.cy
+ || m_data.conical.angle != gradient.m_data.conical.angle)
+ return false;
+ }
+
+ return stops() == gradient.stops();
+}
+
+/*!
+ \internal
+*/
+bool QGradient::operator==(const QGradient &gradient)
+{
+ return const_cast<const QGradient *>(this)->operator==(gradient);
+}
+
+/*!
+ \class QLinearGradient
+ \ingroup painting
+
+ \brief The QLinearGradient class is used in combination with QBrush to
+ specify a linear gradient brush.
+
+ Linear gradients interpolate colors between start and end
+ points. Outside these points the gradient is either padded,
+ reflected or repeated depending on the currently set \l
+ {QGradient::Spread}{spread} method:
+
+ \table
+ \row
+ \o \inlineimage qlineargradient-pad.png
+ \o \inlineimage qlineargradient-reflect.png
+ \o \inlineimage qlineargradient-repeat.png
+ \row
+ \o \l {QGradient::PadSpread}{PadSpread} (default)
+ \o \l {QGradient::ReflectSpread}{ReflectSpread}
+ \o \l {QGradient::RepeatSpread}{RepeatSpread}
+ \endtable
+
+ The colors in a gradient is defined using stop points of the
+ QGradientStop type, i.e. a position and a color. Use the
+ QGradient::setColorAt() or the QGradient::setStops() function to
+ define the stop points. It is the gradient's complete set of stop
+ points that describes how the gradient area should be filled. If
+ no stop points have been specified, a gradient of black at 0 to
+ white at 1 is used.
+
+ In addition to the functions inherited from QGradient, the
+ QLinearGradient class provides the finalStop() function which
+ returns the final stop point of the gradient, and the start()
+ function returning the start point of the gradient.
+
+ \sa QRadialGradient, QConicalGradient, {demos/gradients}{The
+ Gradients Demo}
+*/
+
+
+/*!
+ Constructs a default linear gradient with interpolation area
+ between (0, 0) and (1, 1).
+
+ \sa QGradient::setColorAt(), setStart(), setFinalStop()
+*/
+
+QLinearGradient::QLinearGradient()
+{
+ m_type = LinearGradient;
+ m_spread = PadSpread;
+ m_data.linear.x1 = 0;
+ m_data.linear.y1 = 0;
+ m_data.linear.x2 = 1;
+ m_data.linear.y2 = 1;
+}
+
+
+/*!
+ Constructs a linear gradient with interpolation area between the
+ given \a start point and \a finalStop.
+
+ \note The expected parameter values are in pixels.
+
+ \sa QGradient::setColorAt(), QGradient::setStops()
+*/
+QLinearGradient::QLinearGradient(const QPointF &start, const QPointF &finalStop)
+{
+ m_type = LinearGradient;
+ m_spread = PadSpread;
+ m_data.linear.x1 = start.x();
+ m_data.linear.y1 = start.y();
+ m_data.linear.x2 = finalStop.x();
+ m_data.linear.y2 = finalStop.y();
+}
+
+/*!
+ \fn QLinearGradient::QLinearGradient(qreal x1, qreal y1, qreal x2, qreal y2)
+
+ Constructs a linear gradient with interpolation area between (\a
+ x1, \a y1) and (\a x2, \a y2).
+
+ \note The expected parameter values are in pixels.
+
+ \sa QGradient::setColorAt(), QGradient::setStops()
+*/
+QLinearGradient::QLinearGradient(qreal xStart, qreal yStart, qreal xFinalStop, qreal yFinalStop)
+{
+ m_type = LinearGradient;
+ m_spread = PadSpread;
+ m_data.linear.x1 = xStart;
+ m_data.linear.y1 = yStart;
+ m_data.linear.x2 = xFinalStop;
+ m_data.linear.y2 = yFinalStop;
+}
+
+
+/*!
+ Returns the start point of this linear gradient in logical coordinates.
+
+ \sa QGradient::stops()
+*/
+
+QPointF QLinearGradient::start() const
+{
+ Q_ASSERT(m_type == LinearGradient);
+ return QPointF(m_data.linear.x1, m_data.linear.y1);
+}
+
+/*!
+ \fn void QLinearGradient::setStart(qreal x, qreal y)
+ \overload
+ \since 4.2
+
+ Sets the start point of this linear gradient in logical
+ coordinates to \a x, \a y.
+
+ \sa start()
+*/
+
+/*!
+ \since 4.2
+
+ Sets the start point of this linear gradient in logical
+ coordinates to \a start.
+
+ \sa start()
+*/
+
+void QLinearGradient::setStart(const QPointF &start)
+{
+ Q_ASSERT(m_type == LinearGradient);
+ m_data.linear.x1 = start.x();
+ m_data.linear.y1 = start.y();
+}
+
+
+/*!
+ \fn void QLinearGradient::setFinalStop(qreal x, qreal y)
+ \overload
+ \since 4.2
+
+ Sets the final stop point of this linear gradient in logical
+ coordinates to \a x, \a y.
+
+ \sa start()
+*/
+
+/*!
+ Returns the final stop point of this linear gradient in logical coordinates.
+
+ \sa QGradient::stops()
+*/
+
+QPointF QLinearGradient::finalStop() const
+{
+ Q_ASSERT(m_type == LinearGradient);
+ return QPointF(m_data.linear.x2, m_data.linear.y2);
+}
+
+
+/*!
+ \since 4.2
+
+ Sets the final stop point of this linear gradient in logical
+ coordinates to \a stop.
+
+ \sa finalStop()
+*/
+
+void QLinearGradient::setFinalStop(const QPointF &stop)
+{
+ Q_ASSERT(m_type == LinearGradient);
+ m_data.linear.x2 = stop.x();
+ m_data.linear.y2 = stop.y();
+}
+
+
+/*!
+ \class QRadialGradient
+ \ingroup painting
+
+ \brief The QRadialGradient class is used in combination with QBrush to
+ specify a radial gradient brush.
+
+ Radial gradients interpolate colors between a focal point and end
+ points on a circle surrounding it. Outside the end points the
+ gradient is either padded, reflected or repeated depending on the
+ currently set \l {QGradient::Spread}{spread} method:
+
+ \table
+ \row
+ \o \inlineimage qradialgradient-pad.png
+ \o \inlineimage qradialgradient-reflect.png
+ \o \inlineimage qradialgradient-repeat.png
+ \row
+ \o \l {QGradient::PadSpread}{PadSpread} (default)
+ \o \l {QGradient::ReflectSpread}{ReflectSpread}
+ \o \l {QGradient::RepeatSpread}{RepeatSpread}
+ \endtable
+
+ The colors in a gradient is defined using stop points of the
+ QGradientStop type, i.e. a position and a color. Use the
+ QGradient::setColorAt() or the QGradient::setStops() function to
+ define the stop points. It is the gradient's complete set of stop
+ points that describes how the gradient area should be filled. If
+ no stop points have been specified, a gradient of black at 0 to
+ white at 1 is used.
+
+ In addition to the functions inherited from QGradient, the
+ QRadialGradient class provides the center(), focalPoint() and
+ radius() functions returning the gradient's center, focal point
+ and radius respectively.
+
+ \sa QLinearGradient, QConicalGradient, {demos/gradients}{The
+ Gradients Demo}
+*/
+
+static QPointF qt_radial_gradient_adapt_focal_point(const QPointF &center,
+ qreal radius,
+ const QPointF &focalPoint)
+{
+ // We have a one pixel buffer zone to avoid numerical instability on the
+ // circle border
+ //### this is hacky because technically we should adjust based on current matrix
+ const qreal compensated_radius = radius - radius * qreal(0.001);
+ QLineF line(center, focalPoint);
+ if (line.length() > (compensated_radius))
+ line.setLength(compensated_radius);
+ return line.p2();
+}
+
+/*!
+ Constructs a radial gradient with the given \a center, \a
+ radius and \a focalPoint.
+
+ \sa QGradient::setColorAt(), QGradient::setStops()
+*/
+
+QRadialGradient::QRadialGradient(const QPointF &center, qreal radius, const QPointF &focalPoint)
+{
+ m_type = RadialGradient;
+ m_spread = PadSpread;
+ m_data.radial.cx = center.x();
+ m_data.radial.cy = center.y();
+ m_data.radial.radius = radius;
+
+ QPointF adapted_focal = qt_radial_gradient_adapt_focal_point(center, radius, focalPoint);
+ m_data.radial.fx = adapted_focal.x();
+ m_data.radial.fy = adapted_focal.y();
+}
+
+/*!
+ Constructs a radial gradient with the given \a center, \a
+ radius and the focal point in the circle center.
+
+ \sa QGradient::setColorAt(), QGradient::setStops()
+*/
+QRadialGradient::QRadialGradient(const QPointF &center, qreal radius)
+{
+ m_type = RadialGradient;
+ m_spread = PadSpread;
+ m_data.radial.cx = center.x();
+ m_data.radial.cy = center.y();
+ m_data.radial.radius = radius;
+ m_data.radial.fx = center.x();
+ m_data.radial.fy = center.y();
+}
+
+
+/*!
+ Constructs a radial gradient with the given center (\a cx, \a cy),
+ \a radius and focal point (\a fx, \a fy).
+
+ \sa QGradient::setColorAt(), QGradient::setStops()
+*/
+
+QRadialGradient::QRadialGradient(qreal cx, qreal cy, qreal radius, qreal fx, qreal fy)
+{
+ m_type = RadialGradient;
+ m_spread = PadSpread;
+ m_data.radial.cx = cx;
+ m_data.radial.cy = cy;
+ m_data.radial.radius = radius;
+
+ QPointF adapted_focal = qt_radial_gradient_adapt_focal_point(QPointF(cx, cy),
+ radius,
+ QPointF(fx, fy));
+
+ m_data.radial.fx = adapted_focal.x();
+ m_data.radial.fy = adapted_focal.y();
+}
+
+/*!
+ Constructs a radial gradient with the center at (\a cx, \a cy) and the
+ specified \a radius. The focal point lies at the center of the circle.
+
+ \sa QGradient::setColorAt(), QGradient::setStops()
+ */
+QRadialGradient::QRadialGradient(qreal cx, qreal cy, qreal radius)
+{
+ m_type = RadialGradient;
+ m_spread = PadSpread;
+ m_data.radial.cx = cx;
+ m_data.radial.cy = cy;
+ m_data.radial.radius = radius;
+ m_data.radial.fx = cx;
+ m_data.radial.fy = cy;
+}
+
+
+/*!
+ Constructs a radial gradient with the center and focal point at
+ (0, 0) with a radius of 1.
+*/
+QRadialGradient::QRadialGradient()
+{
+ m_type = RadialGradient;
+ m_spread = PadSpread;
+ m_data.radial.cx = 0;
+ m_data.radial.cy = 0;
+ m_data.radial.radius = 1;
+ m_data.radial.fx = 0;
+ m_data.radial.fy = 0;
+}
+
+
+/*!
+ Returns the center of this radial gradient in logical coordinates.
+
+ \sa QGradient::stops()
+*/
+
+QPointF QRadialGradient::center() const
+{
+ Q_ASSERT(m_type == RadialGradient);
+ return QPointF(m_data.radial.cx, m_data.radial.cy);
+}
+
+/*!
+ \fn void QRadialGradient::setCenter(qreal x, qreal y)
+ \overload
+ \since 4.2
+
+ Sets the center of this radial gradient in logical coordinates
+ to (\a x, \a y).
+
+ \sa center()
+*/
+
+/*!
+ \since 4.2
+
+ Sets the center of this radial gradient in logical coordinates
+ to \a center.
+
+ \sa center()
+*/
+
+void QRadialGradient::setCenter(const QPointF &center)
+{
+ Q_ASSERT(m_type == RadialGradient);
+ m_data.radial.cx = center.x();
+ m_data.radial.cy = center.y();
+}
+
+
+/*!
+ Returns the radius of this radial gradient in logical coordinates.
+
+ \sa QGradient::stops()
+*/
+
+qreal QRadialGradient::radius() const
+{
+ Q_ASSERT(m_type == RadialGradient);
+ return m_data.radial.radius;
+}
+
+
+/*!
+ \since 4.2
+
+ Sets the radius of this radial gradient in logical coordinates
+ to \a radius
+*/
+void QRadialGradient::setRadius(qreal radius)
+{
+ Q_ASSERT(m_type == RadialGradient);
+ m_data.radial.radius = radius;
+}
+
+
+/*!
+ Returns the focal point of this radial gradient in logical
+ coordinates.
+
+ \sa QGradient::stops()
+*/
+
+QPointF QRadialGradient::focalPoint() const
+{
+ Q_ASSERT(m_type == RadialGradient);
+ return QPointF(m_data.radial.fx, m_data.radial.fy);
+}
+
+/*!
+ \fn void QRadialGradient::setFocalPoint(qreal x, qreal y)
+ \overload
+ \since 4.2
+
+ Sets the focal point of this radial gradient in logical
+ coordinates to (\a x, \a y).
+
+ \sa focalPoint()
+*/
+
+/*!
+ \since 4.2
+
+ Sets the focal point of this radial gradient in logical
+ coordinates to \a focalPoint.
+
+ \sa focalPoint()
+*/
+
+void QRadialGradient::setFocalPoint(const QPointF &focalPoint)
+{
+ Q_ASSERT(m_type == RadialGradient);
+ m_data.radial.fx = focalPoint.x();
+ m_data.radial.fy = focalPoint.y();
+}
+
+
+
+/*!
+ \class QConicalGradient
+ \ingroup painting
+
+ \brief The QConicalGradient class is used in combination with QBrush to
+ specify a conical gradient brush.
+
+ Conical gradients interpolate interpolate colors counter-clockwise
+ around a center point.
+
+ \image qconicalgradient.png
+
+ The colors in a gradient is defined using stop points of the
+ QGradientStop type, i.e. a position and a color. Use the
+ QGradient::setColorAt() or the QGradient::setStops() function to
+ define the stop points. It is the gradient's complete set of stop
+ points that describes how the gradient area should be filled. If
+ no stop points have been specified, a gradient of black at 0 to
+ white at 1 is used.
+
+ In addition to the functions inherited from QGradient, the
+ QConicalGradient class provides the angle() and center() functions
+ returning the start angle and center of the gradient.
+
+ Note that the setSpread() function has no effect for conical
+ gradients. The reason is that the conical gradient is closed by
+ definition, i.e. the conical gradient fills the entire circle from
+ 0 - 360 degrees, while the boundary of a radial or a linear
+ gradient can be specified through its radius or final stop points,
+ respectively.
+
+ \sa QLinearGradient, QRadialGradient, {demos/gradients}{The
+ Gradients Demo}
+*/
+
+
+/*!
+ Constructs a conical gradient with the given \a center, starting
+ the interpolation at the given \a angle. The \a angle must be
+ specified in degrees between 0 and 360.
+
+ \sa QGradient::setColorAt(), QGradient::setStops()
+*/
+
+QConicalGradient::QConicalGradient(const QPointF &center, qreal angle)
+{
+ m_type = ConicalGradient;
+ m_spread = PadSpread;
+ m_data.conical.cx = center.x();
+ m_data.conical.cy = center.y();
+ m_data.conical.angle = angle;
+}
+
+
+/*!
+ Constructs a conical gradient with the given center (\a cx, \a
+ cy), starting the interpolation at the given \a angle. The angle
+ must be specified in degrees between 0 and 360.
+
+ \sa QGradient::setColorAt(), QGradient::setStops()
+*/
+
+QConicalGradient::QConicalGradient(qreal cx, qreal cy, qreal angle)
+{
+ m_type = ConicalGradient;
+ m_spread = PadSpread;
+ m_data.conical.cx = cx;
+ m_data.conical.cy = cy;
+ m_data.conical.angle = angle;
+}
+
+
+/*!
+ Constructs a conical with center at (0, 0) starting the
+ interpolation at angle 0.
+
+ \sa QGradient::setColorAt(), setCenter(), setAngle()
+*/
+
+QConicalGradient::QConicalGradient()
+{
+ m_type = ConicalGradient;
+ m_spread = PadSpread;
+ m_data.conical.cx = 0;
+ m_data.conical.cy = 0;
+ m_data.conical.angle = 0;
+}
+
+
+/*!
+ Returns the center of the conical gradient in logical
+ coordinates.
+
+ \sa stops()
+*/
+
+QPointF QConicalGradient::center() const
+{
+ Q_ASSERT(m_type == ConicalGradient);
+ return QPointF(m_data.conical.cx, m_data.conical.cy);
+}
+
+
+/*!
+ \fn void QConicalGradient::setCenter(qreal x, qreal y)
+
+ \overload
+
+ Sets the center of this conical gradient in logical coordinates to
+ (\a x, \a y).
+
+ \sa center()
+*/
+
+/*!
+ Sets the center of this conical gradient in logical coordinates to
+ \a center.
+
+ \sa center()
+*/
+
+void QConicalGradient::setCenter(const QPointF &center)
+{
+ Q_ASSERT(m_type == ConicalGradient);
+ m_data.conical.cx = center.x();
+ m_data.conical.cy = center.y();
+}
+
+/*!
+ Returns the start angle of the conical gradient in logical
+ coordinates.
+
+ \sa stops()
+*/
+
+qreal QConicalGradient::angle() const
+{
+ Q_ASSERT(m_type == ConicalGradient);
+ return m_data.conical.angle;
+}
+
+
+/*!
+ \since 4.2
+
+ Sets \a angle to be the start angle for this conical gradient in
+ logical coordinates.
+
+ \sa angle()
+*/
+
+void QConicalGradient::setAngle(qreal angle)
+{
+ Q_ASSERT(m_type == ConicalGradient);
+ m_data.conical.angle = angle;
+}
+
+/*!
+ \typedef QGradientStop
+ \relates QGradient
+
+ Typedef for QPair<\l qreal, QColor>.
+*/
+
+/*!
+ \typedef QGradientStops
+ \relates QGradient
+
+ Typedef for QVector<QGradientStop>.
+*/
+
+/*!
+ \typedef QBrush::DataPtr
+ \internal
+*/
+
+/*!
+ \fn DataPtr &QBrush::data_ptr()
+ \internal
+*/
+
+
+/*!
+ \fn bool QBrush::isDetached() const
+ \internal
+*/
+
+/*!
+ \fn QTransform QBrush::transform() const
+ \since 4.3
+
+ Returns the current transformation matrix for the brush.
+
+ \sa setTransform()
+*/
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qbrush.h b/src/gui/painting/qbrush.h
new file mode 100644
index 0000000000..8b313196db
--- /dev/null
+++ b/src/gui/painting/qbrush.h
@@ -0,0 +1,338 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QBRUSH_H
+#define QBRUSH_H
+
+#include <QtCore/qpair.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qscopedpointer.h>
+#include <QtGui/qcolor.h>
+#include <QtGui/qmatrix.h>
+#include <QtGui/qtransform.h>
+#include <QtGui/qimage.h>
+#include <QtGui/qpixmap.h>
+
+#if defined(Q_OS_VXWORKS)
+# if defined(m_data)
+# undef m_data
+# endif
+# if defined(m_type)
+# undef m_type
+# endif
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+struct QBrushData;
+class QPixmap;
+class QGradient;
+class QVariant;
+struct QBrushDataPointerDeleter;
+
+class Q_GUI_EXPORT QBrush
+{
+public:
+ QBrush();
+ QBrush(Qt::BrushStyle bs);
+ QBrush(const QColor &color, Qt::BrushStyle bs=Qt::SolidPattern);
+ QBrush(Qt::GlobalColor color, Qt::BrushStyle bs=Qt::SolidPattern);
+
+ QBrush(const QColor &color, const QPixmap &pixmap);
+ QBrush(Qt::GlobalColor color, const QPixmap &pixmap);
+ QBrush(const QPixmap &pixmap);
+ QBrush(const QImage &image);
+
+ QBrush(const QBrush &brush);
+
+ QBrush(const QGradient &gradient);
+
+ ~QBrush();
+ QBrush &operator=(const QBrush &brush);
+#ifdef Q_COMPILER_RVALUE_REFS
+ inline QBrush &operator=(QBrush &&other)
+ { qSwap(d, other.d); return *this; }
+#endif
+ inline void swap(QBrush &other) { qSwap(d, other.d); }
+
+ operator QVariant() const;
+
+ inline Qt::BrushStyle style() const;
+ void setStyle(Qt::BrushStyle);
+
+ inline const QMatrix &matrix() const;
+ void setMatrix(const QMatrix &mat);
+
+ inline QTransform transform() const;
+ void setTransform(const QTransform &);
+
+ QPixmap texture() const;
+ void setTexture(const QPixmap &pixmap);
+
+ QImage textureImage() const;
+ void setTextureImage(const QImage &image);
+
+ inline const QColor &color() const;
+ void setColor(const QColor &color);
+ inline void setColor(Qt::GlobalColor color);
+
+ const QGradient *gradient() const;
+
+ bool isOpaque() const;
+
+ bool operator==(const QBrush &b) const;
+ inline bool operator!=(const QBrush &b) const { return !(operator==(b)); }
+
+#ifdef QT3_SUPPORT
+ inline QT3_SUPPORT operator const QColor&() const;
+ QT3_SUPPORT QPixmap *pixmap() const;
+ inline QT3_SUPPORT void setPixmap(const QPixmap &pixmap) { setTexture(pixmap); }
+#endif
+
+private:
+#if defined(Q_WS_X11)
+ friend class QX11PaintEngine;
+#endif
+ friend class QRasterPaintEngine;
+ friend class QRasterPaintEnginePrivate;
+ friend struct QSpanData;
+ friend class QPainter;
+ friend bool Q_GUI_EXPORT qHasPixmapTexture(const QBrush& brush);
+ void detach(Qt::BrushStyle newStyle);
+ void init(const QColor &color, Qt::BrushStyle bs);
+ QScopedPointer<QBrushData, QBrushDataPointerDeleter> d;
+ void cleanUp(QBrushData *x);
+
+public:
+ inline bool isDetached() const;
+ typedef QScopedPointer<QBrushData, QBrushDataPointerDeleter> DataPtr;
+ inline DataPtr &data_ptr() { return d; }
+};
+
+inline void QBrush::setColor(Qt::GlobalColor acolor)
+{ setColor(QColor(acolor)); }
+
+Q_DECLARE_TYPEINFO(QBrush, Q_MOVABLE_TYPE);
+Q_DECLARE_SHARED(QBrush)
+
+/*****************************************************************************
+ QBrush stream functions
+ *****************************************************************************/
+
+#ifndef QT_NO_DATASTREAM
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QBrush &);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QBrush &);
+#endif
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QBrush &);
+#endif
+
+struct QBrushData
+{
+ QAtomicInt ref;
+ Qt::BrushStyle style;
+ QColor color;
+ QTransform transform;
+};
+
+inline Qt::BrushStyle QBrush::style() const { return d->style; }
+inline const QColor &QBrush::color() const { return d->color; }
+inline const QMatrix &QBrush::matrix() const { return d->transform.toAffine(); }
+inline QTransform QBrush::transform() const { return d->transform; }
+inline bool QBrush::isDetached() const { return d->ref == 1; }
+
+#ifdef QT3_SUPPORT
+inline QBrush::operator const QColor&() const { return d->color; }
+#endif
+
+
+/*******************************************************************************
+ * QGradients
+ */
+class QGradientPrivate;
+
+typedef QPair<qreal, QColor> QGradientStop;
+typedef QVector<QGradientStop> QGradientStops;
+
+class Q_GUI_EXPORT QGradient
+{
+ Q_GADGET
+ Q_ENUMS(Type Spread CoordinateMode)
+public:
+ enum Type {
+ LinearGradient,
+ RadialGradient,
+ ConicalGradient,
+ NoGradient
+ };
+
+ enum Spread {
+ PadSpread,
+ ReflectSpread,
+ RepeatSpread
+ };
+
+ enum CoordinateMode {
+ LogicalMode,
+ StretchToDeviceMode,
+ ObjectBoundingMode
+ };
+
+ enum InterpolationMode {
+ ColorInterpolation,
+ ComponentInterpolation
+ };
+
+ QGradient();
+
+ Type type() const { return m_type; }
+
+ inline void setSpread(Spread spread);
+ Spread spread() const { return m_spread; }
+
+ void setColorAt(qreal pos, const QColor &color);
+
+ void setStops(const QGradientStops &stops);
+ QGradientStops stops() const;
+
+ CoordinateMode coordinateMode() const;
+ void setCoordinateMode(CoordinateMode mode);
+
+ InterpolationMode interpolationMode() const;
+ void setInterpolationMode(InterpolationMode mode);
+
+ bool operator==(const QGradient &gradient) const;
+ inline bool operator!=(const QGradient &other) const
+ { return !operator==(other); }
+
+ bool operator==(const QGradient &gradient); // ### Qt 5: remove
+
+private:
+ friend class QLinearGradient;
+ friend class QRadialGradient;
+ friend class QConicalGradient;
+
+ Type m_type;
+ Spread m_spread;
+ QGradientStops m_stops;
+ union {
+ struct {
+ qreal x1, y1, x2, y2;
+ } linear;
+ struct {
+ qreal cx, cy, fx, fy, radius;
+ } radial;
+ struct {
+ qreal cx, cy, angle;
+ } conical;
+ } m_data;
+ void *dummy;
+};
+
+inline void QGradient::setSpread(Spread aspread)
+{ m_spread = aspread; }
+
+class Q_GUI_EXPORT QLinearGradient : public QGradient
+{
+public:
+ QLinearGradient();
+ QLinearGradient(const QPointF &start, const QPointF &finalStop);
+ QLinearGradient(qreal xStart, qreal yStart, qreal xFinalStop, qreal yFinalStop);
+
+ QPointF start() const;
+ void setStart(const QPointF &start);
+ inline void setStart(qreal x, qreal y) { setStart(QPointF(x, y)); }
+
+ QPointF finalStop() const;
+ void setFinalStop(const QPointF &stop);
+ inline void setFinalStop(qreal x, qreal y) { setFinalStop(QPointF(x, y)); }
+};
+
+
+class Q_GUI_EXPORT QRadialGradient : public QGradient
+{
+public:
+ QRadialGradient();
+ QRadialGradient(const QPointF &center, qreal radius, const QPointF &focalPoint);
+ QRadialGradient(qreal cx, qreal cy, qreal radius, qreal fx, qreal fy);
+
+ QRadialGradient(const QPointF &center, qreal radius);
+ QRadialGradient(qreal cx, qreal cy, qreal radius);
+
+ QPointF center() const;
+ void setCenter(const QPointF &center);
+ inline void setCenter(qreal x, qreal y) { setCenter(QPointF(x, y)); }
+
+ QPointF focalPoint() const;
+ void setFocalPoint(const QPointF &focalPoint);
+ inline void setFocalPoint(qreal x, qreal y) { setFocalPoint(QPointF(x, y)); }
+
+ qreal radius() const;
+ void setRadius(qreal radius);
+};
+
+
+class Q_GUI_EXPORT QConicalGradient : public QGradient
+{
+public:
+ QConicalGradient();
+ QConicalGradient(const QPointF &center, qreal startAngle);
+ QConicalGradient(qreal cx, qreal cy, qreal startAngle);
+
+ QPointF center() const;
+ void setCenter(const QPointF &center);
+ inline void setCenter(qreal x, qreal y) { setCenter(QPointF(x, y)); }
+
+ qreal angle() const;
+ void setAngle(qreal angle);
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QBRUSH_H
diff --git a/src/gui/painting/qcolor.cpp b/src/gui/painting/qcolor.cpp
new file mode 100644
index 0000000000..ff6c24ee9e
--- /dev/null
+++ b/src/gui/painting/qcolor.cpp
@@ -0,0 +1,2720 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcolor.h"
+#include "qcolor_p.h"
+#include "qnamespace.h"
+#include "qcolormap.h"
+#include "qdatastream.h"
+#include "qvariant.h"
+#include "qdebug.h"
+
+#ifdef Q_WS_X11
+# include "qapplication.h"
+# include "qx11info_x11.h"
+# include "private/qt_x11_p.h"
+
+static bool allowX11ColorNames = false;
+
+#endif
+
+#include <math.h>
+#include <stdio.h>
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QColor
+ \brief The QColor class provides colors based on RGB, HSV or CMYK values.
+
+ \ingroup painting
+ \ingroup appearance
+
+
+ A color is normally specified in terms of RGB (red, green, and
+ blue) components, but it is also possible to specify it in terms
+ of HSV (hue, saturation, and value) and CMYK (cyan, magenta,
+ yellow and black) components. In addition a color can be specified
+ using a color name. The color name can be any of the SVG 1.0 color
+ names.
+
+ \table
+ \header
+ \o RGB \o HSV \o CMYK
+ \row
+ \o \inlineimage qcolor-rgb.png
+ \o \inlineimage qcolor-hsv.png
+ \o \inlineimage qcolor-cmyk.png
+ \endtable
+
+ The QColor constructor creates the color based on RGB values. To
+ create a QColor based on either HSV or CMYK values, use the
+ toHsv() and toCmyk() functions respectively. These functions
+ return a copy of the color using the desired format. In addition
+ the static fromRgb(), fromHsv() and fromCmyk() functions create
+ colors from the specified values. Alternatively, a color can be
+ converted to any of the three formats using the convertTo()
+ function (returning a copy of the color in the desired format), or
+ any of the setRgb(), setHsv() and setCmyk() functions altering \e
+ this color's format. The spec() function tells how the color was
+ specified.
+
+ A color can be set by passing an RGB string (such as "#112233"),
+ or a color name (such as "blue"), to the setNamedColor() function.
+ The color names are taken from the SVG 1.0 color names. The name()
+ function returns the name of the color in the format
+ "#RRGGBB". Colors can also be set using setRgb(), setHsv() and
+ setCmyk(). To get a lighter or darker color use the lighter() and
+ darker() functions respectively.
+
+ The isValid() function indicates whether a QColor is legal at
+ all. For example, a RGB color with RGB values out of range is
+ illegal. For performance reasons, QColor mostly disregards illegal
+ colors, and for that reason, the result of using an invalid color
+ is undefined.
+
+ The color components can be retrieved individually, e.g with
+ red(), hue() and cyan(). The values of the color components can
+ also be retrieved in one go using the getRgb(), getHsv() and
+ getCmyk() functions. Using the RGB color model, the color
+ components can in addition be accessed with rgb().
+
+ There are several related non-members: QRgb is a typdef for an
+ unsigned int representing the RGB value triplet (r, g, b). Note
+ that it also can hold a value for the alpha-channel (for more
+ information, see the \l {QColor#Alpha-Blended
+ Drawing}{Alpha-Blended Drawing} section). The qRed(), qBlue() and
+ qGreen() functions return the respective component of the given
+ QRgb value, while the qRgb() and qRgba() functions create and
+ return the QRgb triplet based on the given component
+ values. Finally, the qAlpha() function returns the alpha component
+ of the provided QRgb, and the qGray() function calculates and
+ return a gray value based on the given value.
+
+ QColor is platform and device independent. The QColormap class
+ maps the color to the hardware.
+
+ For more information about painting in general, see the \l{Paint
+ System} documentation.
+
+ \tableofcontents
+
+ \section1 Integer vs. Floating Point Precision
+
+ QColor supports floating point precision and provides floating
+ point versions of all the color components functions,
+ e.g. getRgbF(), hueF() and fromCmykF(). Note that since the
+ components are stored using 16-bit integers, there might be minor
+ deviations between the values set using, for example, setRgbF()
+ and the values returned by the getRgbF() function due to rounding.
+
+ While the integer based functions take values in the range 0-255
+ (except hue() which must have values within the range 0-359),
+ the floating point functions accept values in the range 0.0 - 1.0.
+
+ \section1 Alpha-Blended Drawing
+
+ QColor also support alpha-blended outlining and filling. The
+ alpha channel of a color specifies the transparency effect, 0
+ represents a fully transparent color, while 255 represents a fully
+ opaque color. For example:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qcolor.cpp 0
+
+ The code above produces the following output:
+
+ \img alphafill.png
+
+ Alpha-blended drawing is supported on Windows, Mac OS X, and on
+ X11 systems that have the X Render extension installed.
+
+ The alpha channel of a color can be retrieved and set using the
+ alpha() and setAlpha() functions if its value is an integer, and
+ alphaF() and setAlphaF() if its value is qreal (double). By
+ default, the alpha-channel is set to 255 (opaque). To retrieve and
+ set \e all the RGB color components (including the alpha-channel)
+ in one go, use the rgba() and setRgba() functions.
+
+ \section1 Predefined Colors
+
+ There are 20 predefined QColors described by the Qt::GlobalColor enum,
+ including black, white, primary and secondary colors, darker versions
+ of these colors and three shades of gray. QColor also recognizes a
+ variety of color names; the static colorNames() function returns a
+ QStringList color names that QColor knows about.
+
+ \img qt-colors.png Qt Colors
+
+ Additionally, the Qt::color0, Qt::color1 and Qt::transparent colors
+ are used for special purposes.
+
+ Qt::color0 (zero pixel value) and Qt::color1 (non-zero pixel value)
+ are special colors for drawing in QBitmaps. Painting with Qt::color0
+ sets the bitmap bits to 0 (transparent; i.e., background), and painting
+ with Qt::color1 sets the bits to 1 (opaque; i.e., foreground).
+
+ Qt::transparent is used to indicate a transparent pixel. When painting
+ with this value, a pixel value will be used that is appropriate for the
+ underlying pixel format in use.
+
+ \section1 The HSV Color Model
+
+ The RGB model is hardware-oriented. Its representation is close to
+ what most monitors show. In contrast, HSV represents color in a way
+ more suited to the human perception of color. For example, the
+ relationships "stronger than", "darker than", and "the opposite of"
+ are easily expressed in HSV but are much harder to express in RGB.
+
+ HSV, like RGB, has three components:
+
+ \list
+ \o H, for hue, is in the range 0 to 359 if the color is chromatic (not
+ gray), or meaningless if it is gray. It represents degrees on the
+ color wheel familiar to most people. Red is 0 (degrees), green is
+ 120, and blue is 240.
+
+ \inlineimage qcolor-hue.png
+
+ \o S, for saturation, is in the range 0 to 255, and the bigger it is,
+ the stronger the color is. Grayish colors have saturation near 0; very
+ strong colors have saturation near 255.
+
+ \inlineimage qcolor-saturation.png
+
+ \o V, for value, is in the range 0 to 255 and represents lightness or
+ brightness of the color. 0 is black; 255 is as far from black as
+ possible.
+
+ \inlineimage qcolor-value.png
+ \endlist
+
+ Here are some examples: pure red is H=0, S=255, V=255; a dark red,
+ moving slightly towards the magenta, could be H=350 (equivalent to
+ -10), S=255, V=180; a grayish light red could have H about 0 (say
+ 350-359 or 0-10), S about 50-100, and S=255.
+
+ Qt returns a hue value of -1 for achromatic colors. If you pass a
+ hue value that is too large, Qt forces it into range. Hue 360 or 720 is
+ treated as 0; hue 540 is treated as 180.
+
+ In addition to the standard HSV model, Qt provides an
+ alpha-channel to feature \l {QColor#Alpha-Blended
+ Drawing}{alpha-blended drawing}.
+
+ \section1 The HSL Color Model
+
+ HSL is similar to HSV. Instead of value parameter from HSV,
+ HSL has the lightness parameter.
+ The lightness parameter goes from black to color and from color to white.
+ If you go outside at the night its black or dark gray. At day its colorful but
+ if you look in a really strong light a things they are going to white and
+ wash out.
+
+ \section1 The CMYK Color Model
+
+ While the RGB and HSV color models are used for display on
+ computer monitors, the CMYK model is used in the four-color
+ printing process of printing presses and some hard-copy
+ devices.
+
+ CMYK has four components, all in the range 0-255: cyan (C),
+ magenta (M), yellow (Y) and black (K). Cyan, magenta and yellow
+ are called subtractive colors; the CMYK color model creates color
+ by starting with a white surface and then subtracting color by
+ applying the appropriate components. While combining cyan, magenta
+ and yellow gives the color black, subtracting one or more will
+ yield any other color. When combined in various percentages, these
+ three colors can create the entire spectrum of colors.
+
+ Mixing 100 percent of cyan, magenta and yellow \e does produce
+ black, but the result is unsatisfactory since it wastes ink,
+ increases drying time, and gives a muddy colour when printing. For
+ that reason, black is added in professional printing to provide a
+ solid black tone; hence the term 'four color process'.
+
+ In addition to the standard CMYK model, Qt provides an
+ alpha-channel to feature \l {QColor#Alpha-Blended
+ Drawing}{alpha-blended drawing}.
+
+ \sa QPalette, QBrush, QApplication::setColorSpec()
+*/
+
+#define QCOLOR_INT_RANGE_CHECK(fn, var) \
+ do { \
+ if (var < 0 || var > 255) { \
+ qWarning(#fn": invalid value %d", var); \
+ var = qMax(0, qMin(var, 255)); \
+ } \
+ } while (0)
+
+#define QCOLOR_REAL_RANGE_CHECK(fn, var) \
+ do { \
+ if (var < qreal(0.0) || var > qreal(1.0)) { \
+ qWarning(#fn": invalid value %g", var); \
+ var = qMax(qreal(0.0), qMin(var, qreal(1.0))); \
+ } \
+ } while (0)
+
+/*****************************************************************************
+ QColor member functions
+ *****************************************************************************/
+
+/*!
+ \enum QColor::Spec
+
+ The type of color specified, either RGB, HSV, CMYK or HSL.
+
+ \value Rgb
+ \value Hsv
+ \value Cmyk
+ \value Hsl
+ \value Invalid
+
+ \sa spec(), convertTo()
+*/
+
+/*!
+ \fn Spec QColor::spec() const
+
+ Returns how the color was specified.
+
+ \sa Spec, convertTo()
+*/
+
+
+/*!
+ \fn QColor::QColor()
+
+ Constructs an invalid color with the RGB value (0, 0, 0). An
+ invalid color is a color that is not properly set up for the
+ underlying window system.
+
+ The alpha value of an invalid color is unspecified.
+
+ \sa isValid()
+*/
+
+/*!
+ \overload
+
+ Constructs a new color with a color value of \a color.
+
+ \sa isValid(), {QColor#Predefined Colors}{Predefined Colors}
+ */
+QColor::QColor(Qt::GlobalColor color)
+{
+#define QRGB(r, g, b) \
+ QRgb(((0xffu << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff)))
+#define QRGBA(r, g, b, a) \
+ QRgb(((a & 0xff) << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff))
+
+ static const QRgb global_colors[] = {
+ QRGB(255, 255, 255), // Qt::color0
+ QRGB( 0, 0, 0), // Qt::color1
+ QRGB( 0, 0, 0), // black
+ QRGB(255, 255, 255), // white
+ /*
+ * From the "The Palette Manager: How and Why" by Ron Gery,
+ * March 23, 1992, archived on MSDN:
+ *
+ * The Windows system palette is broken up into two
+ * sections, one with fixed colors and one with colors
+ * that can be changed by applications. The system palette
+ * predefines 20 entries; these colors are known as the
+ * static or reserved colors and consist of the 16 colors
+ * found in the Windows version 3.0 VGA driver and 4
+ * additional colors chosen for their visual appeal. The
+ * DEFAULT_PALETTE stock object is, as the name implies,
+ * the default palette selected into a device context (DC)
+ * and consists of these static colors. Applications can
+ * set the remaining 236 colors using the Palette Manager.
+ *
+ * The 20 reserved entries have indices in [0,9] and
+ * [246,255]. We reuse 17 of them.
+ */
+ QRGB(128, 128, 128), // index 248 medium gray
+ QRGB(160, 160, 164), // index 247 light gray
+ QRGB(192, 192, 192), // index 7 light gray
+ QRGB(255, 0, 0), // index 249 red
+ QRGB( 0, 255, 0), // index 250 green
+ QRGB( 0, 0, 255), // index 252 blue
+ QRGB( 0, 255, 255), // index 254 cyan
+ QRGB(255, 0, 255), // index 253 magenta
+ QRGB(255, 255, 0), // index 251 yellow
+ QRGB(128, 0, 0), // index 1 dark red
+ QRGB( 0, 128, 0), // index 2 dark green
+ QRGB( 0, 0, 128), // index 4 dark blue
+ QRGB( 0, 128, 128), // index 6 dark cyan
+ QRGB(128, 0, 128), // index 5 dark magenta
+ QRGB(128, 128, 0), // index 3 dark yellow
+ QRGBA(0, 0, 0, 0) // transparent
+ };
+#undef QRGB
+#undef QRGBA
+
+ setRgb(qRed(global_colors[color]),
+ qGreen(global_colors[color]),
+ qBlue(global_colors[color]),
+ qAlpha(global_colors[color]));
+}
+
+/*!
+ \fn QColor::QColor(int r, int g, int b, int a = 255)
+
+ Constructs a color with the RGB value \a r, \a g, \a b, and the
+ alpha-channel (transparency) value of \a a.
+
+ The color is left invalid if any of the arguments are invalid.
+
+ \sa setRgba(), isValid()
+*/
+
+/*!
+ Constructs a color with the value \a color. The alpha component is
+ ignored and set to solid.
+
+ \sa fromRgb(), isValid()
+*/
+
+QColor::QColor(QRgb color)
+{
+ cspec = Rgb;
+ ct.argb.alpha = 0xffff;
+ ct.argb.red = qRed(color) * 0x101;
+ ct.argb.green = qGreen(color) * 0x101;
+ ct.argb.blue = qBlue(color) * 0x101;
+ ct.argb.pad = 0;
+}
+
+
+/*!
+ \internal
+
+ Constructs a color with the given \a spec.
+
+ This function is primarly present to avoid that QColor::Invalid
+ becomes a valid color by accident.
+*/
+
+QColor::QColor(Spec spec)
+{
+ switch (spec) {
+ case Invalid:
+ invalidate();
+ break;
+ case Rgb:
+ setRgb(0, 0, 0);
+ break;
+ case Hsv:
+ setHsv(0, 0, 0);
+ break;
+ case Cmyk:
+ setCmyk(0, 0, 0, 0);
+ break;
+ case Hsl:
+ setHsl(0, 0, 0, 0);
+ break;
+ }
+}
+
+/*!
+ \fn QColor::QColor(const QString &name)
+
+ Constructs a named color in the same way as setNamedColor() using
+ the given \a name.
+
+ The color is left invalid if the \a name cannot be parsed.
+
+ \sa setNamedColor(), name(), isValid()
+*/
+
+/*!
+ \fn QColor::QColor(const char *name)
+
+ Constructs a named color in the same way as setNamedColor() using
+ the given \a name.
+
+ The color is left invalid if the \a name cannot be parsed.
+
+ \sa setNamedColor(), name(), isValid()
+*/
+
+/*!
+ \fn QColor::QColor(const QColor &color)
+
+ Constructs a color that is a copy of \a color.
+
+ \sa isValid()
+*/
+
+/*!
+ \fn bool QColor::isValid() const
+
+ Returns true if the color is valid; otherwise returns false.
+*/
+
+/*!
+ Returns the name of the color in the format "#RRGGBB"; i.e. a "#"
+ character followed by three two-digit hexadecimal numbers.
+
+ \sa setNamedColor()
+*/
+
+QString QColor::name() const
+{
+ QString s;
+ s.sprintf("#%02x%02x%02x", red(), green(), blue());
+ return s;
+}
+
+/*!
+ Sets the RGB value of this QColor to \a name, which may be in one
+ of these formats:
+
+ \list
+ \i #RGB (each of R, G, and B is a single hex digit)
+ \i #RRGGBB
+ \i #RRRGGGBBB
+ \i #RRRRGGGGBBBB
+ \i A name from the list of colors defined in the list of \l{SVG color keyword names}
+ provided by the World Wide Web Consortium; for example, "steelblue" or "gainsboro".
+ These color names work on all platforms. Note that these color names are \e not the
+ same as defined by the Qt::GlobalColor enums, e.g. "green" and Qt::green does not
+ refer to the same color.
+ \i \c transparent - representing the absence of a color.
+ \i \e{X11 only}: If allowX11ColorNames() returns true, any valid X11 color name. See
+ the documentation for \c XParseColor() for information about valid X11 color names.
+ \endlist
+
+ The color is invalid if \a name cannot be parsed.
+
+ \sa QColor(), name(), isValid(), allowX11ColorNames()
+*/
+
+void QColor::setNamedColor(const QString &name)
+{
+ if (!setColorFromString(name))
+ qWarning("QColor::setNamedColor: Unknown color name '%s'", name.toLatin1().constData());
+}
+
+/*!
+ \since 4.7
+
+ Returns true if the \a name is a valid color name and can
+ be used to construct a valid QColor object, otherwise returns
+ false.
+
+ It uses the same algorithm used in setNamedColor().
+
+ \sa setNamedColor()
+*/
+bool QColor::isValidColor(const QString &name)
+{
+ return !name.isEmpty() && QColor().setColorFromString(name);
+}
+
+bool QColor::setColorFromString(const QString &name)
+{
+ if (name.isEmpty()) {
+ invalidate();
+ return true;
+ }
+
+ if (name.startsWith(QLatin1Char('#'))) {
+ QRgb rgb;
+ if (qt_get_hex_rgb(name.constData(), name.length(), &rgb)) {
+ setRgb(rgb);
+ return true;
+ } else {
+ invalidate();
+ return false;
+ }
+ }
+
+#ifndef QT_NO_COLORNAMES
+ QRgb rgb;
+ if (qt_get_named_rgb(name.constData(), name.length(), &rgb)) {
+ setRgba(rgb);
+ return true;
+ } else
+#endif
+ {
+#ifdef Q_WS_X11
+ XColor result;
+ if (allowX11ColorNames()
+ && QApplication::instance()
+ && QX11Info::display()
+ && XParseColor(QX11Info::display(), QX11Info::appColormap(), name.toLatin1().constData(), &result)) {
+ setRgb(result.red >> 8, result.green >> 8, result.blue >> 8);
+ return true;
+ } else
+#endif
+ {
+ invalidate();
+ return false;
+ }
+ }
+}
+
+/*!
+ Returns a QStringList containing the color names Qt knows about.
+
+ \sa {QColor#Predefined Colors}{Predefined Colors}
+*/
+QStringList QColor::colorNames()
+{
+#ifndef QT_NO_COLORNAMES
+ return qt_get_colornames();
+#else
+ return QStringList();
+#endif
+}
+
+/*!
+ Sets the contents pointed to by \a h, \a s, \a v, and \a a, to the hue,
+ saturation, value, and alpha-channel (transparency) components of the
+ color's HSV value.
+
+ These components can be retrieved individually using the hueF(),
+ saturationF(), valueF() and alphaF() functions.
+
+ \sa setHsv() {QColor#The HSV Color Model}{The HSV Color Model}
+*/
+void QColor::getHsvF(qreal *h, qreal *s, qreal *v, qreal *a) const
+{
+ if (!h || !s || !v)
+ return;
+
+ if (cspec != Invalid && cspec != Hsv) {
+ toHsv().getHsvF(h, s, v, a);
+ return;
+ }
+
+ *h = ct.ahsv.hue == USHRT_MAX ? qreal(-1.0) : ct.ahsv.hue / qreal(36000.0);
+ *s = ct.ahsv.saturation / qreal(USHRT_MAX);
+ *v = ct.ahsv.value / qreal(USHRT_MAX);
+
+ if (a)
+ *a = ct.ahsv.alpha / qreal(USHRT_MAX);
+}
+
+/*!
+ Sets the contents pointed to by \a h, \a s, \a v, and \a a, to the hue,
+ saturation, value, and alpha-channel (transparency) components of the
+ color's HSV value.
+
+ These components can be retrieved individually using the hue(),
+ saturation(), value() and alpha() functions.
+
+ \sa setHsv(), {QColor#The HSV Color Model}{The HSV Color Model}
+*/
+void QColor::getHsv(int *h, int *s, int *v, int *a) const
+{
+ if (!h || !s || !v)
+ return;
+
+ if (cspec != Invalid && cspec != Hsv) {
+ toHsv().getHsv(h, s, v, a);
+ return;
+ }
+
+ *h = ct.ahsv.hue == USHRT_MAX ? -1 : ct.ahsv.hue / 100;
+ *s = ct.ahsv.saturation >> 8;
+ *v = ct.ahsv.value >> 8;
+
+ if (a)
+ *a = ct.ahsv.alpha >> 8;
+}
+
+/*!
+ Sets a HSV color value; \a h is the hue, \a s is the saturation, \a v is
+ the value and \a a is the alpha component of the HSV color.
+
+ All the values must be in the range 0.0-1.0.
+
+ \sa getHsvF(), setHsv(), {QColor#The HSV Color Model}{The HSV
+ Color Model}
+*/
+void QColor::setHsvF(qreal h, qreal s, qreal v, qreal a)
+{
+ if (((h < qreal(0.0) || h > qreal(1.0)) && h != qreal(-1.0))
+ || (s < qreal(0.0) || s > qreal(1.0))
+ || (v < qreal(0.0) || v > qreal(1.0))
+ || (a < qreal(0.0) || a > qreal(1.0))) {
+ qWarning("QColor::setHsvF: HSV parameters out of range");
+ return;
+ }
+
+ cspec = Hsv;
+ ct.ahsv.alpha = qRound(a * USHRT_MAX);
+ ct.ahsv.hue = h == qreal(-1.0) ? USHRT_MAX : qRound(h * 36000);
+ ct.ahsv.saturation = qRound(s * USHRT_MAX);
+ ct.ahsv.value = qRound(v * USHRT_MAX);
+ ct.ahsv.pad = 0;
+}
+
+/*!
+ Sets a HSV color value; \a h is the hue, \a s is the saturation, \a v is
+ the value and \a a is the alpha component of the HSV color.
+
+ The saturation, value and alpha-channel values must be in the range 0-255,
+ and the hue value must be greater than -1.
+
+ \sa getHsv(), setHsvF(), {QColor#The HSV Color Model}{The HSV
+ Color Model}
+*/
+void QColor::setHsv(int h, int s, int v, int a)
+{
+ if (h < -1 || (uint)s > 255 || (uint)v > 255 || (uint)a > 255) {
+ qWarning("QColor::setHsv: HSV parameters out of range");
+ invalidate();
+ return;
+ }
+
+ cspec = Hsv;
+ ct.ahsv.alpha = a * 0x101;
+ ct.ahsv.hue = h == -1 ? USHRT_MAX : (h % 360) * 100;
+ ct.ahsv.saturation = s * 0x101;
+ ct.ahsv.value = v * 0x101;
+ ct.ahsv.pad = 0;
+}
+
+/*!
+ \since 4.6
+
+ Sets the contents pointed to by \a h, \a s, \a l, and \a a, to the hue,
+ saturation, lightness, and alpha-channel (transparency) components of the
+ color's HSL value.
+
+ These components can be retrieved individually using the hueHslF(),
+ saturationHslF(), lightnessF() and alphaF() functions.
+
+ \sa setHsl()
+*/
+void QColor::getHslF(qreal *h, qreal *s, qreal *l, qreal *a) const
+{
+ if (!h || !s || !l)
+ return;
+
+ if (cspec != Invalid && cspec != Hsl) {
+ toHsl().getHslF(h, s, l, a);
+ return;
+ }
+
+ *h = ct.ahsl.hue == USHRT_MAX ? qreal(-1.0) : ct.ahsl.hue / qreal(36000.0);
+ *s = ct.ahsl.saturation / qreal(USHRT_MAX);
+ *l = ct.ahsl.lightness / qreal(USHRT_MAX);
+
+ if (a)
+ *a = ct.ahsl.alpha / qreal(USHRT_MAX);
+}
+
+/*!
+ \since 4.6
+
+ Sets the contents pointed to by \a h, \a s, \a l, and \a a, to the hue,
+ saturation, lightness, and alpha-channel (transparency) components of the
+ color's HSL value.
+
+ These components can be retrieved individually using the hueHsl(),
+ saturationHsl(), lightness() and alpha() functions.
+
+ \sa setHsl()
+*/
+void QColor::getHsl(int *h, int *s, int *l, int *a) const
+{
+ if (!h || !s || !l)
+ return;
+
+ if (cspec != Invalid && cspec != Hsl) {
+ toHsl().getHsl(h, s, l, a);
+ return;
+ }
+
+ *h = ct.ahsl.hue == USHRT_MAX ? -1 : ct.ahsl.hue / 100;
+ *s = ct.ahsl.saturation >> 8;
+ *l = ct.ahsl.lightness >> 8;
+
+ if (a)
+ *a = ct.ahsl.alpha >> 8;
+}
+
+/*!
+ \since 4.6
+
+ Sets a HSL color lightness; \a h is the hue, \a s is the saturation, \a l is
+ the lightness and \a a is the alpha component of the HSL color.
+
+ All the values must be in the range 0.0-1.0.
+
+ \sa getHslF(), setHsl()
+*/
+void QColor::setHslF(qreal h, qreal s, qreal l, qreal a)
+{
+ if (((h < qreal(0.0) || h > qreal(1.0)) && h != qreal(-1.0))
+ || (s < qreal(0.0) || s > qreal(1.0))
+ || (l < qreal(0.0) || l > qreal(1.0))
+ || (a < qreal(0.0) || a > qreal(1.0))) {
+ qWarning("QColor::setHsvF: HSV parameters out of range");
+ return;
+ }
+
+ cspec = Hsl;
+ ct.ahsl.alpha = qRound(a * USHRT_MAX);
+ ct.ahsl.hue = h == qreal(-1.0) ? USHRT_MAX : qRound(h * 36000);
+ ct.ahsl.saturation = qRound(s * USHRT_MAX);
+ ct.ahsl.lightness = qRound(l * USHRT_MAX);
+ ct.ahsl.pad = 0;
+}
+
+/*!
+ \since 4.6
+
+ Sets a HSL color value; \a h is the hue, \a s is the saturation, \a l is
+ the lightness and \a a is the alpha component of the HSL color.
+
+ The saturation, value and alpha-channel values must be in the range 0-255,
+ and the hue value must be greater than -1.
+
+ \sa getHsl(), setHslF()
+*/
+void QColor::setHsl(int h, int s, int l, int a)
+{
+ if (h < -1 || (uint)s > 255 || (uint)l > 255 || (uint)a > 255) {
+ qWarning("QColor::setHsv: HSV parameters out of range");
+ invalidate();
+ return;
+ }
+
+ cspec = Hsl;
+ ct.ahsl.alpha = a * 0x101;
+ ct.ahsl.hue = h == -1 ? USHRT_MAX : (h % 360) * 100;
+ ct.ahsl.saturation = s * 0x101;
+ ct.ahsl.lightness = l * 0x101;
+ ct.ahsl.pad = 0;
+}
+
+/*!
+ Sets the contents pointed to by \a r, \a g, \a b, and \a a, to the red,
+ green, blue, and alpha-channel (transparency) components of the color's
+ RGB value.
+
+ These components can be retrieved individually using the redF(), greenF(),
+ blueF() and alphaF() functions.
+
+ \sa rgb(), setRgb()
+*/
+void QColor::getRgbF(qreal *r, qreal *g, qreal *b, qreal *a) const
+{
+ if (!r || !g || !b)
+ return;
+
+ if (cspec != Invalid && cspec != Rgb) {
+ toRgb().getRgbF(r, g, b, a);
+ return;
+ }
+
+ *r = ct.argb.red / qreal(USHRT_MAX);
+ *g = ct.argb.green / qreal(USHRT_MAX);
+ *b = ct.argb.blue / qreal(USHRT_MAX);
+
+ if (a)
+ *a = ct.argb.alpha / qreal(USHRT_MAX);
+
+}
+
+/*!
+ Sets the contents pointed to by \a r, \a g, \a b, and \a a, to the red,
+ green, blue, and alpha-channel (transparency) components of the color's
+ RGB value.
+
+ These components can be retrieved individually using the red(), green(),
+ blue() and alpha() functions.
+
+ \sa rgb(), setRgb()
+*/
+void QColor::getRgb(int *r, int *g, int *b, int *a) const
+{
+ if (!r || !g || !b)
+ return;
+
+ if (cspec != Invalid && cspec != Rgb) {
+ toRgb().getRgb(r, g, b, a);
+ return;
+ }
+
+ *r = ct.argb.red >> 8;
+ *g = ct.argb.green >> 8;
+ *b = ct.argb.blue >> 8;
+
+ if (a)
+ *a = ct.argb.alpha >> 8;
+}
+
+/*!
+ \obsolete
+ \fn void QColor::getRgba(int *r, int *g, int *b, int *a) const
+
+ Use getRgb() instead.
+*/
+
+/*!
+ \fn void QColor::setRgbF(qreal r, qreal g, qreal b, qreal a)
+
+ Sets the color channels of this color to \a r (red), \a g (green),
+ \a b (blue) and \a a (alpha, transparency).
+
+ All values must be in the range 0.0-1.0.
+
+ \sa rgb(), getRgbF(), setRgb()
+*/
+void QColor::setRgbF(qreal r, qreal g, qreal b, qreal a)
+{
+ if (r < qreal(0.0) || r > qreal(1.0)
+ || g < qreal(0.0) || g > qreal(1.0)
+ || b < qreal(0.0) || b > qreal(1.0)
+ || a < qreal(0.0) || a > qreal(1.0)) {
+ qWarning("QColor::setRgbF: RGB parameters out of range");
+ invalidate();
+ return;
+ }
+
+ cspec = Rgb;
+ ct.argb.alpha = qRound(a * USHRT_MAX);
+ ct.argb.red = qRound(r * USHRT_MAX);
+ ct.argb.green = qRound(g * USHRT_MAX);
+ ct.argb.blue = qRound(b * USHRT_MAX);
+ ct.argb.pad = 0;
+}
+
+/*!
+ Sets the RGB value to \a r, \a g, \a b and the alpha value to \a a.
+
+ All the values must be in the range 0-255.
+
+ \sa rgb(), getRgb(), setRgbF()
+*/
+void QColor::setRgb(int r, int g, int b, int a)
+{
+ if ((uint)r > 255 || (uint)g > 255 || (uint)b > 255 || (uint)a > 255) {
+ qWarning("QColor::setRgb: RGB parameters out of range");
+ invalidate();
+ return;
+ }
+
+ cspec = Rgb;
+ ct.argb.alpha = a * 0x101;
+ ct.argb.red = r * 0x101;
+ ct.argb.green = g * 0x101;
+ ct.argb.blue = b * 0x101;
+ ct.argb.pad = 0;
+}
+
+/*!
+ \obsolete
+ \fn void QColor::setRgba(int r, int g, int b, int a)
+
+ Use setRgb() instead.
+*/
+
+/*!
+ \fn QRgb QColor::rgba() const
+
+ Returns the RGB value of the color, including its alpha.
+
+ For an invalid color, the alpha value of the returned color is unspecified.
+
+ \sa setRgba(), rgb()
+*/
+
+QRgb QColor::rgba() const
+{
+ if (cspec != Invalid && cspec != Rgb)
+ return toRgb().rgba();
+ return qRgba(ct.argb.red >> 8, ct.argb.green >> 8, ct.argb.blue >> 8, ct.argb.alpha >> 8);
+}
+
+/*!
+ Sets the RGB value to \a rgba, including its alpha.
+
+ \sa rgba(), rgb()
+*/
+void QColor::setRgba(QRgb rgba)
+{
+ cspec = Rgb;
+ ct.argb.alpha = qAlpha(rgba) * 0x101;
+ ct.argb.red = qRed(rgba) * 0x101;
+ ct.argb.green = qGreen(rgba) * 0x101;
+ ct.argb.blue = qBlue(rgba) * 0x101;
+ ct.argb.pad = 0;
+}
+
+/*!
+ \fn QRgb QColor::rgb() const
+
+ Returns the RGB value of the color. The alpha value is opaque.
+
+ \sa getRgb(), rgba()
+*/
+QRgb QColor::rgb() const
+{
+ if (cspec != Invalid && cspec != Rgb)
+ return toRgb().rgb();
+ return qRgb(ct.argb.red >> 8, ct.argb.green >> 8, ct.argb.blue >> 8);
+}
+
+/*!
+ \overload
+
+ Sets the RGB value to \a rgb. The alpha value is set to opaque.
+*/
+void QColor::setRgb(QRgb rgb)
+{
+ cspec = Rgb;
+ ct.argb.alpha = 0xffff;
+ ct.argb.red = qRed(rgb) * 0x101;
+ ct.argb.green = qGreen(rgb) * 0x101;
+ ct.argb.blue = qBlue(rgb) * 0x101;
+ ct.argb.pad = 0;
+}
+
+/*!
+ Returns the alpha color component of this color.
+
+ \sa setAlpha(), alphaF(), {QColor#Alpha-Blended
+ Drawing}{Alpha-Blended Drawing}
+*/
+int QColor::alpha() const
+{ return ct.argb.alpha >> 8; }
+
+
+/*!
+ Sets the alpha of this color to \a alpha. Integer alpha is specified in the
+ range 0-255.
+
+ \sa alpha(), alphaF(), {QColor#Alpha-Blended
+ Drawing}{Alpha-Blended Drawing}
+*/
+
+void QColor::setAlpha(int alpha)
+{
+ QCOLOR_INT_RANGE_CHECK("QColor::setAlpha", alpha);
+ ct.argb.alpha = alpha * 0x101;
+}
+
+/*!
+ Returns the alpha color component of this color.
+
+ \sa setAlphaF(), alpha(), {QColor#Alpha-Blended
+ Drawing}{Alpha-Blended Drawing}
+*/
+qreal QColor::alphaF() const
+{ return ct.argb.alpha / qreal(USHRT_MAX); }
+
+/*!
+ Sets the alpha of this color to \a alpha. qreal alpha is specified in the
+ range 0.0-1.0.
+
+ \sa alphaF(), alpha(), {QColor#Alpha-Blended
+ Drawing}{Alpha-Blended Drawing}
+
+*/
+void QColor::setAlphaF(qreal alpha)
+{
+ QCOLOR_REAL_RANGE_CHECK("QColor::setAlphaF", alpha);
+ qreal tmp = alpha * USHRT_MAX;
+ ct.argb.alpha = qRound(tmp);
+}
+
+
+/*!
+ Returns the red color component of this color.
+
+ \sa setRed(), redF(), getRgb()
+*/
+int QColor::red() const
+{
+ if (cspec != Invalid && cspec != Rgb)
+ return toRgb().red();
+ return ct.argb.red >> 8;
+}
+
+/*!
+ Sets the red color component of this color to \a red. Integer components
+ are specified in the range 0-255.
+
+ \sa red(), redF(), setRgb()
+*/
+void QColor::setRed(int red)
+{
+ QCOLOR_INT_RANGE_CHECK("QColor::setRed", red);
+ if (cspec != Rgb)
+ setRgb(red, green(), blue(), alpha());
+ else
+ ct.argb.red = red * 0x101;
+}
+
+/*!
+ Returns the green color component of this color.
+
+ \sa setGreen(), greenF(), getRgb()
+*/
+int QColor::green() const
+{
+ if (cspec != Invalid && cspec != Rgb)
+ return toRgb().green();
+ return ct.argb.green >> 8;
+}
+
+/*!
+ Sets the green color component of this color to \a green. Integer
+ components are specified in the range 0-255.
+
+ \sa green(), greenF(), setRgb()
+*/
+void QColor::setGreen(int green)
+{
+ QCOLOR_INT_RANGE_CHECK("QColor::setGreen", green);
+ if (cspec != Rgb)
+ setRgb(red(), green, blue(), alpha());
+ else
+ ct.argb.green = green * 0x101;
+}
+
+
+/*!
+ Returns the blue color component of this color.
+
+ \sa setBlue(), blueF(), getRgb()
+*/
+int QColor::blue() const
+{
+ if (cspec != Invalid && cspec != Rgb)
+ return toRgb().blue();
+ return ct.argb.blue >> 8;
+}
+
+
+/*!
+ Sets the blue color component of this color to \a blue. Integer components
+ are specified in the range 0-255.
+
+ \sa blue(), blueF(), setRgb()
+*/
+void QColor::setBlue(int blue)
+{
+ QCOLOR_INT_RANGE_CHECK("QColor::setBlue", blue);
+ if (cspec != Rgb)
+ setRgb(red(), green(), blue, alpha());
+ else
+ ct.argb.blue = blue * 0x101;
+}
+
+/*!
+ Returns the red color component of this color.
+
+ \sa setRedF(), red(), getRgbF()
+*/
+qreal QColor::redF() const
+{
+ if (cspec != Invalid && cspec != Rgb)
+ return toRgb().redF();
+ return ct.argb.red / qreal(USHRT_MAX);
+}
+
+
+/*!
+ Sets the red color component of this color to \a red. Float components
+ are specified in the range 0.0-1.0.
+
+ \sa redF(), red(), setRgbF()
+*/
+void QColor::setRedF(qreal red)
+{
+ QCOLOR_REAL_RANGE_CHECK("QColor::setRedF", red);
+ if (cspec != Rgb)
+ setRgbF(red, greenF(), blueF(), alphaF());
+ else
+ ct.argb.red = qRound(red * USHRT_MAX);
+}
+
+/*!
+ Returns the green color component of this color.
+
+ \sa setGreenF(), green(), getRgbF()
+*/
+qreal QColor::greenF() const
+{
+ if (cspec != Invalid && cspec != Rgb)
+ return toRgb().greenF();
+ return ct.argb.green / qreal(USHRT_MAX);
+}
+
+
+/*!
+ Sets the green color component of this color to \a green. Float components
+ are specified in the range 0.0-1.0.
+
+ \sa greenF(), green(), setRgbF()
+*/
+void QColor::setGreenF(qreal green)
+{
+ QCOLOR_REAL_RANGE_CHECK("QColor::setGreenF", green);
+ if (cspec != Rgb)
+ setRgbF(redF(), green, blueF(), alphaF());
+ else
+ ct.argb.green = qRound(green * USHRT_MAX);
+}
+
+/*!
+ Returns the blue color component of this color.
+
+ \sa setBlueF(), blue(), getRgbF()
+*/
+qreal QColor::blueF() const
+{
+ if (cspec != Invalid && cspec != Rgb)
+ return toRgb().blueF();
+ return ct.argb.blue / qreal(USHRT_MAX);
+}
+
+/*!
+ Sets the blue color component of this color to \a blue. Float components
+ are specified in the range 0.0-1.0.
+
+ \sa blueF(), blue(), setRgbF()
+*/
+void QColor::setBlueF(qreal blue)
+{
+ QCOLOR_REAL_RANGE_CHECK("QColor::setBlueF", blue);
+ if (cspec != Rgb)
+ setRgbF(redF(), greenF(), blue, alphaF());
+ else
+ ct.argb.blue = qRound(blue * USHRT_MAX);
+}
+
+/*!
+ Returns the hue color component of this color.
+
+ The color is implicitly converted to HSV.
+
+ \sa hsvHue(), hueF(), getHsv(), {QColor#The HSV Color Model}{The HSV Color
+ Model}
+*/
+
+int QColor::hue() const
+{
+ return hsvHue();
+}
+
+/*!
+ Returns the hue color component of this color.
+
+ \sa hueF(), getHsv(), {QColor#The HSV Color Model}{The HSV Color
+ Model}
+*/
+int QColor::hsvHue() const
+{
+ if (cspec != Invalid && cspec != Hsv)
+ return toHsv().hue();
+ return ct.ahsv.hue == USHRT_MAX ? -1 : ct.ahsv.hue / 100;
+}
+
+/*!
+ Returns the saturation color component of this color.
+
+ The color is implicitly converted to HSV.
+
+ \sa hsvSaturation(), saturationF(), getHsv(), {QColor#The HSV Color Model}{The HSV Color
+ Model}
+*/
+
+int QColor::saturation() const
+{
+ return hsvSaturation();
+}
+
+/*!
+ Returns the saturation color component of this color.
+
+ \sa saturationF(), getHsv(), {QColor#The HSV Color Model}{The HSV Color
+ Model}
+*/
+int QColor::hsvSaturation() const
+{
+ if (cspec != Invalid && cspec != Hsv)
+ return toHsv().saturation();
+ return ct.ahsv.saturation >> 8;
+}
+
+/*!
+ Returns the value color component of this color.
+
+ \sa valueF(), getHsv(), {QColor#The HSV Color Model}{The HSV Color
+ Model}
+*/
+int QColor::value() const
+{
+ if (cspec != Invalid && cspec != Hsv)
+ return toHsv().value();
+ return ct.ahsv.value >> 8;
+}
+
+/*!
+ Returns the hue color component of this color.
+
+ The color is implicitly converted to HSV.
+
+ \sa hsvHueF(), hue(), getHsvF(), {QColor#The HSV Color Model}{The HSV Color
+ Model}
+*/
+qreal QColor::hueF() const
+{
+ return hsvHueF();
+}
+
+/*!
+ Returns the hue color component of this color.
+
+ \sa hue(), getHsvF(), {QColor#The HSV Color Model}{The HSV Color
+ Model}
+*/
+qreal QColor::hsvHueF() const
+{
+ if (cspec != Invalid && cspec != Hsv)
+ return toHsv().hueF();
+ return ct.ahsv.hue == USHRT_MAX ? qreal(-1.0) : ct.ahsv.hue / qreal(36000.0);
+}
+
+/*!
+ Returns the saturation color component of this color.
+
+ The color is implicitly converted to HSV.
+
+ \sa hsvSaturationF(), saturation() getHsvF(), {QColor#The HSV Color Model}{The HSV Color
+ Model}
+*/
+qreal QColor::saturationF() const
+{
+ return hsvSaturationF();
+}
+
+/*!
+ Returns the saturation color component of this color.
+
+ \sa saturation() getHsvF(), {QColor#The HSV Color Model}{The HSV Color
+ Model}
+*/
+qreal QColor::hsvSaturationF() const
+{
+ if (cspec != Invalid && cspec != Hsv)
+ return toHsv().saturationF();
+ return ct.ahsv.saturation / qreal(USHRT_MAX);
+}
+
+/*!
+ Returns the value color component of this color.
+
+ \sa value() getHsvF(), {QColor#The HSV Color Model}{The HSV Color
+ Model}
+*/
+qreal QColor::valueF() const
+{
+ if (cspec != Invalid && cspec != Hsv)
+ return toHsv().valueF();
+ return ct.ahsv.value / qreal(USHRT_MAX);
+}
+
+/*!
+ \since 4.6
+
+ Returns the hue color component of this color.
+
+ \sa getHslF(), getHsl()
+*/
+int QColor::hslHue() const
+{
+ if (cspec != Invalid && cspec != Hsl)
+ return toHsl().hslHue();
+ return ct.ahsl.hue == USHRT_MAX ? -1 : ct.ahsl.hue / 100;
+}
+
+/*!
+ \since 4.6
+
+ Returns the saturation color component of this color.
+
+ \sa saturationF(), getHsv(), {QColor#The HSV Color Model}{The HSV Color
+ Model}
+*/
+int QColor::hslSaturation() const
+{
+ if (cspec != Invalid && cspec != Hsl)
+ return toHsl().hslSaturation();
+ return ct.ahsl.saturation >> 8;
+}
+
+/*!
+ \since 4.6
+
+ Returns the lightness color component of this color.
+
+ \sa lightnessF(), getHsl()
+*/
+int QColor::lightness() const
+{
+ if (cspec != Invalid && cspec != Hsl)
+ return toHsl().lightness();
+ return ct.ahsl.lightness >> 8;
+}
+
+/*!
+ \since 4.6
+
+ Returns the hue color component of this color.
+
+ \sa hue(), getHslF()
+*/
+qreal QColor::hslHueF() const
+{
+ if (cspec != Invalid && cspec != Hsl)
+ return toHsl().hslHueF();
+ return ct.ahsl.hue == USHRT_MAX ? qreal(-1.0) : ct.ahsl.hue / qreal(36000.0);
+}
+
+/*!
+ \since 4.6
+
+ Returns the saturation color component of this color.
+
+ \sa saturationF() getHslF()
+*/
+qreal QColor::hslSaturationF() const
+{
+ if (cspec != Invalid && cspec != Hsl)
+ return toHsl().hslSaturationF();
+ return ct.ahsl.saturation / qreal(USHRT_MAX);
+}
+
+/*!
+ \since 4.6
+
+ Returns the lightness color component of this color.
+
+ \sa value() getHslF()
+*/
+qreal QColor::lightnessF() const
+{
+ if (cspec != Invalid && cspec != Hsl)
+ return toHsl().lightnessF();
+ return ct.ahsl.lightness / qreal(USHRT_MAX);
+}
+
+/*!
+ Returns the cyan color component of this color.
+
+ \sa cyanF(), getCmyk(), {QColor#The CMYK Color Model}{The CMYK
+ Color Model}
+*/
+int QColor::cyan() const
+{
+ if (cspec != Invalid && cspec != Cmyk)
+ return toCmyk().cyan();
+ return ct.acmyk.cyan >> 8;
+}
+
+/*!
+ Returns the magenta color component of this color.
+
+ \sa magentaF(), getCmyk(), {QColor#The CMYK Color Model}{The CMYK
+ Color Model}
+*/
+int QColor::magenta() const
+{
+ if (cspec != Invalid && cspec != Cmyk)
+ return toCmyk().magenta();
+ return ct.acmyk.magenta >> 8;
+}
+
+/*!
+ Returns the yellow color component of this color.
+
+ \sa yellowF(), getCmyk(), {QColor#The CMYK Color Model}{The CMYK
+ Color Model}
+*/
+int QColor::yellow() const
+{
+ if (cspec != Invalid && cspec != Cmyk)
+ return toCmyk().yellow();
+ return ct.acmyk.yellow >> 8;
+}
+
+/*!
+ Returns the black color component of this color.
+
+ \sa blackF(), getCmyk(), {QColor#The CMYK Color Model}{The CMYK
+ Color Model}
+
+*/
+int QColor::black() const
+{
+ if (cspec != Invalid && cspec != Cmyk)
+ return toCmyk().black();
+ return ct.acmyk.black >> 8;
+}
+
+/*!
+ Returns the cyan color component of this color.
+
+ \sa cyan(), getCmykF(), {QColor#The CMYK Color Model}{The CMYK
+ Color Model}
+*/
+qreal QColor::cyanF() const
+{
+ if (cspec != Invalid && cspec != Cmyk)
+ return toCmyk().cyanF();
+ return ct.acmyk.cyan / qreal(USHRT_MAX);
+}
+
+/*!
+ Returns the magenta color component of this color.
+
+ \sa magenta(), getCmykF(), {QColor#The CMYK Color Model}{The CMYK
+ Color Model}
+*/
+qreal QColor::magentaF() const
+{
+ if (cspec != Invalid && cspec != Cmyk)
+ return toCmyk().magentaF();
+ return ct.acmyk.magenta / qreal(USHRT_MAX);
+}
+
+/*!
+ Returns the yellow color component of this color.
+
+ \sa yellow(), getCmykF(), {QColor#The CMYK Color Model}{The CMYK
+ Color Model}
+*/
+qreal QColor::yellowF() const
+{
+ if (cspec != Invalid && cspec != Cmyk)
+ return toCmyk().yellowF();
+ return ct.acmyk.yellow / qreal(USHRT_MAX);
+}
+
+/*!
+ Returns the black color component of this color.
+
+ \sa black(), getCmykF(), {QColor#The CMYK Color Model}{The CMYK
+ Color Model}
+*/
+qreal QColor::blackF() const
+{
+ if (cspec != Invalid && cspec != Cmyk)
+ return toCmyk().blackF();
+ return ct.acmyk.black / qreal(USHRT_MAX);
+}
+
+/*!
+ Create and returns an RGB QColor based on this color.
+
+ \sa fromRgb(), convertTo(), isValid()
+*/
+QColor QColor::toRgb() const
+{
+ if (!isValid() || cspec == Rgb)
+ return *this;
+
+ QColor color;
+ color.cspec = Rgb;
+ color.ct.argb.alpha = ct.argb.alpha;
+ color.ct.argb.pad = 0;
+
+ switch (cspec) {
+ case Hsv:
+ {
+ if (ct.ahsv.saturation == 0 || ct.ahsv.hue == USHRT_MAX) {
+ // achromatic case
+ color.ct.argb.red = color.ct.argb.green = color.ct.argb.blue = ct.ahsv.value;
+ break;
+ }
+
+ // chromatic case
+ const qreal h = ct.ahsv.hue == 36000 ? 0 : ct.ahsv.hue / 6000.;
+ const qreal s = ct.ahsv.saturation / qreal(USHRT_MAX);
+ const qreal v = ct.ahsv.value / qreal(USHRT_MAX);
+ const int i = int(h);
+ const qreal f = h - i;
+ const qreal p = v * (qreal(1.0) - s);
+
+ if (i & 1) {
+ const qreal q = v * (qreal(1.0) - (s * f));
+
+ switch (i) {
+ case 1:
+ color.ct.argb.red = qRound(q * USHRT_MAX);
+ color.ct.argb.green = qRound(v * USHRT_MAX);
+ color.ct.argb.blue = qRound(p * USHRT_MAX);
+ break;
+ case 3:
+ color.ct.argb.red = qRound(p * USHRT_MAX);
+ color.ct.argb.green = qRound(q * USHRT_MAX);
+ color.ct.argb.blue = qRound(v * USHRT_MAX);
+ break;
+ case 5:
+ color.ct.argb.red = qRound(v * USHRT_MAX);
+ color.ct.argb.green = qRound(p * USHRT_MAX);
+ color.ct.argb.blue = qRound(q * USHRT_MAX);
+ break;
+ }
+ } else {
+ const qreal t = v * (qreal(1.0) - (s * (qreal(1.0) - f)));
+
+ switch (i) {
+ case 0:
+ color.ct.argb.red = qRound(v * USHRT_MAX);
+ color.ct.argb.green = qRound(t * USHRT_MAX);
+ color.ct.argb.blue = qRound(p * USHRT_MAX);
+ break;
+ case 2:
+ color.ct.argb.red = qRound(p * USHRT_MAX);
+ color.ct.argb.green = qRound(v * USHRT_MAX);
+ color.ct.argb.blue = qRound(t * USHRT_MAX);
+ break;
+ case 4:
+ color.ct.argb.red = qRound(t * USHRT_MAX);
+ color.ct.argb.green = qRound(p * USHRT_MAX);
+ color.ct.argb.blue = qRound(v * USHRT_MAX);
+ break;
+ }
+ }
+ break;
+ }
+ case Hsl:
+ {
+ if (ct.ahsl.saturation == 0 || ct.ahsl.hue == USHRT_MAX) {
+ // achromatic case
+ color.ct.argb.red = color.ct.argb.green = color.ct.argb.blue = ct.ahsl.lightness;
+ } else if (ct.ahsl.lightness == 0) {
+ // lightness 0
+ color.ct.argb.red = color.ct.argb.green = color.ct.argb.blue = 0;
+ } else {
+ // chromatic case
+ const qreal h = ct.ahsl.hue == 36000 ? 0 : ct.ahsl.hue / 36000.;
+ const qreal s = ct.ahsl.saturation / qreal(USHRT_MAX);
+ const qreal l = ct.ahsl.lightness / qreal(USHRT_MAX);
+
+ qreal temp2;
+ if (l < qreal(0.5))
+ temp2 = l * (qreal(1.0) + s);
+ else
+ temp2 = l + s - (l * s);
+
+ const qreal temp1 = (qreal(2.0) * l) - temp2;
+ qreal temp3[3] = { h + (qreal(1.0) / qreal(3.0)),
+ h,
+ h - (qreal(1.0) / qreal(3.0)) };
+
+ for (int i = 0; i != 3; ++i) {
+ if (temp3[i] < qreal(0.0))
+ temp3[i] += qreal(1.0);
+ else if (temp3[i] > qreal(1.0))
+ temp3[i] -= qreal(1.0);
+
+ const qreal sixtemp3 = temp3[i] * qreal(6.0);
+ if (sixtemp3 < qreal(1.0))
+ color.ct.array[i+1] = qRound((temp1 + (temp2 - temp1) * sixtemp3) * USHRT_MAX);
+ else if ((temp3[i] * qreal(2.0)) < qreal(1.0))
+ color.ct.array[i+1] = qRound(temp2 * USHRT_MAX);
+ else if ((temp3[i] * qreal(3.0)) < qreal(2.0))
+ color.ct.array[i+1] = qRound((temp1 + (temp2 -temp1) * (qreal(2.0) /qreal(3.0) - temp3[i]) * qreal(6.0)) * USHRT_MAX);
+ else
+ color.ct.array[i+1] = qRound(temp1 * USHRT_MAX);
+ }
+ color.ct.argb.red = color.ct.argb.red == 1 ? 0 : color.ct.argb.red;
+ color.ct.argb.green = color.ct.argb.green == 1 ? 0 : color.ct.argb.green;
+ color.ct.argb.blue = color.ct.argb.blue == 1 ? 0 : color.ct.argb.blue;
+ }
+ break;
+ }
+ case Cmyk:
+ {
+ const qreal c = ct.acmyk.cyan / qreal(USHRT_MAX);
+ const qreal m = ct.acmyk.magenta / qreal(USHRT_MAX);
+ const qreal y = ct.acmyk.yellow / qreal(USHRT_MAX);
+ const qreal k = ct.acmyk.black / qreal(USHRT_MAX);
+
+ color.ct.argb.red = qRound((qreal(1.0) - (c * (qreal(1.0) - k) + k)) * USHRT_MAX);
+ color.ct.argb.green = qRound((qreal(1.0) - (m * (qreal(1.0) - k) + k)) * USHRT_MAX);
+ color.ct.argb.blue = qRound((qreal(1.0) - (y * (qreal(1.0) - k) + k)) * USHRT_MAX);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return color;
+}
+
+
+#define Q_MAX_3(a, b, c) ( ( a > b && a > c) ? a : (b > c ? b : c) )
+#define Q_MIN_3(a, b, c) ( ( a < b && a < c) ? a : (b < c ? b : c) )
+
+
+/*!
+ Creates and returns an HSV QColor based on this color.
+
+ \sa fromHsv(), convertTo(), isValid(), {QColor#The HSV Color
+ Model}{The HSV Color Model}
+*/
+QColor QColor::toHsv() const
+{
+ if (!isValid() || cspec == Hsv)
+ return *this;
+
+ if (cspec != Rgb)
+ return toRgb().toHsv();
+
+ QColor color;
+ color.cspec = Hsv;
+ color.ct.ahsv.alpha = ct.argb.alpha;
+ color.ct.ahsv.pad = 0;
+
+ const qreal r = ct.argb.red / qreal(USHRT_MAX);
+ const qreal g = ct.argb.green / qreal(USHRT_MAX);
+ const qreal b = ct.argb.blue / qreal(USHRT_MAX);
+ const qreal max = Q_MAX_3(r, g, b);
+ const qreal min = Q_MIN_3(r, g, b);
+ const qreal delta = max - min;
+ color.ct.ahsv.value = qRound(max * USHRT_MAX);
+ if (qFuzzyIsNull(delta)) {
+ // achromatic case, hue is undefined
+ color.ct.ahsv.hue = USHRT_MAX;
+ color.ct.ahsv.saturation = 0;
+ } else {
+ // chromatic case
+ qreal hue = 0;
+ color.ct.ahsv.saturation = qRound((delta / max) * USHRT_MAX);
+ if (qFuzzyCompare(r, max)) {
+ hue = ((g - b) /delta);
+ } else if (qFuzzyCompare(g, max)) {
+ hue = (qreal(2.0) + (b - r) / delta);
+ } else if (qFuzzyCompare(b, max)) {
+ hue = (qreal(4.0) + (r - g) / delta);
+ } else {
+ Q_ASSERT_X(false, "QColor::toHsv", "internal error");
+ }
+ hue *= qreal(60.0);
+ if (hue < qreal(0.0))
+ hue += qreal(360.0);
+ color.ct.ahsv.hue = qRound(hue * 100);
+ }
+
+ return color;
+}
+
+/*!
+ Creates and returns an HSL QColor based on this color.
+
+ \sa fromHsl(), convertTo(), isValid()
+*/
+QColor QColor::toHsl() const
+{
+ if (!isValid() || cspec == Hsl)
+ return *this;
+
+ if (cspec != Rgb)
+ return toRgb().toHsl();
+
+ QColor color;
+ color.cspec = Hsl;
+ color.ct.ahsl.alpha = ct.argb.alpha;
+ color.ct.ahsl.pad = 0;
+
+ const qreal r = ct.argb.red / qreal(USHRT_MAX);
+ const qreal g = ct.argb.green / qreal(USHRT_MAX);
+ const qreal b = ct.argb.blue / qreal(USHRT_MAX);
+ const qreal max = Q_MAX_3(r, g, b);
+ const qreal min = Q_MIN_3(r, g, b);
+ const qreal delta = max - min;
+ const qreal delta2 = max + min;
+ const qreal lightness = qreal(0.5) * delta2;
+ color.ct.ahsl.lightness = qRound(lightness * USHRT_MAX);
+ if (qFuzzyIsNull(delta)) {
+ // achromatic case, hue is undefined
+ color.ct.ahsl.hue = USHRT_MAX;
+ color.ct.ahsl.saturation = 0;
+ } else {
+ // chromatic case
+ qreal hue = 0;
+ if (lightness < qreal(0.5))
+ color.ct.ahsl.saturation = qRound((delta / delta2) * USHRT_MAX);
+ else
+ color.ct.ahsl.saturation = qRound((delta / (qreal(2.0) - delta2)) * USHRT_MAX);
+ if (qFuzzyCompare(r, max)) {
+ hue = ((g - b) /delta);
+ } else if (qFuzzyCompare(g, max)) {
+ hue = (qreal(2.0) + (b - r) / delta);
+ } else if (qFuzzyCompare(b, max)) {
+ hue = (qreal(4.0) + (r - g) / delta);
+ } else {
+ Q_ASSERT_X(false, "QColor::toHsv", "internal error");
+ }
+ hue *= qreal(60.0);
+ if (hue < qreal(0.0))
+ hue += qreal(360.0);
+ color.ct.ahsl.hue = qRound(hue * 100);
+ }
+
+ return color;
+}
+
+/*!
+ Creates and returns a CMYK QColor based on this color.
+
+ \sa fromCmyk(), convertTo(), isValid(), {QColor#The CMYK Color
+ Model}{The CMYK Color Model}
+*/
+QColor QColor::toCmyk() const
+{
+ if (!isValid() || cspec == Cmyk)
+ return *this;
+ if (cspec != Rgb)
+ return toRgb().toCmyk();
+
+ QColor color;
+ color.cspec = Cmyk;
+ color.ct.acmyk.alpha = ct.argb.alpha;
+
+ // rgb -> cmy
+ const qreal r = ct.argb.red / qreal(USHRT_MAX);
+ const qreal g = ct.argb.green / qreal(USHRT_MAX);
+ const qreal b = ct.argb.blue / qreal(USHRT_MAX);
+ qreal c = qreal(1.0) - r;
+ qreal m = qreal(1.0) - g;
+ qreal y = qreal(1.0) - b;
+
+ // cmy -> cmyk
+ const qreal k = qMin(c, qMin(m, y));
+
+ if (!qFuzzyIsNull(k - 1)) {
+ c = (c - k) / (qreal(1.0) - k);
+ m = (m - k) / (qreal(1.0) - k);
+ y = (y - k) / (qreal(1.0) - k);
+ }
+
+ color.ct.acmyk.cyan = qRound(c * USHRT_MAX);
+ color.ct.acmyk.magenta = qRound(m * USHRT_MAX);
+ color.ct.acmyk.yellow = qRound(y * USHRT_MAX);
+ color.ct.acmyk.black = qRound(k * USHRT_MAX);
+
+ return color;
+}
+
+QColor QColor::convertTo(QColor::Spec colorSpec) const
+{
+ if (colorSpec == cspec)
+ return *this;
+ switch (colorSpec) {
+ case Rgb:
+ return toRgb();
+ case Hsv:
+ return toHsv();
+ case Cmyk:
+ return toCmyk();
+ case Hsl:
+ return toHsl();
+ case Invalid:
+ break;
+ }
+ return QColor(); // must be invalid
+}
+
+
+/*!
+ Static convenience function that returns a QColor constructed from the
+ given QRgb value \a rgb.
+
+ The alpha component of \a rgb is ignored (i.e. it is automatically set to
+ 255), use the fromRgba() function to include the alpha-channel specified by
+ the given QRgb value.
+
+ \sa fromRgba(), fromRgbF(), toRgb(), isValid()
+*/
+
+QColor QColor::fromRgb(QRgb rgb)
+{
+ return fromRgb(qRed(rgb), qGreen(rgb), qBlue(rgb));
+}
+
+
+/*!
+ Static convenience function that returns a QColor constructed from the
+ given QRgb value \a rgba.
+
+ Unlike the fromRgb() function, the alpha-channel specified by the given
+ QRgb value is included.
+
+ \sa fromRgb(), isValid()
+*/
+
+QColor QColor::fromRgba(QRgb rgba)
+{
+ return fromRgb(qRed(rgba), qGreen(rgba), qBlue(rgba), qAlpha(rgba));
+}
+
+/*!
+ Static convenience function that returns a QColor constructed from the RGB
+ color values, \a r (red), \a g (green), \a b (blue), and \a a
+ (alpha-channel, i.e. transparency).
+
+ All the values must be in the range 0-255.
+
+ \sa toRgb(), fromRgbF(), isValid()
+*/
+QColor QColor::fromRgb(int r, int g, int b, int a)
+{
+ if (r < 0 || r > 255
+ || g < 0 || g > 255
+ || b < 0 || b > 255
+ || a < 0 || a > 255) {
+ qWarning("QColor::fromRgb: RGB parameters out of range");
+ return QColor();
+ }
+
+ QColor color;
+ color.cspec = Rgb;
+ color.ct.argb.alpha = a * 0x101;
+ color.ct.argb.red = r * 0x101;
+ color.ct.argb.green = g * 0x101;
+ color.ct.argb.blue = b * 0x101;
+ color.ct.argb.pad = 0;
+ return color;
+}
+
+/*!
+ Static convenience function that returns a QColor constructed from the RGB
+ color values, \a r (red), \a g (green), \a b (blue), and \a a
+ (alpha-channel, i.e. transparency).
+
+ All the values must be in the range 0.0-1.0.
+
+ \sa fromRgb(), toRgb(), isValid()
+*/
+QColor QColor::fromRgbF(qreal r, qreal g, qreal b, qreal a)
+{
+ if (r < qreal(0.0) || r > qreal(1.0)
+ || g < qreal(0.0) || g > qreal(1.0)
+ || b < qreal(0.0) || b > qreal(1.0)
+ || a < qreal(0.0) || a > qreal(1.0)) {
+ qWarning("QColor::fromRgbF: RGB parameters out of range");
+ return QColor();
+ }
+
+ QColor color;
+ color.cspec = Rgb;
+ color.ct.argb.alpha = qRound(a * USHRT_MAX);
+ color.ct.argb.red = qRound(r * USHRT_MAX);
+ color.ct.argb.green = qRound(g * USHRT_MAX);
+ color.ct.argb.blue = qRound(b * USHRT_MAX);
+ color.ct.argb.pad = 0;
+ return color;
+}
+
+/*!
+ Static convenience function that returns a QColor constructed from the HSV
+ color values, \a h (hue), \a s (saturation), \a v (value), and \a a
+ (alpha-channel, i.e. transparency).
+
+ The value of \a s, \a v, and \a a must all be in the range 0-255; the value
+ of \a h must be in the range 0-359.
+
+ \sa toHsv(), fromHsvF(), isValid(), {QColor#The HSV Color
+ Model}{The HSV Color Model}
+*/
+QColor QColor::fromHsv(int h, int s, int v, int a)
+{
+ if (((h < 0 || h >= 360) && h != -1)
+ || s < 0 || s > 255
+ || v < 0 || v > 255
+ || a < 0 || a > 255) {
+ qWarning("QColor::fromHsv: HSV parameters out of range");
+ return QColor();
+ }
+
+ QColor color;
+ color.cspec = Hsv;
+ color.ct.ahsv.alpha = a * 0x101;
+ color.ct.ahsv.hue = h == -1 ? USHRT_MAX : (h % 360) * 100;
+ color.ct.ahsv.saturation = s * 0x101;
+ color.ct.ahsv.value = v * 0x101;
+ color.ct.ahsv.pad = 0;
+ return color;
+}
+
+/*!
+ \overload
+
+ Static convenience function that returns a QColor constructed from the HSV
+ color values, \a h (hue), \a s (saturation), \a v (value), and \a a
+ (alpha-channel, i.e. transparency).
+
+ All the values must be in the range 0.0-1.0.
+
+ \sa toHsv(), fromHsv(), isValid(), {QColor#The HSV Color
+ Model}{The HSV Color Model}
+*/
+QColor QColor::fromHsvF(qreal h, qreal s, qreal v, qreal a)
+{
+ if (((h < qreal(0.0) || h > qreal(1.0)) && h != qreal(-1.0))
+ || (s < qreal(0.0) || s > qreal(1.0))
+ || (v < qreal(0.0) || v > qreal(1.0))
+ || (a < qreal(0.0) || a > qreal(1.0))) {
+ qWarning("QColor::fromHsvF: HSV parameters out of range");
+ return QColor();
+ }
+
+ QColor color;
+ color.cspec = Hsv;
+ color.ct.ahsv.alpha = qRound(a * USHRT_MAX);
+ color.ct.ahsv.hue = h == qreal(-1.0) ? USHRT_MAX : qRound(h * 36000);
+ color.ct.ahsv.saturation = qRound(s * USHRT_MAX);
+ color.ct.ahsv.value = qRound(v * USHRT_MAX);
+ color.ct.ahsv.pad = 0;
+ return color;
+}
+
+/*!
+ \since 4.6
+
+ Static convenience function that returns a QColor constructed from the HSV
+ color values, \a h (hue), \a s (saturation), \a l (lightness), and \a a
+ (alpha-channel, i.e. transparency).
+
+ The value of \a s, \a l, and \a a must all be in the range 0-255; the value
+ of \a h must be in the range 0-359.
+
+ \sa toHsl(), fromHslF(), isValid()
+*/
+QColor QColor::fromHsl(int h, int s, int l, int a)
+{
+ if (((h < 0 || h >= 360) && h != -1)
+ || s < 0 || s > 255
+ || l < 0 || l > 255
+ || a < 0 || a > 255) {
+ qWarning("QColor::fromHsv: HSV parameters out of range");
+ return QColor();
+ }
+
+ QColor color;
+ color.cspec = Hsl;
+ color.ct.ahsl.alpha = a * 0x101;
+ color.ct.ahsl.hue = h == -1 ? USHRT_MAX : (h % 360) * 100;
+ color.ct.ahsl.saturation = s * 0x101;
+ color.ct.ahsl.lightness = l * 0x101;
+ color.ct.ahsl.pad = 0;
+ return color;
+}
+
+/*!
+ \overload
+ \since 4.6
+
+ Static convenience function that returns a QColor constructed from the HSV
+ color values, \a h (hue), \a s (saturation), \a l (lightness), and \a a
+ (alpha-channel, i.e. transparency).
+
+ All the values must be in the range 0.0-1.0.
+
+ \sa toHsl(), fromHsl(), isValid()
+*/
+QColor QColor::fromHslF(qreal h, qreal s, qreal l, qreal a)
+{
+ if (((h < qreal(0.0) || h > qreal(1.0)) && h != qreal(-1.0))
+ || (s < qreal(0.0) || s > qreal(1.0))
+ || (l < qreal(0.0) || l > qreal(1.0))
+ || (a < qreal(0.0) || a > qreal(1.0))) {
+ qWarning("QColor::fromHsvF: HSV parameters out of range");
+ return QColor();
+ }
+
+ QColor color;
+ color.cspec = Hsl;
+ color.ct.ahsl.alpha = qRound(a * USHRT_MAX);
+ color.ct.ahsl.hue = (h == qreal(-1.0)) ? USHRT_MAX : qRound(h * 36000);
+ if (color.ct.ahsl.hue == 36000)
+ color.ct.ahsl.hue = 0;
+ color.ct.ahsl.saturation = qRound(s * USHRT_MAX);
+ color.ct.ahsl.lightness = qRound(l * USHRT_MAX);
+ color.ct.ahsl.pad = 0;
+ return color;
+}
+
+
+/*!
+ Sets the contents pointed to by \a c, \a m, \a y, \a k, and \a a, to the
+ cyan, magenta, yellow, black, and alpha-channel (transparency) components
+ of the color's CMYK value.
+
+ These components can be retrieved individually using the cyan(), magenta(),
+ yellow(), black() and alpha() functions.
+
+ \sa setCmyk(), {QColor#The CMYK Color Model}{The CMYK Color Model}
+*/
+void QColor::getCmyk(int *c, int *m, int *y, int *k, int *a)
+{
+ if (!c || !m || !y || !k)
+ return;
+
+ if (cspec != Invalid && cspec != Cmyk) {
+ toCmyk().getCmyk(c, m, y, k, a);
+ return;
+ }
+
+ *c = ct.acmyk.cyan >> 8;
+ *m = ct.acmyk.magenta >> 8;
+ *y = ct.acmyk.yellow >> 8;
+ *k = ct.acmyk.black >> 8;
+
+ if (a)
+ *a = ct.acmyk.alpha >> 8;
+}
+
+/*!
+ Sets the contents pointed to by \a c, \a m, \a y, \a k, and \a a, to the
+ cyan, magenta, yellow, black, and alpha-channel (transparency) components
+ of the color's CMYK value.
+
+ These components can be retrieved individually using the cyanF(),
+ magentaF(), yellowF(), blackF() and alphaF() functions.
+
+ \sa setCmykF(), {QColor#The CMYK Color Model}{The CMYK Color Model}
+*/
+void QColor::getCmykF(qreal *c, qreal *m, qreal *y, qreal *k, qreal *a)
+{
+ if (!c || !m || !y || !k)
+ return;
+
+ if (cspec != Invalid && cspec != Cmyk) {
+ toCmyk().getCmykF(c, m, y, k, a);
+ return;
+ }
+
+ *c = ct.acmyk.cyan / qreal(USHRT_MAX);
+ *m = ct.acmyk.magenta / qreal(USHRT_MAX);
+ *y = ct.acmyk.yellow / qreal(USHRT_MAX);
+ *k = ct.acmyk.black / qreal(USHRT_MAX);
+
+ if (a)
+ *a = ct.acmyk.alpha / qreal(USHRT_MAX);
+}
+
+/*!
+ Sets the color to CMYK values, \a c (cyan), \a m (magenta), \a y (yellow),
+ \a k (black), and \a a (alpha-channel, i.e. transparency).
+
+ All the values must be in the range 0-255.
+
+ \sa getCmyk(), setCmykF(), {QColor#The CMYK Color Model}{The
+ CMYK Color Model}
+*/
+void QColor::setCmyk(int c, int m, int y, int k, int a)
+{
+ if (c < 0 || c > 255
+ || m < 0 || m > 255
+ || y < 0 || y > 255
+ || k < 0 || k > 255
+ || a < 0 || a > 255) {
+ qWarning("QColor::setCmyk: CMYK parameters out of range");
+ return;
+ }
+
+ cspec = Cmyk;
+ ct.acmyk.alpha = a * 0x101;
+ ct.acmyk.cyan = c * 0x101;
+ ct.acmyk.magenta = m * 0x101;
+ ct.acmyk.yellow = y * 0x101;
+ ct.acmyk.black = k * 0x101;
+}
+
+/*!
+ \overload
+
+ Sets the color to CMYK values, \a c (cyan), \a m (magenta), \a y (yellow),
+ \a k (black), and \a a (alpha-channel, i.e. transparency).
+
+ All the values must be in the range 0.0-1.0.
+
+ \sa getCmykF() setCmyk(), {QColor#The CMYK Color Model}{The CMYK
+ Color Model}
+*/
+void QColor::setCmykF(qreal c, qreal m, qreal y, qreal k, qreal a)
+{
+ if (c < qreal(0.0) || c > qreal(1.0)
+ || m < qreal(0.0) || m > qreal(1.0)
+ || y < qreal(0.0) || y > qreal(1.0)
+ || k < qreal(0.0) || k > qreal(1.0)
+ || a < qreal(0.0) || a > qreal(1.0)) {
+ qWarning("QColor::setCmykF: CMYK parameters out of range");
+ return;
+ }
+
+ cspec = Cmyk;
+ ct.acmyk.alpha = qRound(a * USHRT_MAX);
+ ct.acmyk.cyan = qRound(c * USHRT_MAX);
+ ct.acmyk.magenta = qRound(m * USHRT_MAX);
+ ct.acmyk.yellow = qRound(y * USHRT_MAX);
+ ct.acmyk.black = qRound(k * USHRT_MAX);
+}
+
+/*!
+ Static convenience function that returns a QColor constructed from the
+ given CMYK color values: \a c (cyan), \a m (magenta), \a y (yellow), \a k
+ (black), and \a a (alpha-channel, i.e. transparency).
+
+ All the values must be in the range 0-255.
+
+ \sa toCmyk(), fromCmykF(), isValid(), {QColor#The CMYK Color Model}{The CMYK
+ Color Model}
+*/
+QColor QColor::fromCmyk(int c, int m, int y, int k, int a)
+{
+ if (c < 0 || c > 255
+ || m < 0 || m > 255
+ || y < 0 || y > 255
+ || k < 0 || k > 255
+ || a < 0 || a > 255) {
+ qWarning("QColor::fromCmyk: CMYK parameters out of range");
+ return QColor();
+ }
+
+ QColor color;
+ color.cspec = Cmyk;
+ color.ct.acmyk.alpha = a * 0x101;
+ color.ct.acmyk.cyan = c * 0x101;
+ color.ct.acmyk.magenta = m * 0x101;
+ color.ct.acmyk.yellow = y * 0x101;
+ color.ct.acmyk.black = k * 0x101;
+ return color;
+}
+
+/*!
+ \overload
+
+ Static convenience function that returns a QColor constructed from the
+ given CMYK color values: \a c (cyan), \a m (magenta), \a y (yellow), \a k
+ (black), and \a a (alpha-channel, i.e. transparency).
+
+ All the values must be in the range 0.0-1.0.
+
+ \sa toCmyk(), fromCmyk(), isValid(), {QColor#The CMYK Color
+ Model}{The CMYK Color Model}
+*/
+QColor QColor::fromCmykF(qreal c, qreal m, qreal y, qreal k, qreal a)
+{
+ if (c < qreal(0.0) || c > qreal(1.0)
+ || m < qreal(0.0) || m > qreal(1.0)
+ || y < qreal(0.0) || y > qreal(1.0)
+ || k < qreal(0.0) || k > qreal(1.0)
+ || a < qreal(0.0) || a > qreal(1.0)) {
+ qWarning("QColor::fromCmykF: CMYK parameters out of range");
+ return QColor();
+ }
+
+ QColor color;
+ color.cspec = Cmyk;
+ color.ct.acmyk.alpha = qRound(a * USHRT_MAX);
+ color.ct.acmyk.cyan = qRound(c * USHRT_MAX);
+ color.ct.acmyk.magenta = qRound(m * USHRT_MAX);
+ color.ct.acmyk.yellow = qRound(y * USHRT_MAX);
+ color.ct.acmyk.black = qRound(k * USHRT_MAX);
+ return color;
+}
+
+/*!
+ \fn QColor QColor::lighter(int factor) const
+ \since 4.3
+
+ Returns a lighter (or darker) color, but does not change this object.
+
+ If the \a factor is greater than 100, this functions returns a lighter
+ color. Setting \a factor to 150 returns a color that is 50% brighter. If
+ the \a factor is less than 100, the return color is darker, but we
+ recommend using the darker() function for this purpose. If the \a factor
+ is 0 or negative, the return value is unspecified.
+
+ The function converts the current RGB color to HSV, multiplies the value
+ (V) component by \a factor and converts the color back to RGB.
+
+ \sa darker(), isValid()
+*/
+
+/*!
+ \obsolete
+
+ Use lighter(\a factor) instead.
+*/
+QColor QColor::light(int factor) const
+{
+ if (factor <= 0) // invalid lightness factor
+ return *this;
+ else if (factor < 100) // makes color darker
+ return darker(10000 / factor);
+
+ QColor hsv = toHsv();
+ int s = hsv.ct.ahsv.saturation;
+ int v = hsv.ct.ahsv.value;
+
+ v = (factor*v)/100;
+ if (v > USHRT_MAX) {
+ // overflow... adjust saturation
+ s -= v - USHRT_MAX;
+ if (s < 0)
+ s = 0;
+ v = USHRT_MAX;
+ }
+
+ hsv.ct.ahsv.saturation = s;
+ hsv.ct.ahsv.value = v;
+
+ // convert back to same color spec as original color
+ return hsv.convertTo(cspec);
+}
+
+/*!
+ \fn QColor QColor::darker(int factor) const
+ \since 4.3
+
+ Returns a darker (or lighter) color, but does not change this object.
+
+ If the \a factor is greater than 100, this functions returns a darker
+ color. Setting \a factor to 300 returns a color that has one-third the
+ brightness. If the \a factor is less than 100, the return color is lighter,
+ but we recommend using the lighter() function for this purpose. If the
+ \a factor is 0 or negative, the return value is unspecified.
+
+ The function converts the current RGB color to HSV, divides the value (V)
+ component by \a factor and converts the color back to RGB.
+
+ \sa lighter(), isValid()
+*/
+
+/*!
+ \obsolete
+
+ Use darker(\a factor) instead.
+*/
+QColor QColor::dark(int factor) const
+{
+ if (factor <= 0) // invalid darkness factor
+ return *this;
+ else if (factor < 100) // makes color lighter
+ return lighter(10000 / factor);
+
+ QColor hsv = toHsv();
+ hsv.ct.ahsv.value = (hsv.ct.ahsv.value * 100) / factor;
+
+ // convert back to same color spec as original color
+ return hsv.convertTo(cspec);
+}
+
+/*!
+ Assigns a copy of \a color to this color, and returns a reference to it.
+*/
+QColor &QColor::operator=(const QColor &color)
+{
+ cspec = color.cspec;
+ ct.argb = color.ct.argb;
+ return *this;
+}
+
+/*! \overload
+ Assigns a copy of \a color and returns a reference to this color.
+ */
+QColor &QColor::operator=(Qt::GlobalColor color)
+{
+ return operator=(QColor(color));
+}
+
+/*!
+ Returns true if this color has the same RGB and alpha values as \a color;
+ otherwise returns false.
+*/
+bool QColor::operator==(const QColor &color) const
+{
+ if (cspec == Hsl && cspec == color.cspec) {
+ return (ct.argb.alpha == color.ct.argb.alpha
+ && ((((ct.ahsl.hue % 36000) == (color.ct.ahsl.hue % 36000)))
+ || (ct.ahsl.hue == color.ct.ahsl.hue))
+ && (qAbs(ct.ahsl.saturation - color.ct.ahsl.saturation) < 50
+ || ct.ahsl.lightness == 0
+ || color.ct.ahsl.lightness == 0
+ || ct.ahsl.lightness == USHRT_MAX
+ || color.ct.ahsl.lightness == USHRT_MAX)
+ && (qAbs(ct.ahsl.lightness - color.ct.ahsl.lightness)) < 50);
+ } else {
+ return (cspec == color.cspec
+ && ct.argb.alpha == color.ct.argb.alpha
+ && (((cspec == QColor::Hsv)
+ && ((ct.ahsv.hue % 36000) == (color.ct.ahsv.hue % 36000)))
+ || (ct.ahsv.hue == color.ct.ahsv.hue))
+ && ct.argb.green == color.ct.argb.green
+ && ct.argb.blue == color.ct.argb.blue
+ && ct.argb.pad == color.ct.argb.pad);
+ }
+}
+
+/*!
+ Returns true if this color has a different RGB and alpha values from
+ \a color; otherwise returns false.
+*/
+bool QColor::operator!=(const QColor &color) const
+{ return !operator==(color); }
+
+
+/*!
+ Returns the color as a QVariant
+*/
+QColor::operator QVariant() const
+{
+ return QVariant(QVariant::Color, this);
+}
+
+#ifdef Q_WS_X11
+/*!
+ Returns true if setNamedColor() is allowed to look up colors in the X11
+ color database. By default, this function returns false.
+
+ \note This function is only available on the X11 platform.
+
+ \sa setAllowX11ColorNames()
+*/
+bool QColor::allowX11ColorNames()
+{
+ return ::allowX11ColorNames;
+}
+
+/*!
+ Allow setNamedColor() to look up colors in the X11 color database if
+ \a enabled. By default, setNamedColor() does \e not look up colors in the
+ X11 color database.
+
+ \note This function is only available on the X11 platform.
+
+ \sa setNamedColor(), allowX11ColorNames()
+*/
+void QColor::setAllowX11ColorNames(bool enabled)
+{
+ ::allowX11ColorNames = enabled;
+}
+#endif
+
+/*! \internal
+
+ Marks the color as invalid and sets all components to zero (alpha is set
+ to fully opaque for compatibility with Qt 3).
+*/
+void QColor::invalidate()
+{
+ cspec = Invalid;
+ ct.argb.alpha = USHRT_MAX;
+ ct.argb.red = 0;
+ ct.argb.green = 0;
+ ct.argb.blue = 0;
+ ct.argb.pad = 0;
+}
+
+#ifdef QT3_SUPPORT
+
+/*!
+ Returns the pixel value used by the underlying window system to refer to a
+ color.
+
+ Use QColormap::pixel() instead.
+
+ \oldcode
+ QColor myColor;
+ uint pixel = myColor.pixel(screen);
+ \newcode
+ QColormap cmap = QColormap::instance(screen);
+ uint pixel = cmap.pixel(*this);
+ \endcode
+*/
+uint QColor::pixel(int screen) const
+{
+ QColormap cmap = QColormap::instance(screen);
+ return cmap.pixel(*this);
+}
+
+#endif // QT3_SUPPORT
+
+/*****************************************************************************
+ QColor stream functions
+ *****************************************************************************/
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QColor &c)
+{
+#ifndef Q_BROKEN_DEBUG_STREAM
+ if (!c.isValid())
+ dbg.nospace() << "QColor(Invalid)";
+ else if (c.spec() == QColor::Rgb)
+ dbg.nospace() << "QColor(ARGB " << c.alphaF() << ", " << c.redF() << ", " << c.greenF() << ", " << c.blueF() << ')';
+ else if (c.spec() == QColor::Hsv)
+ dbg.nospace() << "QColor(AHSV " << c.alphaF() << ", " << c.hueF() << ", " << c.saturationF() << ", " << c.valueF() << ')';
+ else if (c.spec() == QColor::Cmyk)
+ dbg.nospace() << "QColor(ACMYK " << c.alphaF() << ", " << c.cyanF() << ", " << c.magentaF() << ", " << c.yellowF() << ", "
+ << c.blackF()<< ')';
+ else if (c.spec() == QColor::Hsl)
+ dbg.nospace() << "QColor(AHSL " << c.alphaF() << ", " << c.hslHueF() << ", " << c.hslSaturationF() << ", " << c.lightnessF() << ')';
+
+ return dbg.space();
+#else
+ qWarning("This compiler doesn't support streaming QColor to QDebug");
+ return dbg;
+ Q_UNUSED(c);
+#endif
+}
+#endif
+
+#ifndef QT_NO_DATASTREAM
+/*!
+ \fn QDataStream &operator<<(QDataStream &stream, const QColor &color)
+ \relates QColor
+
+ Writes the \a color to the \a stream.
+
+ \sa {Serializing Qt Data Types}
+*/
+QDataStream &operator<<(QDataStream &stream, const QColor &color)
+{
+ if (stream.version() < 7) {
+ if (!color.isValid())
+ return stream << quint32(0x49000000);
+ quint32 p = (quint32)color.rgb();
+ if (stream.version() == 1) // Swap red and blue
+ p = ((p << 16) & 0xff0000) | ((p >> 16) & 0xff) | (p & 0xff00ff00);
+ return stream << p;
+ }
+
+ qint8 s = color.cspec;
+ quint16 a = color.ct.argb.alpha;
+ quint16 r = color.ct.argb.red;
+ quint16 g = color.ct.argb.green;
+ quint16 b = color.ct.argb.blue;
+ quint16 p = color.ct.argb.pad;
+
+ stream << s;
+ stream << a;
+ stream << r;
+ stream << g;
+ stream << b;
+ stream << p;
+
+ return stream;
+}
+
+/*!
+ \fn QDataStream &operator>>(QDataStream &stream, QColor &color)
+ \relates QColor
+
+ Reads the \a color from the \a stream.
+
+ \sa {Serializing Qt Data Types}
+*/
+QDataStream &operator>>(QDataStream &stream, QColor &color)
+{
+ if (stream.version() < 7) {
+ quint32 p;
+ stream >> p;
+ if (p == 0x49000000) {
+ color.invalidate();
+ return stream;
+ }
+ if (stream.version() == 1) // Swap red and blue
+ p = ((p << 16) & 0xff0000) | ((p >> 16) & 0xff) | (p & 0xff00ff00);
+ color.setRgb(p);
+ return stream;
+ }
+
+ qint8 s;
+ quint16 a, r, g, b, p;
+ stream >> s;
+ stream >> a;
+ stream >> r;
+ stream >> g;
+ stream >> b;
+ stream >> p;
+
+ color.cspec = QColor::Spec(s);
+ color.ct.argb.alpha = a;
+ color.ct.argb.red = r;
+ color.ct.argb.green = g;
+ color.ct.argb.blue = b;
+ color.ct.argb.pad = p;
+
+ return stream;
+}
+#endif // QT_NO_DATASTREAM
+
+
+/*****************************************************************************
+ QColor global functions (documentation only)
+ *****************************************************************************/
+
+/*!
+ \fn int qRed(QRgb rgb)
+ \relates QColor
+
+ Returns the red component of the ARGB quadruplet \a rgb.
+
+ \sa qRgb(), QColor::red()
+*/
+
+/*!
+ \fn int qGreen(QRgb rgb)
+ \relates QColor
+
+ Returns the green component of the ARGB quadruplet \a rgb.
+
+ \sa qRgb(), QColor::green()
+*/
+
+/*!
+ \fn int qBlue(QRgb rgb)
+ \relates QColor
+
+ Returns the blue component of the ARGB quadruplet \a rgb.
+
+ \sa qRgb(), QColor::blue()
+*/
+
+/*!
+ \fn int qAlpha(QRgb rgba)
+ \relates QColor
+
+ Returns the alpha component of the ARGB quadruplet \a rgba.
+
+ \sa qRgb(), QColor::alpha()
+*/
+
+/*!
+ \fn QRgb qRgb(int r, int g, int b)
+ \relates QColor
+
+ Returns the ARGB quadruplet (255, \a{r}, \a{g}, \a{b}).
+
+ \sa qRgba(), qRed(), qGreen(), qBlue()
+*/
+
+/*!
+ \fn QRgb qRgba(int r, int g, int b, int a)
+ \relates QColor
+
+ Returns the ARGB quadruplet (\a{a}, \a{r}, \a{g}, \a{b}).
+
+ \sa qRgb(), qRed(), qGreen(), qBlue()
+*/
+
+/*!
+ \fn int qGray(int r, int g, int b)
+ \relates QColor
+
+ Returns a gray value (0 to 255) from the (\a r, \a g, \a b)
+ triplet.
+
+ The gray value is calculated using the formula (\a r * 11 + \a g * 16 +
+ \a b * 5)/32.
+*/
+
+/*!
+ \fn int qGray(QRgb rgb)
+ \overload
+ \relates QColor
+
+ Returns a gray value (0 to 255) from the given ARGB quadruplet \a rgb.
+
+ The gray value is calculated using the formula (R * 11 + G * 16 + B * 5)/32;
+ the alpha-channel is ignored.
+*/
+
+/*!
+ \fn QColor::QColor(int x, int y, int z, Spec colorSpec)
+
+ Use one of the other QColor constructors, or one of the static convenience
+ functions, instead.
+*/
+
+/*!
+ \fn QColor::rgb(int *r, int *g, int *b) const
+
+ Use getRgb() instead.
+*/
+
+/*!
+ \fn QColor::hsv(int *h, int *s, int *v) const
+
+ Use getHsv() instead.
+*/
+
+/*!
+ \fn QColor QColor::convertTo(Spec colorSpec) const
+
+ Creates a copy of \e this color in the format specified by \a colorSpec.
+
+ \sa spec(), toCmyk(), toHsv(), toRgb(), isValid()
+*/
+
+/*!
+ \typedef QRgb
+ \relates QColor
+
+ An ARGB quadruplet on the format #AARRGGBB, equivalent to an unsigned int.
+
+ The type also holds a value for the alpha-channel. The default alpha
+ channel is \c ff, i.e opaque. For more information, see the
+ \l{QColor#Alpha-Blended Drawing}{Alpha-Blended Drawing} section.
+
+ \sa QColor::rgb(), QColor::rgba()
+*/
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qcolor.h b/src/gui/painting/qcolor.h
new file mode 100644
index 0000000000..af7248ce8c
--- /dev/null
+++ b/src/gui/painting/qcolor.h
@@ -0,0 +1,308 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOLOR_H
+#define QCOLOR_H
+
+#include <QtGui/qrgb.h>
+#include <QtCore/qnamespace.h>
+#include <QtCore/qstringlist.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QColor;
+class QColormap;
+class QVariant;
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QColor &);
+#endif
+#ifndef QT_NO_DATASTREAM
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QColor &);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QColor &);
+#endif
+
+class Q_GUI_EXPORT QColor
+{
+public:
+ enum Spec { Invalid, Rgb, Hsv, Cmyk, Hsl };
+
+ QColor();
+ QColor(Qt::GlobalColor color);
+ QColor(int r, int g, int b, int a = 255);
+ QColor(QRgb rgb);
+ QColor(const QString& name);
+ QColor(const char *name);
+ QColor(const QColor &color);
+ QColor(Spec spec);
+
+ bool isValid() const;
+
+ QString name() const;
+ void setNamedColor(const QString& name);
+
+ static QStringList colorNames();
+
+ inline Spec spec() const
+ { return cspec; }
+
+ int alpha() const;
+ void setAlpha(int alpha);
+
+ qreal alphaF() const;
+ void setAlphaF(qreal alpha);
+
+ int red() const;
+ int green() const;
+ int blue() const;
+ void setRed(int red);
+ void setGreen(int green);
+ void setBlue(int blue);
+
+ qreal redF() const;
+ qreal greenF() const;
+ qreal blueF() const;
+ void setRedF(qreal red);
+ void setGreenF(qreal green);
+ void setBlueF(qreal blue);
+
+ void getRgb(int *r, int *g, int *b, int *a = 0) const;
+ void setRgb(int r, int g, int b, int a = 255);
+
+ void getRgbF(qreal *r, qreal *g, qreal *b, qreal *a = 0) const;
+ void setRgbF(qreal r, qreal g, qreal b, qreal a = 1.0);
+
+ QRgb rgba() const;
+ void setRgba(QRgb rgba);
+
+ QRgb rgb() const;
+ void setRgb(QRgb rgb);
+
+ int hue() const; // 0 <= hue < 360
+ int saturation() const;
+ int hsvHue() const; // 0 <= hue < 360
+ int hsvSaturation() const;
+ int value() const;
+
+ qreal hueF() const; // 0.0 <= hueF < 360.0
+ qreal saturationF() const;
+ qreal hsvHueF() const; // 0.0 <= hueF < 360.0
+ qreal hsvSaturationF() const;
+ qreal valueF() const;
+
+ void getHsv(int *h, int *s, int *v, int *a = 0) const;
+ void setHsv(int h, int s, int v, int a = 255);
+
+ void getHsvF(qreal *h, qreal *s, qreal *v, qreal *a = 0) const;
+ void setHsvF(qreal h, qreal s, qreal v, qreal a = 1.0);
+
+ int cyan() const;
+ int magenta() const;
+ int yellow() const;
+ int black() const;
+
+ qreal cyanF() const;
+ qreal magentaF() const;
+ qreal yellowF() const;
+ qreal blackF() const;
+
+ void getCmyk(int *c, int *m, int *y, int *k, int *a = 0);
+ void setCmyk(int c, int m, int y, int k, int a = 255);
+
+ void getCmykF(qreal *c, qreal *m, qreal *y, qreal *k, qreal *a = 0);
+ void setCmykF(qreal c, qreal m, qreal y, qreal k, qreal a = 1.0);
+
+ int hslHue() const; // 0 <= hue < 360
+ int hslSaturation() const;
+ int lightness() const;
+
+ qreal hslHueF() const; // 0.0 <= hueF < 360.0
+ qreal hslSaturationF() const;
+ qreal lightnessF() const;
+
+ void getHsl(int *h, int *s, int *l, int *a = 0) const;
+ void setHsl(int h, int s, int l, int a = 255);
+
+ void getHslF(qreal *h, qreal *s, qreal *l, qreal *a = 0) const;
+ void setHslF(qreal h, qreal s, qreal l, qreal a = 1.0);
+
+ QColor toRgb() const;
+ QColor toHsv() const;
+ QColor toCmyk() const;
+ QColor toHsl() const;
+
+ QColor convertTo(Spec colorSpec) const;
+
+ static QColor fromRgb(QRgb rgb);
+ static QColor fromRgba(QRgb rgba);
+
+ static QColor fromRgb(int r, int g, int b, int a = 255);
+ static QColor fromRgbF(qreal r, qreal g, qreal b, qreal a = 1.0);
+
+ static QColor fromHsv(int h, int s, int v, int a = 255);
+ static QColor fromHsvF(qreal h, qreal s, qreal v, qreal a = 1.0);
+
+ static QColor fromCmyk(int c, int m, int y, int k, int a = 255);
+ static QColor fromCmykF(qreal c, qreal m, qreal y, qreal k, qreal a = 1.0);
+
+ static QColor fromHsl(int h, int s, int l, int a = 255);
+ static QColor fromHslF(qreal h, qreal s, qreal l, qreal a = 1.0);
+
+ QColor light(int f = 150) const;
+ QColor lighter(int f = 150) const;
+ QColor dark(int f = 200) const;
+ QColor darker(int f = 200) const;
+
+ QColor &operator=(const QColor &);
+ QColor &operator=(Qt::GlobalColor color);
+
+ bool operator==(const QColor &c) const;
+ bool operator!=(const QColor &c) const;
+
+ operator QVariant() const;
+
+#ifdef Q_WS_X11
+ static bool allowX11ColorNames();
+ static void setAllowX11ColorNames(bool enabled);
+#endif
+
+#ifdef QT3_SUPPORT
+ inline QT3_SUPPORT_CONSTRUCTOR QColor(int x, int y, int z, Spec colorSpec)
+ { if (colorSpec == Hsv) setHsv(x, y, z); else setRgb(x, y, z); }
+
+ inline QT3_SUPPORT void rgb(int *r, int *g, int *b) const
+ { getRgb(r, g, b); }
+ inline QT3_SUPPORT void hsv(int *h, int *s, int *v) const
+ { getHsv(h, s, v); }
+
+ inline QT3_SUPPORT void setRgba(int r, int g, int b, int a)
+ { setRgb(r, g, b, a); }
+ inline QT3_SUPPORT void getRgba(int *r, int *g, int *b, int *a) const
+ { getRgb(r, g, b, a); }
+
+ QT3_SUPPORT uint pixel(int screen = -1) const;
+#endif
+
+ static bool isValidColor(const QString &name);
+
+private:
+#ifndef QT3_SUPPORT
+ // do not allow a spec to be used as an alpha value
+ QColor(int, int, int, Spec);
+#endif
+
+ void invalidate();
+ bool setColorFromString(const QString &name);
+
+ Spec cspec;
+ union {
+ struct {
+ ushort alpha;
+ ushort red;
+ ushort green;
+ ushort blue;
+ ushort pad;
+ } argb;
+ struct {
+ ushort alpha;
+ ushort hue;
+ ushort saturation;
+ ushort value;
+ ushort pad;
+ } ahsv;
+ struct {
+ ushort alpha;
+ ushort cyan;
+ ushort magenta;
+ ushort yellow;
+ ushort black;
+ } acmyk;
+ struct {
+ ushort alpha;
+ ushort hue;
+ ushort saturation;
+ ushort lightness;
+ ushort pad;
+ } ahsl;
+ ushort array[5];
+ } ct;
+
+ friend class QColormap;
+#ifndef QT_NO_DATASTREAM
+ friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QColor &);
+ friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QColor &);
+#endif
+};
+
+inline QColor::QColor()
+{ invalidate(); }
+
+inline QColor::QColor(int r, int g, int b, int a)
+{ setRgb(r, g, b, a); }
+
+inline QColor::QColor(const char *aname)
+{ setNamedColor(QLatin1String(aname)); }
+
+inline QColor::QColor(const QString& aname)
+{ setNamedColor(aname); }
+
+inline QColor::QColor(const QColor &acolor)
+ : cspec(acolor.cspec)
+{ ct.argb = acolor.ct.argb; }
+
+inline bool QColor::isValid() const
+{ return cspec != Invalid; }
+
+inline QColor QColor::lighter(int f) const
+{ return light(f); }
+
+inline QColor QColor::darker(int f) const
+{ return dark(f); }
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QCOLOR_H
diff --git a/src/gui/painting/qcolor_p.cpp b/src/gui/painting/qcolor_p.cpp
new file mode 100644
index 0000000000..38fed7146d
--- /dev/null
+++ b/src/gui/painting/qcolor_p.cpp
@@ -0,0 +1,370 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qglobal.h"
+
+#if defined(Q_CC_BOR)
+// needed for qsort() because of a std namespace problem on Borland
+#include "qplatformdefs.h"
+#endif
+
+#include "qrgb.h"
+#include "qstringlist.h"
+
+QT_BEGIN_NAMESPACE
+
+static inline int h2i(char hex)
+{
+ if (hex >= '0' && hex <= '9')
+ return hex - '0';
+ if (hex >= 'a' && hex <= 'f')
+ return hex - 'a' + 10;
+ if (hex >= 'A' && hex <= 'F')
+ return hex - 'A' + 10;
+ return -1;
+}
+
+static inline int hex2int(const char *s)
+{
+ return (h2i(s[0]) << 4) | h2i(s[1]);
+}
+
+static inline int hex2int(char s)
+{
+ int h = h2i(s);
+ return (h << 4) | h;
+}
+
+bool qt_get_hex_rgb(const char *name, QRgb *rgb)
+{
+ if(name[0] != '#')
+ return false;
+ name++;
+ int len = qstrlen(name);
+ int r, g, b;
+ if (len == 12) {
+ r = hex2int(name);
+ g = hex2int(name + 4);
+ b = hex2int(name + 8);
+ } else if (len == 9) {
+ r = hex2int(name);
+ g = hex2int(name + 3);
+ b = hex2int(name + 6);
+ } else if (len == 6) {
+ r = hex2int(name);
+ g = hex2int(name + 2);
+ b = hex2int(name + 4);
+ } else if (len == 3) {
+ r = hex2int(name[0]);
+ g = hex2int(name[1]);
+ b = hex2int(name[2]);
+ } else {
+ r = g = b = -1;
+ }
+ if ((uint)r > 255 || (uint)g > 255 || (uint)b > 255) {
+ *rgb = 0;
+ return false;
+ }
+ *rgb = qRgb(r, g ,b);
+ return true;
+}
+
+bool qt_get_hex_rgb(const QChar *str, int len, QRgb *rgb)
+{
+ if (len > 13)
+ return false;
+ char tmp[16];
+ for(int i = 0; i < len; ++i)
+ tmp[i] = str[i].toLatin1();
+ tmp[len] = 0;
+ return qt_get_hex_rgb(tmp, rgb);
+}
+
+#ifndef QT_NO_COLORNAMES
+
+/*
+ CSS color names = SVG 1.0 color names + transparent (rgba(0,0,0,0))
+*/
+
+#ifdef rgb
+# undef rgb
+#endif
+#define rgb(r,g,b) (0xff000000 | (r << 16) | (g << 8) | b)
+
+static const struct RGBData {
+ const char *name;
+ uint value;
+} rgbTbl[] = {
+ { "aliceblue", rgb(240, 248, 255) },
+ { "antiquewhite", rgb(250, 235, 215) },
+ { "aqua", rgb( 0, 255, 255) },
+ { "aquamarine", rgb(127, 255, 212) },
+ { "azure", rgb(240, 255, 255) },
+ { "beige", rgb(245, 245, 220) },
+ { "bisque", rgb(255, 228, 196) },
+ { "black", rgb( 0, 0, 0) },
+ { "blanchedalmond", rgb(255, 235, 205) },
+ { "blue", rgb( 0, 0, 255) },
+ { "blueviolet", rgb(138, 43, 226) },
+ { "brown", rgb(165, 42, 42) },
+ { "burlywood", rgb(222, 184, 135) },
+ { "cadetblue", rgb( 95, 158, 160) },
+ { "chartreuse", rgb(127, 255, 0) },
+ { "chocolate", rgb(210, 105, 30) },
+ { "coral", rgb(255, 127, 80) },
+ { "cornflowerblue", rgb(100, 149, 237) },
+ { "cornsilk", rgb(255, 248, 220) },
+ { "crimson", rgb(220, 20, 60) },
+ { "cyan", rgb( 0, 255, 255) },
+ { "darkblue", rgb( 0, 0, 139) },
+ { "darkcyan", rgb( 0, 139, 139) },
+ { "darkgoldenrod", rgb(184, 134, 11) },
+ { "darkgray", rgb(169, 169, 169) },
+ { "darkgreen", rgb( 0, 100, 0) },
+ { "darkgrey", rgb(169, 169, 169) },
+ { "darkkhaki", rgb(189, 183, 107) },
+ { "darkmagenta", rgb(139, 0, 139) },
+ { "darkolivegreen", rgb( 85, 107, 47) },
+ { "darkorange", rgb(255, 140, 0) },
+ { "darkorchid", rgb(153, 50, 204) },
+ { "darkred", rgb(139, 0, 0) },
+ { "darksalmon", rgb(233, 150, 122) },
+ { "darkseagreen", rgb(143, 188, 143) },
+ { "darkslateblue", rgb( 72, 61, 139) },
+ { "darkslategray", rgb( 47, 79, 79) },
+ { "darkslategrey", rgb( 47, 79, 79) },
+ { "darkturquoise", rgb( 0, 206, 209) },
+ { "darkviolet", rgb(148, 0, 211) },
+ { "deeppink", rgb(255, 20, 147) },
+ { "deepskyblue", rgb( 0, 191, 255) },
+ { "dimgray", rgb(105, 105, 105) },
+ { "dimgrey", rgb(105, 105, 105) },
+ { "dodgerblue", rgb( 30, 144, 255) },
+ { "firebrick", rgb(178, 34, 34) },
+ { "floralwhite", rgb(255, 250, 240) },
+ { "forestgreen", rgb( 34, 139, 34) },
+ { "fuchsia", rgb(255, 0, 255) },
+ { "gainsboro", rgb(220, 220, 220) },
+ { "ghostwhite", rgb(248, 248, 255) },
+ { "gold", rgb(255, 215, 0) },
+ { "goldenrod", rgb(218, 165, 32) },
+ { "gray", rgb(128, 128, 128) },
+ { "green", rgb( 0, 128, 0) },
+ { "greenyellow", rgb(173, 255, 47) },
+ { "grey", rgb(128, 128, 128) },
+ { "honeydew", rgb(240, 255, 240) },
+ { "hotpink", rgb(255, 105, 180) },
+ { "indianred", rgb(205, 92, 92) },
+ { "indigo", rgb( 75, 0, 130) },
+ { "ivory", rgb(255, 255, 240) },
+ { "khaki", rgb(240, 230, 140) },
+ { "lavender", rgb(230, 230, 250) },
+ { "lavenderblush", rgb(255, 240, 245) },
+ { "lawngreen", rgb(124, 252, 0) },
+ { "lemonchiffon", rgb(255, 250, 205) },
+ { "lightblue", rgb(173, 216, 230) },
+ { "lightcoral", rgb(240, 128, 128) },
+ { "lightcyan", rgb(224, 255, 255) },
+ { "lightgoldenrodyellow", rgb(250, 250, 210) },
+ { "lightgray", rgb(211, 211, 211) },
+ { "lightgreen", rgb(144, 238, 144) },
+ { "lightgrey", rgb(211, 211, 211) },
+ { "lightpink", rgb(255, 182, 193) },
+ { "lightsalmon", rgb(255, 160, 122) },
+ { "lightseagreen", rgb( 32, 178, 170) },
+ { "lightskyblue", rgb(135, 206, 250) },
+ { "lightslategray", rgb(119, 136, 153) },
+ { "lightslategrey", rgb(119, 136, 153) },
+ { "lightsteelblue", rgb(176, 196, 222) },
+ { "lightyellow", rgb(255, 255, 224) },
+ { "lime", rgb( 0, 255, 0) },
+ { "limegreen", rgb( 50, 205, 50) },
+ { "linen", rgb(250, 240, 230) },
+ { "magenta", rgb(255, 0, 255) },
+ { "maroon", rgb(128, 0, 0) },
+ { "mediumaquamarine", rgb(102, 205, 170) },
+ { "mediumblue", rgb( 0, 0, 205) },
+ { "mediumorchid", rgb(186, 85, 211) },
+ { "mediumpurple", rgb(147, 112, 219) },
+ { "mediumseagreen", rgb( 60, 179, 113) },
+ { "mediumslateblue", rgb(123, 104, 238) },
+ { "mediumspringgreen", rgb( 0, 250, 154) },
+ { "mediumturquoise", rgb( 72, 209, 204) },
+ { "mediumvioletred", rgb(199, 21, 133) },
+ { "midnightblue", rgb( 25, 25, 112) },
+ { "mintcream", rgb(245, 255, 250) },
+ { "mistyrose", rgb(255, 228, 225) },
+ { "moccasin", rgb(255, 228, 181) },
+ { "navajowhite", rgb(255, 222, 173) },
+ { "navy", rgb( 0, 0, 128) },
+ { "oldlace", rgb(253, 245, 230) },
+ { "olive", rgb(128, 128, 0) },
+ { "olivedrab", rgb(107, 142, 35) },
+ { "orange", rgb(255, 165, 0) },
+ { "orangered", rgb(255, 69, 0) },
+ { "orchid", rgb(218, 112, 214) },
+ { "palegoldenrod", rgb(238, 232, 170) },
+ { "palegreen", rgb(152, 251, 152) },
+ { "paleturquoise", rgb(175, 238, 238) },
+ { "palevioletred", rgb(219, 112, 147) },
+ { "papayawhip", rgb(255, 239, 213) },
+ { "peachpuff", rgb(255, 218, 185) },
+ { "peru", rgb(205, 133, 63) },
+ { "pink", rgb(255, 192, 203) },
+ { "plum", rgb(221, 160, 221) },
+ { "powderblue", rgb(176, 224, 230) },
+ { "purple", rgb(128, 0, 128) },
+ { "red", rgb(255, 0, 0) },
+ { "rosybrown", rgb(188, 143, 143) },
+ { "royalblue", rgb( 65, 105, 225) },
+ { "saddlebrown", rgb(139, 69, 19) },
+ { "salmon", rgb(250, 128, 114) },
+ { "sandybrown", rgb(244, 164, 96) },
+ { "seagreen", rgb( 46, 139, 87) },
+ { "seashell", rgb(255, 245, 238) },
+ { "sienna", rgb(160, 82, 45) },
+ { "silver", rgb(192, 192, 192) },
+ { "skyblue", rgb(135, 206, 235) },
+ { "slateblue", rgb(106, 90, 205) },
+ { "slategray", rgb(112, 128, 144) },
+ { "slategrey", rgb(112, 128, 144) },
+ { "snow", rgb(255, 250, 250) },
+ { "springgreen", rgb( 0, 255, 127) },
+ { "steelblue", rgb( 70, 130, 180) },
+ { "tan", rgb(210, 180, 140) },
+ { "teal", rgb( 0, 128, 128) },
+ { "thistle", rgb(216, 191, 216) },
+ { "tomato", rgb(255, 99, 71) },
+ { "transparent", 0 },
+ { "turquoise", rgb( 64, 224, 208) },
+ { "violet", rgb(238, 130, 238) },
+ { "wheat", rgb(245, 222, 179) },
+ { "white", rgb(255, 255, 255) },
+ { "whitesmoke", rgb(245, 245, 245) },
+ { "yellow", rgb(255, 255, 0) },
+ { "yellowgreen", rgb(154, 205, 50) }
+};
+
+static const int rgbTblSize = sizeof(rgbTbl) / sizeof(RGBData);
+
+#undef rgb
+
+inline bool operator<(const char *name, const RGBData &data)
+{ return qstrcmp(name, data.name) < 0; }
+inline bool operator<(const RGBData &data, const char *name)
+{ return qstrcmp(data.name, name) < 0; }
+
+static bool get_named_rgb(const char *name_no_space, QRgb *rgb)
+{
+ QByteArray name = QByteArray(name_no_space).toLower();
+ const RGBData *r = qBinaryFind(rgbTbl, rgbTbl + rgbTblSize, name.constData());
+ if (r != rgbTbl + rgbTblSize) {
+ *rgb = r->value;
+ return true;
+ }
+ return false;
+}
+
+bool qt_get_named_rgb(const char *name, QRgb* rgb)
+{
+ int len = int(strlen(name));
+ if(len > 255)
+ return false;
+ char name_no_space[256];
+ int pos = 0;
+ for(int i = 0; i < len; i++) {
+ if(name[i] != '\t' && name[i] != ' ')
+ name_no_space[pos++] = name[i];
+ }
+ name_no_space[pos] = 0;
+
+ return get_named_rgb(name_no_space, rgb);
+}
+
+bool qt_get_named_rgb(const QChar *name, int len, QRgb *rgb)
+{
+ if(len > 255)
+ return false;
+ char name_no_space[256];
+ int pos = 0;
+ for(int i = 0; i < len; i++) {
+ if(name[i] != QLatin1Char('\t') && name[i] != QLatin1Char(' '))
+ name_no_space[pos++] = name[i].toLatin1();
+ }
+ name_no_space[pos] = 0;
+ return get_named_rgb(name_no_space, rgb);
+}
+
+
+uint qt_get_rgb_val(const char *name)
+{
+ QRgb r = 0;
+ qt_get_named_rgb(name,&r);
+ return r;
+}
+
+QStringList qt_get_colornames()
+{
+ int i = 0;
+ QStringList lst;
+ for (i = 0; i < rgbTblSize; i++)
+ lst << QLatin1String(rgbTbl[i].name);
+ return lst;
+}
+
+#else
+
+bool qt_get_named_rgb(const char *, QRgb*)
+{
+ return false;
+}
+
+uint qt_get_rgb_val(const char *)
+{
+ return 0;
+}
+QStringList qt_get_colornames()
+{
+ return QStringList();
+}
+#endif // QT_NO_COLORNAMES
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qcolor_p.h b/src/gui/painting/qcolor_p.h
new file mode 100644
index 0000000000..0698a7643c
--- /dev/null
+++ b/src/gui/painting/qcolor_p.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOLOR_P_H
+#define QCOLOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtCore/qglobal.h"
+#include "QtGui/qrgb.h"
+#include "QtCore/qstringlist.h"
+
+QT_BEGIN_NAMESPACE
+
+uint qt_get_rgb_val(const char *name);
+bool qt_get_named_rgb(const char *, QRgb*);
+bool qt_get_named_rgb(const QChar *, int len, QRgb*);
+bool qt_get_hex_rgb(const char *, QRgb *);
+bool qt_get_hex_rgb(const QChar *, int len, QRgb *);
+QStringList qt_get_colornames();
+
+QT_END_NAMESPACE
+
+#endif // QCOLOR_P_H
diff --git a/src/gui/painting/qcolormap.h b/src/gui/painting/qcolormap.h
new file mode 100644
index 0000000000..4d0521d622
--- /dev/null
+++ b/src/gui/painting/qcolormap.h
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOLORMAP_H
+#define QCOLORMAP_H
+
+#include <QtCore/qatomic.h>
+#include <QtGui/qrgb.h>
+#include <QtCore/qvector.h>
+#include <QtGui/qwindowdefs.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QColor;
+class QColormapPrivate;
+
+class Q_GUI_EXPORT QColormap
+{
+public:
+ enum Mode { Direct, Indexed, Gray };
+
+ static void initialize();
+ static void cleanup();
+
+ static QColormap instance(int screen = -1);
+
+ QColormap(const QColormap &colormap);
+ ~QColormap();
+
+ QColormap &operator=(const QColormap &colormap);
+
+ Mode mode() const;
+
+ int depth() const;
+ int size() const;
+
+ uint pixel(const QColor &color) const;
+ const QColor colorAt(uint pixel) const;
+
+ const QVector<QColor> colormap() const;
+
+#ifdef Q_WS_WIN
+ static HPALETTE hPal();
+#endif
+
+private:
+ QColormap();
+ QColormapPrivate *d;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QCOLORMAP_H
diff --git a/src/gui/painting/qcolormap.qdoc b/src/gui/painting/qcolormap.qdoc
new file mode 100644
index 0000000000..04ed407510
--- /dev/null
+++ b/src/gui/painting/qcolormap.qdoc
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Free Documentation License
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of this
+** file.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class QColormap
+ \ingroup painting
+
+ \brief The QColormap class maps device independent QColors to device
+ dependent pixel values.
+*/
+
+/*! \enum QColormap::Mode
+
+ This enum describes how QColormap maps device independent RGB
+ values to device dependent pixel values.
+
+ \value Direct Pixel values are derived directly from the RGB
+ values, also known as "True Color."
+
+ \value Indexed Pixel values represent indexes into a vector of
+ available colors, i.e. QColormap uses the index of the color that
+ most closely matches an RGB value.
+
+ \value Gray Similar to \c Indexed, pixel values represent a vector
+ of available gray tones. QColormap uses the index of the gray
+ tone that most closely matches the computed gray tone of an RGB
+ value.
+*/
+
+/*!
+ \fn QColormap QColormap::instance(int screen)
+
+ Returns the colormap for the specified \a screen. If \a screen is
+ -1, this function returns the colormap for the default screen.
+*/
+
+/*!
+ \fn QColormap::QColormap(const QColormap &colormap)
+
+ Constructs a copy of another \a colormap.
+*/
+
+/*!
+ \fn QColormap::~QColormap()
+
+ Destroys the colormap.
+*/
+
+/*!
+ \fn int QColormap::size() const
+
+ Returns the size of the colormap for \c Indexed and \c Gray modes;
+ Returns -1 for \c Direct mode.
+
+ \sa colormap()
+*/
+
+/*!
+ \fn uint QColormap::pixel(const QColor &color) const
+
+ Returns a device dependent pixel value for the \a color.
+
+ \sa colorAt()
+*/
+
+/*!
+ \fn int QColormap::depth() const
+
+ Returns the depth of the device.
+
+ \sa size()
+*/
+
+/*!
+ \fn QColormap::Mode QColormap::mode() const
+
+ Returns the mode of this colormap.
+
+ \sa QColormap::Mode
+*/
+
+/*!
+ \fn const QColor QColormap::colorAt(uint pixel) const
+
+ Returns a QColor for the \a pixel.
+
+ \sa pixel()
+*/
+
+/*!
+ \fn const QVector<QColor> QColormap::colormap() const
+
+ Returns a vector of colors which represents the devices colormap
+ for \c Indexed and \c Gray modes. This function returns an empty
+ vector for \c Direct mode.
+
+ \sa size()
+*/
+
+/*! \fn HPALETTE QColormap::hPal()
+
+ This function is only available on Windows.
+
+ Returns an handle to the HPALETTE used by this colormap. If no
+ HPALETTE is being used, this function returns zero.
+*/
+
+/*! \since 4.2
+
+ \fn QColormap &QColormap::operator=(const QColormap &colormap)
+
+ Assigns the given \a colormap to \e this color map and returns
+ a reference to \e this color map.
+*/
+
+/*!
+ \fn void QColormap::initialize()
+ \internal
+*/
+
+/*!
+ \fn void QColormap::cleanup()
+ \internal
+*/
diff --git a/src/gui/painting/qcolormap_mac.cpp b/src/gui/painting/qcolormap_mac.cpp
new file mode 100644
index 0000000000..28589f41b8
--- /dev/null
+++ b/src/gui/painting/qcolormap_mac.cpp
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcolormap.h"
+#include "qcolor.h"
+
+QT_BEGIN_NAMESPACE
+
+class QColormapPrivate
+{
+public:
+ inline QColormapPrivate()
+ : ref(1)
+ { }
+
+ QAtomicInt ref;
+};
+static QColormap *qt_mac_global_map = 0;
+
+void QColormap::initialize()
+{
+ qt_mac_global_map = new QColormap;
+}
+
+void QColormap::cleanup()
+{
+ delete qt_mac_global_map;
+ qt_mac_global_map = 0;
+}
+
+QColormap QColormap::instance(int)
+{
+ return *qt_mac_global_map;
+}
+
+QColormap::QColormap() : d(new QColormapPrivate)
+{}
+
+QColormap::QColormap(const QColormap &colormap) :d (colormap.d)
+{ d->ref.ref(); }
+
+QColormap::~QColormap()
+{
+ if (!d->ref.deref())
+ delete d;
+}
+
+QColormap::Mode QColormap::mode() const
+{ return QColormap::Direct; }
+
+int QColormap::depth() const
+{
+ return 32;
+}
+
+int QColormap::size() const
+{
+ return -1;
+}
+
+uint QColormap::pixel(const QColor &color) const
+{ return color.rgba(); }
+
+const QColor QColormap::colorAt(uint pixel) const
+{ return QColor(pixel); }
+
+const QVector<QColor> QColormap::colormap() const
+{ return QVector<QColor>(); }
+
+QColormap &QColormap::operator=(const QColormap &colormap)
+{ qAtomicAssign(d, colormap.d); return *this; }
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qcolormap_qpa.cpp b/src/gui/painting/qcolormap_qpa.cpp
new file mode 100644
index 0000000000..f66607bb05
--- /dev/null
+++ b/src/gui/painting/qcolormap_qpa.cpp
@@ -0,0 +1,231 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcolormap.h"
+#include "qcolor.h"
+#include "qpaintdevice.h"
+#include "private/qapplication_p.h"
+#include "private/qgraphicssystem_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QColormapPrivate
+{
+public:
+ inline QColormapPrivate()
+ : ref(1), mode(QColormap::Direct), depth(0), numcolors(0)
+ { }
+
+ QAtomicInt ref;
+
+ QColormap::Mode mode;
+ int depth;
+ int numcolors;
+};
+
+static QColormapPrivate *screenMap = 0;
+
+void QColormap::initialize()
+{
+ screenMap = new QColormapPrivate;
+
+ QPlatformIntegration *pi = QApplicationPrivate::platformIntegration();
+ QList<QPlatformScreen*> screens = pi->screens();
+
+ screenMap->depth = screens.at(0)->depth();
+ if (screenMap->depth < 8) {
+ screenMap->mode = QColormap::Indexed;
+ screenMap->numcolors = 256;
+ } else {
+ screenMap->mode = QColormap::Direct;
+ screenMap->numcolors = -1;
+ }
+}
+
+void QColormap::cleanup()
+{
+ delete screenMap;
+ screenMap = 0;
+}
+
+QColormap QColormap::instance(int /*screen*/)
+{
+ return QColormap();
+}
+
+QColormap::QColormap()
+ : d(screenMap)
+{ d->ref.ref(); }
+
+QColormap::QColormap(const QColormap &colormap)
+ :d (colormap.d)
+{ d->ref.ref(); }
+
+QColormap::~QColormap()
+{
+ if (!d->ref.deref())
+ delete d;
+}
+
+QColormap::Mode QColormap::mode() const
+{ return d->mode; }
+
+
+int QColormap::depth() const
+{ return d->depth; }
+
+
+int QColormap::size() const
+{
+ return d->numcolors;
+}
+
+#ifndef QT_QWS_DEPTH16_RGB
+#define QT_QWS_DEPTH16_RGB 565
+#endif
+static const int qt_rbits = (QT_QWS_DEPTH16_RGB/100);
+static const int qt_gbits = (QT_QWS_DEPTH16_RGB/10%10);
+static const int qt_bbits = (QT_QWS_DEPTH16_RGB%10);
+static const int qt_red_shift = qt_bbits+qt_gbits-(8-qt_rbits);
+static const int qt_green_shift = qt_bbits-(8-qt_gbits);
+static const int qt_neg_blue_shift = 8-qt_bbits;
+static const int qt_blue_mask = (1<<qt_bbits)-1;
+static const int qt_green_mask = (1<<(qt_gbits+qt_bbits))-(1<<qt_bbits);
+static const int qt_red_mask = (1<<(qt_rbits+qt_gbits+qt_bbits))-(1<<(qt_gbits+qt_bbits));
+
+static const int qt_red_rounding_shift = qt_red_shift + qt_rbits;
+static const int qt_green_rounding_shift = qt_green_shift + qt_gbits;
+static const int qt_blue_rounding_shift = qt_bbits - qt_neg_blue_shift;
+
+inline ushort qt_convRgbTo16(QRgb c)
+{
+ const int tr = qRed(c) << qt_red_shift;
+ const int tg = qGreen(c) << qt_green_shift;
+ const int tb = qBlue(c) >> qt_neg_blue_shift;
+
+ return (tb & qt_blue_mask) | (tg & qt_green_mask) | (tr & qt_red_mask);
+}
+
+inline QRgb qt_conv16ToRgb(ushort c)
+{
+ const int r=(c & qt_red_mask);
+ const int g=(c & qt_green_mask);
+ const int b=(c & qt_blue_mask);
+ const int tr = r >> qt_red_shift | r >> qt_red_rounding_shift;
+ const int tg = g >> qt_green_shift | g >> qt_green_rounding_shift;
+ const int tb = b << qt_neg_blue_shift | b >> qt_blue_rounding_shift;
+
+ return qRgb(tr,tg,tb);
+}
+
+uint QColormap::pixel(const QColor &color) const
+{
+ QRgb rgb = color.rgba();
+ if (d->mode == QColormap::Direct) {
+ switch(d->depth) {
+ case 16:
+ return qt_convRgbTo16(rgb);
+ case 24:
+ case 32:
+ {
+ const int r = qRed(rgb);
+ const int g = qGreen(rgb);
+ const int b = qBlue(rgb);
+ const int red_shift = 16;
+ const int green_shift = 8;
+ const int red_mask = 0xff0000;
+ const int green_mask = 0x00ff00;
+ const int blue_mask = 0x0000ff;
+ const int tg = g << green_shift;
+#ifdef QT_QWS_DEPTH_32_BGR
+ if (qt_screen->pixelType() == QScreen::BGRPixel) {
+ const int tb = b << red_shift;
+ return 0xff000000 | (r & blue_mask) | (tg & green_mask) | (tb & red_mask);
+ }
+#endif
+ const int tr = r << red_shift;
+ return 0xff000000 | (b & blue_mask) | (tg & green_mask) | (tr & red_mask);
+ }
+ }
+ }
+ //XXX
+ //return qt_screen->alloc(qRed(rgb), qGreen(rgb), qBlue(rgb));
+ return 0;
+}
+
+const QColor QColormap::colorAt(uint pixel) const
+{
+ if (d->mode == Direct) {
+ if (d->depth == 16) {
+ pixel = qt_conv16ToRgb(pixel);
+ }
+ const int red_shift = 16;
+ const int green_shift = 8;
+ const int red_mask = 0xff0000;
+ const int green_mask = 0x00ff00;
+ const int blue_mask = 0x0000ff;
+#ifdef QT_QWS_DEPTH_32_BGR
+ if (qt_screen->pixelType() == QScreen::BGRPixel) {
+ return QColor((pixel & blue_mask),
+ (pixel & green_mask) >> green_shift,
+ (pixel & red_mask) >> red_shift);
+ }
+#endif
+ return QColor((pixel & red_mask) >> red_shift,
+ (pixel & green_mask) >> green_shift,
+ (pixel & blue_mask));
+ }
+#if 0 // XXX
+ Q_ASSERT_X(int(pixel) < qt_screen->numCols(), "QColormap::colorAt", "pixel out of bounds of palette");
+ return QColor(qt_screen->clut()[pixel]);
+#endif
+ return QColor();
+}
+
+const QVector<QColor> QColormap::colormap() const
+{
+ return QVector<QColor>();
+}
+
+QColormap &QColormap::operator=(const QColormap &colormap)
+{ qAtomicAssign(d, colormap.d); return *this; }
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qcolormap_qws.cpp b/src/gui/painting/qcolormap_qws.cpp
new file mode 100644
index 0000000000..5c0933d6eb
--- /dev/null
+++ b/src/gui/painting/qcolormap_qws.cpp
@@ -0,0 +1,185 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcolormap.h"
+#include "qcolor.h"
+#include "qpaintdevice.h"
+#include "qscreen_qws.h"
+#include "qwsdisplay_qws.h"
+
+QT_BEGIN_NAMESPACE
+
+class QColormapPrivate
+{
+public:
+ inline QColormapPrivate()
+ : ref(1), mode(QColormap::Direct), depth(0), numcolors(0)
+ { }
+
+ QAtomicInt ref;
+
+ QColormap::Mode mode;
+ int depth;
+ int numcolors;
+};
+
+static QColormapPrivate *screenMap = 0;
+
+void QColormap::initialize()
+{
+ screenMap = new QColormapPrivate;
+
+ screenMap->depth = QPaintDevice::qwsDisplay()->depth();
+ if (screenMap->depth < 8) {
+ screenMap->mode = QColormap::Indexed;
+ screenMap->numcolors = 256;
+ } else {
+ screenMap->mode = QColormap::Direct;
+ screenMap->numcolors = -1;
+ }
+}
+
+void QColormap::cleanup()
+{
+ delete screenMap;
+ screenMap = 0;
+}
+
+QColormap QColormap::instance(int /*screen*/)
+{
+ return QColormap();
+}
+
+QColormap::QColormap()
+ : d(screenMap)
+{ d->ref.ref(); }
+
+QColormap::QColormap(const QColormap &colormap)
+ :d (colormap.d)
+{ d->ref.ref(); }
+
+QColormap::~QColormap()
+{
+ if (!d->ref.deref())
+ delete d;
+}
+
+QColormap::Mode QColormap::mode() const
+{ return d->mode; }
+
+
+int QColormap::depth() const
+{ return d->depth; }
+
+
+int QColormap::size() const
+{
+ return d->numcolors;
+}
+
+uint QColormap::pixel(const QColor &color) const
+{
+ QRgb rgb = color.rgba();
+ if (d->mode == QColormap::Direct) {
+ switch(d->depth) {
+ case 16:
+ return qt_convRgbTo16(rgb);
+ case 24:
+ case 32:
+ {
+ const int r = qRed(rgb);
+ const int g = qGreen(rgb);
+ const int b = qBlue(rgb);
+ const int red_shift = 16;
+ const int green_shift = 8;
+ const int red_mask = 0xff0000;
+ const int green_mask = 0x00ff00;
+ const int blue_mask = 0x0000ff;
+ const int tg = g << green_shift;
+#ifdef QT_QWS_DEPTH_32_BGR
+ if (qt_screen->pixelType() == QScreen::BGRPixel) {
+ const int tb = b << red_shift;
+ return 0xff000000 | (r & blue_mask) | (tg & green_mask) | (tb & red_mask);
+ }
+#endif
+ const int tr = r << red_shift;
+ return 0xff000000 | (b & blue_mask) | (tg & green_mask) | (tr & red_mask);
+ }
+ }
+ }
+ return qt_screen->alloc(qRed(rgb), qGreen(rgb), qBlue(rgb));
+}
+
+const QColor QColormap::colorAt(uint pixel) const
+{
+ if (d->mode == Direct) {
+ if (d->depth == 16) {
+ pixel = qt_conv16ToRgb(pixel);
+ }
+ const int red_shift = 16;
+ const int green_shift = 8;
+ const int red_mask = 0xff0000;
+ const int green_mask = 0x00ff00;
+ const int blue_mask = 0x0000ff;
+#ifdef QT_QWS_DEPTH_32_BGR
+ if (qt_screen->pixelType() == QScreen::BGRPixel) {
+ return QColor((pixel & blue_mask),
+ (pixel & green_mask) >> green_shift,
+ (pixel & red_mask) >> red_shift);
+ }
+#endif
+ return QColor((pixel & red_mask) >> red_shift,
+ (pixel & green_mask) >> green_shift,
+ (pixel & blue_mask));
+ }
+ Q_ASSERT_X(int(pixel) < qt_screen->colorCount(), "QColormap::colorAt", "pixel out of bounds of palette");
+ return QColor(qt_screen->clut()[pixel]);
+}
+
+const QVector<QColor> QColormap::colormap() const
+{
+ return QVector<QColor>();
+}
+
+QColormap &QColormap::operator=(const QColormap &colormap)
+{ qAtomicAssign(d, colormap.d); return *this; }
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qcolormap_s60.cpp b/src/gui/painting/qcolormap_s60.cpp
new file mode 100644
index 0000000000..2c634db8a5
--- /dev/null
+++ b/src/gui/painting/qcolormap_s60.cpp
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcolormap.h"
+#include "qcolor.h"
+
+QT_BEGIN_NAMESPACE
+
+class QColormapPrivate
+{
+public:
+ inline QColormapPrivate()
+ : ref(1)
+ { }
+
+ QAtomicInt ref;
+};
+
+void QColormap::initialize()
+{
+}
+
+void QColormap::cleanup()
+{
+}
+
+QColormap QColormap::instance(int)
+{
+ return QColormap();
+}
+
+QColormap::QColormap() : d(new QColormapPrivate)
+{}
+
+QColormap::QColormap(const QColormap &colormap) :d (colormap.d)
+{ d->ref.ref(); }
+
+QColormap::~QColormap()
+{
+ if (!d->ref.deref())
+ delete d;
+}
+
+QColormap::Mode QColormap::mode() const
+{ return QColormap::Direct; }
+
+int QColormap::depth() const
+{
+ return 32;
+}
+
+int QColormap::size() const
+{
+ return -1;
+}
+
+uint QColormap::pixel(const QColor &color) const
+{ return color.rgba(); }
+
+const QColor QColormap::colorAt(uint pixel) const
+{ return QColor(pixel); }
+
+const QVector<QColor> QColormap::colormap() const
+{ return QVector<QColor>(); }
+
+QColormap &QColormap::operator=(const QColormap &colormap)
+{ qAtomicAssign(d, colormap.d); return *this; }
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qcolormap_win.cpp b/src/gui/painting/qcolormap_win.cpp
new file mode 100644
index 0000000000..1773f717c0
--- /dev/null
+++ b/src/gui/painting/qcolormap_win.cpp
@@ -0,0 +1,201 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcolor.h"
+#include "qcolormap.h"
+#include "qvector.h"
+#include "qt_windows.h"
+
+#if defined(Q_WS_WINCE)
+#include "qguifunctions_wince.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QColormapPrivate
+{
+public:
+ inline QColormapPrivate()
+ : ref(1), mode(QColormap::Direct), depth(0), hpal(0)
+ { }
+
+ QAtomicInt ref;
+
+ QColormap::Mode mode;
+ int depth;
+ int numcolors;
+
+ HPALETTE hpal;
+ QVector<QColor> palette;
+};
+
+static QColormapPrivate *screenMap = 0;
+
+void QColormap::initialize()
+{
+ HDC dc = qt_win_display_dc();
+
+ screenMap = new QColormapPrivate;
+ screenMap->depth = GetDeviceCaps(dc, BITSPIXEL);
+
+ screenMap->numcolors = -1;
+ if (GetDeviceCaps(dc, RASTERCAPS) & RC_PALETTE)
+ screenMap->numcolors = GetDeviceCaps(dc, SIZEPALETTE);
+
+ if (screenMap->numcolors <= 16 || screenMap->numcolors > 256) // no need to create palette
+ return;
+
+ LOGPALETTE* pal = 0;
+ int numPalEntries = 6*6*6; // color cube
+
+ pal = (LOGPALETTE*)malloc(sizeof(LOGPALETTE) + numPalEntries * sizeof(PALETTEENTRY));
+ // Make 6x6x6 color cube
+ int idx = 0;
+ for(int ir = 0x0; ir <= 0xff; ir+=0x33) {
+ for(int ig = 0x0; ig <= 0xff; ig+=0x33) {
+ for(int ib = 0x0; ib <= 0xff; ib+=0x33) {
+ pal->palPalEntry[idx].peRed = ir;
+ pal->palPalEntry[idx].peGreen = ig;
+ pal->palPalEntry[idx].peBlue = ib;
+ pal->palPalEntry[idx].peFlags = 0;
+ idx++;
+ }
+ }
+ }
+
+ pal->palVersion = 0x300;
+ pal->palNumEntries = numPalEntries;
+
+ screenMap->hpal = CreatePalette(pal);
+ if (!screenMap->hpal)
+ qErrnoWarning("QColor::initialize: Failed to create logical palette");
+ free (pal);
+
+ SelectPalette(dc, screenMap->hpal, FALSE);
+ RealizePalette(dc);
+
+ PALETTEENTRY paletteEntries[256];
+ screenMap->numcolors = GetPaletteEntries(screenMap->hpal, 0, 255, paletteEntries);
+
+ screenMap->palette.resize(screenMap->numcolors);
+ for (int i = 0; i < screenMap->numcolors; i++) {
+ screenMap->palette[i] = qRgb(paletteEntries[i].peRed,
+ paletteEntries[i].peGreen,
+ paletteEntries[i].peBlue);
+ }
+}
+
+void QColormap::cleanup()
+{
+ if (!screenMap)
+ return;
+
+ if (screenMap->hpal) { // delete application global
+ DeleteObject(screenMap->hpal); // palette
+ screenMap->hpal = 0;
+ }
+
+ delete screenMap;
+ screenMap = 0;
+}
+
+QColormap QColormap::instance(int)
+{
+ Q_ASSERT_X(screenMap, "QColormap",
+ "A QApplication object needs to be constructed before QColormap is used.");
+ return QColormap();
+}
+
+QColormap::QColormap()
+ : d(screenMap)
+{ d->ref.ref(); }
+
+QColormap::QColormap(const QColormap &colormap)
+ :d (colormap.d)
+{ d->ref.ref(); }
+
+QColormap::~QColormap()
+{
+ if (!d->ref.deref())
+ delete d;
+}
+
+QColormap::Mode QColormap::mode() const
+{ return d->mode; }
+
+int QColormap::depth() const
+{ return d->depth; }
+
+int QColormap::size() const
+{ return d->numcolors; }
+
+uint QColormap::pixel(const QColor &color) const
+{
+ const QColor c = color.toRgb();
+ COLORREF rgb = RGB(c.red(), c.green(), c.blue());
+ if (d->hpal)
+ return PALETTEINDEX(GetNearestPaletteIndex(d->hpal, rgb));
+ return rgb;
+}
+
+const QColor QColormap::colorAt(uint pixel) const
+{
+ if (d->hpal) {
+ if (pixel < uint(d->numcolors))
+ return d->palette.at(pixel);
+ return QColor();
+ }
+ return QColor(GetRValue(pixel), GetGValue(pixel), GetBValue(pixel));
+}
+
+
+HPALETTE QColormap::hPal()
+{ return screenMap ? screenMap->hpal : 0; }
+
+
+const QVector<QColor> QColormap::colormap() const
+{ return d->palette; }
+
+QColormap &QColormap::operator=(const QColormap &colormap)
+{ qAtomicAssign(d, colormap.d); return *this; }
+
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qcolormap_x11.cpp b/src/gui/painting/qcolormap_x11.cpp
new file mode 100644
index 0000000000..05eefa455b
--- /dev/null
+++ b/src/gui/painting/qcolormap_x11.cpp
@@ -0,0 +1,670 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcolormap.h"
+
+#include "qapplication.h"
+#include "qdebug.h"
+#include "qdesktopwidget.h"
+#include "qvarlengtharray.h"
+
+#include "qx11info_x11.h"
+#include <private/qt_x11_p.h>
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+class QColormapPrivate
+{
+public:
+ QColormapPrivate()
+ : ref(1), mode(QColormap::Direct), depth(0),
+ colormap(0), defaultColormap(true),
+ visual(0), defaultVisual(true),
+ r_max(0), g_max(0), b_max(0),
+ r_shift(0), g_shift(0), b_shift(0)
+ {}
+
+ QAtomicInt ref;
+
+ QColormap::Mode mode;
+ int depth;
+
+ Colormap colormap;
+ bool defaultColormap;
+
+ Visual *visual;
+ bool defaultVisual;
+
+ int r_max;
+ int g_max;
+ int b_max;
+
+ uint r_shift;
+ uint g_shift;
+ uint b_shift;
+
+ QVector<QColor> colors;
+ QVector<int> pixels;
+};
+
+
+static uint right_align(uint v)
+{
+ while (!(v & 0x1))
+ v >>= 1;
+ return v;
+}
+
+static int lowest_bit(uint v)
+{
+ int i;
+ uint b = 1u;
+ for (i = 0; ((v & b) == 0u) && i < 32; ++i)
+ b <<= 1u;
+ return i == 32 ? -1 : i;
+}
+
+static int cube_root(int v)
+{
+ if (v == 1)
+ return 1;
+ // brute force algorithm
+ int i = 1;
+ for (;;) {
+ const int b = i * i * i;
+ if (b <= v) {
+ ++i;
+ } else {
+ --i;
+ break;
+ }
+ }
+ return i;
+}
+
+static Visual *find_visual(Display *display,
+ int screen,
+ int visual_class,
+ int visual_id,
+ int *depth,
+ bool *defaultVisual)
+{
+ XVisualInfo *vi, rvi;
+ int count;
+
+ uint mask = VisualScreenMask;
+ rvi.screen = screen;
+
+ if (visual_class != -1) {
+ rvi.c_class = visual_class;
+ mask |= VisualClassMask;
+ }
+ if (visual_id != -1) {
+ rvi.visualid = visual_id;
+ mask |= VisualIDMask;
+ }
+
+ Visual *visual = DefaultVisual(display, screen);
+ *defaultVisual = true;
+ *depth = DefaultDepth(display, screen);
+
+ vi = XGetVisualInfo(display, mask, &rvi, &count);
+ if (vi) {
+ int best = 0;
+ for (int x = 0; x < count; ++x) {
+ if (vi[x].depth > vi[best].depth)
+ best = x;
+ }
+ if (best >= 0 && best <= count && vi[best].visualid != XVisualIDFromVisual(visual)) {
+ visual = vi[best].visual;
+ *defaultVisual = (visual == DefaultVisual(display, screen));
+ *depth = vi[best].depth;
+ }
+ }
+ if (vi)
+ XFree((char *)vi);
+ return visual;
+}
+
+static void query_colormap(QColormapPrivate *d, int screen)
+{
+ Display *display = QX11Info::display();
+
+ // query existing colormap
+ int q_colors = (((1u << d->depth) > 256u) ? 256u : (1u << d->depth));
+ XColor queried[256];
+ memset(queried, 0, sizeof(queried));
+ for (int x = 0; x < q_colors; ++x)
+ queried[x].pixel = x;
+ XQueryColors(display, d->colormap, queried, q_colors);
+
+ d->colors.resize(q_colors);
+ for (int x = 0; x < q_colors; ++x) {
+ if (queried[x].red == 0
+ && queried[x].green == 0
+ && queried[x].blue == 0
+ && queried[x].pixel != BlackPixel(display, screen)) {
+ // unallocated color cell, skip it
+ continue;
+ }
+
+ d->colors[x] = QColor::fromRgbF(queried[x].red / float(USHRT_MAX),
+ queried[x].green / float(USHRT_MAX),
+ queried[x].blue / float(USHRT_MAX));
+ }
+
+ // for missing colors, find the closest color in the existing colormap
+ Q_ASSERT(d->pixels.size());
+ for (int x = 0; x < d->pixels.size(); ++x) {
+ if (d->pixels.at(x) != -1)
+ continue;
+
+ QRgb rgb;
+ if (d->mode == QColormap::Indexed) {
+ const int r = (x / (d->g_max * d->b_max)) % d->r_max;
+ const int g = (x / d->b_max) % d->g_max;
+ const int b = x % d->b_max;
+ rgb = qRgb((r * 0xff + (d->r_max - 1) / 2) / (d->r_max - 1),
+ (g * 0xff + (d->g_max - 1) / 2) / (d->g_max - 1),
+ (b * 0xff + (d->b_max - 1) / 2) / (d->b_max - 1));
+ } else {
+ rgb = qRgb(x, x, x);
+ }
+
+ // find closest color
+ int mindist = INT_MAX, best = -1;
+ for (int y = 0; y < q_colors; ++y) {
+ int r = qRed(rgb) - (queried[y].red >> 8);
+ int g = qGreen(rgb) - (queried[y].green >> 8);
+ int b = qBlue(rgb) - (queried[y].blue >> 8);
+ int dist = (r * r) + (g * g) + (b * b);
+ if (dist < mindist) {
+ mindist = dist;
+ best = y;
+ }
+ }
+
+ Q_ASSERT(best >= 0 && best < q_colors);
+ if (d->visual->c_class & 1) {
+ XColor xcolor;
+ xcolor.red = queried[best].red;
+ xcolor.green = queried[best].green;
+ xcolor.blue = queried[best].blue;
+ xcolor.pixel = queried[best].pixel;
+
+ if (XAllocColor(display, d->colormap, &xcolor)) {
+ d->pixels[x] = xcolor.pixel;
+ } else {
+ // some weird stuff is going on...
+ d->pixels[x] = (qGray(rgb) < 127
+ ? BlackPixel(display, screen)
+ : WhitePixel(display, screen));
+ }
+ } else {
+ d->pixels[x] = best;
+ }
+ }
+}
+
+static void init_gray(QColormapPrivate *d, int screen)
+{
+ d->pixels.resize(d->r_max);
+
+ for (int g = 0; g < d->g_max; ++g) {
+ const int gray = (g * 0xff + (d->r_max - 1) / 2) / (d->r_max - 1);
+ const QRgb rgb = qRgb(gray, gray, gray);
+
+ d->pixels[g] = -1;
+
+ if (d->visual->c_class & 1) {
+ XColor xcolor;
+ xcolor.red = qRed(rgb) * 0x101;
+ xcolor.green = qGreen(rgb) * 0x101;
+ xcolor.blue = qBlue(rgb) * 0x101;
+ xcolor.pixel = 0ul;
+
+ if (XAllocColor(QX11Info::display(), d->colormap, &xcolor))
+ d->pixels[g] = xcolor.pixel;
+ }
+ }
+
+ query_colormap(d, screen);
+}
+
+static void init_indexed(QColormapPrivate *d, int screen)
+{
+ d->pixels.resize(d->r_max * d->g_max * d->b_max);
+
+ // create color cube
+ for (int x = 0, r = 0; r < d->r_max; ++r) {
+ for (int g = 0; g < d->g_max; ++g) {
+ for (int b = 0; b < d->b_max; ++b, ++x) {
+ const QRgb rgb = qRgb((r * 0xff + (d->r_max - 1) / 2) / (d->r_max - 1),
+ (g * 0xff + (d->g_max - 1) / 2) / (d->g_max - 1),
+ (b * 0xff + (d->b_max - 1) / 2) / (d->b_max - 1));
+
+ d->pixels[x] = -1;
+
+ if (d->visual->c_class & 1) {
+ XColor xcolor;
+ xcolor.red = qRed(rgb) * 0x101;
+ xcolor.green = qGreen(rgb) * 0x101;
+ xcolor.blue = qBlue(rgb) * 0x101;
+ xcolor.pixel = 0ul;
+
+ if (XAllocColor(QX11Info::display(), d->colormap, &xcolor))
+ d->pixels[x] = xcolor.pixel;
+ }
+ }
+ }
+ }
+
+ query_colormap(d, screen);
+}
+
+static void init_direct(QColormapPrivate *d, bool ownColormap)
+{
+ if (d->visual->c_class != DirectColor || !ownColormap)
+ return;
+
+ // preallocate 768 on the stack, so that we don't have to malloc
+ // for the common case (<= 24 bpp)
+ QVarLengthArray<XColor, 768> colorTable(d->r_max + d->g_max + d->b_max);
+ int i = 0;
+
+ for (int r = 0; r < d->r_max; ++r) {
+ colorTable[i].red = r << 8 | r;
+ colorTable[i].pixel = r << d->r_shift;
+ colorTable[i].flags = DoRed;
+ ++i;
+ }
+
+ for (int g = 0; g < d->g_max; ++g) {
+ colorTable[i].green = g << 8 | g;
+ colorTable[i].pixel = g << d->g_shift;
+ colorTable[i].flags = DoGreen;
+ ++i;
+ }
+
+ for (int b = 0; b < d->b_max; ++b) {
+ colorTable[i].blue = (b << 8 | b);
+ colorTable[i].pixel = b << d->b_shift;
+ colorTable[i].flags = DoBlue;
+ ++i;
+ }
+
+ XStoreColors(X11->display, d->colormap, colorTable.data(), colorTable.count());
+}
+
+static QColormap **cmaps = 0;
+
+void QColormap::initialize()
+{
+ Display *display = QX11Info::display();
+ const int screens = ScreenCount(display);
+
+ cmaps = new QColormap*[screens];
+
+ for (int i = 0; i < screens; ++i) {
+ cmaps[i] = new QColormap;
+ QColormapPrivate * const d = cmaps[i]->d;
+
+ bool use_stdcmap = false;
+ int color_count = X11->color_count;
+
+ // defaults
+ d->depth = DefaultDepth(display, i);
+ d->colormap = DefaultColormap(display, i);
+ d->defaultColormap = true;
+ d->visual = DefaultVisual(display, i);
+ d->defaultVisual = true;
+
+ Visual *argbVisual = 0;
+
+ if (X11->visual && i == DefaultScreen(display)) {
+ // only use the outside colormap on the default screen
+ d->visual = find_visual(display, i, X11->visual->c_class,
+ XVisualIDFromVisual(X11->visual),
+ &d->depth, &d->defaultVisual);
+ } else if ((X11->visual_class != -1 && X11->visual_class >= 0 && X11->visual_class < 6)
+ || (X11->visual_id != -1)) {
+ // look for a specific visual or type of visual
+ d->visual = find_visual(display, i, X11->visual_class, X11->visual_id,
+ &d->depth, &d->defaultVisual);
+ } else if (QApplication::colorSpec() == QApplication::ManyColor) {
+ // look for a TrueColor w/ a depth higher than 8bpp
+ d->visual = find_visual(display, i, TrueColor, -1, &d->depth, &d->defaultVisual);
+ if (d->depth <= 8) {
+ d->visual = DefaultVisual(display, i);
+ d->defaultVisual = true;
+ color_count = 216;
+ }
+ } else if (!X11->custom_cmap) {
+ XStandardColormap *stdcmap = 0;
+ int ncmaps = 0;
+
+#ifndef QT_NO_XRENDER
+ if (X11->use_xrender) {
+ int nvi;
+ XVisualInfo templ;
+ templ.screen = i;
+ templ.depth = 32;
+ templ.c_class = TrueColor;
+ XVisualInfo *xvi = XGetVisualInfo(X11->display, VisualScreenMask |
+ VisualDepthMask |
+ VisualClassMask, &templ, &nvi);
+ for (int idx = 0; idx < nvi; ++idx) {
+ XRenderPictFormat *format = XRenderFindVisualFormat(X11->display,
+ xvi[idx].visual);
+ if (format->type == PictTypeDirect && format->direct.alphaMask) {
+ argbVisual = xvi[idx].visual;
+ break;
+ }
+ }
+ XFree(xvi);
+ }
+#endif
+ if (XGetRGBColormaps(display, RootWindow(display, i),
+ &stdcmap, &ncmaps, XA_RGB_DEFAULT_MAP)) {
+ if (stdcmap) {
+ for (int c = 0; c < ncmaps; ++c) {
+ if (!stdcmap[c].red_max ||
+ !stdcmap[c].green_max ||
+ !stdcmap[c].blue_max ||
+ !stdcmap[c].red_mult ||
+ !stdcmap[c].green_mult ||
+ !stdcmap[c].blue_mult)
+ continue; // invalid stdcmap
+
+ XVisualInfo proto;
+ proto.visualid = stdcmap[c].visualid;
+ proto.screen = i;
+
+ int nvisuals = 0;
+ XVisualInfo *vi = XGetVisualInfo(display, VisualIDMask | VisualScreenMask,
+ &proto, &nvisuals);
+ if (vi) {
+ if (nvisuals > 0) {
+ use_stdcmap = true;
+
+ d->mode = ((vi[0].visual->c_class < StaticColor)
+ ? Gray
+ : ((vi[0].visual->c_class < TrueColor)
+ ? Indexed
+ : Direct));
+
+ d->depth = vi[0].depth;
+ d->colormap = stdcmap[c].colormap;
+ d->defaultColormap = true;
+ d->visual = vi[0].visual;
+ d->defaultVisual = (d->visual == DefaultVisual(display, i));
+
+ d->r_max = stdcmap[c].red_max + 1;
+ d->g_max = stdcmap[c].green_max + 1;
+ d->b_max = stdcmap[c].blue_max + 1;
+
+ if (d->mode == Direct) {
+ // calculate offsets
+ d->r_shift = lowest_bit(d->visual->red_mask);
+ d->g_shift = lowest_bit(d->visual->green_mask);
+ d->b_shift = lowest_bit(d->visual->blue_mask);
+ } else {
+ d->r_shift = 0;
+ d->g_shift = 0;
+ d->b_shift = 0;
+ }
+ }
+ XFree(vi);
+ }
+ break;
+ }
+ XFree(stdcmap);
+ }
+ }
+ }
+ if (!use_stdcmap) {
+ switch (d->visual->c_class) {
+ case StaticGray:
+ d->mode = Gray;
+
+ d->r_max = d->g_max = d->b_max = d->visual->map_entries;
+ break;
+
+ case XGrayScale:
+ d->mode = Gray;
+
+ // follow precedent set in libXmu...
+ if (color_count != 0)
+ d->r_max = d->g_max = d->b_max = color_count;
+ else if (d->visual->map_entries > 65000)
+ d->r_max = d->g_max = d->b_max = 4096;
+ else if (d->visual->map_entries > 4000)
+ d->r_max = d->g_max = d->b_max = 512;
+ else if (d->visual->map_entries > 250)
+ d->r_max = d->g_max = d->b_max = 12;
+ else
+ d->r_max = d->g_max = d->b_max = 4;
+ break;
+
+ case StaticColor:
+ d->mode = Indexed;
+
+ d->r_max = right_align(d->visual->red_mask) + 1;
+ d->g_max = right_align(d->visual->green_mask) + 1;
+ d->b_max = right_align(d->visual->blue_mask) + 1;
+ break;
+
+ case PseudoColor:
+ d->mode = Indexed;
+
+ // follow precedent set in libXmu...
+ if (color_count != 0)
+ d->r_max = d->g_max = d->b_max = cube_root(color_count);
+ else if (d->visual->map_entries > 65000)
+ d->r_max = d->g_max = d->b_max = 27;
+ else if (d->visual->map_entries > 4000)
+ d->r_max = d->g_max = d->b_max = 12;
+ else if (d->visual->map_entries > 250)
+ d->r_max = d->g_max = d->b_max = cube_root(d->visual->map_entries - 125);
+ else
+ d->r_max = d->g_max = d->b_max = cube_root(d->visual->map_entries);
+ break;
+
+ case TrueColor:
+ case DirectColor:
+ d->mode = Direct;
+
+ d->r_max = right_align(d->visual->red_mask) + 1;
+ d->g_max = right_align(d->visual->green_mask) + 1;
+ d->b_max = right_align(d->visual->blue_mask) + 1;
+
+ d->r_shift = lowest_bit(d->visual->red_mask);
+ d->g_shift = lowest_bit(d->visual->green_mask);
+ d->b_shift = lowest_bit(d->visual->blue_mask);
+ break;
+ }
+ }
+
+ bool ownColormap = false;
+ if (X11->colormap && i == DefaultScreen(display)) {
+ // only use the outside colormap on the default screen
+ d->colormap = X11->colormap;
+ d->defaultColormap = (d->colormap == DefaultColormap(display, i));
+ } else if ((!use_stdcmap
+ && (((d->visual->c_class & 1) && X11->custom_cmap)
+ || d->visual != DefaultVisual(display, i)))
+ || d->visual->c_class == DirectColor) {
+ // allocate custom colormap (we always do this when using DirectColor visuals)
+ d->colormap =
+ XCreateColormap(display, RootWindow(display, i), d->visual,
+ d->visual->c_class == DirectColor ? AllocAll : AllocNone);
+ d->defaultColormap = false;
+ ownColormap = true;
+ }
+
+ switch (d->mode) {
+ case Gray:
+ init_gray(d, i);
+ break;
+ case Indexed:
+ init_indexed(d, i);
+ break;
+ case Direct:
+ init_direct(d, ownColormap);
+ break;
+ }
+
+ QX11InfoData *screen = X11->screens + i;
+ screen->depth = d->depth;
+ screen->visual = d->visual;
+ screen->defaultVisual = d->defaultVisual;
+ screen->colormap = d->colormap;
+ screen->defaultColormap = d->defaultColormap;
+ screen->cells = screen->visual->map_entries;
+
+ if (argbVisual) {
+ X11->argbVisuals[i] = argbVisual;
+ X11->argbColormaps[i] = XCreateColormap(display, RootWindow(display, i), argbVisual, AllocNone);
+ }
+
+ // ###
+ // We assume that 8bpp == pseudocolor, but this is not
+ // always the case (according to the X server), so we need
+ // to make sure that our internal data is setup in a way
+ // that is compatible with our assumptions
+ if (screen->visual->c_class == TrueColor && screen->depth == 8 && screen->cells == 8)
+ screen->cells = 256;
+ }
+}
+
+void QColormap::cleanup()
+{
+ Display *display = QX11Info::display();
+ const int screens = ScreenCount(display);
+
+ for (int i = 0; i < screens; ++i)
+ delete cmaps[i];
+
+ delete [] cmaps;
+ cmaps = 0;
+}
+
+
+QColormap QColormap::instance(int screen)
+{
+ if (screen == -1)
+ screen = QX11Info::appScreen();
+ return *cmaps[screen];
+}
+
+/*! \internal
+ Constructs a new colormap.
+*/
+QColormap::QColormap()
+ : d(new QColormapPrivate)
+{}
+
+QColormap::QColormap(const QColormap &colormap)
+ :d (colormap.d)
+{ d->ref.ref(); }
+
+QColormap::~QColormap()
+{
+ if (!d->ref.deref()) {
+ if (!d->defaultColormap)
+ XFreeColormap(QX11Info::display(), d->colormap);
+ delete d;
+ }
+}
+
+QColormap::Mode QColormap::mode() const
+{ return d->mode; }
+
+int QColormap::depth() const
+{ return d->depth; }
+
+int QColormap::size() const
+{
+ return (d->mode == Gray
+ ? d->r_max
+ : (d->mode == Indexed
+ ? d->r_max * d->g_max * d->b_max
+ : -1));
+}
+
+uint QColormap::pixel(const QColor &color) const
+{
+ const QColor c = color.toRgb();
+ const uint r = (c.ct.argb.red * d->r_max) >> 16;
+ const uint g = (c.ct.argb.green * d->g_max) >> 16;
+ const uint b = (c.ct.argb.blue * d->b_max) >> 16;
+ if (d->mode != Direct) {
+ if (d->mode == Gray)
+ return d->pixels.at((r * 30 + g * 59 + b * 11) / 100);
+ return d->pixels.at(r * d->g_max * d->b_max + g * d->b_max + b);
+ }
+ return (r << d->r_shift) + (g << d->g_shift) + (b << d->b_shift);
+}
+
+const QColor QColormap::colorAt(uint pixel) const
+{
+ if (d->mode != Direct) {
+ Q_ASSERT(pixel <= (uint)d->colors.size());
+ return d->colors.at(pixel);
+ }
+
+ const int r = (((pixel & d->visual->red_mask) >> d->r_shift) << 8) / d->r_max;
+ const int g = (((pixel & d->visual->green_mask) >> d->g_shift) << 8) / d->g_max;
+ const int b = (((pixel & d->visual->blue_mask) >> d->b_shift) << 8) / d->b_max;
+ return QColor(r, g, b);
+}
+
+const QVector<QColor> QColormap::colormap() const
+{ return d->colors; }
+
+QColormap &QColormap::operator=(const QColormap &colormap)
+{
+ qAtomicAssign(d, colormap.d);
+ return *this;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qcssutil.cpp b/src/gui/painting/qcssutil.cpp
new file mode 100644
index 0000000000..729cff504e
--- /dev/null
+++ b/src/gui/painting/qcssutil.cpp
@@ -0,0 +1,408 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcssutil_p.h"
+#include "private/qcssparser_p.h"
+#include "qpainter.h"
+#include <qmath.h>
+
+#ifndef QT_NO_CSSPARSER
+
+QT_BEGIN_NAMESPACE
+
+using namespace QCss;
+
+static QPen qPenFromStyle(const QBrush& b, qreal width, BorderStyle s)
+{
+ Qt::PenStyle ps = Qt::NoPen;
+
+ switch (s) {
+ case BorderStyle_Dotted:
+ ps = Qt::DotLine;
+ break;
+ case BorderStyle_Dashed:
+ ps = width == 1 ? Qt::DotLine : Qt::DashLine;
+ break;
+ case BorderStyle_DotDash:
+ ps = Qt::DashDotLine;
+ break;
+ case BorderStyle_DotDotDash:
+ ps = Qt::DashDotDotLine;
+ break;
+ case BorderStyle_Inset:
+ case BorderStyle_Outset:
+ case BorderStyle_Solid:
+ ps = Qt::SolidLine;
+ break;
+ default:
+ break;
+ }
+
+ return QPen(b, width, ps, Qt::FlatCap);
+}
+
+void qDrawRoundedCorners(QPainter *p, qreal x1, qreal y1, qreal x2, qreal y2,
+ const QSizeF& r1, const QSizeF& r2,
+ Edge edge, BorderStyle s, QBrush c)
+{
+ const qreal pw = (edge == TopEdge || edge == BottomEdge) ? y2-y1 : x2-x1;
+ if (s == BorderStyle_Double) {
+ qreal wby3 = pw/3;
+ switch (edge) {
+ case TopEdge:
+ case BottomEdge:
+ qDrawRoundedCorners(p, x1, y1, x2, y1+wby3, r1, r2, edge, BorderStyle_Solid, c);
+ qDrawRoundedCorners(p, x1, y2-wby3, x2, y2, r1, r2, edge, BorderStyle_Solid, c);
+ break;
+ case LeftEdge:
+ qDrawRoundedCorners(p, x1, y1+1, x1+wby3, y2, r1, r2, LeftEdge, BorderStyle_Solid, c);
+ qDrawRoundedCorners(p, x2-wby3, y1+1, x2, y2, r1, r2, LeftEdge, BorderStyle_Solid, c);
+ break;
+ case RightEdge:
+ qDrawRoundedCorners(p, x1, y1+1, x1+wby3, y2, r1, r2, RightEdge, BorderStyle_Solid, c);
+ qDrawRoundedCorners(p, x2-wby3, y1+1, x2, y2, r1, r2, RightEdge, BorderStyle_Solid, c);
+ break;
+ default:
+ break;
+ }
+ return;
+ } else if (s == BorderStyle_Ridge || s == BorderStyle_Groove) {
+ BorderStyle s1, s2;
+ if (s == BorderStyle_Groove) {
+ s1 = BorderStyle_Inset;
+ s2 = BorderStyle_Outset;
+ } else {
+ s1 = BorderStyle_Outset;
+ s2 = BorderStyle_Inset;
+ }
+ int pwby2 = qRound(pw/2);
+ switch (edge) {
+ case TopEdge:
+ qDrawRoundedCorners(p, x1, y1, x2, y1 + pwby2, r1, r2, TopEdge, s1, c);
+ qDrawRoundedCorners(p, x1, y1 + pwby2, x2, y2, r1, r2, TopEdge, s2, c);
+ break;
+ case BottomEdge:
+ qDrawRoundedCorners(p, x1, y1 + pwby2, x2, y2, r1, r2, BottomEdge, s1, c);
+ qDrawRoundedCorners(p, x1, y1, x2, y2-pwby2, r1, r2, BottomEdge, s2, c);
+ break;
+ case LeftEdge:
+ qDrawRoundedCorners(p, x1, y1, x1 + pwby2, y2, r1, r2, LeftEdge, s1, c);
+ qDrawRoundedCorners(p, x1 + pwby2, y1, x2, y2, r1, r2, LeftEdge, s2, c);
+ break;
+ case RightEdge:
+ qDrawRoundedCorners(p, x1 + pwby2, y1, x2, y2, r1, r2, RightEdge, s1, c);
+ qDrawRoundedCorners(p, x1, y1, x2 - pwby2, y2, r1, r2, RightEdge, s2, c);
+ break;
+ default:
+ break;
+ }
+ } else if ((s == BorderStyle_Outset && (edge == TopEdge || edge == LeftEdge))
+ || (s == BorderStyle_Inset && (edge == BottomEdge || edge == RightEdge)))
+ c = c.color().lighter();
+
+ p->save();
+ qreal pwby2 = pw/2;
+ p->setBrush(Qt::NoBrush);
+ QPen pen = qPenFromStyle(c, pw, s);
+ pen.setCapStyle(Qt::SquareCap); // this eliminates the offby1 errors that we might hit below
+ p->setPen(pen);
+ switch (edge) {
+ case TopEdge:
+ if (!r1.isEmpty())
+ p->drawArc(QRectF(x1 - r1.width() + pwby2, y1 + pwby2,
+ 2*r1.width() - pw, 2*r1.height() - pw), 135*16, -45*16);
+ if (!r2.isEmpty())
+ p->drawArc(QRectF(x2 - r2.width() + pwby2, y1 + pwby2,
+ 2*r2.width() - pw, 2*r2.height() - pw), 45*16, 45*16);
+ break;
+ case BottomEdge:
+ if (!r1.isEmpty())
+ p->drawArc(QRectF(x1 - r1.width() + pwby2, y2 - 2*r1.height() + pwby2,
+ 2*r1.width() - pw, 2*r1.height() - pw), -90 * 16, -45 * 16);
+ if (!r2.isEmpty())
+ p->drawArc(QRectF(x2 - r2.width() + pwby2, y2 - 2*r2.height() + pwby2,
+ 2*r2.width() - pw, 2*r2.height() - pw), -90 * 16, 45 * 16);
+ break;
+ case LeftEdge:
+ if (!r1.isEmpty())
+ p->drawArc(QRectF(x1 + pwby2, y1 - r1.height() + pwby2,
+ 2*r1.width() - pw, 2*r1.height() - pw), 135*16, 45*16);
+ if (!r2.isEmpty())
+ p->drawArc(QRectF(x1 + pwby2, y2 - r2.height() + pwby2,
+ 2*r2.width() - pw, 2*r2.height() - pw), 180*16, 45*16);
+ break;
+ case RightEdge:
+ if (!r1.isEmpty())
+ p->drawArc(QRectF(x2 - 2*r1.width() + pwby2, y1 - r1.height() + pwby2,
+ 2*r1.width() - pw, 2*r1.height() - pw), 45*16, -45*16);
+ if (!r2.isEmpty())
+ p->drawArc(QRectF(x2 - 2*r2.width() + pwby2, y2 - r2.height() + pwby2,
+ 2*r2.width() - pw, 2*r2.height() - pw), 315*16, 45*16);
+ break;
+ default:
+ break;
+ }
+ p->restore();
+}
+
+
+void qDrawEdge(QPainter *p, qreal x1, qreal y1, qreal x2, qreal y2, qreal dw1, qreal dw2,
+ QCss::Edge edge, QCss::BorderStyle style, QBrush c)
+{
+ p->save();
+ const qreal width = (edge == TopEdge || edge == BottomEdge) ? (y2-y1) : (x2-x1);
+
+ if (width <= 2 && style == BorderStyle_Double)
+ style = BorderStyle_Solid;
+
+ switch (style) {
+ case BorderStyle_Inset:
+ case BorderStyle_Outset:
+ if ((style == BorderStyle_Outset && (edge == TopEdge || edge == LeftEdge))
+ || (style == BorderStyle_Inset && (edge == BottomEdge || edge == RightEdge)))
+ c = c.color().lighter();
+ // fall through!
+ case BorderStyle_Solid: {
+ p->setPen(Qt::NoPen);
+ p->setBrush(c);
+ if (width == 1 || (dw1 == 0 && dw2 == 0)) {
+ p->drawRect(QRectF(x1, y1, x2-x1, y2-y1));
+ } else { // draw trapezoid
+ QPolygonF quad;
+ switch (edge) {
+ case TopEdge:
+ quad << QPointF(x1, y1) << QPointF(x1 + dw1, y2)
+ << QPointF(x2 - dw2, y2) << QPointF(x2, y1);
+ break;
+ case BottomEdge:
+ quad << QPointF(x1 + dw1, y1) << QPointF(x1, y2)
+ << QPointF(x2, y2) << QPointF(x2 - dw2, y1);
+ break;
+ case LeftEdge:
+ quad << QPointF(x1, y1) << QPointF(x1, y2)
+ << QPointF(x2, y2 - dw2) << QPointF(x2, y1 + dw1);
+ break;
+ case RightEdge:
+ quad << QPointF(x1, y1 + dw1) << QPointF(x1, y2 - dw2)
+ << QPointF(x2, y2) << QPointF(x2, y1);
+ break;
+ default:
+ break;
+ }
+ p->drawConvexPolygon(quad);
+ }
+ break;
+ }
+ case BorderStyle_Dotted:
+ case BorderStyle_Dashed:
+ case BorderStyle_DotDash:
+ case BorderStyle_DotDotDash:
+ p->setPen(qPenFromStyle(c, width, style));
+ if (width == 1)
+ p->drawLine(QLineF(x1, y1, x2 - 1, y2 - 1));
+ else if (edge == TopEdge || edge == BottomEdge)
+ p->drawLine(QLineF(x1 + width/2, (y1 + y2)/2, x2 - width/2, (y1 + y2)/2));
+ else
+ p->drawLine(QLineF((x1+x2)/2, y1 + width/2, (x1+x2)/2, y2 - width/2));
+ break;
+
+ case BorderStyle_Double: {
+ int wby3 = qRound(width/3);
+ int dw1by3 = qRound(dw1/3);
+ int dw2by3 = qRound(dw2/3);
+ switch (edge) {
+ case TopEdge:
+ qDrawEdge(p, x1, y1, x2, y1 + wby3, dw1by3, dw2by3, TopEdge, BorderStyle_Solid, c);
+ qDrawEdge(p, x1 + dw1 - dw1by3, y2 - wby3, x2 - dw2 + dw1by3, y2,
+ dw1by3, dw2by3, TopEdge, BorderStyle_Solid, c);
+ break;
+ case LeftEdge:
+ qDrawEdge(p, x1, y1, x1 + wby3, y2, dw1by3, dw2by3, LeftEdge, BorderStyle_Solid, c);
+ qDrawEdge(p, x2 - wby3, y1 + dw1 - dw1by3, x2, y2 - dw2 + dw2by3, dw1by3, dw2by3,
+ LeftEdge, BorderStyle_Solid, c);
+ break;
+ case BottomEdge:
+ qDrawEdge(p, x1 + dw1 - dw1by3, y1, x2 - dw2 + dw2by3, y1 + wby3, dw1by3, dw2by3,
+ BottomEdge, BorderStyle_Solid, c);
+ qDrawEdge(p, x1, y2 - wby3, x2, y2, dw1by3, dw2by3, BottomEdge, BorderStyle_Solid, c);
+ break;
+ case RightEdge:
+ qDrawEdge(p, x2 - wby3, y1, x2, y2, dw1by3, dw2by3, RightEdge, BorderStyle_Solid, c);
+ qDrawEdge(p, x1, y1 + dw1 - dw1by3, x1 + wby3, y2 - dw2 + dw2by3, dw1by3, dw2by3,
+ RightEdge, BorderStyle_Solid, c);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case BorderStyle_Ridge:
+ case BorderStyle_Groove: {
+ BorderStyle s1, s2;
+ if (style == BorderStyle_Groove) {
+ s1 = BorderStyle_Inset;
+ s2 = BorderStyle_Outset;
+ } else {
+ s1 = BorderStyle_Outset;
+ s2 = BorderStyle_Inset;
+ }
+ int dw1by2 = qFloor(dw1/2), dw2by2 = qFloor(dw2/2);
+ int wby2 = qRound(width/2);
+ switch (edge) {
+ case TopEdge:
+ qDrawEdge(p, x1, y1, x2, y1 + wby2, dw1by2, dw2by2, TopEdge, s1, c);
+ qDrawEdge(p, x1 + dw1by2, y1 + wby2, x2 - dw2by2, y2, dw1by2, dw2by2, TopEdge, s2, c);
+ break;
+ case BottomEdge:
+ qDrawEdge(p, x1, y1 + wby2, x2, y2, dw1by2, dw2by2, BottomEdge, s1, c);
+ qDrawEdge(p, x1 + dw1by2, y1, x2 - dw2by2, y1 + wby2, dw1by2, dw2by2, BottomEdge, s2, c);
+ break;
+ case LeftEdge:
+ qDrawEdge(p, x1, y1, x1 + wby2, y2, dw1by2, dw2by2, LeftEdge, s1, c);
+ qDrawEdge(p, x1 + wby2, y1 + dw1by2, x2, y2 - dw2by2, dw1by2, dw2by2, LeftEdge, s2, c);
+ break;
+ case RightEdge:
+ qDrawEdge(p, x1 + wby2, y1, x2, y2, dw1by2, dw2by2, RightEdge, s1, c);
+ qDrawEdge(p, x1, y1 + dw1by2, x1 + wby2, y2 - dw2by2, dw1by2, dw2by2, RightEdge, s2, c);
+ break;
+ default:
+ break;
+ }
+ }
+ default:
+ break;
+ }
+ p->restore();
+}
+
+void qNormalizeRadii(const QRect &br, const QSize *radii,
+ QSize *tlr, QSize *trr, QSize *blr, QSize *brr)
+{
+ *tlr = radii[0].expandedTo(QSize(0, 0));
+ *trr = radii[1].expandedTo(QSize(0, 0));
+ *blr = radii[2].expandedTo(QSize(0, 0));
+ *brr = radii[3].expandedTo(QSize(0, 0));
+ if (tlr->width() + trr->width() > br.width())
+ *tlr = *trr = QSize(0, 0);
+ if (blr->width() + brr->width() > br.width())
+ *blr = *brr = QSize(0, 0);
+ if (tlr->height() + blr->height() > br.height())
+ *tlr = *blr = QSize(0, 0);
+ if (trr->height() + brr->height() > br.height())
+ *trr = *brr = QSize(0, 0);
+}
+
+// Determines if Edge e1 draws over Edge e2. Depending on this trapezoids or rectanges are drawn
+static bool paintsOver(const QCss::BorderStyle *styles, const QBrush *colors, QCss::Edge e1, QCss::Edge e2)
+{
+ QCss::BorderStyle s1 = styles[e1];
+ QCss::BorderStyle s2 = styles[e2];
+
+ if (s2 == BorderStyle_None || colors[e2] == Qt::transparent)
+ return true;
+
+ if ((s1 == BorderStyle_Solid && s2 == BorderStyle_Solid) && (colors[e1] == colors[e2]))
+ return true;
+
+ return false;
+}
+
+void qDrawBorder(QPainter *p, const QRect &rect, const QCss::BorderStyle *styles,
+ const int *borders, const QBrush *colors, const QSize *radii)
+{
+ const QRectF br(rect);
+ QSize tlr, trr, blr, brr;
+ qNormalizeRadii(rect, radii, &tlr, &trr, &blr, &brr);
+
+ // Drawn in increasing order of precendence
+ if (styles[BottomEdge] != BorderStyle_None && borders[BottomEdge] > 0) {
+ qreal dw1 = (blr.width() || paintsOver(styles, colors, BottomEdge, LeftEdge)) ? 0 : borders[LeftEdge];
+ qreal dw2 = (brr.width() || paintsOver(styles, colors, BottomEdge, RightEdge)) ? 0 : borders[RightEdge];
+ qreal x1 = br.x() + blr.width();
+ qreal y1 = br.y() + br.height() - borders[BottomEdge];
+ qreal x2 = br.x() + br.width() - brr.width();
+ qreal y2 = br.y() + br.height() ;
+
+ qDrawEdge(p, x1, y1, x2, y2, dw1, dw2, BottomEdge, styles[BottomEdge], colors[BottomEdge]);
+ if (blr.width() || brr.width())
+ qDrawRoundedCorners(p, x1, y1, x2, y2, blr, brr, BottomEdge, styles[BottomEdge], colors[BottomEdge]);
+ }
+ if (styles[RightEdge] != BorderStyle_None && borders[RightEdge] > 0) {
+ qreal dw1 = (trr.height() || paintsOver(styles, colors, RightEdge, TopEdge)) ? 0 : borders[TopEdge];
+ qreal dw2 = (brr.height() || paintsOver(styles, colors, RightEdge, BottomEdge)) ? 0 : borders[BottomEdge];
+ qreal x1 = br.x() + br.width() - borders[RightEdge];
+ qreal y1 = br.y() + trr.height();
+ qreal x2 = br.x() + br.width();
+ qreal y2 = br.y() + br.height() - brr.height();
+
+ qDrawEdge(p, x1, y1, x2, y2, dw1, dw2, RightEdge, styles[RightEdge], colors[RightEdge]);
+ if (trr.height() || brr.height())
+ qDrawRoundedCorners(p, x1, y1, x2, y2, trr, brr, RightEdge, styles[RightEdge], colors[RightEdge]);
+ }
+ if (styles[LeftEdge] != BorderStyle_None && borders[LeftEdge] > 0) {
+ qreal dw1 = (tlr.height() || paintsOver(styles, colors, LeftEdge, TopEdge)) ? 0 : borders[TopEdge];
+ qreal dw2 = (blr.height() || paintsOver(styles, colors, LeftEdge, BottomEdge)) ? 0 : borders[BottomEdge];
+ qreal x1 = br.x();
+ qreal y1 = br.y() + tlr.height();
+ qreal x2 = br.x() + borders[LeftEdge];
+ qreal y2 = br.y() + br.height() - blr.height();
+
+ qDrawEdge(p, x1, y1, x2, y2, dw1, dw2, LeftEdge, styles[LeftEdge], colors[LeftEdge]);
+ if (tlr.height() || blr.height())
+ qDrawRoundedCorners(p, x1, y1, x2, y2, tlr, blr, LeftEdge, styles[LeftEdge], colors[LeftEdge]);
+ }
+ if (styles[TopEdge] != BorderStyle_None && borders[TopEdge] > 0) {
+ qreal dw1 = (tlr.width() || paintsOver(styles, colors, TopEdge, LeftEdge)) ? 0 : borders[LeftEdge];
+ qreal dw2 = (trr.width() || paintsOver(styles, colors, TopEdge, RightEdge)) ? 0 : borders[RightEdge];
+ qreal x1 = br.x() + tlr.width();
+ qreal y1 = br.y();
+ qreal x2 = br.left() + br.width() - trr.width();
+ qreal y2 = br.y() + borders[TopEdge];
+
+ qDrawEdge(p, x1, y1, x2, y2, dw1, dw2, TopEdge, styles[TopEdge], colors[TopEdge]);
+ if (tlr.width() || trr.width())
+ qDrawRoundedCorners(p, x1, y1, x2, y2, tlr, trr, TopEdge, styles[TopEdge], colors[TopEdge]);
+ }
+}
+
+#endif //QT_NO_CSSPARSER
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qcssutil_p.h b/src/gui/painting/qcssutil_p.h
new file mode 100644
index 0000000000..80606b4cb9
--- /dev/null
+++ b/src/gui/painting/qcssutil_p.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCSSUTIL_P_H
+#define QCSSUTIL_P_H
+
+#include "QtCore/qglobal.h"
+
+#ifndef QT_NO_CSSPARSER
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qcssparser_p.h"
+#include "QtCore/qsize.h"
+
+QT_BEGIN_NAMESPACE
+
+class QPainter;
+
+extern void qDrawEdge(QPainter *p, qreal x1, qreal y1, qreal x2, qreal y2, qreal dw1, qreal dw2,
+ QCss::Edge edge, QCss::BorderStyle style, QBrush c);
+
+extern void qDrawRoundedCorners(QPainter *p, qreal x1, qreal y1, qreal x2, qreal y2,
+ const QSizeF& r1, const QSizeF& r2,
+ QCss::Edge edge, QCss::BorderStyle s, QBrush c);
+
+extern void qDrawBorder(QPainter *p, const QRect &rect, const QCss::BorderStyle *styles,
+ const int *borders, const QBrush *colors, const QSize *radii);
+
+extern void qNormalizeRadii(const QRect &br, const QSize *radii,
+ QSize *tlr, QSize *trr, QSize *blr, QSize *brr);
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_CSSPARSER
+
+#endif // QCSSUTIL_P_H
diff --git a/src/gui/painting/qcups.cpp b/src/gui/painting/qcups.cpp
new file mode 100644
index 0000000000..24faa2f300
--- /dev/null
+++ b/src/gui/painting/qcups.cpp
@@ -0,0 +1,401 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <qdebug.h>
+#include "qcups_p.h"
+
+#ifndef QT_NO_CUPS
+
+#ifndef QT_LINUXBASE // LSB merges everything into cups.h
+# include <cups/language.h>
+#endif
+#include <qtextcodec.h>
+
+QT_BEGIN_NAMESPACE
+
+typedef int (*CupsGetDests)(cups_dest_t **dests);
+typedef void (*CupsFreeDests)(int num_dests, cups_dest_t *dests);
+typedef const char* (*CupsGetPPD)(const char *printer);
+typedef int (*CupsMarkOptions)(ppd_file_t *ppd, int num_options, cups_option_t *options);
+typedef ppd_file_t* (*PPDOpenFile)(const char *filename);
+typedef void (*PPDMarkDefaults)(ppd_file_t *ppd);
+typedef int (*PPDMarkOption)(ppd_file_t *ppd, const char *keyword, const char *option);
+typedef void (*PPDClose)(ppd_file_t *ppd);
+typedef int (*PPDMarkOption)(ppd_file_t *ppd, const char *keyword, const char *option);
+typedef void (*CupsFreeOptions)(int num_options, cups_option_t *options);
+typedef void (*CupsSetDests)(int num_dests, cups_dest_t *dests);
+typedef cups_lang_t* (*CupsLangGet)(const char *language);
+typedef const char* (*CupsLangEncoding)(cups_lang_t *language);
+typedef int (*CupsAddOption)(const char *name, const char *value, int num_options, cups_option_t **options);
+typedef int (*CupsTempFd)(char *name, int len);
+typedef int (*CupsPrintFile)(const char * name, const char * filename, const char * title, int num_options, cups_option_t * options);
+
+static bool cupsLoaded = false;
+static int qt_cups_num_printers = 0;
+static CupsGetDests _cupsGetDests = 0;
+static CupsFreeDests _cupsFreeDests = 0;
+static CupsGetPPD _cupsGetPPD = 0;
+static PPDOpenFile _ppdOpenFile = 0;
+static PPDMarkDefaults _ppdMarkDefaults = 0;
+static PPDClose _ppdClose = 0;
+static CupsMarkOptions _cupsMarkOptions = 0;
+static PPDMarkOption _ppdMarkOption = 0;
+static CupsFreeOptions _cupsFreeOptions = 0;
+static CupsSetDests _cupsSetDests = 0;
+static CupsLangGet _cupsLangGet = 0;
+static CupsLangEncoding _cupsLangEncoding = 0;
+static CupsAddOption _cupsAddOption = 0;
+static CupsTempFd _cupsTempFd = 0;
+static CupsPrintFile _cupsPrintFile = 0;
+
+static void resolveCups()
+{
+ QLibrary cupsLib(QLatin1String("cups"), 2);
+ if(cupsLib.load()) {
+ _cupsGetDests = (CupsGetDests) cupsLib.resolve("cupsGetDests");
+ _cupsFreeDests = (CupsFreeDests) cupsLib.resolve("cupsFreeDests");
+ _cupsGetPPD = (CupsGetPPD) cupsLib.resolve("cupsGetPPD");
+ _cupsLangGet = (CupsLangGet) cupsLib.resolve("cupsLangGet");
+ _cupsLangEncoding = (CupsLangEncoding) cupsLib.resolve("cupsLangEncoding");
+ _ppdOpenFile = (PPDOpenFile) cupsLib.resolve("ppdOpenFile");
+ _ppdMarkDefaults = (PPDMarkDefaults) cupsLib.resolve("ppdMarkDefaults");
+ _ppdClose = (PPDClose) cupsLib.resolve("ppdClose");
+ _cupsMarkOptions = (CupsMarkOptions) cupsLib.resolve("cupsMarkOptions");
+ _ppdMarkOption = (PPDMarkOption) cupsLib.resolve("ppdMarkOption");
+ _cupsFreeOptions = (CupsFreeOptions) cupsLib.resolve("cupsFreeOptions");
+ _cupsSetDests = (CupsSetDests) cupsLib.resolve("cupsSetDests");
+ _cupsAddOption = (CupsAddOption) cupsLib.resolve("cupsAddOption");
+ _cupsTempFd = (CupsTempFd) cupsLib.resolve("cupsTempFd");
+ _cupsPrintFile = (CupsPrintFile) cupsLib.resolve("cupsPrintFile");
+
+ if (_cupsGetDests && _cupsFreeDests) {
+ cups_dest_t *printers;
+ int num_printers = _cupsGetDests(&printers);
+ if (num_printers)
+ _cupsFreeDests(num_printers, printers);
+ qt_cups_num_printers = num_printers;
+ }
+ }
+ cupsLoaded = true;
+}
+
+// ================ CUPS Support class ========================
+
+QCUPSSupport::QCUPSSupport()
+ :
+ prnCount(0),
+ printers(0),
+ page_sizes(0),
+ currPrinterIndex(0),
+ currPPD(0)
+{
+ if (!cupsLoaded)
+ resolveCups();
+
+ // getting all available printers
+ if (!isAvailable())
+ return;
+
+ prnCount = _cupsGetDests(&printers);
+
+ for (int i = 0; i < prnCount; ++i) {
+ if (printers[i].is_default) {
+ currPrinterIndex = i;
+ setCurrentPrinter(i);
+ break;
+ }
+ }
+
+#ifndef QT_NO_TEXTCODEC
+ cups_lang_t *cupsLang = _cupsLangGet(0);
+ codec = QTextCodec::codecForName(_cupsLangEncoding(cupsLang));
+ if (!codec)
+ codec = QTextCodec::codecForLocale();
+#endif
+}
+
+QCUPSSupport::~QCUPSSupport()
+{
+ if (currPPD)
+ _ppdClose(currPPD);
+ if (prnCount)
+ _cupsFreeDests(prnCount, printers);
+}
+
+int QCUPSSupport::availablePrintersCount() const
+{
+ return prnCount;
+}
+
+const cups_dest_t* QCUPSSupport::availablePrinters() const
+{
+ return printers;
+}
+
+const ppd_file_t* QCUPSSupport::currentPPD() const
+{
+ return currPPD;
+}
+
+const ppd_file_t* QCUPSSupport::setCurrentPrinter(int index)
+{
+ Q_ASSERT(index >= 0 && index <= prnCount);
+ if (index == prnCount)
+ return 0;
+
+ currPrinterIndex = index;
+
+ if (currPPD)
+ _ppdClose(currPPD);
+ currPPD = 0;
+ page_sizes = 0;
+
+ const char *ppdFile = _cupsGetPPD(printers[index].name);
+
+ if (!ppdFile)
+ return 0;
+
+ currPPD = _ppdOpenFile(ppdFile);
+ unlink(ppdFile);
+
+ // marking default options
+ _ppdMarkDefaults(currPPD);
+
+ // marking options explicitly set
+ _cupsMarkOptions(currPPD, printers[currPrinterIndex].num_options, printers[currPrinterIndex].options);
+
+ // getting pointer to page sizes
+ page_sizes = ppdOption("PageSize");
+
+ return currPPD;
+}
+
+int QCUPSSupport::currentPrinterIndex() const
+{
+ return currPrinterIndex;
+}
+
+bool QCUPSSupport::isAvailable()
+{
+ if(!cupsLoaded)
+ resolveCups();
+
+ return _cupsGetDests &&
+ _cupsFreeDests &&
+ _cupsGetPPD &&
+ _ppdOpenFile &&
+ _ppdMarkDefaults &&
+ _ppdClose &&
+ _cupsMarkOptions &&
+ _ppdMarkOption &&
+ _cupsFreeOptions &&
+ _cupsSetDests &&
+ _cupsLangGet &&
+ _cupsLangEncoding &&
+ _cupsAddOption &&
+ (qt_cups_num_printers > 0);
+}
+
+const ppd_option_t* QCUPSSupport::ppdOption(const char *key) const
+{
+ if (currPPD) {
+ for (int gr = 0; gr < currPPD->num_groups; ++gr) {
+ for (int opt = 0; opt < currPPD->groups[gr].num_options; ++opt) {
+ if (qstrcmp(currPPD->groups[gr].options[opt].keyword, key) == 0)
+ return &currPPD->groups[gr].options[opt];
+ }
+ }
+ }
+ return 0;
+}
+
+const cups_option_t* QCUPSSupport::printerOption(const QString &key) const
+{
+ for (int i = 0; i < printers[currPrinterIndex].num_options; ++i) {
+ if (QLatin1String(printers[currPrinterIndex].options[i].name) == key)
+ return &printers[currPrinterIndex].options[i];
+ }
+ return 0;
+}
+
+const ppd_option_t* QCUPSSupport::pageSizes() const
+{
+ return page_sizes;
+}
+
+int QCUPSSupport::markOption(const char* name, const char* value)
+{
+ return _ppdMarkOption(currPPD, name, value);
+}
+
+void QCUPSSupport::saveOptions(QList<const ppd_option_t*> options, QList<const char*> markedOptions)
+{
+ int oldOptionCount = printers[currPrinterIndex].num_options;
+ cups_option_t* oldOptions = printers[currPrinterIndex].options;
+
+ int newOptionCount = 0;
+ cups_option_t* newOptions = 0;
+
+ // copying old options that are not on the new list
+ for (int i = 0; i < oldOptionCount; ++i) {
+ bool contains = false;
+ for (int j = 0; j < options.count(); ++j) {
+ if (qstrcmp(options.at(j)->keyword, oldOptions[i].name) == 0) {
+ contains = true;
+ break;
+ }
+ }
+
+ if (!contains) {
+ newOptionCount = _cupsAddOption(oldOptions[i].name, oldOptions[i].value, newOptionCount, &newOptions);
+ }
+ }
+
+ // we can release old option list
+ _cupsFreeOptions(oldOptionCount, oldOptions);
+
+ // adding marked options
+ for (int i = 0; i < markedOptions.count(); ++i) {
+ const char* name = markedOptions.at(i);
+ ++i;
+ newOptionCount = _cupsAddOption(name, markedOptions.at(i), newOptionCount, &newOptions);
+ }
+
+ // placing the new option list
+ printers[currPrinterIndex].num_options = newOptionCount;
+ printers[currPrinterIndex].options = newOptions;
+
+ // saving new default values
+ _cupsSetDests(prnCount, printers);
+}
+
+QRect QCUPSSupport::paperRect(const char *choice) const
+{
+ if (!currPPD)
+ return QRect();
+ for (int i = 0; i < currPPD->num_sizes; ++i) {
+ if (qstrcmp(currPPD->sizes[i].name, choice) == 0)
+ return QRect(0, 0, qRound(currPPD->sizes[i].width), qRound(currPPD->sizes[i].length));
+ }
+ return QRect();
+}
+
+QRect QCUPSSupport::pageRect(const char *choice) const
+{
+ if (!currPPD)
+ return QRect();
+ for (int i = 0; i < currPPD->num_sizes; ++i) {
+ if (qstrcmp(currPPD->sizes[i].name, choice) == 0)
+ return QRect(qRound(currPPD->sizes[i].left),
+ qRound(currPPD->sizes[i].length - currPPD->sizes[i].top),
+ qRound(currPPD->sizes[i].right - currPPD->sizes[i].left),
+ qRound(currPPD->sizes[i].top - currPPD->sizes[i].bottom));
+ }
+ return QRect();
+}
+
+QStringList QCUPSSupport::options() const
+{
+ QStringList list;
+ collectMarkedOptions(list);
+ return list;
+}
+
+bool QCUPSSupport::printerHasPPD(const char *printerName)
+{
+ if (!isAvailable())
+ return false;
+ const char *ppdFile = _cupsGetPPD(printerName);
+ if (ppdFile)
+ unlink(ppdFile);
+ return (ppdFile != 0);
+}
+
+QString QCUPSSupport::unicodeString(const char *s)
+{
+#ifndef QT_NO_TEXTCODEC
+ return codec->toUnicode(s);
+#else
+ return QLatin1String(s);
+#endif
+}
+
+void QCUPSSupport::collectMarkedOptions(QStringList& list, const ppd_group_t* group) const
+{
+ if (group == 0) {
+ if (!currPPD)
+ return;
+ for (int i = 0; i < currPPD->num_groups; ++i) {
+ collectMarkedOptions(list, &currPPD->groups[i]);
+ collectMarkedOptionsHelper(list, &currPPD->groups[i]);
+ }
+ } else {
+ for (int i = 0; i < group->num_subgroups; ++i)
+ collectMarkedOptionsHelper(list, &group->subgroups[i]);
+ }
+}
+
+void QCUPSSupport::collectMarkedOptionsHelper(QStringList& list, const ppd_group_t* group) const
+{
+ for (int i = 0; i < group->num_options; ++i) {
+ for (int j = 0; j < group->options[i].num_choices; ++j) {
+ if (group->options[i].choices[j].marked == 1 && qstrcmp(group->options[i].choices[j].choice, group->options[i].defchoice) != 0)
+ list << QString::fromLocal8Bit(group->options[i].keyword) << QString::fromLocal8Bit(group->options[i].choices[j].choice);
+ }
+ }
+}
+
+QPair<int, QString> QCUPSSupport::tempFd()
+{
+ char filename[512];
+ int fd = _cupsTempFd(filename, 512);
+ return QPair<int, QString>(fd, QString::fromLocal8Bit(filename));
+}
+
+// Prints the given file and returns a job id.
+int QCUPSSupport::printFile(const char * printerName, const char * filename, const char * title,
+ int num_options, cups_option_t * options)
+{
+ return _cupsPrintFile(printerName, filename, title, num_options, options);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_CUPS
diff --git a/src/gui/painting/qcups_p.h b/src/gui/painting/qcups_p.h
new file mode 100644
index 0000000000..bc00c7396e
--- /dev/null
+++ b/src/gui/painting/qcups_p.h
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCUPS_P_H
+#define QCUPS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+#include "QtCore/qstring.h"
+#include "QtCore/qstringlist.h"
+#include "QtGui/qprinter.h"
+
+#ifndef QT_NO_CUPS
+#include <QtCore/qlibrary.h>
+#include <cups/cups.h>
+#include <cups/ppd.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_TYPEINFO(cups_option_t, Q_MOVABLE_TYPE | Q_PRIMITIVE_TYPE);
+
+class QCUPSSupport
+{
+public:
+ QCUPSSupport();
+ ~QCUPSSupport();
+
+ static bool isAvailable();
+ static int cupsVersion() { return isAvailable() ? CUPS_VERSION_MAJOR*10000+CUPS_VERSION_MINOR*100+CUPS_VERSION_PATCH : 0; }
+ int availablePrintersCount() const;
+ const cups_dest_t* availablePrinters() const;
+ int currentPrinterIndex() const;
+ const ppd_file_t* setCurrentPrinter(int index);
+
+ const ppd_file_t* currentPPD() const;
+ const ppd_option_t* ppdOption(const char *key) const;
+
+ const cups_option_t* printerOption(const QString &key) const;
+ const ppd_option_t* pageSizes() const;
+
+ int markOption(const char* name, const char* value);
+ void saveOptions(QList<const ppd_option_t*> options, QList<const char*> markedOptions);
+
+ QRect paperRect(const char *choice) const;
+ QRect pageRect(const char *choice) const;
+
+ QStringList options() const;
+
+ static bool printerHasPPD(const char *printerName);
+
+ QString unicodeString(const char *s);
+
+ QPair<int, QString> tempFd();
+ int printFile(const char * printerName, const char * filename, const char * title,
+ int num_options, cups_option_t * options);
+
+private:
+ void collectMarkedOptions(QStringList& list, const ppd_group_t* group = 0) const;
+ void collectMarkedOptionsHelper(QStringList& list, const ppd_group_t* group) const;
+
+ int prnCount;
+ cups_dest_t *printers;
+ const ppd_option_t* page_sizes;
+ int currPrinterIndex;
+ ppd_file_t *currPPD;
+#ifndef QT_NO_TEXTCODEC
+ QTextCodec *codec;
+#endif
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_CUPS
+
+#endif
diff --git a/src/gui/painting/qdatabuffer_p.h b/src/gui/painting/qdatabuffer_p.h
new file mode 100644
index 0000000000..5e50f6e032
--- /dev/null
+++ b/src/gui/painting/qdatabuffer_p.h
@@ -0,0 +1,145 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDATABUFFER_P_H
+#define QDATABUFFER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtCore/qbytearray.h"
+
+QT_BEGIN_NAMESPACE
+
+template <typename Type> class QDataBuffer
+{
+public:
+ QDataBuffer(int res)
+ {
+ capacity = res;
+ if (res)
+ buffer = (Type*) qMalloc(capacity * sizeof(Type));
+ else
+ buffer = 0;
+ siz = 0;
+ }
+
+ ~QDataBuffer()
+ {
+ if (buffer)
+ qFree(buffer);
+ }
+
+ inline void reset() { siz = 0; }
+
+ inline bool isEmpty() const { return siz==0; }
+
+ inline int size() const { return siz; }
+ inline Type *data() const { return buffer; }
+
+ inline Type &at(int i) { Q_ASSERT(i >= 0 && i < siz); return buffer[i]; }
+ inline const Type &at(int i) const { Q_ASSERT(i >= 0 && i < siz); return buffer[i]; }
+ inline Type &last() { Q_ASSERT(!isEmpty()); return buffer[siz-1]; }
+ inline const Type &last() const { Q_ASSERT(!isEmpty()); return buffer[siz-1]; }
+ inline Type &first() { Q_ASSERT(!isEmpty()); return buffer[0]; }
+ inline const Type &first() const { Q_ASSERT(!isEmpty()); return buffer[0]; }
+
+ inline void add(const Type &t) {
+ reserve(siz + 1);
+ buffer[siz] = t;
+ ++siz;
+ }
+
+ inline void pop_back() {
+ Q_ASSERT(siz > 0);
+ --siz;
+ }
+
+ inline void resize(int size) {
+ reserve(size);
+ siz = size;
+ }
+
+ inline void reserve(int size) {
+ if (size > capacity) {
+ if (capacity == 0)
+ capacity = 1;
+ while (capacity < size)
+ capacity *= 2;
+ buffer = (Type*) qRealloc(buffer, capacity * sizeof(Type));
+ }
+ }
+
+ inline void shrink(int size) {
+ capacity = size;
+ if (size)
+ buffer = (Type*) qRealloc(buffer, capacity * sizeof(Type));
+ else {
+ qFree(buffer);
+ buffer = 0;
+ }
+ }
+
+ inline void swap(QDataBuffer<Type> &other) {
+ qSwap(capacity, other.capacity);
+ qSwap(siz, other.siz);
+ qSwap(buffer, other.buffer);
+ }
+
+ inline QDataBuffer &operator<<(const Type &t) { add(t); return *this; }
+
+private:
+ int capacity;
+ int siz;
+ Type *buffer;
+};
+
+QT_END_NAMESPACE
+
+#endif // QDATABUFFER_P_H
diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp
new file mode 100644
index 0000000000..5e75e4a884
--- /dev/null
+++ b/src/gui/painting/qdrawhelper.cpp
@@ -0,0 +1,8078 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qdrawhelper_p.h>
+#include <private/qpaintengine_raster_p.h>
+#include <private/qpainter_p.h>
+#include <private/qdrawhelper_x86_p.h>
+#ifdef QT_HAVE_ARM_SIMD
+#include <private/qdrawhelper_arm_simd_p.h>
+#endif
+#include <private/qdrawhelper_neon_p.h>
+#include <private/qmath_p.h>
+#include <qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+
+#define MASK(src, a) src = BYTE_MUL(src, a)
+
+#if defined(Q_OS_IRIX) && defined(Q_CC_GNU) && __GNUC__ == 3 && __GNUC__ < 4 && QT_POINTER_SIZE == 8
+#define Q_IRIX_GCC3_3_WORKAROUND
+//
+// work around http://gcc.gnu.org/bugzilla/show_bug.cgi?id=14484
+//
+static uint gccBug(uint value) __attribute__((noinline));
+static uint gccBug(uint value)
+{
+ return value;
+}
+#endif
+
+/*
+ constants and structures
+*/
+
+enum {
+ fixed_scale = 1 << 16,
+ half_point = 1 << 15
+};
+static const int buffer_size = 2048;
+
+struct LinearGradientValues
+{
+ qreal dx;
+ qreal dy;
+ qreal l;
+ qreal off;
+};
+
+struct RadialGradientValues
+{
+ qreal dx;
+ qreal dy;
+ qreal a;
+};
+
+struct Operator;
+typedef uint* (QT_FASTCALL *DestFetchProc)(uint *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length);
+typedef void (QT_FASTCALL *DestStoreProc)(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length);
+typedef const uint* (QT_FASTCALL *SourceFetchProc)(uint *buffer, const Operator *o, const QSpanData *data, int y, int x, int length);
+
+
+struct Operator
+{
+ QPainter::CompositionMode mode;
+ DestFetchProc dest_fetch;
+ DestStoreProc dest_store;
+ SourceFetchProc src_fetch;
+ CompositionFunctionSolid funcSolid;
+ CompositionFunction func;
+ union {
+ LinearGradientValues linear;
+ RadialGradientValues radial;
+// TextureValues texture;
+ };
+};
+
+/*
+ Destination fetch. This is simple as we don't have to do bounds checks or
+ transformations
+*/
+
+static uint * QT_FASTCALL destFetchMono(uint *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length)
+{
+ uchar *data = (uchar *)rasterBuffer->scanLine(y);
+ uint *start = buffer;
+ const uint *end = buffer + length;
+ while (buffer < end) {
+ *buffer = data[x>>3] & (0x80 >> (x & 7)) ? rasterBuffer->destColor1 : rasterBuffer->destColor0;
+ ++buffer;
+ ++x;
+ }
+ return start;
+}
+
+static uint * QT_FASTCALL destFetchMonoLsb(uint *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length)
+{
+ uchar *data = (uchar *)rasterBuffer->scanLine(y);
+ uint *start = buffer;
+ const uint *end = buffer + length;
+ while (buffer < end) {
+ *buffer = data[x>>3] & (0x1 << (x & 7)) ? rasterBuffer->destColor1 : rasterBuffer->destColor0;
+ ++buffer;
+ ++x;
+ }
+ return start;
+}
+
+static uint * QT_FASTCALL destFetchARGB32(uint *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length)
+{
+ const uint *data = (const uint *)rasterBuffer->scanLine(y) + x;
+ for (int i = 0; i < length; ++i)
+ buffer[i] = PREMUL(data[i]);
+ return buffer;
+}
+
+static uint * QT_FASTCALL destFetchARGB32P(uint *, QRasterBuffer *rasterBuffer, int x, int y, int)
+{
+ return (uint *)rasterBuffer->scanLine(y) + x;
+}
+
+static uint * QT_FASTCALL destFetchRGB16(uint *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length)
+{
+ const ushort *data = (const ushort *)rasterBuffer->scanLine(y) + x;
+ for (int i = 0; i < length; ++i)
+ buffer[i] = qConvertRgb16To32(data[i]);
+ return buffer;
+}
+
+template <class DST>
+Q_STATIC_TEMPLATE_FUNCTION uint * QT_FASTCALL destFetch(uint *buffer, QRasterBuffer *rasterBuffer,
+ int x, int y, int length)
+{
+ const DST *src = reinterpret_cast<DST*>(rasterBuffer->scanLine(y)) + x;
+ quint32 *dest = reinterpret_cast<quint32*>(buffer);
+ while (length--)
+ *dest++ = *src++;
+ return buffer;
+}
+
+# define SPANFUNC_POINTER_DESTFETCH(Arg) destFetch<Arg>
+
+static DestFetchProc destFetchProc[QImage::NImageFormats] =
+{
+ 0, // Format_Invalid
+ destFetchMono, // Format_Mono,
+ destFetchMonoLsb, // Format_MonoLSB
+ 0, // Format_Indexed8
+ destFetchARGB32P, // Format_RGB32
+ destFetchARGB32, // Format_ARGB32,
+ destFetchARGB32P, // Format_ARGB32_Premultiplied
+ destFetchRGB16, // Format_RGB16
+ SPANFUNC_POINTER_DESTFETCH(qargb8565), // Format_ARGB8565_Premultiplied
+ SPANFUNC_POINTER_DESTFETCH(qrgb666), // Format_RGB666
+ SPANFUNC_POINTER_DESTFETCH(qargb6666), // Format_ARGB6666_Premultiplied
+ SPANFUNC_POINTER_DESTFETCH(qrgb555), // Format_RGB555
+ SPANFUNC_POINTER_DESTFETCH(qargb8555), // Format_ARGB8555_Premultiplied
+ SPANFUNC_POINTER_DESTFETCH(qrgb888), // Format_RGB888
+ SPANFUNC_POINTER_DESTFETCH(qrgb444), // Format_RGB444
+ SPANFUNC_POINTER_DESTFETCH(qargb4444) // Format_ARGB4444_Premultiplied
+};
+
+/*
+ Returns the color in the mono destination color table
+ that is the "nearest" to /color/.
+*/
+static inline QRgb findNearestColor(QRgb color, QRasterBuffer *rbuf)
+{
+ QRgb color_0 = PREMUL(rbuf->destColor0);
+ QRgb color_1 = PREMUL(rbuf->destColor1);
+ color = PREMUL(color);
+
+ int r = qRed(color);
+ int g = qGreen(color);
+ int b = qBlue(color);
+ int rx, gx, bx;
+ int dist_0, dist_1;
+
+ rx = r - qRed(color_0);
+ gx = g - qGreen(color_0);
+ bx = b - qBlue(color_0);
+ dist_0 = rx*rx + gx*gx + bx*bx;
+
+ rx = r - qRed(color_1);
+ gx = g - qGreen(color_1);
+ bx = b - qBlue(color_1);
+ dist_1 = rx*rx + gx*gx + bx*bx;
+
+ if (dist_0 < dist_1)
+ return color_0;
+ return color_1;
+}
+
+/*
+ Destination store.
+*/
+
+static void QT_FASTCALL destStoreMono(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length)
+{
+ uchar *data = (uchar *)rasterBuffer->scanLine(y);
+ if (rasterBuffer->monoDestinationWithClut) {
+ for (int i = 0; i < length; ++i) {
+ if (buffer[i] == rasterBuffer->destColor0) {
+ data[x >> 3] &= ~(0x80 >> (x & 7));
+ } else if (buffer[i] == rasterBuffer->destColor1) {
+ data[x >> 3] |= 0x80 >> (x & 7);
+ } else if (findNearestColor(buffer[i], rasterBuffer) == rasterBuffer->destColor0) {
+ data[x >> 3] &= ~(0x80 >> (x & 7));
+ } else {
+ data[x >> 3] |= 0x80 >> (x & 7);
+ }
+ ++x;
+ }
+ } else {
+ for (int i = 0; i < length; ++i) {
+ if (qGray(buffer[i]) < int(qt_bayer_matrix[y & 15][x & 15]))
+ data[x >> 3] |= 0x80 >> (x & 7);
+ else
+ data[x >> 3] &= ~(0x80 >> (x & 7));
+ ++x;
+ }
+ }
+}
+
+static void QT_FASTCALL destStoreMonoLsb(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length)
+{
+ uchar *data = (uchar *)rasterBuffer->scanLine(y);
+ if (rasterBuffer->monoDestinationWithClut) {
+ for (int i = 0; i < length; ++i) {
+ if (buffer[i] == rasterBuffer->destColor0) {
+ data[x >> 3] &= ~(1 << (x & 7));
+ } else if (buffer[i] == rasterBuffer->destColor1) {
+ data[x >> 3] |= 1 << (x & 7);
+ } else if (findNearestColor(buffer[i], rasterBuffer) == rasterBuffer->destColor0) {
+ data[x >> 3] &= ~(1 << (x & 7));
+ } else {
+ data[x >> 3] |= 1 << (x & 7);
+ }
+ ++x;
+ }
+ } else {
+ for (int i = 0; i < length; ++i) {
+ if (qGray(buffer[i]) < int(qt_bayer_matrix[y & 15][x & 15]))
+ data[x >> 3] |= 1 << (x & 7);
+ else
+ data[x >> 3] &= ~(1 << (x & 7));
+ ++x;
+ }
+ }
+}
+
+static void QT_FASTCALL destStoreARGB32(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length)
+{
+ uint *data = (uint *)rasterBuffer->scanLine(y) + x;
+ for (int i = 0; i < length; ++i) {
+ int p = buffer[i];
+ int alpha = qAlpha(p);
+ if (alpha == 255)
+ data[i] = p;
+ else if (alpha == 0)
+ data[i] = 0;
+ else {
+ int inv_alpha = 0xff0000/qAlpha(buffer[i]);
+ data[i] = (p & 0xff000000)
+ | ((qRed(p)*inv_alpha) & 0xff0000)
+ | (((qGreen(p)*inv_alpha) >> 8) & 0xff00)
+ | ((qBlue(p)*inv_alpha) >> 16);
+ }
+ }
+}
+
+static void QT_FASTCALL destStoreRGB16(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length)
+{
+ quint16 *data = (quint16*)rasterBuffer->scanLine(y) + x;
+ qt_memconvert<quint16, quint32>(data, buffer, length);
+}
+
+template <class DST>
+Q_STATIC_TEMPLATE_FUNCTION void QT_FASTCALL destStore(QRasterBuffer *rasterBuffer,
+ int x, int y,
+ const uint *buffer, int length)
+{
+ DST *dest = reinterpret_cast<DST*>(rasterBuffer->scanLine(y)) + x;
+ const quint32p *src = reinterpret_cast<const quint32p*>(buffer);
+ while (length--)
+ *dest++ = DST(*src++);
+}
+
+# define SPANFUNC_POINTER_DESTSTORE(DEST) destStore<DEST>
+
+static DestStoreProc destStoreProc[QImage::NImageFormats] =
+{
+ 0, // Format_Invalid
+ destStoreMono, // Format_Mono,
+ destStoreMonoLsb, // Format_MonoLSB
+ 0, // Format_Indexed8
+ 0, // Format_RGB32
+ destStoreARGB32, // Format_ARGB32,
+ 0, // Format_ARGB32_Premultiplied
+ destStoreRGB16, // Format_RGB16
+ SPANFUNC_POINTER_DESTSTORE(qargb8565), // Format_ARGB8565_Premultiplied
+ SPANFUNC_POINTER_DESTSTORE(qrgb666), // Format_RGB666
+ SPANFUNC_POINTER_DESTSTORE(qargb6666), // Format_ARGB6666_Premultiplied
+ SPANFUNC_POINTER_DESTSTORE(qrgb555), // Format_RGB555
+ SPANFUNC_POINTER_DESTSTORE(qargb8555), // Format_ARGB8555_Premultiplied
+ SPANFUNC_POINTER_DESTSTORE(qrgb888), // Format_RGB888
+ SPANFUNC_POINTER_DESTSTORE(qrgb444), // Format_RGB444
+ SPANFUNC_POINTER_DESTSTORE(qargb4444) // Format_ARGB4444_Premultiplied
+};
+
+/*
+ Source fetches
+
+ This is a bit more complicated, as we need several fetch routines for every surface type
+
+ We need 5 fetch methods per surface type:
+ untransformed
+ transformed (tiled and not tiled)
+ transformed bilinear (tiled and not tiled)
+
+ We don't need bounds checks for untransformed, but we need them for the other ones.
+
+ The generic implementation does pixel by pixel fetches
+*/
+
+template <QImage::Format format>
+Q_STATIC_TEMPLATE_FUNCTION uint QT_FASTCALL qt_fetchPixel(const uchar *scanLine, int x, const QVector<QRgb> *rgb);
+
+template<>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+uint QT_FASTCALL qt_fetchPixel<QImage::Format_Mono>(const uchar *scanLine,
+ int x, const QVector<QRgb> *rgb)
+{
+ bool pixel = scanLine[x>>3] & (0x80 >> (x & 7));
+ if (rgb) return PREMUL(rgb->at(pixel ? 1 : 0));
+ return pixel ? 0xff000000 : 0xffffffff;
+}
+
+template<>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+uint QT_FASTCALL qt_fetchPixel<QImage::Format_MonoLSB>(const uchar *scanLine,
+ int x, const QVector<QRgb> *rgb)
+{
+ bool pixel = scanLine[x>>3] & (0x1 << (x & 7));
+ if (rgb) return PREMUL(rgb->at(pixel ? 1 : 0));
+ return pixel ? 0xff000000 : 0xffffffff;
+}
+
+template<>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+uint QT_FASTCALL qt_fetchPixel<QImage::Format_Indexed8>(const uchar *scanLine,
+ int x, const QVector<QRgb> *rgb)
+{
+ return PREMUL(rgb->at(scanLine[x]));
+}
+
+template<>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+uint QT_FASTCALL qt_fetchPixel<QImage::Format_ARGB32>(const uchar *scanLine,
+ int x, const QVector<QRgb> *)
+{
+ return PREMUL(((const uint *)scanLine)[x]);
+}
+
+template<>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+uint QT_FASTCALL qt_fetchPixel<QImage::Format_ARGB32_Premultiplied>(const uchar *scanLine,
+ int x, const QVector<QRgb> *)
+{
+ return ((const uint *)scanLine)[x];
+}
+
+template<>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+uint QT_FASTCALL qt_fetchPixel<QImage::Format_RGB16>(const uchar *scanLine,
+ int x, const QVector<QRgb> *)
+{
+ return qConvertRgb16To32(((const ushort *)scanLine)[x]);
+}
+
+template<>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+uint QT_FASTCALL qt_fetchPixel<QImage::Format_ARGB8565_Premultiplied>(const uchar *scanLine,
+ int x,
+ const QVector<QRgb> *)
+{
+ const qargb8565 color = reinterpret_cast<const qargb8565*>(scanLine)[x];
+ return qt_colorConvert<quint32, qargb8565>(color, 0);
+}
+
+template<>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+uint QT_FASTCALL qt_fetchPixel<QImage::Format_RGB666>(const uchar *scanLine,
+ int x,
+ const QVector<QRgb> *)
+{
+ const qrgb666 color = reinterpret_cast<const qrgb666*>(scanLine)[x];
+ return qt_colorConvert<quint32, qrgb666>(color, 0);
+}
+
+template<>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+uint QT_FASTCALL qt_fetchPixel<QImage::Format_ARGB6666_Premultiplied>(const uchar *scanLine,
+ int x,
+ const QVector<QRgb> *)
+{
+ const qargb6666 color = reinterpret_cast<const qargb6666*>(scanLine)[x];
+ return qt_colorConvert<quint32, qargb6666>(color, 0);
+}
+
+template<>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+uint QT_FASTCALL qt_fetchPixel<QImage::Format_RGB555>(const uchar *scanLine,
+ int x,
+ const QVector<QRgb> *)
+{
+ const qrgb555 color = reinterpret_cast<const qrgb555*>(scanLine)[x];
+ return qt_colorConvert<quint32, qrgb555>(color, 0);
+}
+
+template<>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+uint QT_FASTCALL qt_fetchPixel<QImage::Format_ARGB8555_Premultiplied>(const uchar *scanLine,
+ int x,
+ const QVector<QRgb> *)
+{
+ const qargb8555 color = reinterpret_cast<const qargb8555*>(scanLine)[x];
+ return qt_colorConvert<quint32, qargb8555>(color, 0);
+}
+
+template<>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+uint QT_FASTCALL qt_fetchPixel<QImage::Format_RGB888>(const uchar *scanLine,
+ int x,
+ const QVector<QRgb> *)
+{
+ const qrgb888 color = reinterpret_cast<const qrgb888*>(scanLine)[x];
+ return qt_colorConvert<quint32, qrgb888>(color, 0);
+}
+
+template<>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+uint QT_FASTCALL qt_fetchPixel<QImage::Format_RGB444>(const uchar *scanLine,
+ int x,
+ const QVector<QRgb> *)
+{
+ const qrgb444 color = reinterpret_cast<const qrgb444*>(scanLine)[x];
+ return qt_colorConvert<quint32, qrgb444>(color, 0);
+}
+
+template<>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+uint QT_FASTCALL qt_fetchPixel<QImage::Format_ARGB4444_Premultiplied>(const uchar *scanLine,
+ int x,
+ const QVector<QRgb> *)
+{
+ const qargb4444 color = reinterpret_cast<const qargb4444*>(scanLine)[x];
+ return qt_colorConvert<quint32, qargb4444>(color, 0);
+}
+
+template<>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+uint QT_FASTCALL qt_fetchPixel<QImage::Format_Invalid>(const uchar *,
+ int ,
+ const QVector<QRgb> *)
+{
+ return 0;
+}
+
+typedef uint (QT_FASTCALL *FetchPixelProc)(const uchar *scanLine, int x, const QVector<QRgb> *);
+
+#define SPANFUNC_POINTER_FETCHPIXEL(Arg) qt_fetchPixel<QImage::Arg>
+
+
+static const FetchPixelProc fetchPixelProc[QImage::NImageFormats] =
+{
+ 0,
+ SPANFUNC_POINTER_FETCHPIXEL(Format_Mono),
+ SPANFUNC_POINTER_FETCHPIXEL(Format_MonoLSB),
+ SPANFUNC_POINTER_FETCHPIXEL(Format_Indexed8),
+ SPANFUNC_POINTER_FETCHPIXEL(Format_ARGB32_Premultiplied),
+ SPANFUNC_POINTER_FETCHPIXEL(Format_ARGB32),
+ SPANFUNC_POINTER_FETCHPIXEL(Format_ARGB32_Premultiplied),
+ SPANFUNC_POINTER_FETCHPIXEL(Format_RGB16),
+ SPANFUNC_POINTER_FETCHPIXEL(Format_ARGB8565_Premultiplied),
+ SPANFUNC_POINTER_FETCHPIXEL(Format_RGB666),
+ SPANFUNC_POINTER_FETCHPIXEL(Format_ARGB6666_Premultiplied),
+ SPANFUNC_POINTER_FETCHPIXEL(Format_RGB555),
+ SPANFUNC_POINTER_FETCHPIXEL(Format_ARGB8555_Premultiplied),
+ SPANFUNC_POINTER_FETCHPIXEL(Format_RGB888),
+ SPANFUNC_POINTER_FETCHPIXEL(Format_RGB444),
+ SPANFUNC_POINTER_FETCHPIXEL(Format_ARGB4444_Premultiplied)
+};
+
+enum TextureBlendType {
+ BlendUntransformed,
+ BlendTiled,
+ BlendTransformed,
+ BlendTransformedTiled,
+ BlendTransformedBilinear,
+ BlendTransformedBilinearTiled,
+ NBlendTypes
+};
+
+template <QImage::Format format>
+Q_STATIC_TEMPLATE_FUNCTION const uint * QT_FASTCALL qt_fetchUntransformed(uint *buffer, const Operator *, const QSpanData *data,
+ int y, int x, int length)
+{
+ const uchar *scanLine = data->texture.scanLine(y);
+ for (int i = 0; i < length; ++i)
+ buffer[i] = qt_fetchPixel<format>(scanLine, x + i, data->texture.colorTable);
+ return buffer;
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION const uint * QT_FASTCALL
+qt_fetchUntransformed<QImage::Format_ARGB32_Premultiplied>(uint *, const Operator *,
+ const QSpanData *data,
+ int y, int x, int)
+{
+ const uchar *scanLine = data->texture.scanLine(y);
+ return ((const uint *)scanLine) + x;
+}
+
+template<TextureBlendType blendType> /* either BlendTransformed or BlendTransformedTiled */
+Q_STATIC_TEMPLATE_FUNCTION
+const uint * QT_FASTCALL fetchTransformed(uint *buffer, const Operator *, const QSpanData *data,
+ int y, int x, int length)
+{
+ FetchPixelProc fetch = fetchPixelProc[data->texture.format];
+
+ int image_width = data->texture.width;
+ int image_height = data->texture.height;
+
+ const qreal cx = x + qreal(0.5);
+ const qreal cy = y + qreal(0.5);
+
+ const uint *end = buffer + length;
+ uint *b = buffer;
+ if (data->fast_matrix) {
+ // The increment pr x in the scanline
+ int fdx = (int)(data->m11 * fixed_scale);
+ int fdy = (int)(data->m12 * fixed_scale);
+
+ int fx = int((data->m21 * cy
+ + data->m11 * cx + data->dx) * fixed_scale);
+ int fy = int((data->m22 * cy
+ + data->m12 * cx + data->dy) * fixed_scale);
+
+ while (b < end) {
+ int px = fx >> 16;
+ int py = fy >> 16;
+
+ if (blendType == BlendTransformedTiled) {
+ px %= image_width;
+ py %= image_height;
+ if (px < 0) px += image_width;
+ if (py < 0) py += image_height;
+
+ const uchar *scanLine = data->texture.scanLine(py);
+ *b = fetch(scanLine, px, data->texture.colorTable);
+ } else {
+ if ((px < 0) || (px >= image_width)
+ || (py < 0) || (py >= image_height)) {
+ *b = uint(0);
+ } else {
+ const uchar *scanLine = data->texture.scanLine(py);
+ *b = fetch(scanLine, px, data->texture.colorTable);
+ }
+ }
+ fx += fdx;
+ fy += fdy;
+ ++b;
+ }
+ } else {
+ const qreal fdx = data->m11;
+ const qreal fdy = data->m12;
+ const qreal fdw = data->m13;
+
+ qreal fx = data->m21 * cy + data->m11 * cx + data->dx;
+ qreal fy = data->m22 * cy + data->m12 * cx + data->dy;
+ qreal fw = data->m23 * cy + data->m13 * cx + data->m33;
+
+ while (b < end) {
+ const qreal iw = fw == 0 ? 1 : 1 / fw;
+ const qreal tx = fx * iw;
+ const qreal ty = fy * iw;
+ int px = int(tx) - (tx < 0);
+ int py = int(ty) - (ty < 0);
+
+ if (blendType == BlendTransformedTiled) {
+ px %= image_width;
+ py %= image_height;
+ if (px < 0) px += image_width;
+ if (py < 0) py += image_height;
+
+ const uchar *scanLine = data->texture.scanLine(py);
+ *b = fetch(scanLine, px, data->texture.colorTable);
+ } else {
+ if ((px < 0) || (px >= image_width)
+ || (py < 0) || (py >= image_height)) {
+ *b = uint(0);
+ } else {
+ const uchar *scanLine = data->texture.scanLine(py);
+ *b = fetch(scanLine, px, data->texture.colorTable);
+ }
+ }
+ fx += fdx;
+ fy += fdy;
+ fw += fdw;
+ //force increment to avoid /0
+ if (!fw) {
+ fw += fdw;
+ }
+ ++b;
+ }
+ }
+
+ return buffer;
+}
+
+/** \internal
+ interpolate 4 argb pixels with the distx and disty factor.
+ distx and disty bust be between 0 and 16
+ */
+static inline uint interpolate_4_pixels_16(uint tl, uint tr, uint bl, uint br, int distx, int disty)
+{
+ uint distxy = distx * disty;
+ //idistx * disty = (16-distx) * disty = 16*disty - distxy
+ //idistx * idisty = (16-distx) * (16-disty) = 16*16 - 16*distx -16*dity + distxy
+ uint tlrb = (tl & 0x00ff00ff) * (16*16 - 16*distx - 16*disty + distxy);
+ uint tlag = ((tl & 0xff00ff00) >> 8) * (16*16 - 16*distx - 16*disty + distxy);
+ uint trrb = ((tr & 0x00ff00ff) * (distx*16 - distxy));
+ uint trag = (((tr & 0xff00ff00) >> 8) * (distx*16 - distxy));
+ uint blrb = ((bl & 0x00ff00ff) * (disty*16 - distxy));
+ uint blag = (((bl & 0xff00ff00) >> 8) * (disty*16 - distxy));
+ uint brrb = ((br & 0x00ff00ff) * (distxy));
+ uint brag = (((br & 0xff00ff00) >> 8) * (distxy));
+ return (((tlrb + trrb + blrb + brrb) >> 8) & 0x00ff00ff) | ((tlag + trag + blag + brag) & 0xff00ff00);
+}
+
+#if defined(QT_ALWAYS_HAVE_SSE2)
+#define interpolate_4_pixels_16_sse2(tl, tr, bl, br, distx, disty, colorMask, v_256, b) \
+{ \
+ const __m128i dxdy = _mm_mullo_epi16 (distx, disty); \
+ const __m128i distx_ = _mm_slli_epi16(distx, 4); \
+ const __m128i disty_ = _mm_slli_epi16(disty, 4); \
+ const __m128i idxidy = _mm_add_epi16(dxdy, _mm_sub_epi16(v_256, _mm_add_epi16(distx_, disty_))); \
+ const __m128i dxidy = _mm_sub_epi16(distx_, dxdy); \
+ const __m128i idxdy = _mm_sub_epi16(disty_, dxdy); \
+ \
+ __m128i tlAG = _mm_srli_epi16(tl, 8); \
+ __m128i tlRB = _mm_and_si128(tl, colorMask); \
+ __m128i trAG = _mm_srli_epi16(tr, 8); \
+ __m128i trRB = _mm_and_si128(tr, colorMask); \
+ __m128i blAG = _mm_srli_epi16(bl, 8); \
+ __m128i blRB = _mm_and_si128(bl, colorMask); \
+ __m128i brAG = _mm_srli_epi16(br, 8); \
+ __m128i brRB = _mm_and_si128(br, colorMask); \
+ \
+ tlAG = _mm_mullo_epi16(tlAG, idxidy); \
+ tlRB = _mm_mullo_epi16(tlRB, idxidy); \
+ trAG = _mm_mullo_epi16(trAG, dxidy); \
+ trRB = _mm_mullo_epi16(trRB, dxidy); \
+ blAG = _mm_mullo_epi16(blAG, idxdy); \
+ blRB = _mm_mullo_epi16(blRB, idxdy); \
+ brAG = _mm_mullo_epi16(brAG, dxdy); \
+ brRB = _mm_mullo_epi16(brRB, dxdy); \
+ \
+ /* Add the values, and shift to only keep 8 significant bits per colors */ \
+ __m128i rAG =_mm_add_epi16(_mm_add_epi16(tlAG, trAG), _mm_add_epi16(blAG, brAG)); \
+ __m128i rRB =_mm_add_epi16(_mm_add_epi16(tlRB, trRB), _mm_add_epi16(blRB, brRB)); \
+ rAG = _mm_andnot_si128(colorMask, rAG); \
+ rRB = _mm_srli_epi16(rRB, 8); \
+ _mm_storeu_si128((__m128i*)(b), _mm_or_si128(rAG, rRB)); \
+}
+#endif
+
+#if defined(QT_ALWAYS_HAVE_NEON)
+#define interpolate_4_pixels_16_neon(tl, tr, bl, br, distx, disty, disty_, colorMask, invColorMask, v_256, b) \
+{ \
+ const int16x8_t dxdy = vmulq_s16(distx, disty); \
+ const int16x8_t distx_ = vshlq_n_s16(distx, 4); \
+ const int16x8_t idxidy = vaddq_s16(dxdy, vsubq_s16(v_256, vaddq_s16(distx_, disty_))); \
+ const int16x8_t dxidy = vsubq_s16(distx_, dxdy); \
+ const int16x8_t idxdy = vsubq_s16(disty_, dxdy); \
+ \
+ int16x8_t tlAG = vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(tl), 8)); \
+ int16x8_t tlRB = vandq_s16(tl, colorMask); \
+ int16x8_t trAG = vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(tr), 8)); \
+ int16x8_t trRB = vandq_s16(tr, colorMask); \
+ int16x8_t blAG = vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(bl), 8)); \
+ int16x8_t blRB = vandq_s16(bl, colorMask); \
+ int16x8_t brAG = vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(br), 8)); \
+ int16x8_t brRB = vandq_s16(br, colorMask); \
+ \
+ int16x8_t rAG = vmulq_s16(tlAG, idxidy); \
+ int16x8_t rRB = vmulq_s16(tlRB, idxidy); \
+ rAG = vmlaq_s16(rAG, trAG, dxidy); \
+ rRB = vmlaq_s16(rRB, trRB, dxidy); \
+ rAG = vmlaq_s16(rAG, blAG, idxdy); \
+ rRB = vmlaq_s16(rRB, blRB, idxdy); \
+ rAG = vmlaq_s16(rAG, brAG, dxdy); \
+ rRB = vmlaq_s16(rRB, brRB, dxdy); \
+ \
+ rAG = vandq_s16(invColorMask, rAG); \
+ rRB = vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(rRB), 8)); \
+ vst1q_s16((int16_t*)(b), vorrq_s16(rAG, rRB)); \
+}
+#endif
+
+template<TextureBlendType blendType>
+Q_STATIC_TEMPLATE_FUNCTION inline void fetchTransformedBilinear_pixelBounds(int max, int l1, int l2, int &v1, int &v2)
+{
+ if (blendType == BlendTransformedBilinearTiled) {
+ v1 %= max;
+ if (v1 < 0) v1 += max;
+ v2 = v1 + 1;
+ v2 %= max;
+ } else {
+ if (v1 < l1) {
+ v2 = v1 = l1;
+ } else if (v1 >= l2) {
+ v2 = v1 = l2;
+ } else {
+ v2 = v1 + 1;
+ }
+ }
+
+ Q_ASSERT(v1 >= 0 && v1 < max);
+ Q_ASSERT(v2 >= 0 && v2 < max);
+}
+
+template<TextureBlendType blendType, QImage::Format format> /* blendType = BlendTransformedBilinear or BlendTransformedBilinearTiled */
+Q_STATIC_TEMPLATE_FUNCTION
+const uint * QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Operator *, const QSpanData *data,
+ int y, int x, int length)
+{
+#ifdef Q_CC_RVCT // needed to avoid compiler crash in RVCT 2.2
+ FetchPixelProc fetch;
+ if (format != QImage::Format_Invalid)
+ fetch = qt_fetchPixel<format>;
+ else
+ fetch = fetchPixelProc[data->texture.format];
+#else
+ FetchPixelProc fetch = (format != QImage::Format_Invalid) ? FetchPixelProc(qt_fetchPixel<format>) : fetchPixelProc[data->texture.format];
+#endif
+
+ int image_width = data->texture.width;
+ int image_height = data->texture.height;
+
+ int image_x1 = data->texture.x1;
+ int image_y1 = data->texture.y1;
+ int image_x2 = data->texture.x2 - 1;
+ int image_y2 = data->texture.y2 - 1;
+
+ const qreal cx = x + qreal(0.5);
+ const qreal cy = y + qreal(0.5);
+
+ uint *end = buffer + length;
+ uint *b = buffer;
+ if (data->fast_matrix) {
+ // The increment pr x in the scanline
+ int fdx = (int)(data->m11 * fixed_scale);
+ int fdy = (int)(data->m12 * fixed_scale);
+
+ int fx = int((data->m21 * cy
+ + data->m11 * cx + data->dx) * fixed_scale);
+ int fy = int((data->m22 * cy
+ + data->m12 * cx + data->dy) * fixed_scale);
+
+ fx -= half_point;
+ fy -= half_point;
+
+ if (fdy == 0) { //simple scale, no rotation
+ int y1 = (fy >> 16);
+ int y2;
+ fetchTransformedBilinear_pixelBounds<blendType>(image_height, image_y1, image_y2, y1, y2);
+ const uchar *s1 = data->texture.scanLine(y1);
+ const uchar *s2 = data->texture.scanLine(y2);
+
+ if (fdx <= fixed_scale && fdx > 0) { // scale up on X
+ int disty = (fy & 0x0000ffff) >> 8;
+ int idisty = 256 - disty;
+ int x = fx >> 16;
+
+ // The idea is first to do the interpolation between the row s1 and the row s2
+ // into an intermediate buffer, then we interpolate between two pixel of this buffer.
+
+ // intermediate_buffer[0] is a buffer of red-blue component of the pixel, in the form 0x00RR00BB
+ // intermediate_buffer[1] is the alpha-green component of the pixel, in the form 0x00AA00GG
+ quint32 intermediate_buffer[2][buffer_size + 2];
+ // count is the size used in the intermediate_buffer.
+ int count = qCeil(length * data->m11) + 2; //+1 for the last pixel to interpolate with, and +1 for rounding errors.
+ Q_ASSERT(count <= buffer_size + 2); //length is supposed to be <= buffer_size and data->m11 < 1 in this case
+ int f = 0;
+ int lim = count;
+ if (blendType == BlendTransformedBilinearTiled) {
+ x %= image_width;
+ if (x < 0) x += image_width;
+ } else {
+ lim = qMin(count, image_x2-x+1);
+ if (x < image_x1) {
+ Q_ASSERT(x <= image_x2);
+ uint t = fetch(s1, image_x1, data->texture.colorTable);
+ uint b = fetch(s2, image_x1, data->texture.colorTable);
+ quint32 rb = (((t & 0xff00ff) * idisty + (b & 0xff00ff) * disty) >> 8) & 0xff00ff;
+ quint32 ag = ((((t>>8) & 0xff00ff) * idisty + ((b>>8) & 0xff00ff) * disty) >> 8) & 0xff00ff;
+ do {
+ intermediate_buffer[0][f] = rb;
+ intermediate_buffer[1][f] = ag;
+ f++;
+ x++;
+ } while (x < image_x1 && f < lim);
+ }
+ }
+
+ if (blendType != BlendTransformedBilinearTiled &&
+ (format == QImage::Format_ARGB32_Premultiplied || format == QImage::Format_RGB32)) {
+#if defined(QT_ALWAYS_HAVE_SSE2)
+ const __m128i disty_ = _mm_set1_epi16(disty);
+ const __m128i idisty_ = _mm_set1_epi16(idisty);
+ const __m128i colorMask = _mm_set1_epi32(0x00ff00ff);
+
+ lim -= 3;
+ for (; f < lim; x += 4, f += 4) {
+ // Load 4 pixels from s1, and split the alpha-green and red-blue component
+ __m128i top = _mm_loadu_si128((__m128i*)((const uint *)(s1)+x));
+ __m128i topAG = _mm_srli_epi16(top, 8);
+ __m128i topRB = _mm_and_si128(top, colorMask);
+ // Multiplies each colour component by idisty
+ topAG = _mm_mullo_epi16 (topAG, idisty_);
+ topRB = _mm_mullo_epi16 (topRB, idisty_);
+
+ // Same for the s2 vector
+ __m128i bottom = _mm_loadu_si128((__m128i*)((const uint *)(s2)+x));
+ __m128i bottomAG = _mm_srli_epi16(bottom, 8);
+ __m128i bottomRB = _mm_and_si128(bottom, colorMask);
+ bottomAG = _mm_mullo_epi16 (bottomAG, disty_);
+ bottomRB = _mm_mullo_epi16 (bottomRB, disty_);
+
+ // Add the values, and shift to only keep 8 significant bits per colors
+ __m128i rAG =_mm_add_epi16(topAG, bottomAG);
+ rAG = _mm_srli_epi16(rAG, 8);
+ _mm_storeu_si128((__m128i*)(&intermediate_buffer[1][f]), rAG);
+ __m128i rRB =_mm_add_epi16(topRB, bottomRB);
+ rRB = _mm_srli_epi16(rRB, 8);
+ _mm_storeu_si128((__m128i*)(&intermediate_buffer[0][f]), rRB);
+ }
+#elif defined(QT_ALWAYS_HAVE_NEON)
+ const int16x8_t disty_ = vdupq_n_s16(disty);
+ const int16x8_t idisty_ = vdupq_n_s16(idisty);
+ const int16x8_t colorMask = vdupq_n_s16(0x00ff);
+
+ lim -= 3;
+ for (; f < lim; x += 4, f += 4) {
+ // Load 4 pixels from s1, and split the alpha-green and red-blue component
+ int16x8_t top = vld1q_s16((int16_t*)((const uint *)(s1)+x));
+ int16x8_t topAG = vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(top), 8));
+ int16x8_t topRB = vandq_s16(top, colorMask);
+ // Multiplies each colour component by idisty
+ topAG = vmulq_s16(topAG, idisty_);
+ topRB = vmulq_s16(topRB, idisty_);
+
+ // Same for the s2 vector
+ int16x8_t bottom = vld1q_s16((int16_t*)((const uint *)(s2)+x));
+ int16x8_t bottomAG = vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(bottom), 8));
+ int16x8_t bottomRB = vandq_s16(bottom, colorMask);
+ bottomAG = vmulq_s16(bottomAG, disty_);
+ bottomRB = vmulq_s16(bottomRB, disty_);
+
+ // Add the values, and shift to only keep 8 significant bits per colors
+ int16x8_t rAG = vaddq_s16(topAG, bottomAG);
+ rAG = vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(rAG), 8));
+ vst1q_s16((int16_t*)(&intermediate_buffer[1][f]), rAG);
+ int16x8_t rRB = vaddq_s16(topRB, bottomRB);
+ rRB = vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(rRB), 8));
+ vst1q_s16((int16_t*)(&intermediate_buffer[0][f]), rRB);
+ }
+#endif
+ }
+ for (; f < count; f++) { // Same as above but without sse2
+ if (blendType == BlendTransformedBilinearTiled) {
+ if (x >= image_width) x -= image_width;
+ } else {
+ x = qMin(x, image_x2);
+ }
+
+ uint t = fetch(s1, x, data->texture.colorTable);
+ uint b = fetch(s2, x, data->texture.colorTable);
+
+ intermediate_buffer[0][f] = (((t & 0xff00ff) * idisty + (b & 0xff00ff) * disty) >> 8) & 0xff00ff;
+ intermediate_buffer[1][f] = ((((t>>8) & 0xff00ff) * idisty + ((b>>8) & 0xff00ff) * disty) >> 8) & 0xff00ff;
+ x++;
+ }
+ // Now interpolate the values from the intermediate_buffer to get the final result.
+ fx &= fixed_scale - 1;
+ Q_ASSERT((fx >> 16) == 0);
+ while (b < end) {
+ register int x1 = (fx >> 16);
+ register int x2 = x1 + 1;
+ Q_ASSERT(x1 >= 0);
+ Q_ASSERT(x2 < count);
+
+ register int distx = (fx & 0x0000ffff) >> 8;
+ register int idistx = 256 - distx;
+ int rb = ((intermediate_buffer[0][x1] * idistx + intermediate_buffer[0][x2] * distx) >> 8) & 0xff00ff;
+ int ag = (intermediate_buffer[1][x1] * idistx + intermediate_buffer[1][x2] * distx) & 0xff00ff00;
+ *b = rb | ag;
+ b++;
+ fx += fdx;
+ }
+ } else if ((fdx < 0 && fdx > -(fixed_scale / 8)) || fabs(data->m22) < (1./8.)) { // scale up more than 8x
+ int y1 = (fy >> 16);
+ int y2;
+ fetchTransformedBilinear_pixelBounds<blendType>(image_height, image_y1, image_y2, y1, y2);
+ const uchar *s1 = data->texture.scanLine(y1);
+ const uchar *s2 = data->texture.scanLine(y2);
+ int disty = (fy & 0x0000ffff) >> 8;
+ int idisty = 256 - disty;
+ while (b < end) {
+ int x1 = (fx >> 16);
+ int x2;
+ fetchTransformedBilinear_pixelBounds<blendType>(image_width, image_x1, image_x2, x1, x2);
+ uint tl = fetch(s1, x1, data->texture.colorTable);
+ uint tr = fetch(s1, x2, data->texture.colorTable);
+ uint bl = fetch(s2, x1, data->texture.colorTable);
+ uint br = fetch(s2, x2, data->texture.colorTable);
+
+ int distx = (fx & 0x0000ffff) >> 8;
+ int idistx = 256 - distx;
+
+ uint xtop = INTERPOLATE_PIXEL_256(tl, idistx, tr, distx);
+ uint xbot = INTERPOLATE_PIXEL_256(bl, idistx, br, distx);
+ *b = INTERPOLATE_PIXEL_256(xtop, idisty, xbot, disty);
+
+ fx += fdx;
+ ++b;
+ }
+ } else { //scale down
+ int y1 = (fy >> 16);
+ int y2;
+ fetchTransformedBilinear_pixelBounds<blendType>(image_height, image_y1, image_y2, y1, y2);
+ const uchar *s1 = data->texture.scanLine(y1);
+ const uchar *s2 = data->texture.scanLine(y2);
+ int disty = (fy & 0x0000ffff) >> 12;
+
+ if (blendType != BlendTransformedBilinearTiled &&
+ (format == QImage::Format_ARGB32_Premultiplied || format == QImage::Format_RGB32)) {
+
+#define BILINEAR_DOWNSCALE_BOUNDS_PROLOG \
+ while (b < end) { \
+ int x1 = (fx >> 16); \
+ int x2; \
+ fetchTransformedBilinear_pixelBounds<blendType>(image_width, image_x1, image_x2, x1, x2); \
+ if (x1 != x2) \
+ break; \
+ uint tl = fetch(s1, x1, data->texture.colorTable); \
+ uint tr = fetch(s1, x2, data->texture.colorTable); \
+ uint bl = fetch(s2, x1, data->texture.colorTable); \
+ uint br = fetch(s2, x2, data->texture.colorTable); \
+ int distx = (fx & 0x0000ffff) >> 12; \
+ *b = interpolate_4_pixels_16(tl, tr, bl, br, distx, disty); \
+ fx += fdx; \
+ ++b; \
+ } \
+ uint *boundedEnd; \
+ if (fdx > 0) \
+ boundedEnd = qMin(end, buffer + uint((image_x2 - (fx >> 16)) / data->m11)); \
+ else \
+ boundedEnd = qMin(end, buffer + uint((image_x1 - (fx >> 16)) / data->m11)); \
+ boundedEnd -= 3;
+
+#if defined(QT_ALWAYS_HAVE_SSE2)
+ BILINEAR_DOWNSCALE_BOUNDS_PROLOG
+
+ const __m128i colorMask = _mm_set1_epi32(0x00ff00ff);
+ const __m128i v_256 = _mm_set1_epi16(256);
+ const __m128i v_disty = _mm_set1_epi16(disty);
+ __m128i v_fdx = _mm_set1_epi32(fdx*4);
+
+ ptrdiff_t secondLine = reinterpret_cast<const uint *>(s2) - reinterpret_cast<const uint *>(s1);
+
+ union Vect_buffer { __m128i vect; quint32 i[4]; };
+ Vect_buffer v_fx;
+
+ for (int i = 0; i < 4; i++) {
+ v_fx.i[i] = fx;
+ fx += fdx;
+ }
+
+ while (b < boundedEnd) {
+
+ Vect_buffer tl, tr, bl, br;
+
+ for (int i = 0; i < 4; i++) {
+ int x1 = v_fx.i[i] >> 16;
+ const uint *addr_tl = reinterpret_cast<const uint *>(s1) + x1;
+ const uint *addr_tr = addr_tl + 1;
+ tl.i[i] = *addr_tl;
+ tr.i[i] = *addr_tr;
+ bl.i[i] = *(addr_tl+secondLine);
+ br.i[i] = *(addr_tr+secondLine);
+ }
+ __m128i v_distx = _mm_srli_epi16(v_fx.vect, 12);
+ v_distx = _mm_shufflehi_epi16(v_distx, _MM_SHUFFLE(2,2,0,0));
+ v_distx = _mm_shufflelo_epi16(v_distx, _MM_SHUFFLE(2,2,0,0));
+
+ interpolate_4_pixels_16_sse2(tl.vect, tr.vect, bl.vect, br.vect, v_distx, v_disty, colorMask, v_256, b);
+ b+=4;
+ v_fx.vect = _mm_add_epi32(v_fx.vect, v_fdx);
+ }
+ fx = v_fx.i[0];
+#elif defined(QT_ALWAYS_HAVE_NEON)
+ BILINEAR_DOWNSCALE_BOUNDS_PROLOG
+
+ const int16x8_t colorMask = vdupq_n_s16(0x00ff);
+ const int16x8_t invColorMask = vmvnq_s16(colorMask);
+ const int16x8_t v_256 = vdupq_n_s16(256);
+ const int16x8_t v_disty = vdupq_n_s16(disty);
+ const int16x8_t v_disty_ = vshlq_n_s16(v_disty, 4);
+ int32x4_t v_fdx = vdupq_n_s32(fdx*4);
+
+ ptrdiff_t secondLine = reinterpret_cast<const uint *>(s2) - reinterpret_cast<const uint *>(s1);
+
+ union Vect_buffer { int32x4_t vect; quint32 i[4]; };
+ Vect_buffer v_fx;
+
+ for (int i = 0; i < 4; i++) {
+ v_fx.i[i] = fx;
+ fx += fdx;
+ }
+
+ const int32x4_t v_ffff_mask = vdupq_n_s32(0x0000ffff);
+
+ while (b < boundedEnd) {
+
+ Vect_buffer tl, tr, bl, br;
+
+ Vect_buffer v_fx_shifted;
+ v_fx_shifted.vect = vshrq_n_s32(v_fx.vect, 16);
+
+ int32x4_t v_distx = vshrq_n_s32(vandq_s32(v_fx.vect, v_ffff_mask), 12);
+
+ for (int i = 0; i < 4; i++) {
+ int x1 = v_fx_shifted.i[i];
+ const uint *addr_tl = reinterpret_cast<const uint *>(s1) + x1;
+ const uint *addr_tr = addr_tl + 1;
+ tl.i[i] = *addr_tl;
+ tr.i[i] = *addr_tr;
+ bl.i[i] = *(addr_tl+secondLine);
+ br.i[i] = *(addr_tr+secondLine);
+ }
+
+ v_distx = vorrq_s32(v_distx, vshlq_n_s32(v_distx, 16));
+
+ interpolate_4_pixels_16_neon(vreinterpretq_s16_s32(tl.vect), vreinterpretq_s16_s32(tr.vect), vreinterpretq_s16_s32(bl.vect), vreinterpretq_s16_s32(br.vect), vreinterpretq_s16_s32(v_distx), v_disty, v_disty_, colorMask, invColorMask, v_256, b);
+ b+=4;
+ v_fx.vect = vaddq_s32(v_fx.vect, v_fdx);
+ }
+ fx = v_fx.i[0];
+#endif
+ }
+
+ while (b < end) {
+ int x1 = (fx >> 16);
+ int x2;
+ fetchTransformedBilinear_pixelBounds<blendType>(image_width, image_x1, image_x2, x1, x2);
+ uint tl = fetch(s1, x1, data->texture.colorTable);
+ uint tr = fetch(s1, x2, data->texture.colorTable);
+ uint bl = fetch(s2, x1, data->texture.colorTable);
+ uint br = fetch(s2, x2, data->texture.colorTable);
+ int distx = (fx & 0x0000ffff) >> 12;
+ *b = interpolate_4_pixels_16(tl, tr, bl, br, distx, disty);
+ fx += fdx;
+ ++b;
+ }
+ }
+ } else { //rotation
+ if (fabs(data->m11) > 8 || fabs(data->m22) > 8) {
+ //if we are zooming more than 8 times, we use 8bit precision for the position.
+ while (b < end) {
+ int x1 = (fx >> 16);
+ int x2;
+ int y1 = (fy >> 16);
+ int y2;
+
+ fetchTransformedBilinear_pixelBounds<blendType>(image_width, image_x1, image_x2, x1, x2);
+ fetchTransformedBilinear_pixelBounds<blendType>(image_height, image_y1, image_y2, y1, y2);
+
+ const uchar *s1 = data->texture.scanLine(y1);
+ const uchar *s2 = data->texture.scanLine(y2);
+
+ uint tl = fetch(s1, x1, data->texture.colorTable);
+ uint tr = fetch(s1, x2, data->texture.colorTable);
+ uint bl = fetch(s2, x1, data->texture.colorTable);
+ uint br = fetch(s2, x2, data->texture.colorTable);
+
+ int distx = (fx & 0x0000ffff) >> 8;
+ int disty = (fy & 0x0000ffff) >> 8;
+ int idistx = 256 - distx;
+ int idisty = 256 - disty;
+
+ uint xtop = INTERPOLATE_PIXEL_256(tl, idistx, tr, distx);
+ uint xbot = INTERPOLATE_PIXEL_256(bl, idistx, br, distx);
+ *b = INTERPOLATE_PIXEL_256(xtop, idisty, xbot, disty);
+
+ fx += fdx;
+ fy += fdy;
+ ++b;
+ }
+ } else {
+ //we are zooming less than 8x, use 4bit precision
+ while (b < end) {
+ int x1 = (fx >> 16);
+ int x2;
+ int y1 = (fy >> 16);
+ int y2;
+
+ fetchTransformedBilinear_pixelBounds<blendType>(image_width, image_x1, image_x2, x1, x2);
+ fetchTransformedBilinear_pixelBounds<blendType>(image_height, image_y1, image_y2, y1, y2);
+
+ const uchar *s1 = data->texture.scanLine(y1);
+ const uchar *s2 = data->texture.scanLine(y2);
+
+ uint tl = fetch(s1, x1, data->texture.colorTable);
+ uint tr = fetch(s1, x2, data->texture.colorTable);
+ uint bl = fetch(s2, x1, data->texture.colorTable);
+ uint br = fetch(s2, x2, data->texture.colorTable);
+
+ int distx = (fx & 0x0000ffff) >> 12;
+ int disty = (fy & 0x0000ffff) >> 12;
+
+ *b = interpolate_4_pixels_16(tl, tr, bl, br, distx, disty);
+
+ fx += fdx;
+ fy += fdy;
+ ++b;
+ }
+ }
+ }
+ } else {
+ const qreal fdx = data->m11;
+ const qreal fdy = data->m12;
+ const qreal fdw = data->m13;
+
+ qreal fx = data->m21 * cy + data->m11 * cx + data->dx;
+ qreal fy = data->m22 * cy + data->m12 * cx + data->dy;
+ qreal fw = data->m23 * cy + data->m13 * cx + data->m33;
+
+ while (b < end) {
+ const qreal iw = fw == 0 ? 1 : 1 / fw;
+ const qreal px = fx * iw - qreal(0.5);
+ const qreal py = fy * iw - qreal(0.5);
+
+ int x1 = int(px) - (px < 0);
+ int x2;
+ int y1 = int(py) - (py < 0);
+ int y2;
+
+ int distx = int((px - x1) * 256);
+ int disty = int((py - y1) * 256);
+ int idistx = 256 - distx;
+ int idisty = 256 - disty;
+
+ fetchTransformedBilinear_pixelBounds<blendType>(image_width, image_x1, image_x2, x1, x2);
+ fetchTransformedBilinear_pixelBounds<blendType>(image_height, image_y1, image_y2, y1, y2);
+
+ const uchar *s1 = data->texture.scanLine(y1);
+ const uchar *s2 = data->texture.scanLine(y2);
+
+ uint tl = fetch(s1, x1, data->texture.colorTable);
+ uint tr = fetch(s1, x2, data->texture.colorTable);
+ uint bl = fetch(s2, x1, data->texture.colorTable);
+ uint br = fetch(s2, x2, data->texture.colorTable);
+
+ uint xtop = INTERPOLATE_PIXEL_256(tl, idistx, tr, distx);
+ uint xbot = INTERPOLATE_PIXEL_256(bl, idistx, br, distx);
+ *b = INTERPOLATE_PIXEL_256(xtop, idisty, xbot, disty);
+
+ fx += fdx;
+ fy += fdy;
+ fw += fdw;
+ //force increment to avoid /0
+ if (!fw) {
+ fw += fdw;
+ }
+ ++b;
+ }
+ }
+
+ return buffer;
+}
+
+#define SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Arg) qt_fetchUntransformed<QImage::Arg>
+
+static const SourceFetchProc sourceFetch[NBlendTypes][QImage::NImageFormats] = {
+ // Untransformed
+ {
+ 0, // Invalid
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_Mono), // Mono
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_MonoLSB), // MonoLsb
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_Indexed8), // Indexed8
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB32_Premultiplied), // RGB32
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB32), // ARGB32
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB32_Premultiplied), // ARGB32_Premultiplied
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_RGB16), // RGB16
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB8565_Premultiplied),// ARGB8565_Premultiplied
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_RGB666), // RGB666
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB6666_Premultiplied),// ARGB6666_Premultiplied
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_RGB555), // RGB555
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB8555_Premultiplied),// ARGB8555_Premultiplied
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_RGB888), // RGB888
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_RGB444), // RGB444
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB4444_Premultiplied) // ARGB4444_Premultiplied
+ },
+ // Tiled
+ {
+ 0, // Invalid
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_Mono), // Mono
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_MonoLSB), // MonoLsb
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_Indexed8), // Indexed8
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB32_Premultiplied), // RGB32
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB32), // ARGB32
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB32_Premultiplied), // ARGB32_Premultiplied
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_RGB16), // RGB16
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB8565_Premultiplied),// ARGB8565_Premultiplied
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_RGB666), // RGB666
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB6666_Premultiplied),// ARGB6666_Premultiplied
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_RGB555), // RGB555
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB8555_Premultiplied),// ARGB8555_Premultiplied
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_RGB888), // RGB888
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_RGB444), // RGB444
+ SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB4444_Premultiplied) // ARGB4444_Premultiplied
+ },
+ // Transformed
+ {
+ 0, // Invalid
+ fetchTransformed<BlendTransformed>, // Mono
+ fetchTransformed<BlendTransformed>, // MonoLsb
+ fetchTransformed<BlendTransformed>, // Indexed8
+ fetchTransformed<BlendTransformed>, // RGB32
+ fetchTransformed<BlendTransformed>, // ARGB32
+ fetchTransformed<BlendTransformed>, // ARGB32_Premultiplied
+ fetchTransformed<BlendTransformed>, // RGB16
+ fetchTransformed<BlendTransformed>, // ARGB8565_Premultiplied
+ fetchTransformed<BlendTransformed>, // RGB666
+ fetchTransformed<BlendTransformed>, // ARGB6666_Premultiplied
+ fetchTransformed<BlendTransformed>, // RGB555
+ fetchTransformed<BlendTransformed>, // ARGB8555_Premultiplied
+ fetchTransformed<BlendTransformed>, // RGB888
+ fetchTransformed<BlendTransformed>, // RGB444
+ fetchTransformed<BlendTransformed>, // ARGB4444_Premultiplied
+ },
+ {
+ 0, // TransformedTiled
+ fetchTransformed<BlendTransformedTiled>, // Mono
+ fetchTransformed<BlendTransformedTiled>, // MonoLsb
+ fetchTransformed<BlendTransformedTiled>, // Indexed8
+ fetchTransformed<BlendTransformedTiled>, // RGB32
+ fetchTransformed<BlendTransformedTiled>, // ARGB32
+ fetchTransformed<BlendTransformedTiled>, // ARGB32_Premultiplied
+ fetchTransformed<BlendTransformedTiled>, // RGB16
+ fetchTransformed<BlendTransformedTiled>, // ARGB8565_Premultiplied
+ fetchTransformed<BlendTransformedTiled>, // RGB666
+ fetchTransformed<BlendTransformedTiled>, // ARGB6666_Premultiplied
+ fetchTransformed<BlendTransformedTiled>, // RGB555
+ fetchTransformed<BlendTransformedTiled>, // ARGB8555_Premultiplied
+ fetchTransformed<BlendTransformedTiled>, // RGB888
+ fetchTransformed<BlendTransformedTiled>, // RGB444
+ fetchTransformed<BlendTransformedTiled>, // ARGB4444_Premultiplied
+ },
+ {
+ 0, // Bilinear
+ fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_Invalid>, // Mono
+ fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_Invalid>, // MonoLsb
+ fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_Invalid>, // Indexed8
+ fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_ARGB32_Premultiplied>, // RGB32
+ fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_ARGB32>, // ARGB32
+ fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_ARGB32_Premultiplied>, // ARGB32_Premultiplied
+ fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_Invalid>, // RGB16
+ fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_Invalid>, // ARGB8565_Premultiplied
+ fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_Invalid>, // RGB666
+ fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_Invalid>, // ARGB6666_Premultiplied
+ fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_Invalid>, // RGB555
+ fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_Invalid>, // ARGB8555_Premultiplied
+ fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_Invalid>, // RGB888
+ fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_Invalid>, // RGB444
+ fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_Invalid> // ARGB4444_Premultiplied
+ },
+ {
+ 0, // BilinearTiled
+ fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_Invalid>, // Mono
+ fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_Invalid>, // MonoLsb
+ fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_Invalid>, // Indexed8
+ fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_ARGB32_Premultiplied>, // RGB32
+ fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_ARGB32>, // ARGB32
+ fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_ARGB32_Premultiplied>, // ARGB32_Premultiplied
+ fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_Invalid>, // RGB16
+ fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_Invalid>, // ARGB8565_Premultiplied
+ fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_Invalid>, // RGB666
+ fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_Invalid>, // ARGB6666_Premultiplied
+ fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_Invalid>, // RGB555
+ fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_Invalid>, // ARGB8555_Premultiplied
+ fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_Invalid>, // RGB888
+ fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_Invalid>, // RGB444
+ fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_Invalid> // ARGB4444_Premultiplied
+ },
+};
+
+
+static inline uint qt_gradient_pixel(const QGradientData *data, qreal pos)
+{
+ int ipos = int(pos * (GRADIENT_STOPTABLE_SIZE - 1) + qreal(0.5));
+
+ // calculate the actual offset.
+ if (ipos < 0 || ipos >= GRADIENT_STOPTABLE_SIZE) {
+ if (data->spread == QGradient::RepeatSpread) {
+ ipos = ipos % GRADIENT_STOPTABLE_SIZE;
+ ipos = ipos < 0 ? GRADIENT_STOPTABLE_SIZE + ipos : ipos;
+
+ } else if (data->spread == QGradient::ReflectSpread) {
+ const int limit = GRADIENT_STOPTABLE_SIZE * 2 - 1;
+ ipos = ipos % limit;
+ ipos = ipos < 0 ? limit + ipos : ipos;
+ ipos = ipos >= GRADIENT_STOPTABLE_SIZE ? limit - ipos : ipos;
+
+ } else {
+ if (ipos < 0) ipos = 0;
+ else if (ipos >= GRADIENT_STOPTABLE_SIZE) ipos = GRADIENT_STOPTABLE_SIZE-1;
+ }
+ }
+
+ Q_ASSERT(ipos >= 0);
+ Q_ASSERT(ipos < GRADIENT_STOPTABLE_SIZE);
+
+ return data->colorTable[ipos];
+}
+
+#define FIXPT_BITS 8
+#define FIXPT_SIZE (1<<FIXPT_BITS)
+
+static uint qt_gradient_pixel_fixed(const QGradientData *data, int fixed_pos)
+{
+ int ipos = (fixed_pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS;
+
+ // calculate the actual offset.
+ if (ipos < 0 || ipos >= GRADIENT_STOPTABLE_SIZE) {
+ if (data->spread == QGradient::RepeatSpread) {
+ ipos = ipos % GRADIENT_STOPTABLE_SIZE;
+ ipos = ipos < 0 ? GRADIENT_STOPTABLE_SIZE + ipos : ipos;
+
+ } else if (data->spread == QGradient::ReflectSpread) {
+ const int limit = GRADIENT_STOPTABLE_SIZE * 2 - 1;
+ ipos = ipos % limit;
+ ipos = ipos < 0 ? limit + ipos : ipos;
+ ipos = ipos >= GRADIENT_STOPTABLE_SIZE ? limit - ipos : ipos;
+
+ } else {
+ if (ipos < 0) ipos = 0;
+ else if (ipos >= GRADIENT_STOPTABLE_SIZE) ipos = GRADIENT_STOPTABLE_SIZE-1;
+ }
+ }
+
+ Q_ASSERT(ipos >= 0);
+ Q_ASSERT(ipos < GRADIENT_STOPTABLE_SIZE);
+
+ return data->colorTable[ipos];
+}
+
+static void QT_FASTCALL getLinearGradientValues(LinearGradientValues *v, const QSpanData *data)
+{
+ v->dx = data->gradient.linear.end.x - data->gradient.linear.origin.x;
+ v->dy = data->gradient.linear.end.y - data->gradient.linear.origin.y;
+ v->l = v->dx * v->dx + v->dy * v->dy;
+ v->off = 0;
+ if (v->l != 0) {
+ v->dx /= v->l;
+ v->dy /= v->l;
+ v->off = -v->dx * data->gradient.linear.origin.x - v->dy * data->gradient.linear.origin.y;
+ }
+}
+
+static const uint * QT_FASTCALL fetchLinearGradient(uint *buffer, const Operator *op, const QSpanData *data,
+ int y, int x, int length)
+{
+ const uint *b = buffer;
+ qreal t, inc;
+
+ bool affine = true;
+ qreal rx=0, ry=0;
+ if (op->linear.l == 0) {
+ t = inc = 0;
+ } else {
+ rx = data->m21 * (y + qreal(0.5)) + data->m11 * (x + qreal(0.5)) + data->dx;
+ ry = data->m22 * (y + qreal(0.5)) + data->m12 * (x + qreal(0.5)) + data->dy;
+ t = op->linear.dx*rx + op->linear.dy*ry + op->linear.off;
+ inc = op->linear.dx * data->m11 + op->linear.dy * data->m12;
+ affine = !data->m13 && !data->m23;
+
+ if (affine) {
+ t *= (GRADIENT_STOPTABLE_SIZE - 1);
+ inc *= (GRADIENT_STOPTABLE_SIZE - 1);
+ }
+ }
+
+ const uint *end = buffer + length;
+ if (affine) {
+ if (inc > qreal(-1e-5) && inc < qreal(1e-5)) {
+ QT_MEMFILL_UINT(buffer, length, qt_gradient_pixel_fixed(&data->gradient, int(t * FIXPT_SIZE)));
+ } else {
+ if (t+inc*length < qreal(INT_MAX >> (FIXPT_BITS + 1)) &&
+ t+inc*length > qreal(INT_MIN >> (FIXPT_BITS + 1))) {
+ // we can use fixed point math
+ int t_fixed = int(t * FIXPT_SIZE);
+ int inc_fixed = int(inc * FIXPT_SIZE);
+ while (buffer < end) {
+ *buffer = qt_gradient_pixel_fixed(&data->gradient, t_fixed);
+ t_fixed += inc_fixed;
+ ++buffer;
+ }
+ } else {
+ // we have to fall back to float math
+ while (buffer < end) {
+ *buffer = qt_gradient_pixel(&data->gradient, t/GRADIENT_STOPTABLE_SIZE);
+ t += inc;
+ ++buffer;
+ }
+ }
+ }
+ } else { // fall back to float math here as well
+ qreal rw = data->m23 * (y + qreal(0.5)) + data->m13 * (x + qreal(0.5)) + data->m33;
+ while (buffer < end) {
+ qreal x = rx/rw;
+ qreal y = ry/rw;
+ t = (op->linear.dx*x + op->linear.dy *y) + op->linear.off;
+
+ *buffer = qt_gradient_pixel(&data->gradient, t);
+ rx += data->m11;
+ ry += data->m12;
+ rw += data->m13;
+ if (!rw) {
+ rw += data->m13;
+ }
+ ++buffer;
+ }
+ }
+
+ return b;
+}
+
+static inline qreal determinant(qreal a, qreal b, qreal c)
+{
+ return (b * b) - (4 * a * c);
+}
+
+// function to evaluate real roots
+static inline qreal realRoots(qreal a, qreal b, qreal detSqrt)
+{
+ return (-b + detSqrt)/(2 * a);
+}
+
+static inline qreal qSafeSqrt(qreal x)
+{
+ return x > 0 ? qSqrt(x) : 0;
+}
+
+static void QT_FASTCALL getRadialGradientValues(RadialGradientValues *v, const QSpanData *data)
+{
+ v->dx = data->gradient.radial.center.x - data->gradient.radial.focal.x;
+ v->dy = data->gradient.radial.center.y - data->gradient.radial.focal.y;
+ v->a = data->gradient.radial.radius*data->gradient.radial.radius - v->dx*v->dx - v->dy*v->dy;
+}
+
+static const uint * QT_FASTCALL fetchRadialGradient(uint *buffer, const Operator *op, const QSpanData *data,
+ int y, int x, int length)
+{
+ const uint *b = buffer;
+ qreal rx = data->m21 * (y + qreal(0.5))
+ + data->dx + data->m11 * (x + qreal(0.5));
+ qreal ry = data->m22 * (y + qreal(0.5))
+ + data->dy + data->m12 * (x + qreal(0.5));
+ bool affine = !data->m13 && !data->m23;
+ //qreal r = data->gradient.radial.radius;
+
+ const uint *end = buffer + length;
+ if (affine) {
+ rx -= data->gradient.radial.focal.x;
+ ry -= data->gradient.radial.focal.y;
+
+ qreal inv_a = 1 / qreal(2 * op->radial.a);
+
+ const qreal delta_rx = data->m11;
+ const qreal delta_ry = data->m12;
+
+ qreal b = 2*(rx * op->radial.dx + ry * op->radial.dy);
+ qreal delta_b = 2*(delta_rx * op->radial.dx + delta_ry * op->radial.dy);
+ const qreal b_delta_b = 2 * b * delta_b;
+ const qreal delta_b_delta_b = 2 * delta_b * delta_b;
+
+ const qreal bb = b * b;
+ const qreal delta_bb = delta_b * delta_b;
+
+ b *= inv_a;
+ delta_b *= inv_a;
+
+ const qreal rxrxryry = rx * rx + ry * ry;
+ const qreal delta_rxrxryry = delta_rx * delta_rx + delta_ry * delta_ry;
+ const qreal rx_plus_ry = 2*(rx * delta_rx + ry * delta_ry);
+ const qreal delta_rx_plus_ry = 2 * delta_rxrxryry;
+
+ inv_a *= inv_a;
+
+ qreal det = (bb + 4 * op->radial.a * rxrxryry) * inv_a;
+ qreal delta_det = (b_delta_b + delta_bb + 4 * op->radial.a * (rx_plus_ry + delta_rxrxryry)) * inv_a;
+ const qreal delta_delta_det = (delta_b_delta_b + 4 * op->radial.a * delta_rx_plus_ry) * inv_a;
+
+ while (buffer < end) {
+ *buffer = qt_gradient_pixel(&data->gradient, qSafeSqrt(det) - b);
+
+ det += delta_det;
+ delta_det += delta_delta_det;
+ b += delta_b;
+
+ ++buffer;
+ }
+ } else {
+ qreal rw = data->m23 * (y + qreal(0.5))
+ + data->m33 + data->m13 * (x + qreal(0.5));
+ if (!rw)
+ rw = 1;
+ while (buffer < end) {
+ qreal gx = rx/rw - data->gradient.radial.focal.x;
+ qreal gy = ry/rw - data->gradient.radial.focal.y;
+ qreal b = 2*(gx*op->radial.dx + gy*op->radial.dy);
+ qreal det = determinant(op->radial.a, b , -(gx*gx + gy*gy));
+ qreal s = realRoots(op->radial.a, b, qSafeSqrt(det));
+
+ *buffer = qt_gradient_pixel(&data->gradient, s);
+
+ rx += data->m11;
+ ry += data->m12;
+ rw += data->m13;
+ if (!rw) {
+ rw += data->m13;
+ }
+ ++buffer;
+ }
+ }
+
+ return b;
+}
+
+static const uint * QT_FASTCALL fetchConicalGradient(uint *buffer, const Operator *, const QSpanData *data,
+ int y, int x, int length)
+{
+ const uint *b = buffer;
+ qreal rx = data->m21 * (y + qreal(0.5))
+ + data->dx + data->m11 * (x + qreal(0.5));
+ qreal ry = data->m22 * (y + qreal(0.5))
+ + data->dy + data->m12 * (x + qreal(0.5));
+ bool affine = !data->m13 && !data->m23;
+
+ const uint *end = buffer + length;
+ if (affine) {
+ rx -= data->gradient.conical.center.x;
+ ry -= data->gradient.conical.center.y;
+ while (buffer < end) {
+ qreal angle = qAtan2(ry, rx) + data->gradient.conical.angle;
+
+ *buffer = qt_gradient_pixel(&data->gradient, 1 - angle / (2*Q_PI));
+
+ rx += data->m11;
+ ry += data->m12;
+ ++buffer;
+ }
+ } else {
+ qreal rw = data->m23 * (y + qreal(0.5))
+ + data->m33 + data->m13 * (x + qreal(0.5));
+ if (!rw)
+ rw = 1;
+ while (buffer < end) {
+ qreal angle = qAtan2(ry/rw - data->gradient.conical.center.x,
+ rx/rw - data->gradient.conical.center.y)
+ + data->gradient.conical.angle;
+
+ *buffer = qt_gradient_pixel(&data->gradient, 1. - angle / (2*Q_PI));
+
+ rx += data->m11;
+ ry += data->m12;
+ rw += data->m13;
+ if (!rw) {
+ rw += data->m13;
+ }
+ ++buffer;
+ }
+ }
+ return b;
+}
+
+#if defined(Q_CC_RVCT)
+// Force ARM code generation for comp_func_* -methods
+# pragma push
+# pragma arm
+# if defined(QT_HAVE_ARMV6)
+static __forceinline void preload(const uint *start)
+{
+ asm( "pld [start]" );
+}
+static const uint L2CacheLineLength = 32;
+static const uint L2CacheLineLengthInInts = L2CacheLineLength/sizeof(uint);
+# define PRELOAD_INIT(x) preload(x);
+# define PRELOAD_INIT2(x,y) PRELOAD_INIT(x) PRELOAD_INIT(y)
+# define PRELOAD_COND(x) if (((uint)&x[i])%L2CacheLineLength == 0) preload(&x[i] + L2CacheLineLengthInInts);
+// Two consecutive preloads stall, so space them out a bit by using different modulus.
+# define PRELOAD_COND2(x,y) if (((uint)&x[i])%L2CacheLineLength == 0) preload(&x[i] + L2CacheLineLengthInInts); \
+ if (((uint)&y[i])%L2CacheLineLength == 16) preload(&y[i] + L2CacheLineLengthInInts);
+# endif // QT_HAVE_ARMV6
+#endif // Q_CC_RVCT
+
+#if !defined(Q_CC_RVCT) || !defined(QT_HAVE_ARMV6)
+# define PRELOAD_INIT(x)
+# define PRELOAD_INIT2(x,y)
+# define PRELOAD_COND(x)
+# define PRELOAD_COND2(x,y)
+#endif
+
+/* The constant alpha factor describes an alpha factor that gets applied
+ to the result of the composition operation combining it with the destination.
+
+ The intent is that if const_alpha == 0. we get back dest, and if const_alpha == 1.
+ we get the unmodified operation
+
+ result = src op dest
+ dest = result * const_alpha + dest * (1. - const_alpha)
+
+ This means that in the comments below, the first line is the const_alpha==255 case, the
+ second line the general one.
+
+ In the lines below:
+ s == src, sa == alpha(src), sia = 1 - alpha(src)
+ d == dest, da == alpha(dest), dia = 1 - alpha(dest)
+ ca = const_alpha, cia = 1 - const_alpha
+
+ The methods exist in two variants. One where we have a constant source, the other
+ where the source is an array of pixels.
+*/
+
+/*
+ result = 0
+ d = d * cia
+*/
+#define comp_func_Clear_impl(dest, length, const_alpha)\
+{\
+ if (const_alpha == 255) {\
+ QT_MEMFILL_UINT(dest, length, 0);\
+ } else {\
+ int ialpha = 255 - const_alpha;\
+ PRELOAD_INIT(dest)\
+ for (int i = 0; i < length; ++i) {\
+ PRELOAD_COND(dest)\
+ dest[i] = BYTE_MUL(dest[i], ialpha);\
+ }\
+ }\
+}
+
+void QT_FASTCALL comp_func_solid_Clear(uint *dest, int length, uint, uint const_alpha)
+{
+ comp_func_Clear_impl(dest, length, const_alpha);
+}
+
+void QT_FASTCALL comp_func_Clear(uint *dest, const uint *, int length, uint const_alpha)
+{
+ comp_func_Clear_impl(dest, length, const_alpha);
+}
+
+/*
+ result = s
+ dest = s * ca + d * cia
+*/
+void QT_FASTCALL comp_func_solid_Source(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255) {
+ QT_MEMFILL_UINT(dest, length, color);
+ } else {
+ int ialpha = 255 - const_alpha;
+ color = BYTE_MUL(color, const_alpha);
+ PRELOAD_INIT(dest)
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND(dest)
+ dest[i] = color + BYTE_MUL(dest[i], ialpha);
+ }
+ }
+}
+
+void QT_FASTCALL comp_func_Source(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255) {
+ ::memcpy(dest, src, length * sizeof(uint));
+ } else {
+ int ialpha = 255 - const_alpha;
+ PRELOAD_INIT2(dest, src)
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND2(dest, src)
+ dest[i] = INTERPOLATE_PIXEL_255(src[i], const_alpha, dest[i], ialpha);
+ }
+ }
+}
+
+void QT_FASTCALL comp_func_solid_Destination(uint *, int, uint, uint)
+{
+}
+
+void QT_FASTCALL comp_func_Destination(uint *, const uint *, int, uint)
+{
+}
+
+/*
+ result = s + d * sia
+ dest = (s + d * sia) * ca + d * cia
+ = s * ca + d * (sia * ca + cia)
+ = s * ca + d * (1 - sa*ca)
+*/
+void QT_FASTCALL comp_func_solid_SourceOver(uint *dest, int length, uint color, uint const_alpha)
+{
+ if ((const_alpha & qAlpha(color)) == 255) {
+ QT_MEMFILL_UINT(dest, length, color);
+ } else {
+ if (const_alpha != 255)
+ color = BYTE_MUL(color, const_alpha);
+ PRELOAD_INIT(dest)
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND(dest)
+ dest[i] = color + BYTE_MUL(dest[i], qAlpha(~color));
+ }
+ }
+}
+
+void QT_FASTCALL comp_func_SourceOver(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ PRELOAD_INIT2(dest, src)
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND2(dest, src)
+ uint s = src[i];
+ if (s >= 0xff000000)
+ dest[i] = s;
+ else if (s != 0)
+ dest[i] = s + BYTE_MUL(dest[i], qAlpha(~s));
+ }
+ } else {
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND2(dest, src)
+ uint s = BYTE_MUL(src[i], const_alpha);
+ dest[i] = s + BYTE_MUL(dest[i], qAlpha(~s));
+ }
+ }
+}
+
+/*
+ result = d + s * dia
+ dest = (d + s * dia) * ca + d * cia
+ = d + s * dia * ca
+*/
+void QT_FASTCALL comp_func_solid_DestinationOver(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha != 255)
+ color = BYTE_MUL(color, const_alpha);
+ PRELOAD_INIT(dest)
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND(dest)
+ uint d = dest[i];
+ dest[i] = d + BYTE_MUL(color, qAlpha(~d));
+ }
+}
+
+void QT_FASTCALL comp_func_DestinationOver(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ PRELOAD_INIT2(dest, src)
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND2(dest, src)
+ uint d = dest[i];
+ dest[i] = d + BYTE_MUL(src[i], qAlpha(~d));
+ }
+ } else {
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND2(dest, src)
+ uint d = dest[i];
+ uint s = BYTE_MUL(src[i], const_alpha);
+ dest[i] = d + BYTE_MUL(s, qAlpha(~d));
+ }
+ }
+}
+
+/*
+ result = s * da
+ dest = s * da * ca + d * cia
+*/
+void QT_FASTCALL comp_func_solid_SourceIn(uint *dest, int length, uint color, uint const_alpha)
+{
+ PRELOAD_INIT(dest)
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND(dest)
+ dest[i] = BYTE_MUL(color, qAlpha(dest[i]));
+ }
+ } else {
+ color = BYTE_MUL(color, const_alpha);
+ uint cia = 255 - const_alpha;
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND(dest)
+ uint d = dest[i];
+ dest[i] = INTERPOLATE_PIXEL_255(color, qAlpha(d), d, cia);
+ }
+ }
+}
+
+void QT_FASTCALL comp_func_SourceIn(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ PRELOAD_INIT2(dest, src)
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND2(dest, src)
+ dest[i] = BYTE_MUL(src[i], qAlpha(dest[i]));
+ }
+ } else {
+ uint cia = 255 - const_alpha;
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND2(dest, src)
+ uint d = dest[i];
+ uint s = BYTE_MUL(src[i], const_alpha);
+ dest[i] = INTERPOLATE_PIXEL_255(s, qAlpha(d), d, cia);
+ }
+ }
+}
+
+/*
+ result = d * sa
+ dest = d * sa * ca + d * cia
+ = d * (sa * ca + cia)
+*/
+void QT_FASTCALL comp_func_solid_DestinationIn(uint *dest, int length, uint color, uint const_alpha)
+{
+ uint a = qAlpha(color);
+ if (const_alpha != 255) {
+ a = BYTE_MUL(a, const_alpha) + 255 - const_alpha;
+ }
+ PRELOAD_INIT(dest)
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND(dest)
+ dest[i] = BYTE_MUL(dest[i], a);
+ }
+}
+
+void QT_FASTCALL comp_func_DestinationIn(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ PRELOAD_INIT2(dest, src)
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND2(dest, src)
+ dest[i] = BYTE_MUL(dest[i], qAlpha(src[i]));
+ }
+ } else {
+ int cia = 255 - const_alpha;
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND2(dest, src)
+ uint a = BYTE_MUL(qAlpha(src[i]), const_alpha) + cia;
+ dest[i] = BYTE_MUL(dest[i], a);
+ }
+ }
+}
+
+/*
+ result = s * dia
+ dest = s * dia * ca + d * cia
+*/
+
+void QT_FASTCALL comp_func_solid_SourceOut(uint *dest, int length, uint color, uint const_alpha)
+{
+ PRELOAD_INIT(dest)
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND(dest)
+ dest[i] = BYTE_MUL(color, qAlpha(~dest[i]));
+ }
+ } else {
+ color = BYTE_MUL(color, const_alpha);
+ int cia = 255 - const_alpha;
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND(dest)
+ uint d = dest[i];
+ dest[i] = INTERPOLATE_PIXEL_255(color, qAlpha(~d), d, cia);
+ }
+ }
+}
+
+void QT_FASTCALL comp_func_SourceOut(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ PRELOAD_INIT2(dest, src)
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND2(dest, src)
+ dest[i] = BYTE_MUL(src[i], qAlpha(~dest[i]));
+ }
+ } else {
+ int cia = 255 - const_alpha;
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND2(dest, src)
+ uint s = BYTE_MUL(src[i], const_alpha);
+ uint d = dest[i];
+ dest[i] = INTERPOLATE_PIXEL_255(s, qAlpha(~d), d, cia);
+ }
+ }
+}
+
+/*
+ result = d * sia
+ dest = d * sia * ca + d * cia
+ = d * (sia * ca + cia)
+*/
+void QT_FASTCALL comp_func_solid_DestinationOut(uint *dest, int length, uint color, uint const_alpha)
+{
+ uint a = qAlpha(~color);
+ if (const_alpha != 255)
+ a = BYTE_MUL(a, const_alpha) + 255 - const_alpha;
+ PRELOAD_INIT(dest)
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND(dest)
+ dest[i] = BYTE_MUL(dest[i], a);
+ }
+}
+
+void QT_FASTCALL comp_func_DestinationOut(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ PRELOAD_INIT2(dest, src)
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND2(dest, src)
+ dest[i] = BYTE_MUL(dest[i], qAlpha(~src[i]));
+ }
+ } else {
+ int cia = 255 - const_alpha;
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND2(dest, src)
+ uint sia = BYTE_MUL(qAlpha(~src[i]), const_alpha) + cia;
+ dest[i] = BYTE_MUL(dest[i], sia);
+ }
+ }
+}
+
+/*
+ result = s*da + d*sia
+ dest = s*da*ca + d*sia*ca + d *cia
+ = s*ca * da + d * (sia*ca + cia)
+ = s*ca * da + d * (1 - sa*ca)
+*/
+void QT_FASTCALL comp_func_solid_SourceAtop(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha != 255) {
+ color = BYTE_MUL(color, const_alpha);
+ }
+ uint sia = qAlpha(~color);
+ PRELOAD_INIT(dest)
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND(dest)
+ dest[i] = INTERPOLATE_PIXEL_255(color, qAlpha(dest[i]), dest[i], sia);
+ }
+}
+
+void QT_FASTCALL comp_func_SourceAtop(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ PRELOAD_INIT2(dest, src)
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND2(dest, src)
+ uint s = src[i];
+ uint d = dest[i];
+ dest[i] = INTERPOLATE_PIXEL_255(s, qAlpha(d), d, qAlpha(~s));
+ }
+ } else {
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND2(dest, src)
+ uint s = BYTE_MUL(src[i], const_alpha);
+ uint d = dest[i];
+ dest[i] = INTERPOLATE_PIXEL_255(s, qAlpha(d), d, qAlpha(~s));
+ }
+ }
+}
+
+/*
+ result = d*sa + s*dia
+ dest = d*sa*ca + s*dia*ca + d *cia
+ = s*ca * dia + d * (sa*ca + cia)
+*/
+void QT_FASTCALL comp_func_solid_DestinationAtop(uint *dest, int length, uint color, uint const_alpha)
+{
+ uint a = qAlpha(color);
+ if (const_alpha != 255) {
+ color = BYTE_MUL(color, const_alpha);
+ a = qAlpha(color) + 255 - const_alpha;
+ }
+ PRELOAD_INIT(dest)
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND(dest)
+ uint d = dest[i];
+ dest[i] = INTERPOLATE_PIXEL_255(d, a, color, qAlpha(~d));
+ }
+}
+
+void QT_FASTCALL comp_func_DestinationAtop(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ PRELOAD_INIT2(dest, src)
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND2(dest, src)
+ uint s = src[i];
+ uint d = dest[i];
+ dest[i] = INTERPOLATE_PIXEL_255(d, qAlpha(s), s, qAlpha(~d));
+ }
+ } else {
+ int cia = 255 - const_alpha;
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND2(dest, src)
+ uint s = BYTE_MUL(src[i], const_alpha);
+ uint d = dest[i];
+ uint a = qAlpha(s) + cia;
+ dest[i] = INTERPOLATE_PIXEL_255(d, a, s, qAlpha(~d));
+ }
+ }
+}
+
+/*
+ result = d*sia + s*dia
+ dest = d*sia*ca + s*dia*ca + d *cia
+ = s*ca * dia + d * (sia*ca + cia)
+ = s*ca * dia + d * (1 - sa*ca)
+*/
+void QT_FASTCALL comp_func_solid_XOR(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha != 255)
+ color = BYTE_MUL(color, const_alpha);
+ uint sia = qAlpha(~color);
+
+ PRELOAD_INIT(dest)
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND(dest)
+ uint d = dest[i];
+ dest[i] = INTERPOLATE_PIXEL_255(color, qAlpha(~d), d, sia);
+ }
+}
+
+void QT_FASTCALL comp_func_XOR(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ PRELOAD_INIT2(dest, src)
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND2(dest, src)
+ uint d = dest[i];
+ uint s = src[i];
+ dest[i] = INTERPOLATE_PIXEL_255(s, qAlpha(~d), d, qAlpha(~s));
+ }
+ } else {
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND2(dest, src)
+ uint d = dest[i];
+ uint s = BYTE_MUL(src[i], const_alpha);
+ dest[i] = INTERPOLATE_PIXEL_255(s, qAlpha(~d), d, qAlpha(~s));
+ }
+ }
+}
+
+struct QFullCoverage {
+ inline void store(uint *dest, const uint src) const
+ {
+ *dest = src;
+ }
+};
+
+struct QPartialCoverage {
+ inline QPartialCoverage(uint const_alpha)
+ : ca(const_alpha)
+ , ica(255 - const_alpha)
+ {
+ }
+
+ inline void store(uint *dest, const uint src) const
+ {
+ *dest = INTERPOLATE_PIXEL_255(src, ca, *dest, ica);
+ }
+
+private:
+ const uint ca;
+ const uint ica;
+};
+
+static inline int mix_alpha(int da, int sa)
+{
+ return 255 - ((255 - sa) * (255 - da) >> 8);
+}
+
+/*
+ Dca' = Sca.Da + Dca.Sa + Sca.(1 - Da) + Dca.(1 - Sa)
+ = Sca + Dca
+*/
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Plus_impl(uint *dest, int length, uint color, const T &coverage)
+{
+ uint s = color;
+
+ PRELOAD_INIT(dest)
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND(dest)
+ uint d = dest[i];
+ d = comp_func_Plus_one_pixel(d, s);
+ coverage.store(&dest[i], d);
+ }
+}
+
+void QT_FASTCALL comp_func_solid_Plus(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_solid_Plus_impl(dest, length, color, QFullCoverage());
+ else
+ comp_func_solid_Plus_impl(dest, length, color, QPartialCoverage(const_alpha));
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Plus_impl(uint *dest, const uint *src, int length, const T &coverage)
+{
+ PRELOAD_INIT2(dest, src)
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND2(dest, src)
+ uint d = dest[i];
+ uint s = src[i];
+
+ d = comp_func_Plus_one_pixel(d, s);
+
+ coverage.store(&dest[i], d);
+ }
+}
+
+void QT_FASTCALL comp_func_Plus(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_Plus_impl(dest, src, length, QFullCoverage());
+ else
+ comp_func_Plus_impl(dest, src, length, QPartialCoverage(const_alpha));
+}
+
+/*
+ Dca' = Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa)
+*/
+static inline int multiply_op(int dst, int src, int da, int sa)
+{
+ return qt_div_255(src * dst + src * (255 - da) + dst * (255 - sa));
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Multiply_impl(uint *dest, int length, uint color, const T &coverage)
+{
+ int sa = qAlpha(color);
+ int sr = qRed(color);
+ int sg = qGreen(color);
+ int sb = qBlue(color);
+
+ PRELOAD_INIT(dest)
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND(dest)
+ uint d = dest[i];
+ int da = qAlpha(d);
+
+#define OP(a, b) multiply_op(a, b, da, sa)
+ int r = OP( qRed(d), sr);
+ int b = OP( qBlue(d), sb);
+ int g = OP(qGreen(d), sg);
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+void QT_FASTCALL comp_func_solid_Multiply(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_solid_Multiply_impl(dest, length, color, QFullCoverage());
+ else
+ comp_func_solid_Multiply_impl(dest, length, color, QPartialCoverage(const_alpha));
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Multiply_impl(uint *dest, const uint *src, int length, const T &coverage)
+{
+ PRELOAD_INIT2(dest, src)
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND2(dest, src)
+ uint d = dest[i];
+ uint s = src[i];
+
+ int da = qAlpha(d);
+ int sa = qAlpha(s);
+
+#define OP(a, b) multiply_op(a, b, da, sa)
+ int r = OP( qRed(d), qRed(s));
+ int b = OP( qBlue(d), qBlue(s));
+ int g = OP(qGreen(d), qGreen(s));
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+void QT_FASTCALL comp_func_Multiply(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_Multiply_impl(dest, src, length, QFullCoverage());
+ else
+ comp_func_Multiply_impl(dest, src, length, QPartialCoverage(const_alpha));
+}
+
+/*
+ Dca' = (Sca.Da + Dca.Sa - Sca.Dca) + Sca.(1 - Da) + Dca.(1 - Sa)
+ = Sca + Dca - Sca.Dca
+*/
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Screen_impl(uint *dest, int length, uint color, const T &coverage)
+{
+ int sa = qAlpha(color);
+ int sr = qRed(color);
+ int sg = qGreen(color);
+ int sb = qBlue(color);
+
+ PRELOAD_INIT(dest)
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND(dest)
+ uint d = dest[i];
+ int da = qAlpha(d);
+
+#define OP(a, b) 255 - qt_div_255((255-a) * (255-b))
+ int r = OP( qRed(d), sr);
+ int b = OP( qBlue(d), sb);
+ int g = OP(qGreen(d), sg);
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+void QT_FASTCALL comp_func_solid_Screen(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_solid_Screen_impl(dest, length, color, QFullCoverage());
+ else
+ comp_func_solid_Screen_impl(dest, length, color, QPartialCoverage(const_alpha));
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Screen_impl(uint *dest, const uint *src, int length, const T &coverage)
+{
+ PRELOAD_INIT2(dest, src)
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND2(dest, src)
+ uint d = dest[i];
+ uint s = src[i];
+
+ int da = qAlpha(d);
+ int sa = qAlpha(s);
+
+#define OP(a, b) 255 - (((255-a) * (255-b)) >> 8)
+ int r = OP( qRed(d), qRed(s));
+ int b = OP( qBlue(d), qBlue(s));
+ int g = OP(qGreen(d), qGreen(s));
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+void QT_FASTCALL comp_func_Screen(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_Screen_impl(dest, src, length, QFullCoverage());
+ else
+ comp_func_Screen_impl(dest, src, length, QPartialCoverage(const_alpha));
+}
+
+/*
+ if 2.Dca < Da
+ Dca' = 2.Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa)
+ otherwise
+ Dca' = Sa.Da - 2.(Da - Dca).(Sa - Sca) + Sca.(1 - Da) + Dca.(1 - Sa)
+*/
+static inline int overlay_op(int dst, int src, int da, int sa)
+{
+ const int temp = src * (255 - da) + dst * (255 - sa);
+ if (2 * dst < da)
+ return qt_div_255(2 * src * dst + temp);
+ else
+ return qt_div_255(sa * da - 2 * (da - dst) * (sa - src) + temp);
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Overlay_impl(uint *dest, int length, uint color, const T &coverage)
+{
+ int sa = qAlpha(color);
+ int sr = qRed(color);
+ int sg = qGreen(color);
+ int sb = qBlue(color);
+
+ PRELOAD_INIT(dest)
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND(dest)
+ uint d = dest[i];
+ int da = qAlpha(d);
+
+#define OP(a, b) overlay_op(a, b, da, sa)
+ int r = OP( qRed(d), sr);
+ int b = OP( qBlue(d), sb);
+ int g = OP(qGreen(d), sg);
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+void QT_FASTCALL comp_func_solid_Overlay(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_solid_Overlay_impl(dest, length, color, QFullCoverage());
+ else
+ comp_func_solid_Overlay_impl(dest, length, color, QPartialCoverage(const_alpha));
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Overlay_impl(uint *dest, const uint *src, int length, const T &coverage)
+{
+ PRELOAD_INIT2(dest, src)
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND2(dest, src)
+ uint d = dest[i];
+ uint s = src[i];
+
+ int da = qAlpha(d);
+ int sa = qAlpha(s);
+
+#define OP(a, b) overlay_op(a, b, da, sa)
+ int r = OP( qRed(d), qRed(s));
+ int b = OP( qBlue(d), qBlue(s));
+ int g = OP(qGreen(d), qGreen(s));
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+void QT_FASTCALL comp_func_Overlay(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_Overlay_impl(dest, src, length, QFullCoverage());
+ else
+ comp_func_Overlay_impl(dest, src, length, QPartialCoverage(const_alpha));
+}
+
+/*
+ Dca' = min(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca.(1 - Sa)
+ Da' = Sa + Da - Sa.Da
+*/
+static inline int darken_op(int dst, int src, int da, int sa)
+{
+ return qt_div_255(qMin(src * da, dst * sa) + src * (255 - da) + dst * (255 - sa));
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Darken_impl(uint *dest, int length, uint color, const T &coverage)
+{
+ int sa = qAlpha(color);
+ int sr = qRed(color);
+ int sg = qGreen(color);
+ int sb = qBlue(color);
+
+ PRELOAD_INIT(dest)
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND(dest)
+ uint d = dest[i];
+ int da = qAlpha(d);
+
+#define OP(a, b) darken_op(a, b, da, sa)
+ int r = OP( qRed(d), sr);
+ int b = OP( qBlue(d), sb);
+ int g = OP(qGreen(d), sg);
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+void QT_FASTCALL comp_func_solid_Darken(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_solid_Darken_impl(dest, length, color, QFullCoverage());
+ else
+ comp_func_solid_Darken_impl(dest, length, color, QPartialCoverage(const_alpha));
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Darken_impl(uint *dest, const uint *src, int length, const T &coverage)
+{
+ PRELOAD_INIT2(dest, src)
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND2(dest, src)
+ uint d = dest[i];
+ uint s = src[i];
+
+ int da = qAlpha(d);
+ int sa = qAlpha(s);
+
+#define OP(a, b) darken_op(a, b, da, sa)
+ int r = OP( qRed(d), qRed(s));
+ int b = OP( qBlue(d), qBlue(s));
+ int g = OP(qGreen(d), qGreen(s));
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+void QT_FASTCALL comp_func_Darken(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_Darken_impl(dest, src, length, QFullCoverage());
+ else
+ comp_func_Darken_impl(dest, src, length, QPartialCoverage(const_alpha));
+}
+
+/*
+ Dca' = max(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca.(1 - Sa)
+ Da' = Sa + Da - Sa.Da
+*/
+static inline int lighten_op(int dst, int src, int da, int sa)
+{
+ return qt_div_255(qMax(src * da, dst * sa) + src * (255 - da) + dst * (255 - sa));
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Lighten_impl(uint *dest, int length, uint color, const T &coverage)
+{
+ int sa = qAlpha(color);
+ int sr = qRed(color);
+ int sg = qGreen(color);
+ int sb = qBlue(color);
+
+ PRELOAD_INIT(dest)
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND(dest)
+ uint d = dest[i];
+ int da = qAlpha(d);
+
+#define OP(a, b) lighten_op(a, b, da, sa)
+ int r = OP( qRed(d), sr);
+ int b = OP( qBlue(d), sb);
+ int g = OP(qGreen(d), sg);
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+void QT_FASTCALL comp_func_solid_Lighten(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_solid_Lighten_impl(dest, length, color, QFullCoverage());
+ else
+ comp_func_solid_Lighten_impl(dest, length, color, QPartialCoverage(const_alpha));
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Lighten_impl(uint *dest, const uint *src, int length, const T &coverage)
+{
+ PRELOAD_INIT2(dest, src)
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND2(dest, src)
+ uint d = dest[i];
+ uint s = src[i];
+
+ int da = qAlpha(d);
+ int sa = qAlpha(s);
+
+#define OP(a, b) lighten_op(a, b, da, sa)
+ int r = OP( qRed(d), qRed(s));
+ int b = OP( qBlue(d), qBlue(s));
+ int g = OP(qGreen(d), qGreen(s));
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+void QT_FASTCALL comp_func_Lighten(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_Lighten_impl(dest, src, length, QFullCoverage());
+ else
+ comp_func_Lighten_impl(dest, src, length, QPartialCoverage(const_alpha));
+}
+
+/*
+ if Sca.Da + Dca.Sa >= Sa.Da
+ Dca' = Sa.Da + Sca.(1 - Da) + Dca.(1 - Sa)
+ otherwise
+ Dca' = Dca.Sa/(1-Sca/Sa) + Sca.(1 - Da) + Dca.(1 - Sa)
+*/
+static inline int color_dodge_op(int dst, int src, int da, int sa)
+{
+ const int sa_da = sa * da;
+ const int dst_sa = dst * sa;
+ const int src_da = src * da;
+
+ const int temp = src * (255 - da) + dst * (255 - sa);
+ if (src_da + dst_sa >= sa_da)
+ return qt_div_255(sa_da + temp);
+ else
+ return qt_div_255(255 * dst_sa / (255 - 255 * src / sa) + temp);
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_ColorDodge_impl(uint *dest, int length, uint color, const T &coverage)
+{
+ int sa = qAlpha(color);
+ int sr = qRed(color);
+ int sg = qGreen(color);
+ int sb = qBlue(color);
+
+ PRELOAD_INIT(dest)
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND(dest)
+ uint d = dest[i];
+ int da = qAlpha(d);
+
+#define OP(a,b) color_dodge_op(a, b, da, sa)
+ int r = OP( qRed(d), sr);
+ int b = OP( qBlue(d), sb);
+ int g = OP(qGreen(d), sg);
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+void QT_FASTCALL comp_func_solid_ColorDodge(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_solid_ColorDodge_impl(dest, length, color, QFullCoverage());
+ else
+ comp_func_solid_ColorDodge_impl(dest, length, color, QPartialCoverage(const_alpha));
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_ColorDodge_impl(uint *dest, const uint *src, int length, const T &coverage)
+{
+ PRELOAD_INIT2(dest, src)
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND2(dest, src)
+ uint d = dest[i];
+ uint s = src[i];
+
+ int da = qAlpha(d);
+ int sa = qAlpha(s);
+
+#define OP(a, b) color_dodge_op(a, b, da, sa)
+ int r = OP( qRed(d), qRed(s));
+ int b = OP( qBlue(d), qBlue(s));
+ int g = OP(qGreen(d), qGreen(s));
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+void QT_FASTCALL comp_func_ColorDodge(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_ColorDodge_impl(dest, src, length, QFullCoverage());
+ else
+ comp_func_ColorDodge_impl(dest, src, length, QPartialCoverage(const_alpha));
+}
+
+/*
+ if Sca.Da + Dca.Sa <= Sa.Da
+ Dca' = Sca.(1 - Da) + Dca.(1 - Sa)
+ otherwise
+ Dca' = Sa.(Sca.Da + Dca.Sa - Sa.Da)/Sca + Sca.(1 - Da) + Dca.(1 - Sa)
+*/
+static inline int color_burn_op(int dst, int src, int da, int sa)
+{
+ const int src_da = src * da;
+ const int dst_sa = dst * sa;
+ const int sa_da = sa * da;
+
+ const int temp = src * (255 - da) + dst * (255 - sa);
+
+ if (src == 0 || src_da + dst_sa <= sa_da)
+ return qt_div_255(temp);
+ return qt_div_255(sa * (src_da + dst_sa - sa_da) / src + temp);
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_ColorBurn_impl(uint *dest, int length, uint color, const T &coverage)
+{
+ int sa = qAlpha(color);
+ int sr = qRed(color);
+ int sg = qGreen(color);
+ int sb = qBlue(color);
+
+ PRELOAD_INIT(dest)
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND(dest)
+ uint d = dest[i];
+ int da = qAlpha(d);
+
+#define OP(a, b) color_burn_op(a, b, da, sa)
+ int r = OP( qRed(d), sr);
+ int b = OP( qBlue(d), sb);
+ int g = OP(qGreen(d), sg);
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+void QT_FASTCALL comp_func_solid_ColorBurn(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_solid_ColorBurn_impl(dest, length, color, QFullCoverage());
+ else
+ comp_func_solid_ColorBurn_impl(dest, length, color, QPartialCoverage(const_alpha));
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_ColorBurn_impl(uint *dest, const uint *src, int length, const T &coverage)
+{
+ PRELOAD_INIT2(dest, src)
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND2(dest, src)
+ uint d = dest[i];
+ uint s = src[i];
+
+ int da = qAlpha(d);
+ int sa = qAlpha(s);
+
+#define OP(a, b) color_burn_op(a, b, da, sa)
+ int r = OP( qRed(d), qRed(s));
+ int b = OP( qBlue(d), qBlue(s));
+ int g = OP(qGreen(d), qGreen(s));
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+void QT_FASTCALL comp_func_ColorBurn(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_ColorBurn_impl(dest, src, length, QFullCoverage());
+ else
+ comp_func_ColorBurn_impl(dest, src, length, QPartialCoverage(const_alpha));
+}
+
+/*
+ if 2.Sca < Sa
+ Dca' = 2.Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa)
+ otherwise
+ Dca' = Sa.Da - 2.(Da - Dca).(Sa - Sca) + Sca.(1 - Da) + Dca.(1 - Sa)
+*/
+static inline uint hardlight_op(int dst, int src, int da, int sa)
+{
+ const uint temp = src * (255 - da) + dst * (255 - sa);
+
+ if (2 * src < sa)
+ return qt_div_255(2 * src * dst + temp);
+ else
+ return qt_div_255(sa * da - 2 * (da - dst) * (sa - src) + temp);
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_HardLight_impl(uint *dest, int length, uint color, const T &coverage)
+{
+ int sa = qAlpha(color);
+ int sr = qRed(color);
+ int sg = qGreen(color);
+ int sb = qBlue(color);
+
+ PRELOAD_INIT(dest)
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND(dest)
+ uint d = dest[i];
+ int da = qAlpha(d);
+
+#define OP(a, b) hardlight_op(a, b, da, sa)
+ int r = OP( qRed(d), sr);
+ int b = OP( qBlue(d), sb);
+ int g = OP(qGreen(d), sg);
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+void QT_FASTCALL comp_func_solid_HardLight(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_solid_HardLight_impl(dest, length, color, QFullCoverage());
+ else
+ comp_func_solid_HardLight_impl(dest, length, color, QPartialCoverage(const_alpha));
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_HardLight_impl(uint *dest, const uint *src, int length, const T &coverage)
+{
+ PRELOAD_INIT2(dest, src)
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND2(dest, src)
+ uint d = dest[i];
+ uint s = src[i];
+
+ int da = qAlpha(d);
+ int sa = qAlpha(s);
+
+#define OP(a, b) hardlight_op(a, b, da, sa)
+ int r = OP( qRed(d), qRed(s));
+ int b = OP( qBlue(d), qBlue(s));
+ int g = OP(qGreen(d), qGreen(s));
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+void QT_FASTCALL comp_func_HardLight(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_HardLight_impl(dest, src, length, QFullCoverage());
+ else
+ comp_func_HardLight_impl(dest, src, length, QPartialCoverage(const_alpha));
+}
+
+/*
+ if 2.Sca <= Sa
+ Dca' = Dca.(Sa + (2.Sca - Sa).(1 - Dca/Da)) + Sca.(1 - Da) + Dca.(1 - Sa)
+ otherwise if 2.Sca > Sa and 4.Dca <= Da
+ Dca' = Dca.Sa + Da.(2.Sca - Sa).(4.Dca/Da.(4.Dca/Da + 1).(Dca/Da - 1) + 7.Dca/Da) + Sca.(1 - Da) + Dca.(1 - Sa)
+ otherwise if 2.Sca > Sa and 4.Dca > Da
+ Dca' = Dca.Sa + Da.(2.Sca - Sa).((Dca/Da)^0.5 - Dca/Da) + Sca.(1 - Da) + Dca.(1 - Sa)
+*/
+static inline int soft_light_op(int dst, int src, int da, int sa)
+{
+ const int src2 = src << 1;
+ const int dst_np = da != 0 ? (255 * dst) / da : 0;
+ const int temp = (src * (255 - da) + dst * (255 - sa)) * 255;
+
+ if (src2 < sa)
+ return (dst * (sa * 255 + (src2 - sa) * (255 - dst_np)) + temp) / 65025;
+ else if (4 * dst <= da)
+ return (dst * sa * 255 + da * (src2 - sa) * ((((16 * dst_np - 12 * 255) * dst_np + 3 * 65025) * dst_np) / 65025) + temp) / 65025;
+ else {
+# ifdef Q_CC_RVCT // needed to avoid compiler crash in RVCT 2.2
+ return (dst * sa * 255 + da * (src2 - sa) * (qIntSqrtInt(dst_np * 255) - dst_np) + temp) / 65025;
+# else
+ return (dst * sa * 255 + da * (src2 - sa) * (int(qSqrt(qreal(dst_np * 255))) - dst_np) + temp) / 65025;
+# endif
+ }
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_SoftLight_impl(uint *dest, int length, uint color, const T &coverage)
+{
+ int sa = qAlpha(color);
+ int sr = qRed(color);
+ int sg = qGreen(color);
+ int sb = qBlue(color);
+
+ PRELOAD_INIT(dest)
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND(dest)
+ uint d = dest[i];
+ int da = qAlpha(d);
+
+#define OP(a, b) soft_light_op(a, b, da, sa)
+ int r = OP( qRed(d), sr);
+ int b = OP( qBlue(d), sb);
+ int g = OP(qGreen(d), sg);
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+void QT_FASTCALL comp_func_solid_SoftLight(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_solid_SoftLight_impl(dest, length, color, QFullCoverage());
+ else
+ comp_func_solid_SoftLight_impl(dest, length, color, QPartialCoverage(const_alpha));
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_SoftLight_impl(uint *dest, const uint *src, int length, const T &coverage)
+{
+ PRELOAD_INIT2(dest, src)
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND2(dest, src)
+ uint d = dest[i];
+ uint s = src[i];
+
+ int da = qAlpha(d);
+ int sa = qAlpha(s);
+
+#define OP(a, b) soft_light_op(a, b, da, sa)
+ int r = OP( qRed(d), qRed(s));
+ int b = OP( qBlue(d), qBlue(s));
+ int g = OP(qGreen(d), qGreen(s));
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+void QT_FASTCALL comp_func_SoftLight(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_SoftLight_impl(dest, src, length, QFullCoverage());
+ else
+ comp_func_SoftLight_impl(dest, src, length, QPartialCoverage(const_alpha));
+}
+
+/*
+ Dca' = abs(Dca.Sa - Sca.Da) + Sca.(1 - Da) + Dca.(1 - Sa)
+ = Sca + Dca - 2.min(Sca.Da, Dca.Sa)
+*/
+static inline int difference_op(int dst, int src, int da, int sa)
+{
+ return src + dst - qt_div_255(2 * qMin(src * da, dst * sa));
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Difference_impl(uint *dest, int length, uint color, const T &coverage)
+{
+ int sa = qAlpha(color);
+ int sr = qRed(color);
+ int sg = qGreen(color);
+ int sb = qBlue(color);
+
+ PRELOAD_INIT(dest)
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND(dest)
+ uint d = dest[i];
+ int da = qAlpha(d);
+
+#define OP(a, b) difference_op(a, b, da, sa)
+ int r = OP( qRed(d), sr);
+ int b = OP( qBlue(d), sb);
+ int g = OP(qGreen(d), sg);
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+void QT_FASTCALL comp_func_solid_Difference(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_solid_Difference_impl(dest, length, color, QFullCoverage());
+ else
+ comp_func_solid_Difference_impl(dest, length, color, QPartialCoverage(const_alpha));
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Difference_impl(uint *dest, const uint *src, int length, const T &coverage)
+{
+ PRELOAD_INIT2(dest, src)
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND2(dest, src)
+ uint d = dest[i];
+ uint s = src[i];
+
+ int da = qAlpha(d);
+ int sa = qAlpha(s);
+
+#define OP(a, b) difference_op(a, b, da, sa)
+ int r = OP( qRed(d), qRed(s));
+ int b = OP( qBlue(d), qBlue(s));
+ int g = OP(qGreen(d), qGreen(s));
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+void QT_FASTCALL comp_func_Difference(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_Difference_impl(dest, src, length, QFullCoverage());
+ else
+ comp_func_Difference_impl(dest, src, length, QPartialCoverage(const_alpha));
+}
+
+/*
+ Dca' = (Sca.Da + Dca.Sa - 2.Sca.Dca) + Sca.(1 - Da) + Dca.(1 - Sa)
+*/
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void QT_FASTCALL comp_func_solid_Exclusion_impl(uint *dest, int length, uint color, const T &coverage)
+{
+ int sa = qAlpha(color);
+ int sr = qRed(color);
+ int sg = qGreen(color);
+ int sb = qBlue(color);
+
+ PRELOAD_INIT(dest)
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND(dest)
+ uint d = dest[i];
+ int da = qAlpha(d);
+
+#define OP(a, b) (a + b - qt_div_255(2*(a*b)))
+ int r = OP( qRed(d), sr);
+ int b = OP( qBlue(d), sb);
+ int g = OP(qGreen(d), sg);
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+void QT_FASTCALL comp_func_solid_Exclusion(uint *dest, int length, uint color, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_solid_Exclusion_impl(dest, length, color, QFullCoverage());
+ else
+ comp_func_solid_Exclusion_impl(dest, length, color, QPartialCoverage(const_alpha));
+}
+
+template <typename T>
+Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Exclusion_impl(uint *dest, const uint *src, int length, const T &coverage)
+{
+ PRELOAD_INIT2(dest, src)
+ for (int i = 0; i < length; ++i) {
+ PRELOAD_COND2(dest, src)
+ uint d = dest[i];
+ uint s = src[i];
+
+ int da = qAlpha(d);
+ int sa = qAlpha(s);
+
+#define OP(a, b) (a + b - ((a*b) >> 7))
+ int r = OP( qRed(d), qRed(s));
+ int b = OP( qBlue(d), qBlue(s));
+ int g = OP(qGreen(d), qGreen(s));
+ int a = mix_alpha(da, sa);
+#undef OP
+
+ coverage.store(&dest[i], qRgba(r, g, b, a));
+ }
+}
+
+void QT_FASTCALL comp_func_Exclusion(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255)
+ comp_func_Exclusion_impl(dest, src, length, QFullCoverage());
+ else
+ comp_func_Exclusion_impl(dest, src, length, QPartialCoverage(const_alpha));
+}
+
+#if defined(Q_CC_RVCT)
+// Restore pragma state from previous #pragma arm
+# pragma pop
+#endif
+
+void QT_FASTCALL rasterop_solid_SourceOrDestination(uint *dest,
+ int length,
+ uint color,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ while (length--)
+ *dest++ |= color;
+}
+
+void QT_FASTCALL rasterop_SourceOrDestination(uint *dest,
+ const uint *src,
+ int length,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ while (length--)
+ *dest++ |= *src++;
+}
+
+void QT_FASTCALL rasterop_solid_SourceAndDestination(uint *dest,
+ int length,
+ uint color,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ color |= 0xff000000;
+ while (length--)
+ *dest++ &= color;
+}
+
+void QT_FASTCALL rasterop_SourceAndDestination(uint *dest,
+ const uint *src,
+ int length,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ while (length--) {
+ *dest = (*src & *dest) | 0xff000000;
+ ++dest; ++src;
+ }
+}
+
+void QT_FASTCALL rasterop_solid_SourceXorDestination(uint *dest,
+ int length,
+ uint color,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ color &= 0x00ffffff;
+ while (length--)
+ *dest++ ^= color;
+}
+
+void QT_FASTCALL rasterop_SourceXorDestination(uint *dest,
+ const uint *src,
+ int length,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ while (length--) {
+ *dest = (*src ^ *dest) | 0xff000000;
+ ++dest; ++src;
+ }
+}
+
+void QT_FASTCALL rasterop_solid_NotSourceAndNotDestination(uint *dest,
+ int length,
+ uint color,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ color = ~color;
+ while (length--) {
+ *dest = (color & ~(*dest)) | 0xff000000;
+ ++dest;
+ }
+}
+
+void QT_FASTCALL rasterop_NotSourceAndNotDestination(uint *dest,
+ const uint *src,
+ int length,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ while (length--) {
+ *dest = (~(*src) & ~(*dest)) | 0xff000000;
+ ++dest; ++src;
+ }
+}
+
+void QT_FASTCALL rasterop_solid_NotSourceOrNotDestination(uint *dest,
+ int length,
+ uint color,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ color = ~color | 0xff000000;
+ while (length--) {
+ *dest = color | ~(*dest);
+ ++dest;
+ }
+}
+
+void QT_FASTCALL rasterop_NotSourceOrNotDestination(uint *dest,
+ const uint *src,
+ int length,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ while (length--) {
+ *dest = ~(*src) | ~(*dest) | 0xff000000;
+ ++dest; ++src;
+ }
+}
+
+void QT_FASTCALL rasterop_solid_NotSourceXorDestination(uint *dest,
+ int length,
+ uint color,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ color = ~color & 0x00ffffff;
+ while (length--) {
+ *dest = color ^ (*dest);
+ ++dest;
+ }
+}
+
+void QT_FASTCALL rasterop_NotSourceXorDestination(uint *dest,
+ const uint *src,
+ int length,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ while (length--) {
+ *dest = ((~(*src)) ^ (*dest)) | 0xff000000;
+ ++dest; ++src;
+ }
+}
+
+void QT_FASTCALL rasterop_solid_NotSource(uint *dest, int length,
+ uint color, uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ qt_memfill(dest, ~color | 0xff000000, length);
+}
+
+void QT_FASTCALL rasterop_NotSource(uint *dest, const uint *src,
+ int length, uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ while (length--)
+ *dest++ = ~(*src++) | 0xff000000;
+}
+
+void QT_FASTCALL rasterop_solid_NotSourceAndDestination(uint *dest,
+ int length,
+ uint color,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ color = ~color | 0xff000000;
+ while (length--) {
+ *dest = color & *dest;
+ ++dest;
+ }
+}
+
+void QT_FASTCALL rasterop_NotSourceAndDestination(uint *dest,
+ const uint *src,
+ int length,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ while (length--) {
+ *dest = (~(*src) & *dest) | 0xff000000;
+ ++dest; ++src;
+ }
+}
+
+void QT_FASTCALL rasterop_solid_SourceAndNotDestination(uint *dest,
+ int length,
+ uint color,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ while (length--) {
+ *dest = (color & ~(*dest)) | 0xff000000;
+ ++dest;
+ }
+}
+
+void QT_FASTCALL rasterop_SourceAndNotDestination(uint *dest,
+ const uint *src,
+ int length,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ while (length--) {
+ *dest = (*src & ~(*dest)) | 0xff000000;
+ ++dest; ++src;
+ }
+}
+
+static CompositionFunctionSolid functionForModeSolid_C[] = {
+ comp_func_solid_SourceOver,
+ comp_func_solid_DestinationOver,
+ comp_func_solid_Clear,
+ comp_func_solid_Source,
+ comp_func_solid_Destination,
+ comp_func_solid_SourceIn,
+ comp_func_solid_DestinationIn,
+ comp_func_solid_SourceOut,
+ comp_func_solid_DestinationOut,
+ comp_func_solid_SourceAtop,
+ comp_func_solid_DestinationAtop,
+ comp_func_solid_XOR,
+ comp_func_solid_Plus,
+ comp_func_solid_Multiply,
+ comp_func_solid_Screen,
+ comp_func_solid_Overlay,
+ comp_func_solid_Darken,
+ comp_func_solid_Lighten,
+ comp_func_solid_ColorDodge,
+ comp_func_solid_ColorBurn,
+ comp_func_solid_HardLight,
+ comp_func_solid_SoftLight,
+ comp_func_solid_Difference,
+ comp_func_solid_Exclusion,
+ rasterop_solid_SourceOrDestination,
+ rasterop_solid_SourceAndDestination,
+ rasterop_solid_SourceXorDestination,
+ rasterop_solid_NotSourceAndNotDestination,
+ rasterop_solid_NotSourceOrNotDestination,
+ rasterop_solid_NotSourceXorDestination,
+ rasterop_solid_NotSource,
+ rasterop_solid_NotSourceAndDestination,
+ rasterop_solid_SourceAndNotDestination
+};
+
+static const CompositionFunctionSolid *functionForModeSolid = functionForModeSolid_C;
+
+static CompositionFunction functionForMode_C[] = {
+ comp_func_SourceOver,
+ comp_func_DestinationOver,
+ comp_func_Clear,
+ comp_func_Source,
+ comp_func_Destination,
+ comp_func_SourceIn,
+ comp_func_DestinationIn,
+ comp_func_SourceOut,
+ comp_func_DestinationOut,
+ comp_func_SourceAtop,
+ comp_func_DestinationAtop,
+ comp_func_XOR,
+ comp_func_Plus,
+ comp_func_Multiply,
+ comp_func_Screen,
+ comp_func_Overlay,
+ comp_func_Darken,
+ comp_func_Lighten,
+ comp_func_ColorDodge,
+ comp_func_ColorBurn,
+ comp_func_HardLight,
+ comp_func_SoftLight,
+ comp_func_Difference,
+ comp_func_Exclusion,
+ rasterop_SourceOrDestination,
+ rasterop_SourceAndDestination,
+ rasterop_SourceXorDestination,
+ rasterop_NotSourceAndNotDestination,
+ rasterop_NotSourceOrNotDestination,
+ rasterop_NotSourceXorDestination,
+ rasterop_NotSource,
+ rasterop_NotSourceAndDestination,
+ rasterop_SourceAndNotDestination
+};
+
+static const CompositionFunction *functionForMode = functionForMode_C;
+
+static TextureBlendType getBlendType(const QSpanData *data)
+{
+ TextureBlendType ft;
+ if (data->txop <= QTransform::TxTranslate)
+ if (data->texture.type == QTextureData::Tiled)
+ ft = BlendTiled;
+ else
+ ft = BlendUntransformed;
+ else if (data->bilinear)
+ if (data->texture.type == QTextureData::Tiled)
+ ft = BlendTransformedBilinearTiled;
+ else
+ ft = BlendTransformedBilinear;
+ else
+ if (data->texture.type == QTextureData::Tiled)
+ ft = BlendTransformedTiled;
+ else
+ ft = BlendTransformed;
+ return ft;
+}
+
+static inline Operator getOperator(const QSpanData *data, const QSpan *spans, int spanCount)
+{
+ Operator op;
+ bool solidSource = false;
+
+ switch(data->type) {
+ case QSpanData::Solid:
+ solidSource = (qAlpha(data->solid.color) == 255);
+ break;
+ case QSpanData::LinearGradient:
+ solidSource = !data->gradient.alphaColor;
+ getLinearGradientValues(&op.linear, data);
+ op.src_fetch = fetchLinearGradient;
+ break;
+ case QSpanData::RadialGradient:
+ solidSource = !data->gradient.alphaColor;
+ getRadialGradientValues(&op.radial, data);
+ op.src_fetch = fetchRadialGradient;
+ break;
+ case QSpanData::ConicalGradient:
+ solidSource = !data->gradient.alphaColor;
+ op.src_fetch = fetchConicalGradient;
+ break;
+ case QSpanData::Texture:
+ op.src_fetch = sourceFetch[getBlendType(data)][data->texture.format];
+ solidSource = !data->texture.hasAlpha;
+ default:
+ break;
+ }
+
+ op.mode = data->rasterBuffer->compositionMode;
+ if (op.mode == QPainter::CompositionMode_SourceOver && solidSource)
+ op.mode = QPainter::CompositionMode_Source;
+
+ op.dest_fetch = destFetchProc[data->rasterBuffer->format];
+ if (op.mode == QPainter::CompositionMode_Source) {
+ switch (data->rasterBuffer->format) {
+ case QImage::Format_RGB32:
+ case QImage::Format_ARGB32_Premultiplied:
+ // don't clear dest_fetch as it sets up the pointer correctly to save one copy
+ break;
+ default: {
+ const QSpan *lastSpan = spans + spanCount;
+ bool alphaSpans = false;
+ while (spans < lastSpan) {
+ if (spans->coverage != 255) {
+ alphaSpans = true;
+ break;
+ }
+ ++spans;
+ }
+ if (!alphaSpans)
+ op.dest_fetch = 0;
+ }
+ }
+ }
+
+ op.dest_store = destStoreProc[data->rasterBuffer->format];
+
+ op.funcSolid = functionForModeSolid[op.mode];
+ op.func = functionForMode[op.mode];
+
+ return op;
+}
+
+
+
+// -------------------- blend methods ---------------------
+
+enum SpanMethod {
+ RegularSpans,
+ CallbackSpans
+};
+
+#if !defined(Q_CC_SUN)
+static
+#endif
+void drawBufferSpan(QSpanData *data, const uint *buffer, int bufsize,
+ int x, int y, int length, uint const_alpha)
+{
+#if defined (Q_WS_QWS) && !defined(QT_NO_RASTERCALLBACKS)
+ data->rasterEngine->drawBufferSpan(buffer, bufsize, x, y, length, const_alpha);
+#else
+ Q_UNUSED(data);
+ Q_UNUSED(buffer);
+ Q_UNUSED(bufsize);
+ Q_UNUSED(x);
+ Q_UNUSED(y);
+ Q_UNUSED(length);
+ Q_UNUSED(const_alpha);
+#endif
+}
+
+#if !defined(Q_CC_SUN)
+static
+#endif
+void blend_color_generic(int count, const QSpan *spans, void *userData)
+{
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+ uint buffer[buffer_size];
+ Operator op = getOperator(data, spans, count);
+
+ while (count--) {
+ int x = spans->x;
+ int length = spans->len;
+ while (length) {
+ int l = qMin(buffer_size, length);
+ uint *dest = op.dest_fetch ? op.dest_fetch(buffer, data->rasterBuffer, x, spans->y, l) : buffer;
+ op.funcSolid(dest, l, data->solid.color, spans->coverage);
+ if (op.dest_store)
+ op.dest_store(data->rasterBuffer, x, spans->y, dest, l);
+ length -= l;
+ x += l;
+ }
+ ++spans;
+ }
+}
+
+#if defined (Q_WS_QWS) && !defined(QT_NO_RASTERCALLBACKS)
+static void blend_color_generic_callback(int count, const QSpan *spans, void *userData)
+{
+ // ### Falcon
+ Q_UNUSED(count);
+ Q_UNUSED(spans);
+ Q_UNUSED(userData);
+// QSpanData *data = reinterpret_cast<QSpanData*>(userData);
+// data->rasterEngine->drawColorSpans(spans, count, data->solid.color);
+}
+#endif // QT_NO_RASTERCALLBACKS
+
+static void blend_color_argb(int count, const QSpan *spans, void *userData)
+{
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ Operator op = getOperator(data, spans, count);
+
+ if (op.mode == QPainter::CompositionMode_Source) {
+ // inline for performance
+ while (count--) {
+ uint *target = ((uint *)data->rasterBuffer->scanLine(spans->y)) + spans->x;
+ if (spans->coverage == 255) {
+ QT_MEMFILL_UINT(target, spans->len, data->solid.color);
+ } else {
+ uint c = BYTE_MUL(data->solid.color, spans->coverage);
+ int ialpha = 255 - spans->coverage;
+ for (int i = 0; i < spans->len; ++i)
+ target[i] = c + BYTE_MUL(target[i], ialpha);
+ }
+ ++spans;
+ }
+ return;
+ }
+
+ while (count--) {
+ uint *target = ((uint *)data->rasterBuffer->scanLine(spans->y)) + spans->x;
+ op.funcSolid(target, spans->len, data->solid.color, spans->coverage);
+ ++spans;
+ }
+}
+
+template <class T>
+Q_STATIC_TEMPLATE_FUNCTION void blendColor(int count, const QSpan *spans, void *userData)
+{
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+ Operator op = getOperator(data, spans, count);
+
+ if (op.mode == QPainter::CompositionMode_Source) {
+ const T c = qt_colorConvert<T, quint32p>(quint32p::fromRawData(data->solid.color), 0);
+ while (count--) {
+ T *target = ((T*)data->rasterBuffer->scanLine(spans->y))
+ + spans->x;
+ if (spans->coverage == 255) {
+ qt_memfill(target, c, spans->len);
+ } else {
+ const quint8 alpha = T::alpha(spans->coverage);
+ const T color = c.byte_mul(alpha);
+ const int ialpha = T::ialpha(spans->coverage);
+ const T *end = target + spans->len;
+ while (target < end) {
+ *target = color + target->byte_mul(ialpha);
+ ++target;
+ }
+ }
+ ++spans;
+ }
+ return;
+ }
+
+ if (op.mode == QPainter::CompositionMode_SourceOver) {
+ while (count--) {
+ const quint32 color = BYTE_MUL(data->solid.color, spans->coverage);
+ const T c = qt_colorConvert<T, quint32p>(quint32p::fromRawData(color), 0);
+ const quint8 ialpha = T::alpha(qAlpha(~color));
+ T *target = ((T*)data->rasterBuffer->scanLine(spans->y)) + spans->x;
+ const T *end = target + spans->len;
+ while (target != end) {
+ *target = c + target->byte_mul(ialpha);
+ ++target;
+ }
+ ++spans;
+ }
+ return;
+ }
+
+ blend_color_generic(count, spans, userData);
+}
+
+#define SPANFUNC_POINTER_BLENDCOLOR(DST) blendColor<DST>
+
+static void blend_color_rgb16(int count, const QSpan *spans, void *userData)
+{
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ /*
+ We duplicate a little logic from getOperator() and calculate the
+ composition mode directly. This allows blend_color_rgb16 to be used
+ from qt_gradient_quint16 with minimal overhead.
+ */
+ QPainter::CompositionMode mode = data->rasterBuffer->compositionMode;
+ if (mode == QPainter::CompositionMode_SourceOver &&
+ qAlpha(data->solid.color) == 255)
+ mode = QPainter::CompositionMode_Source;
+
+ if (mode == QPainter::CompositionMode_Source) {
+ // inline for performance
+ ushort c = qConvertRgb32To16(data->solid.color);
+ while (count--) {
+ ushort *target = ((ushort *)data->rasterBuffer->scanLine(spans->y)) + spans->x;
+ if (spans->coverage == 255) {
+ QT_MEMFILL_USHORT(target, spans->len, c);
+ } else {
+ ushort color = BYTE_MUL_RGB16(c, spans->coverage);
+ int ialpha = 255 - spans->coverage;
+ const ushort *end = target + spans->len;
+ while (target < end) {
+ *target = color + BYTE_MUL_RGB16(*target, ialpha);
+ ++target;
+ }
+ }
+ ++spans;
+ }
+ return;
+ }
+
+ if (mode == QPainter::CompositionMode_SourceOver) {
+ while (count--) {
+ uint color = BYTE_MUL(data->solid.color, spans->coverage);
+ int ialpha = qAlpha(~color);
+ ushort c = qConvertRgb32To16(color);
+ ushort *target = ((ushort *)data->rasterBuffer->scanLine(spans->y)) + spans->x;
+ int len = spans->len;
+ bool pre = (((quintptr)target) & 0x3) != 0;
+ bool post = false;
+ if (pre) {
+ // skip to word boundary
+ *target = c + BYTE_MUL_RGB16(*target, ialpha);
+ ++target;
+ --len;
+ }
+ if (len & 0x1) {
+ post = true;
+ --len;
+ }
+ uint *target32 = (uint*)target;
+ uint c32 = c | (c<<16);
+ len >>= 1;
+ uint salpha = (ialpha+1) >> 3; // calculate here rather than in loop
+ while (len--) {
+ // blend full words
+ *target32 = c32 + BYTE_MUL_RGB16_32(*target32, salpha);
+ ++target32;
+ target += 2;
+ }
+ if (post) {
+ // one last pixel beyond a full word
+ *target = c + BYTE_MUL_RGB16(*target, ialpha);
+ }
+ ++spans;
+ }
+ return;
+ }
+
+ blend_color_generic(count, spans, userData);
+}
+
+template <typename T>
+void handleSpans(int count, const QSpan *spans, const QSpanData *data, T &handler)
+{
+ uint const_alpha = 256;
+ if (data->type == QSpanData::Texture)
+ const_alpha = data->texture.const_alpha;
+
+ int coverage = 0;
+ while (count) {
+ int x = spans->x;
+ const int y = spans->y;
+ int right = x + spans->len;
+
+ // compute length of adjacent spans
+ for (int i = 1; i < count && spans[i].y == y && spans[i].x == right; ++i)
+ right += spans[i].len;
+ int length = right - x;
+
+ while (length) {
+ int l = qMin(buffer_size, length);
+ length -= l;
+
+ int process_length = l;
+ int process_x = x;
+
+ const uint *src = handler.fetch(process_x, y, process_length);
+ int offset = 0;
+ while (l > 0) {
+ if (x == spans->x) // new span?
+ coverage = (spans->coverage * const_alpha) >> 8;
+
+ int right = spans->x + spans->len;
+ int len = qMin(l, right - x);
+
+ handler.process(x, y, len, coverage, src, offset);
+
+ l -= len;
+ x += len;
+ offset += len;
+
+ if (x == right) { // done with current span?
+ ++spans;
+ --count;
+ }
+ }
+ handler.store(process_x, y, process_length);
+ }
+ }
+}
+
+struct QBlendBase
+{
+ QBlendBase(QSpanData *d, Operator o)
+ : data(d)
+ , op(o)
+ , dest(0)
+ {
+ }
+
+ QSpanData *data;
+ Operator op;
+
+ uint *dest;
+
+ uint buffer[buffer_size];
+ uint src_buffer[buffer_size];
+};
+
+template <SpanMethod spanMethod>
+class BlendSrcGeneric : public QBlendBase
+{
+public:
+ BlendSrcGeneric(QSpanData *d, Operator o)
+ : QBlendBase(d, o)
+ {
+ }
+
+ const uint *fetch(int x, int y, int len)
+ {
+ if (spanMethod == RegularSpans)
+ dest = op.dest_fetch ? op.dest_fetch(buffer, data->rasterBuffer, x, y, len) : buffer;
+
+ return op.src_fetch(src_buffer, &op, data, y, x, len);
+ }
+
+ void process(int x, int y, int len, int coverage, const uint *src, int offset)
+ {
+ if (spanMethod == RegularSpans)
+ op.func(dest + offset, src + offset, len, coverage);
+ else
+ drawBufferSpan(data, src + offset, len, x, y, len, coverage);
+ }
+
+ void store(int x, int y, int len)
+ {
+ if (spanMethod == RegularSpans && op.dest_store) {
+ op.dest_store(data->rasterBuffer, x, y, dest, len);
+ }
+ }
+};
+
+template <SpanMethod spanMethod>
+Q_STATIC_TEMPLATE_FUNCTION void blend_src_generic(int count, const QSpan *spans, void *userData)
+{
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+ BlendSrcGeneric<spanMethod> blend(data, getOperator(data, spans, count));
+ handleSpans(count, spans, data, blend);
+}
+
+template <SpanMethod spanMethod>
+Q_STATIC_TEMPLATE_FUNCTION void blend_untransformed_generic(int count, const QSpan *spans, void *userData)
+{
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ uint buffer[buffer_size];
+ uint src_buffer[buffer_size];
+ Operator op = getOperator(data, spans, count);
+
+ const int image_width = data->texture.width;
+ const int image_height = data->texture.height;
+ int xoff = -qRound(-data->dx);
+ int yoff = -qRound(-data->dy);
+
+ while (count--) {
+ int x = spans->x;
+ int length = spans->len;
+ int sx = xoff + x;
+ int sy = yoff + spans->y;
+ if (sy >= 0 && sy < image_height && sx < image_width) {
+ if (sx < 0) {
+ x -= sx;
+ length += sx;
+ sx = 0;
+ }
+ if (sx + length > image_width)
+ length = image_width - sx;
+ if (length > 0) {
+ const int coverage = (spans->coverage * data->texture.const_alpha) >> 8;
+ while (length) {
+ int l = qMin(buffer_size, length);
+ const uint *src = op.src_fetch(src_buffer, &op, data, sy, sx, l);
+ if (spanMethod == RegularSpans) {
+ uint *dest = op.dest_fetch ? op.dest_fetch(buffer, data->rasterBuffer, x, spans->y, l) : buffer;
+ op.func(dest, src, l, coverage);
+ if (op.dest_store)
+ op.dest_store(data->rasterBuffer, x, spans->y, dest, l);
+ } else {
+ drawBufferSpan(data, src, l, x, spans->y,
+ l, coverage);
+ }
+ x += l;
+ sx += l;
+ length -= l;
+ }
+ }
+ }
+ ++spans;
+ }
+}
+
+template <SpanMethod spanMethod>
+Q_STATIC_TEMPLATE_FUNCTION void blend_untransformed_argb(int count, const QSpan *spans, void *userData)
+{
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+ if (data->texture.format != QImage::Format_ARGB32_Premultiplied
+ && data->texture.format != QImage::Format_RGB32) {
+ blend_untransformed_generic<spanMethod>(count, spans, userData);
+ return;
+ }
+
+ Operator op = getOperator(data, spans, count);
+
+ const int image_width = data->texture.width;
+ const int image_height = data->texture.height;
+ int xoff = -qRound(-data->dx);
+ int yoff = -qRound(-data->dy);
+
+ while (count--) {
+ int x = spans->x;
+ int length = spans->len;
+ int sx = xoff + x;
+ int sy = yoff + spans->y;
+ if (sy >= 0 && sy < image_height && sx < image_width) {
+ if (sx < 0) {
+ x -= sx;
+ length += sx;
+ sx = 0;
+ }
+ if (sx + length > image_width)
+ length = image_width - sx;
+ if (length > 0) {
+ const int coverage = (spans->coverage * data->texture.const_alpha) >> 8;
+ const uint *src = (uint *)data->texture.scanLine(sy) + sx;
+ if (spanMethod == RegularSpans) {
+ uint *dest = ((uint *)data->rasterBuffer->scanLine(spans->y)) + x;
+ op.func(dest, src, length, coverage);
+ } else {
+ drawBufferSpan(data, src, length, x,
+ spans->y, length, coverage);
+ }
+ }
+ }
+ ++spans;
+ }
+}
+
+static inline quint16 interpolate_pixel_rgb16_255(quint16 x, quint8 a,
+ quint16 y, quint8 b)
+{
+ quint16 t = ((((x & 0x07e0) * a) + ((y & 0x07e0) * b)) >> 5) & 0x07e0;
+ t |= ((((x & 0xf81f) * a) + ((y & 0xf81f) * b)) >> 5) & 0xf81f;
+
+ return t;
+}
+
+static inline quint32 interpolate_pixel_rgb16x2_255(quint32 x, quint8 a,
+ quint32 y, quint8 b)
+{
+ uint t;
+ t = ((((x & 0xf81f07e0) >> 5) * a) + (((y & 0xf81f07e0) >> 5) * b)) & 0xf81f07e0;
+ t |= ((((x & 0x07e0f81f) * a) + ((y & 0x07e0f81f) * b)) >> 5) & 0x07e0f81f;
+ return t;
+}
+
+static inline void blend_sourceOver_rgb16_rgb16(quint16 *dest,
+ const quint16 *src,
+ int length,
+ const quint8 alpha,
+ const quint8 ialpha)
+{
+ const int dstAlign = ((quintptr)dest) & 0x3;
+ if (dstAlign) {
+ *dest = interpolate_pixel_rgb16_255(*src, alpha, *dest, ialpha);
+ ++dest;
+ ++src;
+ --length;
+ }
+ const int srcAlign = ((quintptr)src) & 0x3;
+ int length32 = length >> 1;
+ if (length32 && srcAlign == 0) {
+ while (length32--) {
+ const quint32 *src32 = reinterpret_cast<const quint32*>(src);
+ quint32 *dest32 = reinterpret_cast<quint32*>(dest);
+ *dest32 = interpolate_pixel_rgb16x2_255(*src32, alpha,
+ *dest32, ialpha);
+ dest += 2;
+ src += 2;
+ }
+ length &= 0x1;
+ }
+ while (length--) {
+ *dest = interpolate_pixel_rgb16_255(*src, alpha, *dest, ialpha);
+ ++dest;
+ ++src;
+ }
+}
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline void madd_2(DST *dest, const quint16 alpha, const SRC *src)
+{
+ Q_ASSERT((quintptr(dest) & 0x3) == 0);
+ Q_ASSERT((quintptr(src) & 0x3) == 0);
+ dest[0] = dest[0].byte_mul(alpha >> 8) + DST(src[0]);
+ dest[1] = dest[1].byte_mul(alpha & 0xff) + DST(src[1]);
+}
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline void madd_4(DST *dest, const quint32 alpha, const SRC *src)
+{
+ Q_ASSERT((quintptr(dest) & 0x3) == 0);
+ Q_ASSERT((quintptr(src) & 0x3) == 0);
+ dest[0] = dest[0].byte_mul(alpha >> 24) + DST(src[0]);
+ dest[1] = dest[1].byte_mul((alpha >> 16) & 0xff) + DST(src[1]);
+ dest[2] = dest[2].byte_mul((alpha >> 8) & 0xff) + DST(src[2]);
+ dest[3] = dest[3].byte_mul(alpha & 0xff) + DST(src[3]);
+}
+
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline void madd_4(qargb8565 *dest, const quint32 a, const qargb8565 *src)
+{
+ Q_ASSERT((quintptr(dest) & 0x3) == 0);
+ Q_ASSERT((quintptr(src) & 0x3) == 0);
+
+ const quint32 *src32 = reinterpret_cast<const quint32*>(src);
+ quint32 *dest32 = reinterpret_cast<quint32*>(dest);
+ quint32 x, y, t;
+ quint8 a8;
+
+ {
+ x = dest32[0];
+ y = src32[0];
+
+ a8 = a >> 24;
+
+ // a0,g0
+ t = ((((x & 0x0007e0ff) * a8) >> 5) & 0x0007e0ff) + (y & 0x0007c0f8);
+
+ // r0,b0
+ t |= ((((x & 0x00f81f00) * a8) >> 5) & 0x00f81f00) + (y & 0x00f81f00);
+
+ a8 = (a >> 16) & 0xff;
+
+ // a1
+ t |= ((((x & 0xff000000) >> 5) * a8) & 0xff000000) + (y & 0xf8000000);
+
+ dest32[0] = t;
+ }
+ {
+ x = dest32[1];
+ y = src32[1];
+
+ // r1,b1
+ t = ((((x & 0x0000f81f) * a8) >> 5) & 0x0000f81f) + (y & 0x0000f81f);
+
+ // g1
+ t |= ((((x & 0x000007e0) * a8) >> 5) & 0x000007e0) + (y & 0x000007c0);
+
+ a8 = (a >> 8) & 0xff;
+
+ // a2
+ t |= ((((x & 0x00ff0000) * a8) >> 5) & 0x00ff0000) + (y & 0x00f80000);
+
+ {
+ // rgb2
+ quint16 x16 = (x >> 24) | ((dest32[2] & 0x000000ff) << 8);
+ quint16 y16 = (y >> 24) | ((src32[2] & 0x000000ff) << 8);
+ quint16 t16;
+
+ t16 = ((((x16 & 0xf81f) * a8) >> 5) & 0xf81f) + (y16 & 0xf81f);
+ t16 |= ((((x16 & 0x07e0) * a8) >> 5) & 0x07e0) + (y16 & 0x07c0);
+
+ // rg2
+ t |= ((t16 & 0x00ff) << 24);
+
+ dest32[1] = t;
+
+ x = dest32[2];
+ y = src32[2];
+
+ // gb2
+ t = (t16 >> 8);
+ }
+ }
+ {
+ a8 = a & 0xff;
+
+ // g3,a3
+ t |= ((((x & 0x07e0ff00) * a8) >> 5) & 0x07e0ff00) + (y & 0x07c0f800);
+
+ // r3,b3
+ t |= ((((x & 0xf81f0000) >> 5) * a8) & 0xf81f0000)+ (y & 0xf81f0000);
+
+ dest32[2] = t;
+ }
+}
+#endif
+
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline void madd_4(qargb8555 *dest, const quint32 a, const qargb8555 *src)
+{
+ Q_ASSERT((quintptr(dest) & 0x3) == 0);
+ Q_ASSERT((quintptr(src) & 0x3) == 0);
+
+ const quint32 *src32 = reinterpret_cast<const quint32*>(src);
+ quint32 *dest32 = reinterpret_cast<quint32*>(dest);
+ quint32 x, y, t;
+ quint8 a8;
+
+ {
+ x = dest32[0];
+ y = src32[0];
+
+ a8 = a >> 24;
+
+ // a0,g0
+ t = ((((x & 0x0003e0ff) * a8) >> 5) & 0x0003e0ff) + (y & 0x0003e0f8);
+
+ // r0,b0
+ t |= ((((x & 0x007c1f00) * a8) >> 5) & 0x007c1f00) + (y & 0x007c1f00);
+
+ a8 = (a >> 16) & 0xff;
+
+ // a1
+ t |= ((((x & 0xff000000) >> 5) * a8) & 0xff000000) + (y & 0xf8000000);
+
+ dest32[0] = t;
+ }
+ {
+ x = dest32[1];
+ y = src32[1];
+
+ // r1,b1
+ t = ((((x & 0x00007c1f) * a8) >> 5) & 0x00007c1f) + (y & 0x00007c1f);
+
+ // g1
+ t |= ((((x & 0x000003e0) * a8) >> 5) & 0x000003e0) + (y & 0x000003e0);
+
+ a8 = (a >> 8) & 0xff;
+
+ // a2
+ t |= ((((x & 0x00ff0000) * a8) >> 5) & 0x00ff0000) + (y & 0x00f80000);
+
+ {
+ // rgb2
+ quint16 x16 = (x >> 24) | ((dest32[2] & 0x000000ff) << 8);
+ quint16 y16 = (y >> 24) | ((src32[2] & 0x000000ff) << 8);
+ quint16 t16;
+
+ t16 = ((((x16 & 0x7c1f) * a8) >> 5) & 0x7c1f) + (y16 & 0x7c1f);
+ t16 |= ((((x16 & 0x03e0) * a8) >> 5) & 0x03e0) + (y16 & 0x03e0);
+
+ // rg2
+ t |= ((t16 & 0x00ff) << 24);
+
+ dest32[1] = t;
+
+ x = dest32[2];
+ y = src32[2];
+
+ // gb2
+ t = (t16 >> 8);
+ }
+ }
+ {
+ a8 = a & 0xff;
+
+ // g3,a3
+ t |= ((((x & 0x03e0ff00) * a8) >> 5) & 0x03e0ff00) + (y & 0x03e0f800);
+
+ // r3,b3
+ t |= ((((x & 0x7c1f0000) >> 5) * a8) & 0x7c1f0000)+ (y & 0x7c1f0000);
+
+ dest32[2] = t;
+ }
+}
+#endif
+
+template <class T>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint16 alpha_2(const T *src)
+{
+ Q_ASSERT((quintptr(src) & 0x3) == 0);
+
+ if (T::hasAlpha())
+ return (src[0].alpha() << 8) | src[1].alpha();
+ else
+ return 0xffff;
+}
+
+template <class T>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint32 alpha_4(const T *src)
+{
+ Q_ASSERT((quintptr(src) & 0x3) == 0);
+
+ if (T::hasAlpha()) {
+ return (src[0].alpha() << 24) | (src[1].alpha() << 16)
+ | (src[2].alpha() << 8) | src[3].alpha();
+ } else {
+ return 0xffffffff;
+ }
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint32 alpha_4(const qargb8565 *src)
+{
+ const quint8 *src8 = reinterpret_cast<const quint8*>(src);
+ return src8[0] << 24 | src8[3] << 16 | src8[6] << 8 | src8[9];
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint32 alpha_4(const qargb6666 *src)
+{
+ const quint8 *src8 = reinterpret_cast<const quint8*>(src);
+ return ((src8[2] & 0xfc) | (src8[2] >> 6)) << 24
+ | ((src8[5] & 0xfc) | (src8[5] >> 6)) << 16
+ | ((src8[8] & 0xfc) | (src8[8] >> 6)) << 8
+ | ((src8[11] & 0xfc) | (src8[11] >> 6));
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint32 alpha_4(const qargb8555 *src)
+{
+ Q_ASSERT((quintptr(src) & 0x3) == 0);
+ const quint8 *src8 = reinterpret_cast<const quint8*>(src);
+ return src8[0] << 24 | src8[3] << 16 | src8[6] << 8 | src8[9];
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint16 alpha_2(const qargb4444 *src)
+{
+ const quint32 *src32 = reinterpret_cast<const quint32*>(src);
+ const quint32 t = (*src32 & 0xf000f000) |
+ ((*src32 & 0xf000f000) >> 4);
+ return (t >> 24) | (t & 0xff00);
+}
+
+template <class T>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint16 eff_alpha_2(quint16 alpha, const T*)
+{
+ return (T::alpha((alpha >> 8) & 0xff) << 8)
+ | T::alpha(alpha & 0xff);
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint16 eff_alpha_2(quint16 a, const qrgb565*)
+{
+ return ((((a & 0xff00) + 0x0100) >> 3) & 0xff00)
+ | ((((a & 0x00ff) + 0x0001) >> 3) & 0x00ff);
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint16 eff_alpha_2(quint16 a, const qrgb444*)
+{
+ return (((a & 0x00ff) + 0x0001) >> 4)
+ | ((((a & 0xff00) + 0x0100) >> 4) & 0xff00);
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint16 eff_alpha_2(quint16 a, const qargb4444*)
+{
+ return (((a & 0x00ff) + 0x0001) >> 4)
+ | ((((a & 0xff00) + 0x0100) >> 4) & 0xff00);
+}
+
+template <class T>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint16 eff_ialpha_2(quint16 alpha, const T*)
+{
+ return (T::ialpha((alpha >> 8) & 0xff) << 8)
+ | T::ialpha(alpha & 0xff);
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint16 eff_ialpha_2(quint16 a, const qrgb565 *dummy)
+{
+ return 0x2020 - eff_alpha_2(a, dummy);
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint16 eff_ialpha_2(quint16 a, const qargb4444 *dummy)
+{
+ return 0x1010 - eff_alpha_2(a, dummy);
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint16 eff_ialpha_2(quint16 a, const qrgb444 *dummy)
+{
+ return 0x1010 - eff_alpha_2(a, dummy);
+}
+
+template <class T>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint32 eff_alpha_4(quint32 alpha, const T*)
+{
+ return (T::alpha(alpha >> 24) << 24)
+ | (T::alpha((alpha >> 16) & 0xff) << 16)
+ | (T::alpha((alpha >> 8) & 0xff) << 8)
+ | T::alpha(alpha & 0xff);
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint32 eff_alpha_4(quint32 a, const qrgb888*)
+{
+ return a;
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint32 eff_alpha_4(quint32 a, const qargb8565*)
+{
+ return ((((a & 0xff00ff00) + 0x01000100) >> 3) & 0xff00ff00)
+ | ((((a & 0x00ff00ff) + 0x00010001) >> 3) & 0x00ff00ff);
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint32 eff_alpha_4(quint32 a, const qargb6666*)
+{
+ return ((((a & 0xff00ff00) >> 2) + 0x00400040) & 0xff00ff00)
+ | ((((a & 0x00ff00ff) + 0x00010001) >> 2) & 0x00ff00ff);
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint32 eff_alpha_4(quint32 a, const qrgb666*)
+{
+ return ((((a & 0xff00ff00) >> 2) + 0x00400040) & 0xff00ff00)
+ | ((((a & 0x00ff00ff) + 0x00010001) >> 2) & 0x00ff00ff);
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint32 eff_alpha_4(quint32 a, const qargb8555*)
+{
+ return ((((a & 0xff00ff00) + 0x01000100) >> 3) & 0xff00ff00)
+ | ((((a & 0x00ff00ff) + 0x00010001) >> 3) & 0x00ff00ff);
+}
+
+template <class T>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint32 eff_ialpha_4(quint32 alpha, const T*)
+{
+ return (T::ialpha(alpha >> 24) << 24)
+ | (T::ialpha((alpha >> 16) & 0xff) << 16)
+ | (T::ialpha((alpha >> 8) & 0xff) << 8)
+ | T::ialpha(alpha & 0xff);
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint32 eff_ialpha_4(quint32 a, const qrgb888*)
+{
+ return ~a;
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint32 eff_ialpha_4(quint32 a, const qargb8565 *dummy)
+{
+ return 0x20202020 - eff_alpha_4(a, dummy);
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint32 eff_ialpha_4(quint32 a, const qargb6666 *dummy)
+{
+ return 0x40404040 - eff_alpha_4(a, dummy);
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint32 eff_ialpha_4(quint32 a, const qrgb666 *dummy)
+{
+ return 0x40404040 - eff_alpha_4(a, dummy);
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline quint32 eff_ialpha_4(quint32 a, const qargb8555 *dummy)
+{
+ return 0x20202020 - eff_alpha_4(a, dummy);
+}
+
+template <class DST, class SRC>
+inline void interpolate_pixel_unaligned_2(DST *dest, const SRC *src,
+ quint16 alpha)
+{
+ const quint16 a = eff_alpha_2(alpha, dest);
+ const quint16 ia = eff_ialpha_2(alpha, dest);
+ dest[0] = DST(src[0]).byte_mul(a >> 8) + dest[0].byte_mul(ia >> 8);
+ dest[1] = DST(src[1]).byte_mul(a & 0xff) + dest[1].byte_mul(ia & 0xff);
+}
+
+template <class DST, class SRC>
+inline void interpolate_pixel_2(DST *dest, const SRC *src, quint16 alpha)
+{
+ Q_ASSERT((quintptr(dest) & 0x3) == 0);
+ Q_ASSERT((quintptr(src) & 0x3) == 0);
+
+ const quint16 a = eff_alpha_2(alpha, dest);
+ const quint16 ia = eff_ialpha_2(alpha, dest);
+
+ dest[0] = DST(src[0]).byte_mul(a >> 8) + dest[0].byte_mul(ia >> 8);
+ dest[1] = DST(src[1]).byte_mul(a & 0xff) + dest[1].byte_mul(ia & 0xff);
+}
+
+template <class DST, class SRC>
+inline void interpolate_pixel(DST &dest, quint8 a, const SRC &src, quint8 b)
+{
+ if (SRC::hasAlpha() && !DST::hasAlpha())
+ interpolate_pixel(dest, a, DST(src), b);
+ else
+ dest = dest.byte_mul(a) + DST(src).byte_mul(b);
+}
+
+template <>
+inline void interpolate_pixel(qargb8565 &dest, quint8 a,
+ const qargb8565 &src, quint8 b)
+{
+ quint8 *d = reinterpret_cast<quint8*>(&dest);
+ const quint8 *s = reinterpret_cast<const quint8*>(&src);
+ d[0] = (d[0] * a + s[0] * b) >> 5;
+
+ const quint16 x = (d[2] << 8) | d[1];
+ const quint16 y = (s[2] << 8) | s[1];
+ quint16 t = (((x & 0x07e0) * a + (y & 0x07e0) * b) >> 5) & 0x07e0;
+ t |= (((x & 0xf81f) * a + (y & 0xf81f) * b) >> 5) & 0xf81f;
+
+ d[1] = t & 0xff;
+ d[2] = t >> 8;
+}
+
+template <>
+inline void interpolate_pixel(qrgb565 &dest, quint8 a,
+ const qrgb565 &src, quint8 b)
+{
+ const quint16 x = dest.rawValue();
+ const quint16 y = src.rawValue();
+ quint16 t = (((x & 0x07e0) * a + (y & 0x07e0) * b) >> 5) & 0x07e0;
+ t |= (((x & 0xf81f) * a + (y & 0xf81f) * b) >> 5) & 0xf81f;
+ dest = t;
+}
+
+template <>
+inline void interpolate_pixel(qrgb555 &dest, quint8 a,
+ const qrgb555 &src, quint8 b)
+{
+ const quint16 x = dest.rawValue();
+ const quint16 y = src.rawValue();
+ quint16 t = (((x & 0x03e0) * a + (y & 0x03e0) * b) >> 5) & 0x03e0;
+ t |= ((((x & 0x7c1f) * a) + ((y & 0x7c1f) * b)) >> 5) & 0x7c1f;
+ dest = t;
+}
+
+template <>
+inline void interpolate_pixel(qrgb444 &dest, quint8 a,
+ const qrgb444 &src, quint8 b)
+{
+ const quint16 x = dest.rawValue();
+ const quint16 y = src.rawValue();
+ quint16 t = ((x & 0x00f0) * a + (y & 0x00f0) * b) & 0x0f00;
+ t |= ((x & 0x0f0f) * a + (y & 0x0f0f) * b) & 0xf0f0;
+ quint16 *d = reinterpret_cast<quint16*>(&dest);
+ *d = (t >> 4);
+}
+
+template <class DST, class SRC>
+inline void interpolate_pixel_2(DST *dest, quint8 a,
+ const SRC *src, quint8 b)
+{
+ Q_ASSERT((quintptr(dest) & 0x3) == 0);
+ Q_ASSERT((quintptr(src) & 0x3) == 0);
+
+ Q_ASSERT(!SRC::hasAlpha());
+
+ dest[0] = dest[0].byte_mul(a) + DST(src[0]).byte_mul(b);
+ dest[1] = dest[1].byte_mul(a) + DST(src[1]).byte_mul(b);
+}
+
+template <>
+inline void interpolate_pixel_2(qrgb565 *dest, quint8 a,
+ const qrgb565 *src, quint8 b)
+{
+ quint32 *x = reinterpret_cast<quint32*>(dest);
+ const quint32 *y = reinterpret_cast<const quint32*>(src);
+ quint32 t = (((*x & 0xf81f07e0) >> 5) * a +
+ ((*y & 0xf81f07e0) >> 5) * b) & 0xf81f07e0;
+ t |= (((*x & 0x07e0f81f) * a
+ + (*y & 0x07e0f81f) * b) >> 5) & 0x07e0f81f;
+ *x = t;
+}
+
+template <>
+inline void interpolate_pixel_2(qrgb555 *dest, quint8 a,
+ const qrgb555 *src, quint8 b)
+{
+ quint32 *x = reinterpret_cast<quint32*>(dest);
+ const quint32 *y = reinterpret_cast<const quint32*>(src);
+ quint32 t = (((*x & 0x7c1f03e0) >> 5) * a +
+ ((*y & 0x7c1f03e0) >> 5) * b) & 0x7c1f03e0;
+ t |= (((*x & 0x03e07c1f) * a
+ + (*y & 0x03e07c1f) * b) >> 5) & 0x03e07c1f;
+ *x = t;
+}
+
+template <>
+inline void interpolate_pixel_2(qrgb444 *dest, quint8 a,
+ const qrgb444 *src, quint8 b)
+{
+ quint32 *x = reinterpret_cast<quint32*>(dest);
+ const quint32 *y = reinterpret_cast<const quint32*>(src);
+ quint32 t = ((*x & 0x0f0f0f0f) * a + (*y & 0x0f0f0f0f) * b) & 0xf0f0f0f0;
+ t |= ((*x & 0x00f000f0) * a + (*y & 0x00f000f0) * b) & 0x0f000f00;
+ *x = t >> 4;
+}
+
+template <class DST, class SRC>
+inline void interpolate_pixel_4(DST *dest, const SRC *src, quint32 alpha)
+{
+ Q_ASSERT((quintptr(dest) & 0x3) == 0);
+ Q_ASSERT((quintptr(src) & 0x3) == 0);
+
+ const quint32 a = eff_alpha_4(alpha, dest);
+ const quint32 ia = eff_ialpha_4(alpha, dest);
+ dest[0] = DST(src[0]).byte_mul(a >> 24)
+ + dest[0].byte_mul(ia >> 24);
+ dest[1] = DST(src[1]).byte_mul((a >> 16) & 0xff)
+ + dest[1].byte_mul((ia >> 16) & 0xff);
+ dest[2] = DST(src[2]).byte_mul((a >> 8) & 0xff)
+ + dest[2].byte_mul((ia >> 8) & 0xff);
+ dest[3] = DST(src[3]).byte_mul(a & 0xff)
+ + dest[3].byte_mul(ia & 0xff);
+}
+
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+template <>
+inline void interpolate_pixel_4(qargb8565 *dest, const qargb8565 *src,
+ quint32 alpha)
+{
+ Q_ASSERT((quintptr(dest) & 0x3) == 0);
+ Q_ASSERT((quintptr(src) & 0x3) == 0);
+
+ const quint32 a = eff_alpha_4(alpha, dest);
+ const quint32 ia = eff_ialpha_4(alpha, dest);
+ const quint32 *src32 = reinterpret_cast<const quint32*>(src);
+ quint32 *dest32 = reinterpret_cast<quint32*>(dest);
+
+ quint32 x, y, t;
+ quint8 a8, ia8;
+ {
+ x = src32[0];
+ y = dest32[0];
+
+ a8 = a >> 24;
+ ia8 = ia >> 24;
+
+ // a0,g0
+ t = (((x & 0x0007e0ff) * a8 + (y & 0x0007e0ff) * ia8) >> 5)
+ & 0x0007e0ff;
+
+ // r0,b0
+ t |= (((x & 0x00f81f00) * a8 + (y & 0x00f81f00) * ia8) >> 5)
+ & 0x00f81f00;
+
+ a8 = (a >> 16) & 0xff;
+ ia8 = (ia >> 16) & 0xff;
+
+ // a1
+ t |= (((x & 0xff000000) >> 5) * a8 + ((y & 0xff000000) >> 5) * ia8)
+ & 0xff000000;
+
+ dest32[0] = t;
+ }
+ {
+ x = src32[1];
+ y = dest32[1];
+
+ // r1,b1
+ t = (((x & 0x0000f81f) * a8 + (y & 0x0000f81f) * ia8) >> 5)
+ & 0x0000f81f;
+
+ // g1
+ t |= (((x & 0x000007e0) * a8 + (y & 0x000007e0) * ia8) >> 5)
+ & 0x000007e0;
+
+ a8 = (a >> 8) & 0xff;
+ ia8 = (ia >> 8) & 0xff;
+
+ // a2
+ t |= (((x & 0x00ff0000) * a8 + (y & 0x00ff0000) * ia8) >> 5)
+ & 0x00ff0000;
+
+ {
+ // rgb2
+ quint16 x16 = (x >> 24) | ((src32[2] & 0x000000ff) << 8);
+ quint16 y16 = (y >> 24) | ((dest32[2] & 0x000000ff) << 8);
+ quint16 t16;
+
+ t16 = (((x16 & 0xf81f) * a8 + (y16 & 0xf81f) * ia8) >> 5) & 0xf81f;
+ t16 |= (((x16 & 0x07e0) * a8 + (y16 & 0x07e0) * ia8) >> 5) & 0x07e0;
+
+ // rg2
+ t |= ((t16 & 0x00ff) << 24);
+
+ dest32[1] = t;
+
+ x = src32[2];
+ y = dest32[2];
+
+ // gb2
+ t = (t16 >> 8);
+ }
+ }
+ {
+ a8 = a & 0xff;
+ ia8 = ia & 0xff;
+
+ // g3,a3
+ t |= (((x & 0x07e0ff00) * a8 + (y & 0x07e0ff00) * ia8) >> 5)
+ & 0x07e0ff00;
+
+ // r3,b3
+ t |= (((x & 0xf81f0000) >> 5) * a8 + ((y & 0xf81f0000) >> 5) * ia8)
+ & 0xf81f0000;
+
+ dest32[2] = t;
+ }
+}
+#endif
+
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+template <>
+inline void interpolate_pixel_4(qargb8555 *dest, const qargb8555 *src,
+ quint32 alpha)
+{
+ Q_ASSERT((quintptr(dest) & 0x3) == 0);
+ Q_ASSERT((quintptr(src) & 0x3) == 0);
+
+
+ const quint32 a = eff_alpha_4(alpha, dest);
+ const quint32 ia = eff_ialpha_4(alpha, dest);
+ const quint32 *src32 = reinterpret_cast<const quint32*>(src);
+ quint32 *dest32 = reinterpret_cast<quint32*>(dest);
+
+ quint32 x, y, t;
+ quint8 a8, ia8;
+ {
+ x = src32[0];
+ y = dest32[0];
+
+ a8 = a >> 24;
+ ia8 = ia >> 24;
+
+ // a0,g0
+ t = (((x & 0x0003e0ff) * a8 + (y & 0x0003e0ff) * ia8) >> 5)
+ & 0x0003e0ff;
+
+ // r0,b0
+ t |= (((x & 0x007c1f00) * a8 + (y & 0x007c1f00) * ia8) >> 5)
+ & 0x007c1f00;
+
+ a8 = (a >> 16) & 0xff;
+ ia8 = (ia >> 16) & 0xff;
+
+ // a1
+ t |= (((x & 0xff000000) >> 5) * a8 + ((y & 0xff000000) >> 5) * ia8)
+ & 0xff000000;
+
+ dest32[0] = t;
+ }
+ {
+ x = src32[1];
+ y = dest32[1];
+
+ // r1,b1
+ t = (((x & 0x00007c1f) * a8 + (y & 0x00007c1f) * ia8) >> 5)
+ & 0x00007c1f;
+
+ // g1
+ t |= (((x & 0x000003e0) * a8 + (y & 0x000003e0) * ia8) >> 5)
+ & 0x000003e0;
+
+ a8 = (a >> 8) & 0xff;
+ ia8 = (ia >> 8) & 0xff;
+
+ // a2
+ t |= (((x & 0x00ff0000) * a8 + (y & 0x00ff0000) * ia8) >> 5)
+ & 0x00ff0000;
+
+ {
+ // rgb2
+ quint16 x16 = (x >> 24) | ((src32[2] & 0x000000ff) << 8);
+ quint16 y16 = (y >> 24) | ((dest32[2] & 0x000000ff) << 8);
+ quint16 t16;
+
+ t16 = (((x16 & 0x7c1f) * a8 + (y16 & 0x7c1f) * ia8) >> 5) & 0x7c1f;
+ t16 |= (((x16 & 0x03e0) * a8 + (y16 & 0x03e0) * ia8) >> 5) & 0x03e0;
+
+ // rg2
+ t |= ((t16 & 0x00ff) << 24);
+
+ dest32[1] = t;
+
+ x = src32[2];
+ y = dest32[2];
+
+ // gb2
+ t = (t16 >> 8);
+ }
+ }
+ {
+ a8 = a & 0xff;
+ ia8 = ia & 0xff;
+
+ // g3,a3
+ t |= (((x & 0x03e0ff00) * a8 + (y & 0x03e0ff00) * ia8) >> 5)
+ & 0x03e0ff00;
+
+ // r3,b3
+ t |= (((x & 0x7c1f0000) >> 5) * a8 + ((y & 0x7c1f0000) >> 5) * ia8)
+ & 0x7c1f0000;
+
+ dest32[2] = t;
+ }
+}
+#endif
+
+template <>
+inline void interpolate_pixel_4(qrgb888 *dest, const qrgb888 *src,
+ quint32 alpha)
+{
+ Q_ASSERT((quintptr(dest) & 0x3) == 0);
+ Q_ASSERT((quintptr(src) & 0x3) == 0);
+
+ const quint32 a = eff_alpha_4(alpha, dest);
+ const quint32 ia = eff_ialpha_4(alpha, dest);
+ const quint32 *src32 = reinterpret_cast<const quint32*>(src);
+ quint32 *dest32 = reinterpret_cast<quint32*>(dest);
+
+ {
+ quint32 x = src32[0];
+ quint32 y = dest32[0];
+
+ quint32 t;
+ t = ((x >> 8) & 0xff00ff) * (a >> 24)
+ + ((y >> 8) & 0xff00ff) * (ia >> 24);
+ t = (t + ((t >> 8) & 0xff00ff) + 0x800080);
+ t &= 0xff00ff00;
+
+ x = (x & 0xff0000) * (a >> 24)
+ + (x & 0x0000ff) * ((a >> 16) & 0xff)
+ + (y & 0xff0000) * (ia >> 24)
+ + (y & 0x0000ff) * ((ia >> 16) & 0xff);
+ x = (x + ((x >> 8) & 0xff00ff) + 0x800080) >> 8;
+ x &= 0x00ff00ff;
+
+ dest32[0] = x | t;
+ }
+ {
+ quint32 x = src32[1];
+ quint32 y = dest32[1];
+
+ quint32 t;
+ t = ((x >> 8) & 0xff0000) * ((a >> 16) & 0xff)
+ + ((x >> 8) & 0x0000ff) * ((a >> 8) & 0xff)
+ + ((y >> 8) & 0xff0000) * ((ia >> 16) & 0xff)
+ + ((y >> 8) & 0x0000ff) * ((ia >> 8) & 0xff);
+ t = (t + ((t >> 8) & 0xff00ff) + 0x800080);
+ t &= 0xff00ff00;
+
+ x = (x & 0xff0000) * ((a >> 16) & 0xff)
+ + (x & 0x0000ff) * ((a >> 8) & 0xff)
+ + (y & 0xff0000) * ((ia >> 16) & 0xff)
+ + (y & 0x0000ff) * ((ia >> 8) & 0xff);
+ x = (x + ((x >> 8) & 0xff00ff) + 0x800080) >> 8;
+ x &= 0x00ff00ff;
+
+ dest32[1] = x | t;
+ }
+ {
+ quint32 x = src32[2];
+ quint32 y = dest32[2];
+
+ quint32 t;
+ t = ((x >> 8) & 0xff0000) * ((a >> 8) & 0xff)
+ + ((x >> 8) & 0x0000ff) * (a & 0xff)
+ + ((y >> 8) & 0xff0000) * ((ia >> 8) & 0xff)
+ + ((y >> 8) & 0x0000ff) * (ia & 0xff);
+ t = (t + ((t >> 8) & 0xff00ff) + 0x800080);
+ t &= 0xff00ff00;
+
+ x = (x & 0xff00ff) * (a & 0xff)
+ + (y & 0xff00ff) * (ia & 0xff);
+ x = (x + ((x >> 8) & 0xff00ff) + 0x800080) >> 8;
+ x &= 0x00ff00ff;
+
+ dest32[2] = x | t;
+ }
+}
+
+template <class DST, class SRC>
+inline void interpolate_pixel_4(DST *dest, quint8 a,
+ const SRC *src, quint8 b)
+{
+ Q_ASSERT((quintptr(dest) & 0x3) == 0);
+ Q_ASSERT((quintptr(src) & 0x3) == 0);
+
+ dest[0] = dest[0].byte_mul(a) + DST(src[0]).byte_mul(b);
+ dest[1] = dest[1].byte_mul(a) + DST(src[1]).byte_mul(b);
+ dest[2] = dest[2].byte_mul(a) + DST(src[2]).byte_mul(b);
+ dest[3] = dest[3].byte_mul(a) + DST(src[3]).byte_mul(b);
+}
+
+template <class DST, class SRC>
+inline void blend_sourceOver_4(DST *dest, const SRC *src)
+{
+ Q_ASSERT((quintptr(dest) & 0x3) == 0);
+ Q_ASSERT((quintptr(src) & 0x3) == 0);
+
+ const quint32 a = alpha_4(src);
+ if (a == 0xffffffff) {
+ qt_memconvert(dest, src, 4);
+ } else if (a > 0) {
+ quint32 buf[3]; // array of quint32 to get correct alignment
+ qt_memconvert((DST*)(void*)buf, src, 4);
+ madd_4(dest, eff_ialpha_4(a, dest), (DST*)(void*)buf);
+ }
+}
+
+template <>
+inline void blend_sourceOver_4(qargb8565 *dest, const qargb8565 *src)
+{
+ Q_ASSERT((quintptr(dest) & 0x3) == 0);
+ Q_ASSERT((quintptr(src) & 0x3) == 0);
+
+ const quint32 a = alpha_4(src);
+ if (a == 0xffffffff) {
+ qt_memconvert(dest, src, 4);
+ } else if (a > 0) {
+ madd_4(dest, eff_ialpha_4(a, dest), src);
+ }
+}
+
+template <>
+inline void blend_sourceOver_4(qargb8555 *dest, const qargb8555 *src)
+{
+ Q_ASSERT((quintptr(dest) & 0x3) == 0);
+ Q_ASSERT((quintptr(src) & 0x3) == 0);
+
+ const quint32 a = alpha_4(src);
+ if (a == 0xffffffff) {
+ qt_memconvert(dest, src, 4);
+ } else if (a > 0) {
+ madd_4(dest, eff_ialpha_4(a, dest), src);
+ }
+}
+
+template <>
+inline void blend_sourceOver_4(qargb6666 *dest, const qargb6666 *src)
+{
+ Q_ASSERT((quintptr(dest) & 0x3) == 0);
+ Q_ASSERT((quintptr(src) & 0x3) == 0);
+
+ const quint32 a = alpha_4(src);
+ if (a == 0xffffffff) {
+ qt_memconvert(dest, src, 4);
+ } else if (a > 0) {
+ madd_4(dest, eff_ialpha_4(a, dest), src);
+ }
+}
+
+template <class DST, class SRC>
+void QT_FASTCALL blendUntransformed_unaligned(DST *dest, const SRC *src,
+ quint8 coverage, int length)
+{
+ Q_ASSERT(coverage > 0);
+
+ if (coverage < 255) {
+ if (SRC::hasAlpha()) {
+ for (int i = 0; i < length; ++i) {
+ if (src[i].alpha()) {
+ const quint8 alpha = qt_div_255(int(src[i].alpha()) * int(coverage));
+ interpolate_pixel(dest[i], DST::ialpha(alpha),
+ src[i], DST::alpha(alpha));
+ }
+ }
+ } else {
+ const quint8 alpha = DST::alpha(coverage);
+ const quint8 ialpha = DST::ialpha(coverage);
+ if (alpha) {
+ for (int i = 0; i < length; ++i)
+ interpolate_pixel(dest[i], ialpha, src[i], alpha);
+ }
+ }
+ return;
+ }
+
+ Q_ASSERT(coverage == 0xff);
+ Q_ASSERT(SRC::hasAlpha());
+
+ if (SRC::hasAlpha()) {
+ for (int i = 0; i < length; ++i) {
+ const quint8 a = src->alpha();
+ if (a == 0xff)
+ *dest = DST(*src);
+ else if (a > 0) {
+ if (DST::hasAlpha())
+ *dest = DST(*src).truncedAlpha() + dest->byte_mul(DST::ialpha(a));
+ else
+ *dest = DST(SRC(*src).truncedAlpha()) + dest->byte_mul(DST::ialpha(a));
+ }
+ ++src;
+ ++dest;
+ }
+ }
+}
+
+template <class DST, class SRC>
+void QT_FASTCALL blendUntransformed_dest16(DST *dest, const SRC *src,
+ quint8 coverage, int length)
+{
+ Q_ASSERT(sizeof(DST) == 2);
+ Q_ASSERT(sizeof(SRC) == 2);
+ Q_ASSERT((quintptr(dest) & 0x3) == (quintptr(src) & 0x3));
+ Q_ASSERT(coverage > 0);
+
+ const int align = quintptr(dest) & 0x3;
+
+ if (coverage < 255) {
+ // align
+ if (align) {
+ const quint8 alpha = SRC::hasAlpha()
+ ? qt_div_255(int(src->alpha()) * int(coverage))
+ : coverage;
+ if (alpha) {
+ interpolate_pixel(*dest, DST::ialpha(alpha),
+ *src, DST::alpha(alpha));
+ }
+ ++dest;
+ ++src;
+ --length;
+ }
+
+ if (SRC::hasAlpha()) {
+ while (length >= 2) {
+ const quint16 alpha16 = BYTE_MUL(uint(alpha_2(src)), uint(coverage));
+ interpolate_pixel_2(dest, src, alpha16);
+ length -= 2;
+ src += 2;
+ dest += 2;
+ }
+ } else {
+ const quint8 alpha = DST::alpha(coverage);
+ const quint8 ialpha = DST::ialpha(coverage);
+
+ while (length >= 2) {
+ interpolate_pixel_2(dest, ialpha, src, alpha);
+ length -= 2;
+ src += 2;
+ dest += 2;
+ }
+ }
+
+ // tail
+ if (length) {
+ const quint8 alpha = SRC::hasAlpha()
+ ? qt_div_255(int(src->alpha()) * int(coverage))
+ : coverage;
+ if (alpha) {
+ interpolate_pixel(*dest, DST::ialpha(alpha),
+ *src, DST::alpha(alpha));
+ }
+ }
+
+ return;
+ }
+
+ Q_ASSERT(SRC::hasAlpha());
+ if (SRC::hasAlpha()) {
+ if (align) {
+ const quint8 alpha = src->alpha();
+ if (alpha == 0xff)
+ *dest = DST(*src);
+ else if (alpha > 0)
+ *dest = DST(*src).truncedAlpha() + dest->byte_mul(DST::ialpha(alpha));
+ ++dest;
+ ++src;
+ --length;
+ }
+
+ while (length >= 2) {
+ Q_ASSERT((quintptr(dest) & 3) == 0);
+ Q_ASSERT((quintptr(src) & 3) == 0);
+
+ const quint16 a = alpha_2(src);
+ if (a == 0xffff) {
+ qt_memconvert(dest, src, 2);
+ } else if (a > 0) {
+ quint32 buf;
+ if (sizeof(DST) == 2)
+ qt_memconvert((DST*)(void*)&buf, src, 2);
+ madd_2(dest, eff_ialpha_2(a, dest), (DST*)(void*)&buf);
+ }
+
+ length -= 2;
+ src += 2;
+ dest += 2;
+ }
+
+ if (length) {
+ const quint8 alpha = src->alpha();
+ if (alpha == 0xff)
+ *dest = DST(*src);
+ else if (alpha > 0)
+ *dest = DST(*src).truncedAlpha() + dest->byte_mul(DST::ialpha(alpha));
+ }
+ }
+}
+
+template <class DST, class SRC>
+void QT_FASTCALL blendUntransformed_dest24(DST *dest, const SRC *src,
+ quint8 coverage, int length)
+{
+ Q_ASSERT((quintptr(dest) & 0x3) == (quintptr(src) & 0x3));
+ Q_ASSERT(sizeof(DST) == 3);
+ Q_ASSERT(coverage > 0);
+
+ const int align = quintptr(dest) & 0x3;
+
+ if (coverage < 255) {
+ // align
+ for (int i = 0; i < align; ++i) {
+ if (SRC::hasAlpha()) {
+ const quint8 alpha = qt_div_255(int(src->alpha()) * int(coverage));
+ if (alpha)
+ interpolate_pixel(*dest, DST::ialpha(alpha),
+ *src, DST::alpha(alpha));
+ } else {
+ interpolate_pixel(*dest, DST::ialpha(coverage),
+ *src, DST::alpha(coverage));
+ }
+ ++dest;
+ ++src;
+ --length;
+ }
+
+ if (SRC::hasAlpha()) {
+ while (length >= 4) {
+ const quint32 alpha = QT_PREPEND_NAMESPACE(BYTE_MUL)(uint(alpha_4(src)), uint(coverage));
+ if (alpha)
+ interpolate_pixel_4(dest, src, alpha);
+ length -= 4;
+ src += 4;
+ dest += 4;
+ }
+ } else {
+ const quint8 alpha = DST::alpha(coverage);
+ const quint8 ialpha = DST::ialpha(coverage);
+ while (length >= 4) {
+ interpolate_pixel_4(dest, ialpha, src, alpha);
+ length -= 4;
+ src += 4;
+ dest += 4;
+ }
+ }
+
+ // tail
+ while (length--) {
+ if (SRC::hasAlpha()) {
+ const quint8 alpha = qt_div_255(int(src->alpha()) * int(coverage));
+ if (alpha)
+ interpolate_pixel(*dest, DST::ialpha(alpha),
+ *src, DST::alpha(alpha));
+ } else {
+ interpolate_pixel(*dest, DST::ialpha(coverage),
+ *src, DST::alpha(coverage));
+ }
+ ++dest;
+ ++src;
+ }
+
+ return;
+ }
+
+
+ Q_ASSERT(coverage == 255);
+ Q_ASSERT(SRC::hasAlpha());
+
+ if (SRC::hasAlpha()) {
+ // align
+ for (int i = 0; i < align; ++i) {
+ const quint8 a = src->alpha();
+ if (a == 0xff) {
+ *dest = DST(*src);
+ } else if (a > 0) {
+ *dest = DST(*src).truncedAlpha() + dest->byte_mul(DST::ialpha(a));
+ }
+ ++dest;
+ ++src;
+ --length;
+ }
+
+ while (length >= 4) {
+ blend_sourceOver_4(dest, src);
+ length -= 4;
+ src += 4;
+ dest += 4;
+ }
+
+ // tail
+ while (length--) {
+ const quint8 a = src->alpha();
+ if (a == 0xff) {
+ *dest = DST(*src);
+ } else if (a > 0) {
+ *dest = DST(*src).truncedAlpha() + dest->byte_mul(DST::ialpha(a));
+ }
+ ++dest;
+ ++src;
+ }
+ }
+}
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+void QT_FASTCALL blendUntransformed(int count, const QSpan *spans, void *userData)
+{
+ QSpanData *data = reinterpret_cast<QSpanData*>(userData);
+ QPainter::CompositionMode mode = data->rasterBuffer->compositionMode;
+
+ if (mode != QPainter::CompositionMode_SourceOver &&
+ mode != QPainter::CompositionMode_Source)
+ {
+ blend_src_generic<RegularSpans>(count, spans, userData);
+ return;
+ }
+
+ const bool modeSource = !SRC::hasAlpha() ||
+ mode == QPainter::CompositionMode_Source;
+ const int image_width = data->texture.width;
+ const int image_height = data->texture.height;
+ int xoff = -qRound(-data->dx);
+ int yoff = -qRound(-data->dy);
+
+ while (count--) {
+ const quint8 coverage = (data->texture.const_alpha * spans->coverage) >> 8;
+ if (coverage == 0) {
+ ++spans;
+ continue;
+ }
+
+ int x = spans->x;
+ int length = spans->len;
+ int sx = xoff + x;
+ int sy = yoff + spans->y;
+ if (sy >= 0 && sy < image_height && sx < image_width) {
+ if (sx < 0) {
+ x -= sx;
+ length += sx;
+ sx = 0;
+ }
+ if (sx + length > image_width)
+ length = image_width - sx;
+ if (length > 0) {
+ DST *dest = ((DST*)data->rasterBuffer->scanLine(spans->y)) + x;
+ const SRC *src = (SRC*)data->texture.scanLine(sy) + sx;
+ if (modeSource && coverage == 255) {
+ qt_memconvert<DST, SRC>(dest, src, length);
+ } else if (sizeof(DST) == 3 && sizeof(SRC) == 3 && length >= 3 &&
+ (quintptr(dest) & 3) == (quintptr(src) & 3))
+ {
+ blendUntransformed_dest24(dest, src, coverage, length);
+ } else if (sizeof(DST) == 2 && sizeof(SRC) == 2 && length >= 3 &&
+ (quintptr(dest) & 3) == (quintptr(src) & 3))
+ {
+ blendUntransformed_dest16(dest, src, coverage, length);
+ } else {
+ blendUntransformed_unaligned(dest, src, coverage, length);
+ }
+ }
+ }
+ ++spans;
+ }
+}
+
+static void blend_untransformed_rgb888(int count, const QSpan *spans,
+ void *userData)
+{
+#if defined(QT_QWS_DEPTH_24)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_RGB888)
+ blendUntransformed<qrgb888, qrgb888>(count, spans, userData);
+ else
+#endif
+ blend_untransformed_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_untransformed_argb6666(int count, const QSpan *spans,
+ void *userData)
+{
+#if defined(QT_QWS_DEPTH_18)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB6666_Premultiplied)
+ blendUntransformed<qargb6666, qargb6666>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB666)
+ blendUntransformed<qargb6666, qrgb666>(count, spans, userData);
+ else
+#endif
+ blend_untransformed_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_untransformed_rgb666(int count, const QSpan *spans,
+ void *userData)
+{
+#if defined(QT_QWS_DEPTH_18)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB6666_Premultiplied)
+ blendUntransformed<qrgb666, qargb6666>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB666)
+ blendUntransformed<qrgb666, qrgb666>(count, spans, userData);
+ else
+#endif
+ blend_untransformed_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_untransformed_argb8565(int count, const QSpan *spans,
+ void *userData)
+{
+#if defined(QT_QWS_DEPTH_16)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8565_Premultiplied)
+ blendUntransformed<qargb8565, qargb8565>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB16)
+ blendUntransformed<qargb8565, qrgb565>(count, spans, userData);
+ else
+#endif
+ blend_untransformed_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_untransformed_rgb565(int count, const QSpan *spans,
+ void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_16)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8565_Premultiplied)
+ blendUntransformed<qrgb565, qargb8565>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB16)
+ blendUntransformed<qrgb565, qrgb565>(count, spans, userData);
+ else
+#endif
+ blend_untransformed_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_untransformed_argb8555(int count, const QSpan *spans,
+ void *userData)
+{
+#if defined(QT_QWS_DEPTH_15)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8555_Premultiplied)
+ blendUntransformed<qargb8555, qargb8555>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB555)
+ blendUntransformed<qargb8555, qrgb555>(count, spans, userData);
+ else
+#endif
+ blend_untransformed_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_untransformed_rgb555(int count, const QSpan *spans,
+ void *userData)
+{
+#if defined(QT_QWS_DEPTH_15)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8555_Premultiplied)
+ blendUntransformed<qrgb555, qargb8555>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB555)
+ blendUntransformed<qrgb555, qrgb555>(count, spans, userData);
+ else
+#endif
+ blend_untransformed_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_untransformed_argb4444(int count, const QSpan *spans,
+ void *userData)
+{
+#if defined(QT_QWS_DEPTH_12)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB4444_Premultiplied)
+ blendUntransformed<qargb4444, qargb4444>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB444)
+ blendUntransformed<qargb4444, qrgb444>(count, spans, userData);
+ else
+#endif
+ blend_untransformed_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_untransformed_rgb444(int count, const QSpan *spans,
+ void *userData)
+{
+#if defined(QT_QWS_DEPTH_12)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB4444_Premultiplied)
+ blendUntransformed<qrgb444, qargb4444>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB444)
+ blendUntransformed<qrgb444, qrgb444>(count, spans, userData);
+ else
+#endif
+ blend_untransformed_generic<RegularSpans>(count, spans, userData);
+}
+
+template <SpanMethod spanMethod>
+Q_STATIC_TEMPLATE_FUNCTION void blend_tiled_generic(int count, const QSpan *spans, void *userData)
+{
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ uint buffer[buffer_size];
+ uint src_buffer[buffer_size];
+ Operator op = getOperator(data, spans, count);
+
+ const int image_width = data->texture.width;
+ const int image_height = data->texture.height;
+ int xoff = -qRound(-data->dx) % image_width;
+ int yoff = -qRound(-data->dy) % image_height;
+
+ if (xoff < 0)
+ xoff += image_width;
+ if (yoff < 0)
+ yoff += image_height;
+
+ while (count--) {
+ int x = spans->x;
+ int length = spans->len;
+ int sx = (xoff + spans->x) % image_width;
+ int sy = (spans->y + yoff) % image_height;
+ if (sx < 0)
+ sx += image_width;
+ if (sy < 0)
+ sy += image_height;
+
+ const int coverage = (spans->coverage * data->texture.const_alpha) >> 8;
+ while (length) {
+ int l = qMin(image_width - sx, length);
+ if (buffer_size < l)
+ l = buffer_size;
+ const uint *src = op.src_fetch(src_buffer, &op, data, sy, sx, l);
+ if (spanMethod == RegularSpans) {
+ uint *dest = op.dest_fetch ? op.dest_fetch(buffer, data->rasterBuffer, x, spans->y, l) : buffer;
+ op.func(dest, src, l, coverage);
+ if (op.dest_store)
+ op.dest_store(data->rasterBuffer, x, spans->y, dest, l);
+ } else {
+ drawBufferSpan(data, src, l, x, spans->y, l,
+ coverage);
+ }
+ x += l;
+ sx += l;
+ length -= l;
+ if (sx >= image_width)
+ sx = 0;
+ }
+ ++spans;
+ }
+}
+
+template <SpanMethod spanMethod>
+Q_STATIC_TEMPLATE_FUNCTION void blend_tiled_argb(int count, const QSpan *spans, void *userData)
+{
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+ if (data->texture.format != QImage::Format_ARGB32_Premultiplied
+ && data->texture.format != QImage::Format_RGB32) {
+ blend_tiled_generic<spanMethod>(count, spans, userData);
+ return;
+ }
+
+ Operator op = getOperator(data, spans, count);
+
+ int image_width = data->texture.width;
+ int image_height = data->texture.height;
+ int xoff = -qRound(-data->dx) % image_width;
+ int yoff = -qRound(-data->dy) % image_height;
+
+ if (xoff < 0)
+ xoff += image_width;
+ if (yoff < 0)
+ yoff += image_height;
+
+ while (count--) {
+ int x = spans->x;
+ int length = spans->len;
+ int sx = (xoff + spans->x) % image_width;
+ int sy = (spans->y + yoff) % image_height;
+ if (sx < 0)
+ sx += image_width;
+ if (sy < 0)
+ sy += image_height;
+
+ const int coverage = (spans->coverage * data->texture.const_alpha) >> 8;
+ while (length) {
+ int l = qMin(image_width - sx, length);
+ if (buffer_size < l)
+ l = buffer_size;
+ const uint *src = (uint *)data->texture.scanLine(sy) + sx;
+ if (spanMethod == RegularSpans) {
+ uint *dest = ((uint *)data->rasterBuffer->scanLine(spans->y)) + x;
+ op.func(dest, src, l, coverage);
+ } else {
+ drawBufferSpan(data, src, buffer_size,
+ x, spans->y, l, coverage);
+ }
+ x += l;
+ length -= l;
+ sx = 0;
+ }
+ ++spans;
+ }
+}
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_FUNCTION void blendTiled(int count, const QSpan *spans, void *userData)
+{
+ QSpanData *data = reinterpret_cast<QSpanData*>(userData);
+ QPainter::CompositionMode mode = data->rasterBuffer->compositionMode;
+
+ if (mode != QPainter::CompositionMode_SourceOver &&
+ mode != QPainter::CompositionMode_Source)
+ {
+ blend_src_generic<RegularSpans>(count, spans, userData);
+ return;
+ }
+
+ const bool modeSource = !SRC::hasAlpha() ||
+ mode == QPainter::CompositionMode_Source;
+ const int image_width = data->texture.width;
+ const int image_height = data->texture.height;
+ int xoff = -qRound(-data->dx) % image_width;
+ int yoff = -qRound(-data->dy) % image_height;
+
+ if (xoff < 0)
+ xoff += image_width;
+ if (yoff < 0)
+ yoff += image_height;
+
+ while (count--) {
+ const quint8 coverage = (data->texture.const_alpha * spans->coverage) >> 8;
+ if (coverage == 0) {
+ ++spans;
+ continue;
+ }
+
+ int x = spans->x;
+ int length = spans->len;
+ int sx = (xoff + spans->x) % image_width;
+ int sy = (spans->y + yoff) % image_height;
+ if (sx < 0)
+ sx += image_width;
+ if (sy < 0)
+ sy += image_height;
+
+ if (modeSource && coverage == 255) {
+ // Copy the first texture block
+ length = qMin(image_width,length);
+ int tx = x;
+ while (length) {
+ int l = qMin(image_width - sx, length);
+ if (buffer_size < l)
+ l = buffer_size;
+ DST *dest = ((DST*)data->rasterBuffer->scanLine(spans->y)) + tx;
+ const SRC *src = (SRC*)data->texture.scanLine(sy) + sx;
+
+ qt_memconvert<DST, SRC>(dest, src, l);
+ length -= l;
+ tx += l;
+ sx = 0;
+ }
+
+ // Now use the rasterBuffer as the source of the texture,
+ // We can now progressively copy larger blocks
+ // - Less cpu time in code figuring out what to copy
+ // We are dealing with one block of data
+ // - More likely to fit in the cache
+ // - can use memcpy
+ int copy_image_width = qMin(image_width, int(spans->len));
+ length = spans->len - copy_image_width;
+ DST *src = ((DST*)data->rasterBuffer->scanLine(spans->y)) + x;
+ DST *dest = src + copy_image_width;
+ while (copy_image_width < length) {
+ qt_memconvert(dest, src, copy_image_width);
+ dest += copy_image_width;
+ length -= copy_image_width;
+ copy_image_width *= 2;
+ }
+ if (length > 0)
+ qt_memconvert(dest, src, length);
+ } else {
+ while (length) {
+ int l = qMin(image_width - sx, length);
+ if (buffer_size < l)
+ l = buffer_size;
+ DST *dest = ((DST*)data->rasterBuffer->scanLine(spans->y)) + x;
+ const SRC *src = (SRC*)data->texture.scanLine(sy) + sx;
+ if (sizeof(DST) == 3 && sizeof(SRC) == 3 && l >= 4 &&
+ (quintptr(dest) & 3) == (quintptr(src) & 3))
+ {
+ blendUntransformed_dest24(dest, src, coverage, l);
+ } else if (sizeof(DST) == 2 && sizeof(SRC) == 2 && l >= 2 &&
+ (quintptr(dest) & 3) == (quintptr(src) & 3))
+ {
+ blendUntransformed_dest16(dest, src, coverage, l);
+ } else {
+ blendUntransformed_unaligned(dest, src, coverage, l);
+ }
+
+ x += l;
+ length -= l;
+ sx = 0;
+ }
+ }
+ ++spans;
+ }
+}
+
+static void blend_tiled_rgb888(int count, const QSpan *spans, void *userData)
+{
+#if defined(QT_QWS_DEPTH_24)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_RGB888)
+ blendTiled<qrgb888, qrgb888>(count, spans, userData);
+ else
+#endif
+ blend_tiled_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_tiled_argb6666(int count, const QSpan *spans, void *userData)
+{
+#if defined(QT_QWS_DEPTH_18)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB6666_Premultiplied)
+ blendTiled<qargb6666, qargb6666>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB666)
+ blendTiled<qargb6666, qrgb666>(count, spans, userData);
+ else
+#endif
+ blend_tiled_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_tiled_rgb666(int count, const QSpan *spans, void *userData)
+{
+#if defined(QT_QWS_DEPTH_18)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB6666_Premultiplied)
+ blendTiled<qrgb666, qargb6666>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB666)
+ blendTiled<qrgb666, qrgb666>(count, spans, userData);
+ else
+#endif
+ blend_tiled_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_tiled_argb8565(int count, const QSpan *spans, void *userData)
+{
+#if defined(QT_QWS_DEPTH_16)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8565_Premultiplied)
+ blendTiled<qargb8565, qargb8565>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB16)
+ blendTiled<qargb8565, qrgb565>(count, spans, userData);
+ else
+#endif
+ blend_tiled_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_tiled_rgb565(int count, const QSpan *spans, void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_16)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8565_Premultiplied)
+ blendTiled<qrgb565, qargb8565>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB16)
+ blendTiled<qrgb565, qrgb565>(count, spans, userData);
+ else
+#endif
+ blend_tiled_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_tiled_argb8555(int count, const QSpan *spans, void *userData)
+{
+#if defined(QT_QWS_DEPTH_15)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8555_Premultiplied)
+ blendTiled<qargb8555, qargb8555>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB555)
+ blendTiled<qargb8555, qrgb555>(count, spans, userData);
+ else
+#endif
+ blend_tiled_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_tiled_rgb555(int count, const QSpan *spans, void *userData)
+{
+#if defined(QT_QWS_DEPTH_15)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8555_Premultiplied)
+ blendTiled<qrgb555, qargb8555>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB555)
+ blendTiled<qrgb555, qrgb555>(count, spans, userData);
+ else
+#endif
+ blend_tiled_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_tiled_argb4444(int count, const QSpan *spans, void *userData)
+{
+#if defined(QT_QWS_DEPTH_12)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB4444_Premultiplied)
+ blendTiled<qargb4444, qargb4444>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB444)
+ blendTiled<qargb4444, qrgb444>(count, spans, userData);
+ else
+#endif
+ blend_tiled_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_tiled_rgb444(int count, const QSpan *spans, void *userData)
+{
+#if defined(QT_QWS_DEPTH_12)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB4444_Premultiplied)
+ blendTiled<qrgb444, qargb4444>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB444)
+ blendTiled<qrgb444, qrgb444>(count, spans, userData);
+ else
+#endif
+ blend_tiled_generic<RegularSpans>(count, spans, userData);
+}
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_FUNCTION void blendTransformedBilinear(int count, const QSpan *spans,
+ void *userData)
+{
+ QSpanData *data = reinterpret_cast<QSpanData*>(userData);
+ QPainter::CompositionMode mode = data->rasterBuffer->compositionMode;
+
+
+ if (mode != QPainter::CompositionMode_SourceOver) {
+ blend_src_generic<RegularSpans>(count, spans, userData);
+ return;
+ }
+
+ SRC buffer[buffer_size];
+
+ const int src_minx = data->texture.x1;
+ const int src_miny = data->texture.y1;
+ const int src_maxx = data->texture.x2 - 1;
+ const int src_maxy = data->texture.y2 - 1;
+
+ if (data->fast_matrix) {
+ // The increment pr x in the scanline
+ const int fdx = (int)(data->m11 * fixed_scale);
+ const int fdy = (int)(data->m12 * fixed_scale);
+
+ while (count--) {
+ const quint8 coverage = (data->texture.const_alpha * spans->coverage) >> 8;
+ if (coverage == 0) {
+ ++spans;
+ continue;
+ }
+
+ DST *dest = (DST*)data->rasterBuffer->scanLine(spans->y)
+ + spans->x;
+ const qreal cx = spans->x + qreal(0.5);
+ const qreal cy = spans->y + qreal(0.5);
+ int x = int((data->m21 * cy
+ + data->m11 * cx + data->dx) * fixed_scale) - half_point;
+ int y = int((data->m22 * cy
+ + data->m12 * cx + data->dy) * fixed_scale) - half_point;
+ int length = spans->len;
+
+ while (length) {
+ const int l = qMin(length, buffer_size);
+
+ const SRC *end = buffer + l;
+ SRC *b = buffer;
+ while (b < end) {
+ int x1 = (x >> 16);
+ int x2;
+ int y1 = (y >> 16);
+ int y2;
+
+ const int distx = (x & 0x0000ffff) >> 8;
+ const int disty = (y & 0x0000ffff) >> 8;
+
+ if (x1 < src_minx) {
+ x2 = x1 = src_minx;
+ } else if (x1 >= src_maxx) {
+ x2 = x1 = src_maxx;
+ } else {
+ x2 = x1 + 1;
+ }
+ if (y1 < src_miny) {
+ y2 = y1 = src_miny;
+ } else if (y1 >= src_maxy) {
+ y2 = y1 = src_maxy;
+ } else {
+ y2 = y1 + 1;
+ }
+#if 0
+ if (x1 == x2) {
+ if (y1 == y2) {
+ *b = ((SRC*)data->texture.scanLine(y1))[x1];
+ } else {
+ *b = ((SRC*)data->texture.scanLine(y1))[x1];
+ const SRC t = data->texture.scanLine(y2)[x1];
+ interpolate_pixel(*b, SRC::ialpha(disty),
+ t, SRC::alpha(disty));
+ }
+ } else if (y1 == y2) {
+ *b = ((SRC*)data->texture.scanLine(y1))[x1];
+ const SRC t = ((SRC*)data->texture.scanLine(y1))[x2];
+ interpolate_pixel(*b, SRC::ialpha(distx),
+ t, SRC::alpha(distx));
+ } else
+#endif
+ {
+ const SRC *src1 = (SRC*)data->texture.scanLine(y1);
+ const SRC *src2 = (SRC*)data->texture.scanLine(y2);
+ SRC tl = src1[x1];
+ const SRC tr = src1[x2];
+ SRC bl = src2[x1];
+ const SRC br = src2[x2];
+ const quint8 ax = SRC::alpha(distx);
+ const quint8 iax = SRC::ialpha(distx);
+
+ interpolate_pixel(tl, iax, tr, ax);
+ interpolate_pixel(bl, iax, br, ax);
+ interpolate_pixel(tl, SRC::ialpha(disty),
+ bl, SRC::alpha(disty));
+ *b = tl;
+ }
+ ++b;
+
+ x += fdx;
+ y += fdy;
+ }
+
+ if (!SRC::hasAlpha() && coverage == 255) {
+ qt_memconvert(dest, buffer, l);
+ } else if (sizeof(DST) == 3 && l >= 4 &&
+ (quintptr(dest) & 3) == (quintptr(buffer) & 3))
+ {
+ blendUntransformed_dest24(dest, buffer, coverage, l);
+ } else if (sizeof(DST) == 2 && sizeof(SRC) == 2 && l >= 2 &&
+ (quintptr(dest) & 3) == (quintptr(buffer) & 3)) {
+ blendUntransformed_dest16(dest, buffer, coverage, l);
+ } else {
+ blendUntransformed_unaligned(dest, buffer, coverage, l);
+ }
+
+ dest += l;
+ length -= l;
+ }
+ ++spans;
+ }
+ } else {
+ const qreal fdx = data->m11;
+ const qreal fdy = data->m12;
+ const qreal fdw = data->m13;
+
+ while (count--) {
+ const quint8 coverage = (data->texture.const_alpha * spans->coverage) >> 8;
+ if (coverage == 0) {
+ ++spans;
+ continue;
+ }
+
+ DST *dest = (DST*)data->rasterBuffer->scanLine(spans->y)
+ + spans->x;
+
+ const qreal cx = spans->x + qreal(0.5);
+ const qreal cy = spans->y + qreal(0.5);
+
+ qreal x = data->m21 * cy + data->m11 * cx + data->dx;
+ qreal y = data->m22 * cy + data->m12 * cx + data->dy;
+ qreal w = data->m23 * cy + data->m13 * cx + data->m33;
+
+ int length = spans->len;
+ while (length) {
+ const int l = qMin(length, buffer_size);
+ const SRC *end = buffer + l;
+ SRC *b = buffer;
+ while (b < end) {
+ const qreal iw = w == 0 ? 1 : 1 / w;
+ const qreal px = x * iw - qreal(0.5);
+ const qreal py = y * iw - qreal(0.5);
+
+ int x1 = int(px) - (px < 0);
+ int x2;
+ int y1 = int(py) - (py < 0);
+ int y2;
+
+ const int distx = int((px - x1) * 256);
+ const int disty = int((py - y1) * 256);
+
+ if (x1 < src_minx) {
+ x2 = x1 = src_minx;
+ } else if (x1 >= src_maxx) {
+ x2 = x1 = src_maxx;
+ } else {
+ x2 = x1 + 1;
+ }
+ if (y1 < src_miny) {
+ y2 = y1 = src_miny;
+ } else if (y1 >= src_maxy) {
+ y2 = y1 = src_maxy;
+ } else {
+ y2 = y1 + 1;
+ }
+
+ const SRC *src1 = (SRC*)data->texture.scanLine(y1);
+ const SRC *src2 = (SRC*)data->texture.scanLine(y2);
+ SRC tl = src1[x1];
+ const SRC tr = src1[x2];
+ SRC bl = src2[x1];
+ const SRC br = src2[x2];
+ const quint8 ax = SRC::alpha(distx);
+ const quint8 iax = SRC::ialpha(distx);
+
+ interpolate_pixel(tl, iax, tr, ax);
+ interpolate_pixel(bl, iax, br, ax);
+ interpolate_pixel(tl, SRC::ialpha(disty),
+ bl, SRC::alpha(disty));
+ *b = tl;
+ ++b;
+
+ x += fdx;
+ y += fdy;
+ w += fdw;
+ }
+ if (!SRC::hasAlpha() && coverage == 255) {
+ qt_memconvert(dest, buffer, l);
+ } else if (sizeof(DST) == 3 && l >= 4 &&
+ (quintptr(dest) & 3) == (quintptr(buffer) & 3))
+ {
+ blendUntransformed_dest24(dest, buffer, coverage, l);
+ } else if (sizeof(DST) == 2 && sizeof(SRC) == 2 && l >= 2 &&
+ (quintptr(dest) & 3) == (quintptr(buffer) & 3)) {
+ blendUntransformed_dest16(dest, buffer, coverage, l);
+ } else {
+ blendUntransformed_unaligned(dest, buffer, coverage, l);
+ }
+
+ dest += l;
+ length -= l;
+ }
+ ++spans;
+ }
+ }
+}
+
+static void blend_transformed_bilinear_rgb888(int count, const QSpan *spans, void *userData)
+{
+#if defined(QT_QWS_DEPTH_24)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_RGB888)
+ blendTransformedBilinear<qrgb888, qrgb888>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_transformed_bilinear_argb6666(int count, const QSpan *spans, void *userData)
+{
+#if defined(QT_QWS_DEPTH_18)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB6666_Premultiplied)
+ blendTransformedBilinear<qargb6666, qargb6666>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB666)
+ blendTransformedBilinear<qargb6666, qrgb666>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_transformed_bilinear_rgb666(int count, const QSpan *spans, void *userData)
+{
+#if defined(QT_QWS_DEPTH_18)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB6666_Premultiplied)
+ blendTransformedBilinear<qrgb666, qargb6666>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB666)
+ blendTransformedBilinear<qrgb666, qrgb666>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_transformed_bilinear_argb8565(int count, const QSpan *spans, void *userData)
+{
+#if defined(QT_QWS_DEPTH_16)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8565_Premultiplied)
+ blendTransformedBilinear<qargb8565, qargb8565>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB16)
+ blendTransformedBilinear<qargb8565, qrgb565>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_transformed_bilinear_rgb565(int count, const QSpan *spans,
+ void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_16)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_RGB16)
+ blendTransformedBilinear<qrgb565, qrgb565>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_ARGB8565_Premultiplied)
+ blendTransformedBilinear<qrgb565, qargb8565>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_transformed_bilinear_argb8555(int count, const QSpan *spans, void *userData)
+{
+#if defined(QT_QWS_DEPTH_15)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8555_Premultiplied)
+ blendTransformedBilinear<qargb8555, qargb8555>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB555)
+ blendTransformedBilinear<qargb8555, qrgb555>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_transformed_bilinear_rgb555(int count, const QSpan *spans, void *userData)
+{
+#if defined(QT_QWS_DEPTH_15)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8555_Premultiplied)
+ blendTransformedBilinear<qrgb555, qargb8555>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB555)
+ blendTransformedBilinear<qrgb555, qrgb555>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_transformed_bilinear_argb4444(int count, const QSpan *spans, void *userData)
+{
+#if defined(QT_QWS_DEPTH_12)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB4444_Premultiplied)
+ blendTransformedBilinear<qargb4444, qargb4444>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB444)
+ blendTransformedBilinear<qargb4444, qrgb444>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_transformed_bilinear_rgb444(int count, const QSpan *spans, void *userData)
+{
+#if defined(QT_QWS_DEPTH_12)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB4444_Premultiplied)
+ blendTransformedBilinear<qrgb444, qargb4444>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB444)
+ blendTransformedBilinear<qrgb444, qrgb444>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData);
+}
+
+template <SpanMethod spanMethod>
+Q_STATIC_TEMPLATE_FUNCTION void blend_transformed_argb(int count, const QSpan *spans, void *userData)
+{
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+ if (data->texture.format != QImage::Format_ARGB32_Premultiplied
+ && data->texture.format != QImage::Format_RGB32) {
+ blend_src_generic<spanMethod>(count, spans, userData);
+ return;
+ }
+
+ CompositionFunction func = functionForMode[data->rasterBuffer->compositionMode];
+ uint buffer[buffer_size];
+
+ int image_width = data->texture.width;
+ int image_height = data->texture.height;
+ const int scanline_offset = data->texture.bytesPerLine / 4;
+
+ if (data->fast_matrix) {
+ // The increment pr x in the scanline
+ int fdx = (int)(data->m11 * fixed_scale);
+ int fdy = (int)(data->m12 * fixed_scale);
+
+ while (count--) {
+ void *t = data->rasterBuffer->scanLine(spans->y);
+
+ uint *target = ((uint *)t) + spans->x;
+ uint *image_bits = (uint *)data->texture.imageData;
+
+ const qreal cx = spans->x + qreal(0.5);
+ const qreal cy = spans->y + qreal(0.5);
+
+ int x = int((data->m21 * cy
+ + data->m11 * cx + data->dx) * fixed_scale);
+ int y = int((data->m22 * cy
+ + data->m12 * cx + data->dy) * fixed_scale);
+
+ int length = spans->len;
+ const int coverage = (spans->coverage * data->texture.const_alpha) >> 8;
+ while (length) {
+ int l = qMin(length, buffer_size);
+ const uint *end = buffer + l;
+ uint *b = buffer;
+ while (b < end) {
+ int px = x >> 16;
+ int py = y >> 16;
+
+ bool out = (px < 0) || (px >= image_width)
+ || (py < 0) || (py >= image_height);
+
+ int y_offset = py * scanline_offset;
+ *b = out ? uint(0) : image_bits[y_offset + px];
+ x += fdx;
+ y += fdy;
+ ++b;
+ }
+ if (spanMethod == RegularSpans)
+ func(target, buffer, l, coverage);
+ else
+ drawBufferSpan(data, buffer, buffer_size,
+ spans->x + spans->len - length,
+ spans->y, l, coverage);
+ target += l;
+ length -= l;
+ }
+ ++spans;
+ }
+ } else {
+ const qreal fdx = data->m11;
+ const qreal fdy = data->m12;
+ const qreal fdw = data->m13;
+ while (count--) {
+ void *t = data->rasterBuffer->scanLine(spans->y);
+
+ uint *target = ((uint *)t) + spans->x;
+ uint *image_bits = (uint *)data->texture.imageData;
+
+ const qreal cx = spans->x + qreal(0.5);
+ const qreal cy = spans->y + qreal(0.5);
+
+ qreal x = data->m21 * cy + data->m11 * cx + data->dx;
+ qreal y = data->m22 * cy + data->m12 * cx + data->dy;
+ qreal w = data->m23 * cy + data->m13 * cx + data->m33;
+
+ int length = spans->len;
+ const int coverage = (spans->coverage * data->texture.const_alpha) >> 8;
+ while (length) {
+ int l = qMin(length, buffer_size);
+ const uint *end = buffer + l;
+ uint *b = buffer;
+ while (b < end) {
+ const qreal iw = w == 0 ? 1 : 1 / w;
+ const qreal tx = x * iw;
+ const qreal ty = y * iw;
+ const int px = int(tx) - (tx < 0);
+ const int py = int(ty) - (ty < 0);
+
+ bool out = (px < 0) || (px >= image_width)
+ || (py < 0) || (py >= image_height);
+
+ int y_offset = py * scanline_offset;
+ *b = out ? uint(0) : image_bits[y_offset + px];
+ x += fdx;
+ y += fdy;
+ w += fdw;
+
+ ++b;
+ }
+ if (spanMethod == RegularSpans)
+ func(target, buffer, l, coverage);
+ else
+ drawBufferSpan(data, buffer, buffer_size,
+ spans->x + spans->len - length,
+ spans->y, l, coverage);
+ target += l;
+ length -= l;
+ }
+ ++spans;
+ }
+ }
+}
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_FUNCTION void blendTransformed(int count, const QSpan *spans, void *userData)
+{
+ QSpanData *data = reinterpret_cast<QSpanData*>(userData);
+ QPainter::CompositionMode mode = data->rasterBuffer->compositionMode;
+
+ if (mode != QPainter::CompositionMode_SourceOver) {
+ blend_src_generic<RegularSpans>(count, spans, userData);
+ return;
+ }
+
+ SRC buffer[buffer_size];
+ const int image_width = data->texture.width;
+ const int image_height = data->texture.height;
+
+ if (data->fast_matrix) {
+ // The increment pr x in the scanline
+ const int fdx = (int)(data->m11 * fixed_scale);
+ const int fdy = (int)(data->m12 * fixed_scale);
+
+ while (count--) {
+ const quint8 coverage = (data->texture.const_alpha * spans->coverage) >> 8;
+ if (coverage == 0) {
+ ++spans;
+ continue;
+ }
+
+ DST *dest = (DST*)data->rasterBuffer->scanLine(spans->y)
+ + spans->x;
+ const qreal cx = spans->x + qreal(0.5);
+ const qreal cy = spans->y + qreal(0.5);
+ int x = int((data->m21 * cy
+ + data->m11 * cx + data->dx) * fixed_scale);
+ int y = int((data->m22 * cy
+ + data->m12 * cx + data->dy) * fixed_scale);
+ int length = spans->len;
+
+ while (length) {
+ const int l = qMin(length, buffer_size);
+
+ const SRC *end = buffer + l;
+ SRC *b = buffer;
+ while (b < end) {
+ const int px = (x >> 16);
+ const int py = (y >> 16);
+
+ if ((px < 0) || (px >= image_width) ||
+ (py < 0) || (py >= image_height))
+ {
+ *b = 0;
+ } else {
+ *b = ((SRC*)data->texture.scanLine(py))[px];
+ }
+ ++b;
+
+ x += fdx;
+ y += fdy;
+ }
+
+ if (!SRC::hasAlpha() && coverage == 255) {
+ qt_memconvert(dest, buffer, l);
+ } else if (sizeof(DST) == 3 && sizeof(SRC) == 3 && l >= 4 &&
+ (quintptr(dest) & 3) == (quintptr(buffer) & 3))
+ {
+ blendUntransformed_dest24(dest, buffer, coverage, l);
+ } else if (sizeof(DST) == 2 && sizeof(SRC) == 2 && l >= 2 &&
+ (quintptr(dest) & 3) == (quintptr(buffer) & 3)) {
+ blendUntransformed_dest16(dest, buffer, coverage, l);
+ } else {
+ blendUntransformed_unaligned(dest, buffer, coverage, l);
+ }
+
+ dest += l;
+ length -= l;
+ }
+ ++spans;
+ }
+ } else {
+ const qreal fdx = data->m11;
+ const qreal fdy = data->m12;
+ const qreal fdw = data->m13;
+
+ while (count--) {
+ const quint8 coverage = (data->texture.const_alpha * spans->coverage) >> 8;
+ if (coverage == 0) {
+ ++spans;
+ continue;
+ }
+
+ DST *dest = (DST*)data->rasterBuffer->scanLine(spans->y)
+ + spans->x;
+
+ const qreal cx = spans->x + qreal(0.5);
+ const qreal cy = spans->y + qreal(0.5);
+
+ qreal x = data->m21 * cy + data->m11 * cx + data->dx;
+ qreal y = data->m22 * cy + data->m12 * cx + data->dy;
+ qreal w = data->m23 * cy + data->m13 * cx + data->m33;
+
+ int length = spans->len;
+ while (length) {
+ const int l = qMin(length, buffer_size);
+ const SRC *end = buffer + l;
+ SRC *b = buffer;
+ while (b < end) {
+ const qreal iw = w == 0 ? 1 : 1 / w;
+ const qreal tx = x * iw;
+ const qreal ty = y * iw;
+
+ const int px = int(tx) - (tx < 0);
+ const int py = int(ty) - (ty < 0);
+
+ if ((px < 0) || (px >= image_width) ||
+ (py < 0) || (py >= image_height))
+ {
+ *b = 0;
+ } else {
+ *b = ((SRC*)data->texture.scanLine(py))[px];
+ }
+ ++b;
+
+ x += fdx;
+ y += fdy;
+ w += fdw;
+ }
+ if (!SRC::hasAlpha() && coverage == 255) {
+ qt_memconvert(dest, buffer, l);
+ } else if (sizeof(DST) == 3 && sizeof(SRC) == 3 && l >= 4 &&
+ (quintptr(dest) & 3) == (quintptr(buffer) & 3))
+ {
+ blendUntransformed_dest24(dest, buffer, coverage, l);
+ } else if (sizeof(DST) == 2 && sizeof(SRC) == 2 && l >= 2 &&
+ (quintptr(dest) & 3) == (quintptr(buffer) & 3)) {
+ blendUntransformed_dest16(dest, buffer, coverage, l);
+ } else {
+ blendUntransformed_unaligned(dest, buffer, coverage, l);
+ }
+
+ dest += l;
+ length -= l;
+ }
+ ++spans;
+ }
+ }
+}
+
+static void blend_transformed_rgb888(int count, const QSpan *spans,
+ void *userData)
+{
+#if defined(QT_QWS_DEPTH_24)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_RGB888)
+ blendTransformed<qrgb888, qrgb888>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_transformed_argb6666(int count, const QSpan *spans,
+ void *userData)
+{
+#if defined(QT_QWS_DEPTH_18)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB6666_Premultiplied)
+ blendTransformed<qargb6666, qargb6666>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB666)
+ blendTransformed<qargb6666, qrgb666>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_transformed_rgb666(int count, const QSpan *spans,
+ void *userData)
+{
+#if defined(QT_QWS_DEPTH_18)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB6666_Premultiplied)
+ blendTransformed<qrgb666, qargb6666>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB666)
+ blendTransformed<qrgb666, qrgb666>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_transformed_argb8565(int count, const QSpan *spans,
+ void *userData)
+{
+#if defined(QT_QWS_DEPTH_16)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8565_Premultiplied)
+ blendTransformed<qargb8565, qargb8565>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB16)
+ blendTransformed<qargb8565, qrgb565>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_transformed_rgb565(int count, const QSpan *spans,
+ void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_16)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8565_Premultiplied)
+ blendTransformed<qrgb565, qargb8565>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB16)
+ blendTransformed<qrgb565, qrgb565>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_transformed_argb8555(int count, const QSpan *spans,
+ void *userData)
+{
+#if defined(QT_QWS_DEPTH_15)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8555_Premultiplied)
+ blendTransformed<qargb8555, qargb8555>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB555)
+ blendTransformed<qargb8555, qrgb555>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_transformed_rgb555(int count, const QSpan *spans,
+ void *userData)
+{
+#if defined(QT_QWS_DEPTH_15)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8555_Premultiplied)
+ blendTransformed<qrgb555, qargb8555>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB555)
+ blendTransformed<qrgb555, qrgb555>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_transformed_argb4444(int count, const QSpan *spans,
+ void *userData)
+{
+#if defined(QT_QWS_DEPTH_12)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB4444_Premultiplied)
+ blendTransformed<qargb4444, qargb4444>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB444)
+ blendTransformed<qargb4444, qrgb444>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_transformed_rgb444(int count, const QSpan *spans,
+ void *userData)
+{
+#if defined(QT_QWS_DEPTH_12)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB4444_Premultiplied)
+ blendTransformed<qrgb444, qargb4444>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB444)
+ blendTransformed<qrgb444, qrgb444>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData);
+}
+
+template <SpanMethod spanMethod>
+Q_STATIC_TEMPLATE_FUNCTION void blend_transformed_tiled_argb(int count, const QSpan *spans, void *userData)
+{
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+ if (data->texture.format != QImage::Format_ARGB32_Premultiplied
+ && data->texture.format != QImage::Format_RGB32) {
+ blend_src_generic<spanMethod>(count, spans, userData);
+ return;
+ }
+
+ CompositionFunction func = functionForMode[data->rasterBuffer->compositionMode];
+ uint buffer[buffer_size];
+
+ int image_width = data->texture.width;
+ int image_height = data->texture.height;
+ const int scanline_offset = data->texture.bytesPerLine / 4;
+
+ if (data->fast_matrix) {
+ // The increment pr x in the scanline
+ int fdx = (int)(data->m11 * fixed_scale);
+ int fdy = (int)(data->m12 * fixed_scale);
+
+ while (count--) {
+ void *t = data->rasterBuffer->scanLine(spans->y);
+
+ uint *target = ((uint *)t) + spans->x;
+ uint *image_bits = (uint *)data->texture.imageData;
+
+ const qreal cx = spans->x + qreal(0.5);
+ const qreal cy = spans->y + qreal(0.5);
+
+ int x = int((data->m21 * cy
+ + data->m11 * cx + data->dx) * fixed_scale);
+ int y = int((data->m22 * cy
+ + data->m12 * cx + data->dy) * fixed_scale);
+
+ const int coverage = (spans->coverage * data->texture.const_alpha) >> 8;
+ int length = spans->len;
+ while (length) {
+ int l = qMin(length, buffer_size);
+ const uint *end = buffer + l;
+ uint *b = buffer;
+ while (b < end) {
+ int px = x >> 16;
+ int py = y >> 16;
+ px %= image_width;
+ py %= image_height;
+ if (px < 0) px += image_width;
+ if (py < 0) py += image_height;
+ int y_offset = py * scanline_offset;
+
+ Q_ASSERT(px >= 0 && px < image_width);
+ Q_ASSERT(py >= 0 && py < image_height);
+
+ *b = image_bits[y_offset + px];
+ x += fdx;
+ y += fdy;
+ ++b;
+ }
+ if (spanMethod == RegularSpans)
+ func(target, buffer, l, coverage);
+ else
+ drawBufferSpan(data, buffer, buffer_size,
+ spans->x + spans->len - length,
+ spans->y, l, coverage);
+ target += l;
+ length -= l;
+ }
+ ++spans;
+ }
+ } else {
+ const qreal fdx = data->m11;
+ const qreal fdy = data->m12;
+ const qreal fdw = data->m13;
+ while (count--) {
+ void *t = data->rasterBuffer->scanLine(spans->y);
+
+ uint *target = ((uint *)t) + spans->x;
+ uint *image_bits = (uint *)data->texture.imageData;
+
+ const qreal cx = spans->x + qreal(0.5);
+ const qreal cy = spans->y + qreal(0.5);
+
+ qreal x = data->m21 * cy + data->m11 * cx + data->dx;
+ qreal y = data->m22 * cy + data->m12 * cx + data->dy;
+ qreal w = data->m23 * cy + data->m13 * cx + data->m33;
+
+ const int coverage = (spans->coverage * data->texture.const_alpha) >> 8;
+ int length = spans->len;
+ while (length) {
+ int l = qMin(length, buffer_size);
+ const uint *end = buffer + l;
+ uint *b = buffer;
+ while (b < end) {
+ const qreal iw = w == 0 ? 1 : 1 / w;
+ const qreal tx = x * iw;
+ const qreal ty = y * iw;
+ int px = int(tx) - (tx < 0);
+ int py = int(ty) - (ty < 0);
+
+ px %= image_width;
+ py %= image_height;
+ if (px < 0) px += image_width;
+ if (py < 0) py += image_height;
+ int y_offset = py * scanline_offset;
+
+ Q_ASSERT(px >= 0 && px < image_width);
+ Q_ASSERT(py >= 0 && py < image_height);
+
+ *b = image_bits[y_offset + px];
+ x += fdx;
+ y += fdy;
+ w += fdw;
+ //force increment to avoid /0
+ if (!w) {
+ w += fdw;
+ }
+ ++b;
+ }
+ if (spanMethod == RegularSpans)
+ func(target, buffer, l, coverage);
+ else
+ drawBufferSpan(data, buffer, buffer_size,
+ spans->x + spans->len - length,
+ spans->y, l, coverage);
+ target += l;
+ length -= l;
+ }
+ ++spans;
+ }
+ }
+}
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_FUNCTION void blendTransformedTiled(int count, const QSpan *spans, void *userData)
+{
+ QSpanData *data = reinterpret_cast<QSpanData*>(userData);
+ QPainter::CompositionMode mode = data->rasterBuffer->compositionMode;
+
+ if (mode != QPainter::CompositionMode_SourceOver) {
+ blend_src_generic<RegularSpans>(count, spans, userData);
+ return;
+ }
+
+ SRC buffer[buffer_size];
+ const int image_width = data->texture.width;
+ const int image_height = data->texture.height;
+
+ if (data->fast_matrix) {
+ // The increment pr x in the scanline
+ const int fdx = (int)(data->m11 * fixed_scale);
+ const int fdy = (int)(data->m12 * fixed_scale);
+
+ while (count--) {
+ const quint8 coverage = (data->texture.const_alpha * spans->coverage) >> 8;
+ if (coverage == 0) {
+ ++spans;
+ continue;
+ }
+
+ DST *dest = (DST*)data->rasterBuffer->scanLine(spans->y)
+ + spans->x;
+ const qreal cx = spans->x + qreal(0.5);
+ const qreal cy = spans->y + qreal(0.5);
+ int x = int((data->m21 * cy
+ + data->m11 * cx + data->dx) * fixed_scale);
+ int y = int((data->m22 * cy
+ + data->m12 * cx + data->dy) * fixed_scale);
+ int length = spans->len;
+
+ while (length) {
+ const int l = qMin(length, buffer_size);
+
+ const SRC *end = buffer + l;
+ SRC *b = buffer;
+ while (b < end) {
+ int px = (x >> 16) % image_width;
+ int py = (y >> 16) % image_height;
+
+ if (px < 0)
+ px += image_width;
+ if (py < 0)
+ py += image_height;
+
+ *b = ((SRC*)data->texture.scanLine(py))[px];
+ ++b;
+
+ x += fdx;
+ y += fdy;
+ }
+
+ if (!SRC::hasAlpha() && coverage == 255) {
+ qt_memconvert(dest, buffer, l);
+ } else if (sizeof(DST) == 3 && sizeof(SRC) == 3 && l >= 4 &&
+ (quintptr(dest) & 3) == (quintptr(buffer) & 3))
+ {
+ blendUntransformed_dest24(dest, buffer, coverage, l);
+ } else if (sizeof(DST) == 2 && sizeof(SRC) == 2 && l >= 2 &&
+ (quintptr(dest) & 3) == (quintptr(buffer) & 3)) {
+ blendUntransformed_dest16(dest, buffer, coverage, l);
+ } else {
+ blendUntransformed_unaligned(dest, buffer, coverage, l);
+ }
+
+ dest += l;
+ length -= l;
+ }
+ ++spans;
+ }
+ } else {
+ const qreal fdx = data->m11;
+ const qreal fdy = data->m12;
+ const qreal fdw = data->m13;
+
+ while (count--) {
+ const quint8 coverage = (data->texture.const_alpha * spans->coverage) >> 8;
+ if (coverage == 0) {
+ ++spans;
+ continue;
+ }
+
+ DST *dest = (DST*)data->rasterBuffer->scanLine(spans->y)
+ + spans->x;
+
+ const qreal cx = spans->x + qreal(0.5);
+ const qreal cy = spans->y + qreal(0.5);
+
+ qreal x = data->m21 * cy + data->m11 * cx + data->dx;
+ qreal y = data->m22 * cy + data->m12 * cx + data->dy;
+ qreal w = data->m23 * cy + data->m13 * cx + data->m33;
+
+ int length = spans->len;
+ while (length) {
+ const int l = qMin(length, buffer_size);
+ const SRC *end = buffer + l;
+ SRC *b = buffer;
+ while (b < end) {
+ const qreal iw = w == 0 ? 1 : 1 / w;
+ const qreal tx = x * iw;
+ const qreal ty = y * iw;
+
+ int px = int(tx) - (tx < 0);
+ int py = int(ty) - (ty < 0);
+
+ px %= image_width;
+ py %= image_height;
+ if (px < 0)
+ px += image_width;
+ if (py < 0)
+ py += image_height;
+
+ *b = ((SRC*)data->texture.scanLine(py))[px];
+ ++b;
+
+ x += fdx;
+ y += fdy;
+ w += fdw;
+ // force increment to avoid /0
+ if (!w)
+ w += fdw;
+ }
+ if (!SRC::hasAlpha() && coverage == 255) {
+ qt_memconvert(dest, buffer, l);
+ } else if (sizeof(DST) == 3 && sizeof(SRC) == 3 && l >= 4 &&
+ (quintptr(dest) & 3) == (quintptr(buffer) & 3))
+ {
+ blendUntransformed_dest24(dest, buffer, coverage, l);
+ } else if (sizeof(DST) == 2 && sizeof(SRC) == 2 && l >= 2 &&
+ (quintptr(dest) & 3) == (quintptr(buffer) & 3)) {
+ blendUntransformed_dest16(dest, buffer, coverage, l);
+ } else {
+ blendUntransformed_unaligned(dest, buffer, coverage, l);
+ }
+
+ dest += l;
+ length -= l;
+ }
+ ++spans;
+ }
+ }
+}
+
+static void blend_transformed_tiled_rgb888(int count, const QSpan *spans,
+ void *userData)
+{
+#if defined(QT_QWS_DEPTH_24)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_RGB888)
+ blendTransformedTiled<qrgb888, qrgb888>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_transformed_tiled_argb6666(int count, const QSpan *spans,
+ void *userData)
+{
+#if defined(QT_QWS_DEPTH_18)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB6666_Premultiplied)
+ blendTransformedTiled<qargb6666, qargb6666>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB666)
+ blendTransformedTiled<qargb6666, qrgb666>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_transformed_tiled_rgb666(int count, const QSpan *spans,
+ void *userData)
+{
+#if defined(QT_QWS_DEPTH_18)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB6666_Premultiplied)
+ blendTransformedTiled<qrgb666, qargb6666>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB666)
+ blendTransformedTiled<qrgb666, qrgb666>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_transformed_tiled_argb8565(int count, const QSpan *spans,
+ void *userData)
+{
+#if defined(QT_QWS_DEPTH_16)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8565_Premultiplied)
+ blendTransformedTiled<qargb8565, qargb8565>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB16)
+ blendTransformedTiled<qargb8565, qrgb565>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_transformed_tiled_rgb565(int count, const QSpan *spans,
+ void *userData)
+{
+#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_16)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8565_Premultiplied)
+ blendTransformedTiled<qrgb565, qargb8565>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB16)
+ blendTransformedTiled<qrgb565, qrgb565>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_transformed_tiled_argb8555(int count, const QSpan *spans,
+ void *userData)
+{
+#if defined(QT_QWS_DEPTH_15)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8555_Premultiplied)
+ blendTransformedTiled<qargb8555, qargb8555>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB555)
+ blendTransformedTiled<qargb8555, qrgb555>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_transformed_tiled_rgb555(int count, const QSpan *spans,
+ void *userData)
+{
+#if defined(QT_QWS_DEPTH_15)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB8555_Premultiplied)
+ blendTransformedTiled<qrgb555, qargb8555>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB555)
+ blendTransformedTiled<qrgb555, qrgb555>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_transformed_tiled_argb4444(int count, const QSpan *spans,
+ void *userData)
+{
+#if defined(QT_QWS_DEPTH_12)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB4444_Premultiplied)
+ blendTransformedTiled<qargb4444, qargb4444>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB444)
+ blendTransformedTiled<qargb4444, qrgb444>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData);
+}
+
+static void blend_transformed_tiled_rgb444(int count, const QSpan *spans,
+ void *userData)
+{
+#if defined(QT_QWS_DEPTH_12)
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ if (data->texture.format == QImage::Format_ARGB4444_Premultiplied)
+ blendTransformedTiled<qrgb444, qargb4444>(count, spans, userData);
+ else if (data->texture.format == QImage::Format_RGB444)
+ blendTransformedTiled<qrgb444, qrgb444>(count, spans, userData);
+ else
+#endif
+ blend_src_generic<RegularSpans>(count, spans, userData);
+}
+
+# define SPANFUNC_POINTER(Name, Arg) Name<Arg>
+
+
+/* Image formats here are target formats */
+static const ProcessSpans processTextureSpans[NBlendTypes][QImage::NImageFormats] = {
+ // Untransformed
+ {
+ 0, // Invalid
+ SPANFUNC_POINTER(blend_untransformed_generic, RegularSpans), // Mono
+ SPANFUNC_POINTER(blend_untransformed_generic, RegularSpans), // MonoLsb
+ SPANFUNC_POINTER(blend_untransformed_generic, RegularSpans), // Indexed8
+ SPANFUNC_POINTER(blend_untransformed_generic, RegularSpans), // RGB32
+ SPANFUNC_POINTER(blend_untransformed_generic, RegularSpans), // ARGB32
+ SPANFUNC_POINTER(blend_untransformed_argb, RegularSpans), // ARGB32_Premultiplied
+ blend_untransformed_rgb565,
+ blend_untransformed_argb8565,
+ blend_untransformed_rgb666,
+ blend_untransformed_argb6666,
+ blend_untransformed_rgb555,
+ blend_untransformed_argb8555,
+ blend_untransformed_rgb888,
+ blend_untransformed_rgb444,
+ blend_untransformed_argb4444,
+ },
+ // Tiled
+ {
+ 0, // Invalid
+ SPANFUNC_POINTER(blend_tiled_generic, RegularSpans), // Mono
+ SPANFUNC_POINTER(blend_tiled_generic, RegularSpans), // MonoLsb
+ SPANFUNC_POINTER(blend_tiled_generic, RegularSpans), // Indexed8
+ SPANFUNC_POINTER(blend_tiled_generic, RegularSpans), // RGB32
+ SPANFUNC_POINTER(blend_tiled_generic, RegularSpans), // ARGB32
+ SPANFUNC_POINTER(blend_tiled_argb, RegularSpans), // ARGB32_Premultiplied
+ blend_tiled_rgb565,
+ blend_tiled_argb8565,
+ blend_tiled_rgb666,
+ blend_tiled_argb6666,
+ blend_tiled_rgb555,
+ blend_tiled_argb8555,
+ blend_tiled_rgb888,
+ blend_tiled_rgb444,
+ blend_tiled_argb4444,
+ },
+ // Transformed
+ {
+ 0, // Invalid
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // Mono
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // MonoLsb
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // Indexed8
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // RGB32
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // ARGB32
+ SPANFUNC_POINTER(blend_transformed_argb, RegularSpans), // ARGB32_Premultiplied
+ blend_transformed_rgb565,
+ blend_transformed_argb8565,
+ blend_transformed_rgb666,
+ blend_transformed_argb6666,
+ blend_transformed_rgb555,
+ blend_transformed_argb8555,
+ blend_transformed_rgb888,
+ blend_transformed_rgb444,
+ blend_transformed_argb4444,
+ },
+ // TransformedTiled
+ {
+ 0,
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // Mono
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // MonoLsb
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // Indexed8
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // RGB32
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // ARGB32
+ SPANFUNC_POINTER(blend_transformed_tiled_argb, RegularSpans), // ARGB32_Premultiplied
+ blend_transformed_tiled_rgb565,
+ blend_transformed_tiled_argb8565,
+ blend_transformed_tiled_rgb666,
+ blend_transformed_tiled_argb6666,
+ blend_transformed_tiled_rgb555,
+ blend_transformed_tiled_argb8555,
+ blend_transformed_tiled_rgb888,
+ blend_transformed_tiled_rgb444,
+ blend_transformed_tiled_argb4444
+ },
+ // Bilinear
+ {
+ 0,
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // Mono
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // MonoLsb
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // Indexed8
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // RGB32
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // ARGB32
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // ARGB32_Premultiplied
+ blend_transformed_bilinear_rgb565,
+ blend_transformed_bilinear_argb8565,
+ blend_transformed_bilinear_rgb666,
+ blend_transformed_bilinear_argb6666,
+ blend_transformed_bilinear_rgb555,
+ blend_transformed_bilinear_argb8555,
+ blend_transformed_bilinear_rgb888,
+ blend_transformed_bilinear_rgb444,
+ blend_transformed_bilinear_argb4444,
+ },
+ // BilinearTiled
+ {
+ 0,
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // Mono
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // MonoLsb
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // Indexed8
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // RGB32
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // ARGB32
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // ARGB32_Premultiplied
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // RGB16
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // ARGB8565_Premultiplied
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // RGB666
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // ARGB6666_Premultiplied
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // RGB555
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // ARGB8555_Premultiplied
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // RGB888
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // RGB444
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans), // ARGB4444_Premultiplied
+ }
+};
+
+#if defined (Q_WS_QWS) && !defined(QT_NO_RASTERCALLBACKS)
+static const ProcessSpans processTextureSpansCallback[NBlendTypes][QImage::NImageFormats] = {
+ // Untransformed
+ {
+ 0, // Invalid
+ blend_untransformed_generic<CallbackSpans>, // Mono
+ blend_untransformed_generic<CallbackSpans>, // MonoLsb
+ blend_untransformed_generic<CallbackSpans>, // Indexed8
+ blend_untransformed_generic<CallbackSpans>, // RGB32
+ blend_untransformed_generic<CallbackSpans>, // ARGB32
+ blend_untransformed_argb<CallbackSpans>, // ARGB32_Premultiplied
+ blend_untransformed_generic<CallbackSpans>, // RGB16
+ blend_untransformed_generic<CallbackSpans>, // ARGB8565_Premultiplied
+ blend_untransformed_generic<CallbackSpans>, // RGB666
+ blend_untransformed_generic<CallbackSpans>, // ARGB6666_Premultiplied
+ blend_untransformed_generic<CallbackSpans>, // RGB555
+ blend_untransformed_generic<CallbackSpans>, // ARGB8555_Premultiplied
+ blend_untransformed_generic<CallbackSpans>, // RGB888
+ blend_untransformed_generic<CallbackSpans>, // RGB444
+ blend_untransformed_generic<CallbackSpans> // ARGB4444_Premultiplied
+ },
+ // Tiled
+ {
+ 0, // Invalid
+ blend_tiled_generic<CallbackSpans>, // Mono
+ blend_tiled_generic<CallbackSpans>, // MonoLsb
+ blend_tiled_generic<CallbackSpans>, // Indexed8
+ blend_tiled_generic<CallbackSpans>, // RGB32
+ blend_tiled_generic<CallbackSpans>, // ARGB32
+ blend_tiled_argb<CallbackSpans>, // ARGB32_Premultiplied
+ blend_tiled_generic<CallbackSpans>, // RGB16
+ blend_tiled_generic<CallbackSpans>, // ARGB8565_Premultiplied
+ blend_tiled_generic<CallbackSpans>, // RGB666
+ blend_tiled_generic<CallbackSpans>, // ARGB6666_Premultiplied
+ blend_tiled_generic<CallbackSpans>, // RGB555
+ blend_tiled_generic<CallbackSpans>, // ARGB8555_Premultiplied
+ blend_tiled_generic<CallbackSpans>, // RGB888
+ blend_tiled_generic<CallbackSpans>, // RGB444
+ blend_tiled_generic<CallbackSpans> // ARGB4444_Premultiplied
+ },
+ // Transformed
+ {
+ 0, // Invalid
+ blend_src_generic<CallbackSpans>, // Mono
+ blend_src_generic<CallbackSpans>, // MonoLsb
+ blend_src_generic<CallbackSpans>, // Indexed8
+ blend_src_generic<CallbackSpans>, // RGB32
+ blend_src_generic<CallbackSpans>, // ARGB32
+ blend_transformed_argb<CallbackSpans>, // ARGB32_Premultiplied
+ blend_src_generic<CallbackSpans>, // RGB16
+ blend_src_generic<CallbackSpans>, // ARGB8565_Premultiplied
+ blend_src_generic<CallbackSpans>, // RGB666
+ blend_src_generic<CallbackSpans>, // ARGB6666_Premultiplied
+ blend_src_generic<CallbackSpans>, // RGB555
+ blend_src_generic<CallbackSpans>, // ARGB8555_Premultiplied
+ blend_src_generic<CallbackSpans>, // RGB888
+ blend_src_generic<CallbackSpans>, // RGB444
+ blend_src_generic<CallbackSpans>, // ARGB4444_Premultiplied
+ },
+ // TransformedTiled
+ {
+ 0,
+ blend_src_generic<CallbackSpans>, // Mono
+ blend_src_generic<CallbackSpans>, // MonoLsb
+ blend_src_generic<CallbackSpans>, // Indexed8
+ blend_src_generic<CallbackSpans>, // RGB32
+ blend_src_generic<CallbackSpans>, // ARGB32
+ blend_transformed_tiled_argb<CallbackSpans>, // ARGB32_Premultiplied
+ blend_src_generic<CallbackSpans>, // RGB16
+ blend_src_generic<CallbackSpans>, // ARGB8565_Premultiplied
+ blend_src_generic<CallbackSpans>, // RGB666
+ blend_src_generic<CallbackSpans>, // ARGB6666_Premultiplied
+ blend_src_generic<CallbackSpans>, // RGB555
+ blend_src_generic<CallbackSpans>, // ARGB8555_Premultiplied
+ blend_src_generic<CallbackSpans>, // RGB888
+ blend_src_generic<CallbackSpans>, // RGB444
+ blend_src_generic<CallbackSpans> // ARGB4444_Premultiplied
+ },
+ // Bilinear
+ {
+ 0,
+ blend_src_generic<CallbackSpans>, // Mono
+ blend_src_generic<CallbackSpans>, // MonoLsb
+ blend_src_generic<CallbackSpans>, // Indexed8
+ blend_src_generic<CallbackSpans>, // RGB32
+ blend_src_generic<CallbackSpans>, // ARGB32
+ blend_src_generic<CallbackSpans>, // ARGB32_Premultiplied
+ blend_src_generic<CallbackSpans>, // RGB16
+ blend_src_generic<CallbackSpans>, // ARGB8565_Premultiplied
+ blend_src_generic<CallbackSpans>, // RGB666
+ blend_src_generic<CallbackSpans>, // ARGB6666_Premultiplied
+ blend_src_generic<CallbackSpans>, // RGB555
+ blend_src_generic<CallbackSpans>, // ARGB8555_Premultiplied
+ blend_src_generic<CallbackSpans>, // RGB888
+ blend_src_generic<CallbackSpans>, // RGB444
+ blend_src_generic<CallbackSpans> // ARGB4444_Premultiplied
+ },
+ // BilinearTiled
+ {
+ 0,
+ blend_src_generic<CallbackSpans>, // Mono
+ blend_src_generic<CallbackSpans>, // MonoLsb
+ blend_src_generic<CallbackSpans>, // Indexed8
+ blend_src_generic<CallbackSpans>, // RGB32
+ blend_src_generic<CallbackSpans>, // ARGB32
+ blend_src_generic<CallbackSpans>, // ARGB32_Premultiplied
+ blend_src_generic<CallbackSpans>, // RGB16
+ blend_src_generic<CallbackSpans>, // ARGB8565_Premultiplied
+ blend_src_generic<CallbackSpans>, // RGB666
+ blend_src_generic<CallbackSpans>, // ARGB6666_Premultiplied
+ blend_src_generic<CallbackSpans>, // RGB555
+ blend_src_generic<CallbackSpans>, // ARGB8555_Premultiplied
+ blend_src_generic<CallbackSpans>, // RGB888
+ blend_src_generic<CallbackSpans>, // RGB444
+ blend_src_generic<CallbackSpans> // ARGB4444_Premultiplied
+ }
+};
+#endif // QT_NO_RASTERCALLBACKS
+
+void qBlendTexture(int count, const QSpan *spans, void *userData)
+{
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+ ProcessSpans proc = processTextureSpans[getBlendType(data)][data->rasterBuffer->format];
+ proc(count, spans, userData);
+}
+
+#if defined (Q_WS_QWS) && !defined(QT_NO_RASTERCALLBACKS)
+void qBlendTextureCallback(int count, const QSpan *spans, void *userData)
+{
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+ ProcessSpans proc = processTextureSpansCallback[getBlendType(data)][data->rasterBuffer->format];
+ proc(count, spans, userData);
+}
+#endif // QT_NO_RASTERCALLBACKS
+
+template <class DST>
+inline void qt_bitmapblit_template(QRasterBuffer *rasterBuffer,
+ int x, int y, quint32 color,
+ const uchar *map,
+ int mapWidth, int mapHeight, int mapStride,
+ DST dummy = 0)
+{
+ Q_UNUSED(dummy);
+ const DST c = qt_colorConvert<DST, quint32>(color, 0);
+ DST *dest = reinterpret_cast<DST*>(rasterBuffer->scanLine(y)) + x;
+ const int destStride = rasterBuffer->bytesPerLine() / sizeof(DST);
+
+ if (mapWidth > 8) {
+ while (mapHeight--) {
+ int x0 = 0;
+ int n = 0;
+ for (int x = 0; x < mapWidth; x += 8) {
+ uchar s = map[x >> 3];
+ for (int i = 0; i < 8; ++i) {
+ if (s & 0x80) {
+ ++n;
+ } else {
+ if (n) {
+ qt_memfill(dest + x0, c, n);
+ x0 += n + 1;
+ n = 0;
+ } else {
+ ++x0;
+ }
+ if (!s) {
+ x0 += 8 - 1 - i;
+ break;
+ }
+ }
+ s <<= 1;
+ }
+ }
+ if (n)
+ qt_memfill(dest + x0, c, n);
+ dest += destStride;
+ map += mapStride;
+ }
+ } else {
+ while (mapHeight--) {
+ int x0 = 0;
+ int n = 0;
+ for (uchar s = *map; s; s <<= 1) {
+ if (s & 0x80) {
+ ++n;
+ } else if (n) {
+ qt_memfill(dest + x0, c, n);
+ x0 += n + 1;
+ n = 0;
+ } else {
+ ++x0;
+ }
+ }
+ if (n)
+ qt_memfill(dest + x0, c, n);
+ dest += destStride;
+ map += mapStride;
+ }
+ }
+}
+
+static void qt_gradient_quint32(int count, const QSpan *spans, void *userData)
+{
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ bool isVerticalGradient =
+ data->txop <= QTransform::TxScale &&
+ data->type == QSpanData::LinearGradient &&
+ data->gradient.linear.end.x == data->gradient.linear.origin.x;
+
+ if (isVerticalGradient) {
+ LinearGradientValues linear;
+ getLinearGradientValues(&linear, data);
+
+ CompositionFunctionSolid funcSolid =
+ functionForModeSolid[data->rasterBuffer->compositionMode];
+
+ /*
+ The logic for vertical gradient calculations is a mathematically
+ reduced copy of that in fetchLinearGradient() - which is basically:
+
+ qreal ry = data->m22 * (y + 0.5) + data->dy;
+ qreal t = linear.dy*ry + linear.off;
+ t *= (GRADIENT_STOPTABLE_SIZE - 1);
+ quint32 color =
+ qt_gradient_pixel_fixed(&data->gradient,
+ int(t * FIXPT_SIZE));
+
+ This has then been converted to fixed point to improve performance.
+ */
+ const int gss = GRADIENT_STOPTABLE_SIZE - 1;
+ int yinc = int((linear.dy * data->m22 * gss) * FIXPT_SIZE);
+ int off = int((((linear.dy * (data->m22 * qreal(0.5) + data->dy) + linear.off) * gss) * FIXPT_SIZE));
+
+ while (count--) {
+ int y = spans->y;
+ int x = spans->x;
+
+ quint32 *dst = (quint32 *)(data->rasterBuffer->scanLine(y)) + x;
+ quint32 color =
+ qt_gradient_pixel_fixed(&data->gradient, yinc * y + off);
+
+ funcSolid(dst, spans->len, color, spans->coverage);
+ ++spans;
+ }
+
+ } else {
+ blend_src_generic<RegularSpans>(count, spans, userData);
+ }
+}
+
+static void qt_gradient_quint16(int count, const QSpan *spans, void *userData)
+{
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+
+ bool isVerticalGradient =
+ data->txop <= QTransform::TxScale &&
+ data->type == QSpanData::LinearGradient &&
+ data->gradient.linear.end.x == data->gradient.linear.origin.x;
+
+ if (isVerticalGradient) {
+
+ LinearGradientValues linear;
+ getLinearGradientValues(&linear, data);
+
+ /*
+ The logic for vertical gradient calculations is a mathematically
+ reduced copy of that in fetchLinearGradient() - which is basically:
+
+ qreal ry = data->m22 * (y + 0.5) + data->dy;
+ qreal t = linear.dy*ry + linear.off;
+ t *= (GRADIENT_STOPTABLE_SIZE - 1);
+ quint32 color =
+ qt_gradient_pixel_fixed(&data->gradient,
+ int(t * FIXPT_SIZE));
+
+ This has then been converted to fixed point to improve performance.
+ */
+ const int gss = GRADIENT_STOPTABLE_SIZE - 1;
+ int yinc = int((linear.dy * data->m22 * gss) * FIXPT_SIZE);
+ int off = int((((linear.dy * (data->m22 * qreal(0.5) + data->dy) + linear.off) * gss) * FIXPT_SIZE));
+
+ uint oldColor = data->solid.color;
+ while (count--) {
+ int y = spans->y;
+
+ quint32 color = qt_gradient_pixel_fixed(&data->gradient, yinc * y + off);
+
+ data->solid.color = color;
+ blend_color_rgb16(1, spans, userData);
+ ++spans;
+ }
+ data->solid.color = oldColor;
+
+ } else {
+ blend_src_generic<RegularSpans>(count, spans, userData);
+ }
+}
+
+inline static void qt_bitmapblit_quint32(QRasterBuffer *rasterBuffer,
+ int x, int y, quint32 color,
+ const uchar *map,
+ int mapWidth, int mapHeight, int mapStride)
+{
+ qt_bitmapblit_template<quint32>(rasterBuffer, x, y, color,
+ map, mapWidth, mapHeight, mapStride);
+}
+
+inline static void qt_bitmapblit_quint16(QRasterBuffer *rasterBuffer,
+ int x, int y, quint32 color,
+ const uchar *map,
+ int mapWidth, int mapHeight, int mapStride)
+{
+ qt_bitmapblit_template<quint16>(rasterBuffer, x, y, color,
+ map, mapWidth, mapHeight, mapStride);
+}
+
+
+uchar qt_pow_rgb_gamma[256];
+uchar qt_pow_rgb_invgamma[256];
+
+uint qt_pow_gamma[256];
+uchar qt_pow_invgamma[2048];
+
+static void qt_alphamapblit_quint16(QRasterBuffer *rasterBuffer,
+ int x, int y, quint32 color,
+ const uchar *map,
+ int mapWidth, int mapHeight, int mapStride,
+ const QClipData *)
+{
+ const quint16 c = qt_colorConvert<quint16, quint32>(color, 0);
+ quint16 *dest = reinterpret_cast<quint16*>(rasterBuffer->scanLine(y)) + x;
+ const int destStride = rasterBuffer->bytesPerLine() / sizeof(quint16);
+
+ while (mapHeight--) {
+ for (int i = 0; i < mapWidth; ++i) {
+ const int coverage = map[i];
+
+ if (coverage == 0) {
+ // nothing
+ } else if (coverage == 255) {
+ dest[i] = c;
+ } else {
+ int ialpha = 255 - coverage;
+ dest[i] = BYTE_MUL_RGB16(c, coverage)
+ + BYTE_MUL_RGB16(dest[i], ialpha);
+ }
+ }
+ dest += destStride;
+ map += mapStride;
+ }
+}
+
+void qt_build_pow_tables() {
+ qreal smoothing = qreal(1.7);
+
+#ifdef Q_WS_MAC
+ // decided by testing a few things on an iMac, should probably get this from the
+ // system...
+ smoothing = qreal(2.0);
+#endif
+
+#ifdef Q_WS_WIN
+ int winSmooth;
+ if (SystemParametersInfo(0x200C /* SPI_GETFONTSMOOTHINGCONTRAST */, 0, &winSmooth, 0))
+ smoothing = winSmooth / qreal(1000.0);
+
+ // Safeguard ourselves against corrupt registry values...
+ if (smoothing > 5 || smoothing < 1)
+ smoothing = qreal(1.4);
+
+#endif
+
+#ifdef Q_WS_X11
+ Q_UNUSED(smoothing);
+ for (int i=0; i<256; ++i) {
+ qt_pow_rgb_gamma[i] = uchar(i);
+ qt_pow_rgb_invgamma[i] = uchar(i);
+ }
+#else
+ for (int i=0; i<256; ++i) {
+ qt_pow_rgb_gamma[i] = uchar(qRound(qPow(i / qreal(255.0), smoothing) * 255));
+ qt_pow_rgb_invgamma[i] = uchar(qRound(qPow(i / qreal(255.), 1 / smoothing) * 255));
+ }
+#endif
+
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+ const qreal gray_gamma = 2.31;
+ for (int i=0; i<256; ++i)
+ qt_pow_gamma[i] = uint(qRound(qPow(i / qreal(255.), gray_gamma) * 2047));
+ for (int i=0; i<2048; ++i)
+ qt_pow_invgamma[i] = uchar(qRound(qPow(i / qreal(2047.0), 1 / gray_gamma) * 255));
+#endif
+}
+
+static inline void rgbBlendPixel(quint32 *dst, int coverage, int sr, int sg, int sb)
+{
+ // Do a gray alphablend...
+ int da = qAlpha(*dst);
+ int dr = qRed(*dst);
+ int dg = qGreen(*dst);
+ int db = qBlue(*dst);
+
+ if (da != 255
+#if defined (Q_WS_WIN)
+ // Work around GDI messing up alpha channel
+ && qRed(*dst) <= da && qBlue(*dst) <= da && qGreen(*dst) <= da
+#endif
+ ) {
+
+ int a = qGray(coverage);
+ sr = qt_div_255(qt_pow_rgb_invgamma[sr] * a);
+ sg = qt_div_255(qt_pow_rgb_invgamma[sg] * a);
+ sb = qt_div_255(qt_pow_rgb_invgamma[sb] * a);
+
+ int ia = 255 - a;
+ dr = qt_div_255(dr * ia);
+ dg = qt_div_255(dg * ia);
+ db = qt_div_255(db * ia);
+
+ *dst = ((a + qt_div_255((255 - a) * da)) << 24)
+ | ((sr + dr) << 16)
+ | ((sg + dg) << 8)
+ | ((sb + db));
+ return;
+ }
+
+ int mr = qRed(coverage);
+ int mg = qGreen(coverage);
+ int mb = qBlue(coverage);
+
+ dr = qt_pow_rgb_gamma[dr];
+ dg = qt_pow_rgb_gamma[dg];
+ db = qt_pow_rgb_gamma[db];
+
+ int nr = qt_div_255((sr - dr) * mr) + dr;
+ int ng = qt_div_255((sg - dg) * mg) + dg;
+ int nb = qt_div_255((sb - db) * mb) + db;
+
+ nr = qt_pow_rgb_invgamma[nr];
+ ng = qt_pow_rgb_invgamma[ng];
+ nb = qt_pow_rgb_invgamma[nb];
+
+ *dst = qRgb(nr, ng, nb);
+}
+
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+static inline void grayBlendPixel(quint32 *dst, int coverage, int sr, int sg, int sb)
+{
+ // Do a gammacorrected gray alphablend...
+ int dr = qRed(*dst);
+ int dg = qGreen(*dst);
+ int db = qBlue(*dst);
+
+ dr = qt_pow_gamma[dr];
+ dg = qt_pow_gamma[dg];
+ db = qt_pow_gamma[db];
+
+ int alpha = coverage;
+ int ialpha = 255 - alpha;
+ int nr = (sr * alpha + ialpha * dr) / 255;
+ int ng = (sg * alpha + ialpha * dg) / 255;
+ int nb = (sb * alpha + ialpha * db) / 255;
+
+ nr = qt_pow_invgamma[nr];
+ ng = qt_pow_invgamma[ng];
+ nb = qt_pow_invgamma[nb];
+
+ *dst = qRgb(nr, ng, nb);
+}
+#endif
+
+static void qt_alphamapblit_quint32(QRasterBuffer *rasterBuffer,
+ int x, int y, quint32 color,
+ const uchar *map,
+ int mapWidth, int mapHeight, int mapStride,
+ const QClipData *clip)
+{
+ const quint32 c = color;
+ const int destStride = rasterBuffer->bytesPerLine() / sizeof(quint32);
+
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+ int sr = qRed(color);
+ int sg = qGreen(color);
+ int sb = qBlue(color);
+
+ sr = qt_pow_gamma[sr];
+ sg = qt_pow_gamma[sg];
+ sb = qt_pow_gamma[sb];
+ bool opaque_src = (qAlpha(color) == 255);
+#endif
+
+ if (!clip) {
+ quint32 *dest = reinterpret_cast<quint32*>(rasterBuffer->scanLine(y)) + x;
+ while (mapHeight--) {
+ for (int i = 0; i < mapWidth; ++i) {
+ const int coverage = map[i];
+
+ if (coverage == 0) {
+ // nothing
+ } else if (coverage == 255) {
+ dest[i] = c;
+ } else {
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+ if (QSysInfo::WindowsVersion >= QSysInfo::WV_XP && opaque_src
+ && qAlpha(dest[i]) == 255) {
+ grayBlendPixel(dest+i, coverage, sr, sg, sb);
+ } else
+#endif
+ {
+ int ialpha = 255 - coverage;
+ dest[i] = INTERPOLATE_PIXEL_255(c, coverage, dest[i], ialpha);
+ }
+ }
+ }
+ dest += destStride;
+ map += mapStride;
+ }
+ } else {
+ int bottom = qMin(y + mapHeight, rasterBuffer->height());
+
+ int top = qMax(y, 0);
+ map += (top - y) * mapStride;
+
+ const_cast<QClipData *>(clip)->initialize();
+ for (int yp = top; yp<bottom; ++yp) {
+ const QClipData::ClipLine &line = clip->m_clipLines[yp];
+
+ quint32 *dest = reinterpret_cast<quint32 *>(rasterBuffer->scanLine(yp));
+
+ for (int i=0; i<line.count; ++i) {
+ const QSpan &clip = line.spans[i];
+
+ int start = qMax<int>(x, clip.x);
+ int end = qMin<int>(x + mapWidth, clip.x + clip.len);
+
+ for (int xp=start; xp<end; ++xp) {
+ const int coverage = map[xp - x];
+
+ if (coverage == 0) {
+ // nothing
+ } else if (coverage == 255) {
+ dest[xp] = c;
+ } else {
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+ if (QSysInfo::WindowsVersion >= QSysInfo::WV_XP && opaque_src
+ && qAlpha(dest[xp]) == 255) {
+ grayBlendPixel(dest+xp, coverage, sr, sg, sb);
+ } else
+#endif
+ {
+ int ialpha = 255 - coverage;
+ dest[xp] = INTERPOLATE_PIXEL_255(c, coverage, dest[xp], ialpha);
+ }
+ }
+
+ } // for (i -> line.count)
+ } // for (yp -> bottom)
+ map += mapStride;
+ }
+ }
+}
+
+static void qt_alphargbblit_quint32(QRasterBuffer *rasterBuffer,
+ int x, int y, quint32 color,
+ const uint *src, int mapWidth, int mapHeight, int srcStride,
+ const QClipData *clip)
+{
+ const quint32 c = color;
+
+ int sr = qRed(color);
+ int sg = qGreen(color);
+ int sb = qBlue(color);
+ int sa = qAlpha(color);
+
+ sr = qt_pow_rgb_gamma[sr];
+ sg = qt_pow_rgb_gamma[sg];
+ sb = qt_pow_rgb_gamma[sb];
+
+ if (sa == 0)
+ return;
+
+ if (!clip) {
+ quint32 *dst = reinterpret_cast<quint32*>(rasterBuffer->scanLine(y)) + x;
+ const int destStride = rasterBuffer->bytesPerLine() / sizeof(quint32);
+ while (mapHeight--) {
+ for (int i = 0; i < mapWidth; ++i) {
+ const uint coverage = src[i];
+ if (coverage == 0xffffffff) {
+ dst[i] = c;
+ } else if (coverage != 0xff000000) {
+ rgbBlendPixel(dst+i, coverage, sr, sg, sb);
+ }
+ }
+
+ dst += destStride;
+ src += srcStride;
+ }
+ } else {
+ int bottom = qMin(y + mapHeight, rasterBuffer->height());
+
+ int top = qMax(y, 0);
+ src += (top - y) * srcStride;
+
+ const_cast<QClipData *>(clip)->initialize();
+ for (int yp = top; yp<bottom; ++yp) {
+ const QClipData::ClipLine &line = clip->m_clipLines[yp];
+
+ quint32 *dst = reinterpret_cast<quint32 *>(rasterBuffer->scanLine(yp));
+
+ for (int i=0; i<line.count; ++i) {
+ const QSpan &clip = line.spans[i];
+
+ int start = qMax<int>(x, clip.x);
+ int end = qMin<int>(x + mapWidth, clip.x + clip.len);
+
+ for (int xp=start; xp<end; ++xp) {
+ const uint coverage = src[xp - x];
+ if (coverage == 0xffffffff) {
+ dst[xp] = c;
+ } else if (coverage != 0xff000000) {
+ rgbBlendPixel(dst+xp, coverage, sr, sg, sb);
+ }
+ }
+ } // for (i -> line.count)
+ src += srcStride;
+ } // for (yp -> bottom)
+
+ }
+}
+
+template <class T>
+inline void qt_rectfill_template(QRasterBuffer *rasterBuffer,
+ int x, int y, int width, int height,
+ quint32 color, T dummy = 0)
+{
+ Q_UNUSED(dummy);
+
+ qt_rectfill<T>(reinterpret_cast<T*>(rasterBuffer->buffer()),
+ qt_colorConvert<T, quint32p>(quint32p::fromRawData(color), 0),
+ x, y, width, height, rasterBuffer->bytesPerLine());
+}
+
+#define QT_RECTFILL(T) \
+ inline static void qt_rectfill_##T(QRasterBuffer *rasterBuffer, \
+ int x, int y, int width, int height, \
+ quint32 color) \
+ { \
+ qt_rectfill_template<T>(rasterBuffer, x, y, width, height, color); \
+ }
+
+QT_RECTFILL(quint32)
+QT_RECTFILL(quint16)
+QT_RECTFILL(qargb8565)
+QT_RECTFILL(qrgb666)
+QT_RECTFILL(qargb6666)
+QT_RECTFILL(qrgb555)
+QT_RECTFILL(qargb8555)
+QT_RECTFILL(qrgb888)
+QT_RECTFILL(qrgb444)
+QT_RECTFILL(qargb4444)
+#undef QT_RECTFILL
+
+inline static void qt_rectfill_nonpremul_quint32(QRasterBuffer *rasterBuffer,
+ int x, int y, int width, int height,
+ quint32 color)
+{
+ qt_rectfill<quint32>(reinterpret_cast<quint32 *>(rasterBuffer->buffer()),
+ INV_PREMUL(color), x, y, width, height, rasterBuffer->bytesPerLine());
+}
+
+
+// Map table for destination image format. Contains function pointers
+// for blends of various types unto the destination
+
+DrawHelper qDrawHelper[QImage::NImageFormats] =
+{
+ // Format_Invalid,
+ { 0, 0, 0, 0, 0, 0 },
+ // Format_Mono,
+ {
+ blend_color_generic,
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans),
+ 0, 0, 0, 0
+ },
+ // Format_MonoLSB,
+ {
+ blend_color_generic,
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans),
+ 0, 0, 0, 0
+ },
+ // Format_Indexed8,
+ {
+ blend_color_generic,
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans),
+ 0, 0, 0, 0
+ },
+ // Format_RGB32,
+ {
+ blend_color_argb,
+ qt_gradient_quint32,
+ qt_bitmapblit_quint32,
+ qt_alphamapblit_quint32,
+ qt_alphargbblit_quint32,
+ qt_rectfill_quint32
+ },
+ // Format_ARGB32,
+ {
+ blend_color_generic,
+ qt_gradient_quint32,
+ qt_bitmapblit_quint32,
+ qt_alphamapblit_quint32,
+ qt_alphargbblit_quint32,
+ qt_rectfill_nonpremul_quint32
+ },
+ // Format_ARGB32_Premultiplied
+ {
+ blend_color_argb,
+ qt_gradient_quint32,
+ qt_bitmapblit_quint32,
+ qt_alphamapblit_quint32,
+ qt_alphargbblit_quint32,
+ qt_rectfill_quint32
+ },
+ // Format_RGB16
+ {
+ blend_color_rgb16,
+ qt_gradient_quint16,
+ qt_bitmapblit_quint16,
+ qt_alphamapblit_quint16,
+ 0,
+ qt_rectfill_quint16
+ },
+ // Format_ARGB8565_Premultiplied
+ {
+ SPANFUNC_POINTER_BLENDCOLOR(qargb8565),
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans),
+ 0, 0, 0,
+ qt_rectfill_qargb8565
+ },
+ // Format_RGB666
+ {
+ SPANFUNC_POINTER_BLENDCOLOR(qrgb666),
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans),
+ 0, 0, 0,
+ qt_rectfill_qrgb666
+ },
+ // Format_ARGB6666_Premultiplied
+ {
+ SPANFUNC_POINTER_BLENDCOLOR(qargb6666),
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans),
+ 0, 0, 0,
+ qt_rectfill_qargb6666
+ },
+ // Format_RGB555
+ {
+ SPANFUNC_POINTER_BLENDCOLOR(qrgb555),
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans),
+ 0, 0, 0,
+ qt_rectfill_qrgb555
+ },
+ // Format_ARGB8555_Premultiplied
+ {
+ SPANFUNC_POINTER_BLENDCOLOR(qargb8555),
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans),
+ 0, 0, 0,
+ qt_rectfill_qargb8555
+ },
+ // Format_RGB888
+ {
+ SPANFUNC_POINTER_BLENDCOLOR(qrgb888),
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans),
+ 0, 0, 0,
+ qt_rectfill_qrgb888
+ },
+ // Format_RGB444
+ {
+ SPANFUNC_POINTER_BLENDCOLOR(qrgb444),
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans),
+ 0, 0, 0,
+ qt_rectfill_qrgb444
+ },
+ // Format_ARGB4444_Premultiplied
+ {
+ SPANFUNC_POINTER_BLENDCOLOR(qargb4444),
+ SPANFUNC_POINTER(blend_src_generic, RegularSpans),
+ 0, 0, 0,
+ qt_rectfill_qargb4444
+ }
+};
+
+#if defined (Q_WS_QWS) && !defined(QT_NO_RASTERCALLBACKS)
+DrawHelper qDrawHelperCallback[QImage::NImageFormats] =
+{
+ // Format_Invalid,
+ { 0, 0, 0, 0, 0, 0 },
+ // Format_Mono,
+ {
+ blend_color_generic_callback,
+ blend_src_generic<CallbackSpans>,
+ 0, 0, 0, 0
+ },
+ // Format_MonoLSB,
+ {
+ blend_color_generic_callback,
+ blend_src_generic<CallbackSpans>,
+ 0, 0, 0, 0
+ },
+ // Format_Indexed8,
+ {
+ blend_color_generic_callback,
+ blend_src_generic<CallbackSpans>,
+ 0, 0, 0, 0
+ },
+ // Format_RGB32,
+ {
+ blend_color_generic_callback,
+ blend_src_generic<CallbackSpans>,
+ 0, 0, 0, 0
+ },
+ // Format_ARGB32,
+ {
+ blend_color_generic_callback,
+ blend_src_generic<CallbackSpans>,
+ 0, 0, 0, 0
+ },
+ // Format_ARGB32_Premultiplied
+ {
+ blend_color_generic_callback,
+ blend_src_generic<CallbackSpans>,
+ 0, 0, 0, 0
+ },
+ // Format_RGB16
+ {
+ blend_color_generic_callback,
+ blend_src_generic<CallbackSpans>,
+ 0, 0, 0, 0
+ },
+ // Format_ARGB8565_Premultiplied
+ {
+ blend_color_generic_callback,
+ blend_src_generic<CallbackSpans>,
+ 0, 0, 0, 0
+ },
+ // Format_RGB666
+ {
+ blend_color_generic_callback,
+ blend_src_generic<CallbackSpans>,
+ 0, 0, 0, 0
+ },
+ // Format_ARGB6666_Premultiplied
+ {
+ blend_color_generic_callback,
+ blend_src_generic<CallbackSpans>,
+ 0, 0, 0, 0
+ },
+ // Format_RGB555
+ {
+ blend_color_generic_callback,
+ blend_src_generic<CallbackSpans>,
+ 0, 0, 0, 0
+ },
+ // Format_ARGB8555_Premultiplied
+ {
+ blend_color_generic_callback,
+ blend_src_generic<CallbackSpans>,
+ 0, 0, 0, 0
+ },
+ // Format_RGB888
+ {
+ blend_color_generic_callback,
+ blend_src_generic<CallbackSpans>,
+ 0, 0, 0, 0
+ },
+ // Format_RGB444
+ {
+ blend_color_generic_callback,
+ blend_src_generic<CallbackSpans>,
+ 0, 0, 0, 0
+ },
+ // Format_ARGB4444_Premultiplied
+ {
+ blend_color_generic_callback,
+ blend_src_generic<CallbackSpans>,
+ 0, 0, 0, 0
+ }
+};
+#endif
+
+
+
+#if defined(Q_CC_MSVC) && !defined(_MIPS_)
+template <class DST, class SRC>
+inline void qt_memfill_template(DST *dest, SRC color, int count)
+{
+ const DST c = qt_colorConvert<DST, SRC>(color, 0);
+ while (count--)
+ *dest++ = c;
+}
+
+#else
+
+template <class DST, class SRC>
+inline void qt_memfill_template(DST *dest, SRC color, int count)
+{
+ const DST c = qt_colorConvert<DST, SRC>(color, 0);
+ int n = (count + 7) / 8;
+ switch (count & 0x07)
+ {
+ case 0: do { *dest++ = c;
+ case 7: *dest++ = c;
+ case 6: *dest++ = c;
+ case 5: *dest++ = c;
+ case 4: *dest++ = c;
+ case 3: *dest++ = c;
+ case 2: *dest++ = c;
+ case 1: *dest++ = c;
+ } while (--n > 0);
+ }
+}
+
+template <>
+inline void qt_memfill_template(quint16 *dest, quint16 value, int count)
+{
+ if (count < 3) {
+ switch (count) {
+ case 2: *dest++ = value;
+ case 1: *dest = value;
+ }
+ return;
+ }
+
+ const int align = (quintptr)(dest) & 0x3;
+ switch (align) {
+ case 2: *dest++ = value; --count;
+ }
+
+ const quint32 value32 = (value << 16) | value;
+ qt_memfill(reinterpret_cast<quint32*>(dest), value32, count / 2);
+ if (count & 0x1)
+ dest[count - 1] = value;
+}
+#endif
+
+static void qt_memfill_quint16(quint16 *dest, quint16 color, int count)
+{
+ qt_memfill_template<quint16, quint16>(dest, color, count);
+}
+
+typedef void (*qt_memfill32_func)(quint32 *dest, quint32 value, int count);
+typedef void (*qt_memfill16_func)(quint16 *dest, quint16 value, int count);
+static void qt_memfill32_setup(quint32 *dest, quint32 value, int count);
+static void qt_memfill16_setup(quint16 *dest, quint16 value, int count);
+
+qt_memfill32_func qt_memfill32 = qt_memfill32_setup;
+qt_memfill16_func qt_memfill16 = qt_memfill16_setup;
+
+void qInitDrawhelperAsm()
+{
+
+ qt_memfill32 = qt_memfill_template<quint32, quint32>;
+ qt_memfill16 = qt_memfill_quint16; //qt_memfill_template<quint16, quint16>;
+
+ CompositionFunction *functionForModeAsm = 0;
+ CompositionFunctionSolid *functionForModeSolidAsm = 0;
+
+ const uint features = qDetectCPUFeatures();
+ if (false) {
+#ifdef QT_HAVE_SSE2
+ } else if (features & SSE2) {
+ qt_memfill32 = qt_memfill32_sse2;
+ qt_memfill16 = qt_memfill16_sse2;
+ qDrawHelper[QImage::Format_RGB32].bitmapBlit = qt_bitmapblit32_sse2;
+ qDrawHelper[QImage::Format_ARGB32].bitmapBlit = qt_bitmapblit32_sse2;
+ qDrawHelper[QImage::Format_ARGB32_Premultiplied].bitmapBlit = qt_bitmapblit32_sse2;
+ qDrawHelper[QImage::Format_RGB16].bitmapBlit = qt_bitmapblit16_sse2;
+#endif
+#ifdef QT_HAVE_SSE
+ } else if (features & SSE) {
+// qt_memfill32 = qt_memfill32_sse;
+ qDrawHelper[QImage::Format_RGB16].bitmapBlit = qt_bitmapblit16_sse;
+#ifdef QT_HAVE_3DNOW
+ if (features & MMX3DNOW) {
+ qt_memfill32 = qt_memfill32_sse3dnow;
+ qDrawHelper[QImage::Format_RGB16].bitmapBlit = qt_bitmapblit16_sse3dnow;
+ }
+#endif
+#endif // SSE
+ }
+#ifdef QT_HAVE_MMX
+ if (features & MMX) {
+ functionForModeAsm = qt_functionForMode_MMX;
+
+ functionForModeSolidAsm = qt_functionForModeSolid_MMX;
+ qDrawHelper[QImage::Format_ARGB32_Premultiplied].blendColor = qt_blend_color_argb_mmx;
+#ifdef QT_HAVE_3DNOW
+ if (features & MMX3DNOW) {
+ functionForModeAsm = qt_functionForMode_MMX3DNOW;
+ functionForModeSolidAsm = qt_functionForModeSolid_MMX3DNOW;
+ qDrawHelper[QImage::Format_ARGB32_Premultiplied].blendColor = qt_blend_color_argb_mmx3dnow;
+ }
+#endif // 3DNOW
+
+ extern void qt_blend_rgb32_on_rgb32_mmx(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha);
+ extern void qt_blend_argb32_on_argb32_mmx(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha);
+
+ qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_mmx;
+ qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_mmx;
+ qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_mmx;
+ qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_mmx;
+
+ }
+#endif // MMX
+
+#ifdef QT_HAVE_SSE
+ if (features & SSE) {
+ extern void qt_blend_rgb32_on_rgb32_sse(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha);
+ extern void qt_blend_argb32_on_argb32_sse(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha);
+
+ qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_sse;
+ qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_sse;
+ qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_sse;
+ qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_sse;
+ }
+#endif // SSE
+
+#ifdef QT_HAVE_SSE2
+ if (features & SSE2) {
+ extern void qt_blend_rgb32_on_rgb32_sse2(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha);
+ extern void qt_blend_argb32_on_argb32_sse2(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha);
+
+ qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_sse2;
+ qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_sse2;
+ qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_sse2;
+ qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_sse2;
+ }
+
+#ifdef QT_HAVE_SSSE3
+ if (features & SSSE3) {
+ extern void qt_blend_argb32_on_argb32_ssse3(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha);
+
+ qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_ssse3;
+ qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_ssse3;
+ }
+#endif // SSSE3
+
+#endif // SSE2
+
+#ifdef QT_HAVE_SSE
+ if (features & SSE) {
+ functionForModeAsm = qt_functionForMode_SSE;
+ functionForModeSolidAsm = qt_functionForModeSolid_SSE;
+ qDrawHelper[QImage::Format_ARGB32_Premultiplied].blendColor = qt_blend_color_argb_sse;
+#ifdef QT_HAVE_3DNOW
+ if (features & MMX3DNOW) {
+ functionForModeAsm = qt_functionForMode_SSE3DNOW;
+ functionForModeSolidAsm = qt_functionForModeSolid_SSE3DNOW;
+ qDrawHelper[QImage::Format_ARGB32_Premultiplied].blendColor = qt_blend_color_argb_sse3dnow;
+ }
+#endif // 3DNOW
+
+
+#ifdef QT_HAVE_SSE2
+ if (features & SSE2) {
+ extern void QT_FASTCALL comp_func_SourceOver_sse2(uint *destPixels,
+ const uint *srcPixels,
+ int length,
+ uint const_alpha);
+ extern void QT_FASTCALL comp_func_solid_SourceOver_sse2(uint *destPixels, int length, uint color, uint const_alpha);
+ extern void QT_FASTCALL comp_func_Plus_sse2(uint *dst, const uint *src, int length, uint const_alpha);
+ extern void QT_FASTCALL comp_func_Source_sse2(uint *dst, const uint *src, int length, uint const_alpha);
+
+ functionForModeAsm[0] = comp_func_SourceOver_sse2;
+ functionForModeAsm[QPainter::CompositionMode_Source] = comp_func_Source_sse2;
+ functionForModeAsm[QPainter::CompositionMode_Plus] = comp_func_Plus_sse2;
+ functionForModeSolidAsm[0] = comp_func_solid_SourceOver_sse2;
+ }
+#endif
+ }
+#elif defined(QT_HAVE_SSE2)
+ // this is the special case when SSE2 is usable but MMX/SSE is not usable (e.g.: Windows x64 + visual studio)
+ if (features & SSE2) {
+ functionForModeAsm = qt_functionForMode_onlySSE2;
+ functionForModeSolidAsm = qt_functionForModeSolid_onlySSE2;
+ }
+#endif
+
+#ifdef QT_HAVE_IWMMXT
+ if (features & IWMMXT) {
+ functionForModeAsm = qt_functionForMode_IWMMXT;
+ functionForModeSolidAsm = qt_functionForModeSolid_IWMMXT;
+ qDrawHelper[QImage::Format_ARGB32_Premultiplied].blendColor = qt_blend_color_argb_iwmmxt;
+ }
+#endif // IWMMXT
+
+#if defined(QT_HAVE_ARM_SIMD)
+ qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_arm_simd;
+ qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_arm_simd;
+ qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_arm_simd;
+ qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_arm_simd;
+#elif defined(QT_HAVE_NEON)
+ if (features & NEON) {
+ qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_neon;
+ qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_neon;
+ qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_neon;
+ qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_neon;
+ qBlendFunctions[QImage::Format_RGB16][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_rgb16_neon;
+ qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB16] = qt_blend_rgb16_on_argb32_neon;
+ qBlendFunctions[QImage::Format_RGB16][QImage::Format_RGB16] = qt_blend_rgb16_on_rgb16_neon;
+
+ qScaleFunctions[QImage::Format_RGB16][QImage::Format_ARGB32_Premultiplied] = qt_scale_image_argb32_on_rgb16_neon;
+ qScaleFunctions[QImage::Format_RGB16][QImage::Format_RGB16] = qt_scale_image_rgb16_on_rgb16_neon;
+
+ qTransformFunctions[QImage::Format_RGB16][QImage::Format_ARGB32_Premultiplied] = qt_transform_image_argb32_on_rgb16_neon;
+ qTransformFunctions[QImage::Format_RGB16][QImage::Format_RGB16] = qt_transform_image_rgb16_on_rgb16_neon;
+
+ qDrawHelper[QImage::Format_RGB16].alphamapBlit = qt_alphamapblit_quint16_neon;
+
+ functionForMode_C[QPainter::CompositionMode_SourceOver] = qt_blend_argb32_on_argb32_scanline_neon;
+ functionForModeSolid_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_neon;
+ functionForMode_C[QPainter::CompositionMode_Plus] = comp_func_Plus_neon;
+ destFetchProc[QImage::Format_RGB16] = qt_destFetchRGB16_neon;
+ destStoreProc[QImage::Format_RGB16] = qt_destStoreRGB16_neon;
+
+ qMemRotateFunctions[QImage::Format_RGB16][0] = qt_memrotate90_16_neon;
+ qMemRotateFunctions[QImage::Format_RGB16][2] = qt_memrotate270_16_neon;
+ qt_memfill32 = qt_memfill32_neon;
+ }
+#endif
+
+ if (functionForModeSolidAsm) {
+ const int destinationMode = QPainter::CompositionMode_Destination;
+ functionForModeSolidAsm[destinationMode] = functionForModeSolid_C[destinationMode];
+
+ // use the default qdrawhelper implementation for the
+ // extended composition modes
+ for (int mode = 12; mode < 24; ++mode)
+ functionForModeSolidAsm[mode] = functionForModeSolid_C[mode];
+
+ functionForModeSolid = functionForModeSolidAsm;
+ }
+ if (functionForModeAsm)
+ functionForMode = functionForModeAsm;
+
+ qt_build_pow_tables();
+}
+
+static void qt_memfill32_setup(quint32 *dest, quint32 value, int count)
+{
+ qInitDrawhelperAsm();
+ qt_memfill32(dest, value, count);
+}
+
+static void qt_memfill16_setup(quint16 *dest, quint16 value, int count)
+{
+ qInitDrawhelperAsm();
+ qt_memfill16(dest, value, count);
+}
+
+#ifdef QT_QWS_DEPTH_GENERIC
+
+int qrgb::bpp = 0;
+int qrgb::len_red = 0;
+int qrgb::len_green = 0;
+int qrgb::len_blue = 0;
+int qrgb::len_alpha = 0;
+int qrgb::off_red = 0;
+int qrgb::off_green = 0;
+int qrgb::off_blue = 0;
+int qrgb::off_alpha = 0;
+
+template <typename SRC>
+Q_STATIC_TEMPLATE_FUNCTION inline void qt_rectconvert_rgb(qrgb *dest, const SRC *src,
+ int x, int y, int width, int height,
+ int dstStride, int srcStride)
+{
+ quint8 *dest8 = reinterpret_cast<quint8*>(dest)
+ + y * dstStride + x * qrgb::bpp;
+
+ srcStride = srcStride / sizeof(SRC) - width;
+ dstStride -= (width * qrgb::bpp);
+
+ for (int j = 0; j < height; ++j) {
+ for (int i = 0; i < width; ++i) {
+ const quint32 v = qt_convertToRgb<SRC>(*src++);
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+ for (int j = qrgb::bpp - 1; j >= 0; --j)
+ *dest8++ = (v >> (8 * j)) & 0xff;
+#else
+ for (int j = 0; j < qrgb::bpp; ++j)
+ *dest8++ = (v >> (8 * j)) & 0xff;
+#endif
+ }
+
+ dest8 += dstStride;
+ src += srcStride;
+ }
+}
+
+template <>
+void qt_rectconvert(qrgb *dest, const quint32 *src,
+ int x, int y, int width, int height,
+ int dstStride, int srcStride)
+{
+ qt_rectconvert_rgb<quint32>(dest, src, x, y, width, height,
+ dstStride, srcStride);
+}
+
+template <>
+void qt_rectconvert(qrgb *dest, const quint16 *src,
+ int x, int y, int width, int height,
+ int dstStride, int srcStride)
+{
+ qt_rectconvert_rgb<quint16>(dest, src, x, y, width, height,
+ dstStride, srcStride);
+}
+
+#endif // QT_QWS_DEPTH_GENERIC
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qdrawhelper_arm_simd.cpp b/src/gui/painting/qdrawhelper_arm_simd.cpp
new file mode 100644
index 0000000000..af9854c03b
--- /dev/null
+++ b/src/gui/painting/qdrawhelper_arm_simd.cpp
@@ -0,0 +1,332 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdrawhelper_arm_simd_p.h"
+
+#include <private/qpaintengine_raster_p.h>
+#include <private/qblendfunctions_p.h>
+
+#ifdef QT_HAVE_ARM_SIMD
+
+#if defined(Q_OS_SYMBIAN)
+#if !defined(__SWITCH_TO_ARM)
+#ifdef __MARM_THUMB__
+#ifndef __ARMCC__
+#define __SWITCH_TO_ARM asm("push {r0} ");\
+ asm("add r0, pc, #4 ");\
+ asm("bx r0 ");\
+ asm("nop ");\
+ asm(".align 2 ");\
+ asm(".code 32 ");\
+ asm("ldr r0, [sp], #4 ")
+#define __END_ARM asm(".code 16 ")
+#else
+#define __SWITCH_TO_ARM asm(".code 32 ");
+#define __END_ARM
+#endif // __ARMCC__
+#else
+#define __SWITCH_TO_ARM
+#define __END_ARM
+#endif //__MARM_THUMB__
+#endif
+#endif
+
+#if defined(Q_OS_SYMBIAN) && defined(Q_CC_RVCT)
+__asm void qt_blend_argb32_on_argb32_arm_simd(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha)
+{
+#ifndef __ARMCC__
+ __SWITCH_TO_ARM;
+#else
+ CODE32
+#endif // __ARMCC__
+
+ stmfd sp!, {r4-r12, r14}
+
+ // read arguments off the stack
+ add r8, sp, #10 * 4
+ ldmia r8, {r4-r6}
+
+ // adjust dbpl and sbpl
+ mov r14, #4
+ mul r14, r4, r14
+ sub r1, r1, r14
+ sub r3, r3, r14
+
+ // load 0xFF00FF00 to r12
+ mov r12, #0xFF000000
+ add r12, r12, #0xFF00
+
+ // load 0x800080 to r14
+ mov r14, #0x800000
+ add r14, r14, #0x80
+
+ /*
+ Registers:
+ r0 dst
+ r1 dbpl
+ r2 src
+ r3 sbpl
+ r4 w
+ r5 h
+ r6 const_alpha
+ r12 0xFF0000
+ r14 0x800080
+ */
+
+ cmp r6, #256 //test if we have fully opaque constant alpha value
+ bne argb32constalpha // branch if not
+
+argb32_next_row
+
+ mov r7, r4
+
+argb32_next_pixel
+
+ ldr r8, [r2], #4 // load src pixel
+
+ // Negate r8 and extract src alpha
+ mvn r11, r8 // bitwise not
+ uxtb r11, r11, ror #24
+
+ cmp r11, #0 // test for full src opacity (negated)
+ beq argb32_no_blend
+
+ cmp r11, #255 // test for full src transparency (negated)
+ addeq r0, #4
+ beq argb32_nop
+
+ ldr r9, [r0] // load dst pixel
+
+ // blend
+ uxtb16 r10, r9
+ uxtb16 r6, r9, ror #8
+ mla r10, r11, r10, r14
+ mla r9, r6, r11, r14
+ uxtab16 r10, r10, r10, ror #8
+ uxtab16 r9, r9, r9, ror #8
+ and r9, r9, r12
+ uxtab16 r10, r9, r10, ror #8
+
+ uqadd8 r8, r10, r8
+
+argb32_no_blend
+
+ str r8, [r0], #4
+
+argb32_nop
+
+ subs r7, r7, #1
+ bgt argb32_next_pixel
+
+ add r0, r0, r1 // dest = dest + dbpl
+ add r2, r2, r3 // src = src + sbpl
+
+ subs r5, r5, #1
+ bgt argb32_next_row
+
+ b argb32_blend_exit
+
+argb32constalpha
+
+ cmp r6, #0
+ beq argb32_blend_exit
+
+ ; const_alpha = (const_alpha * 255) >> 8;
+ mov r11, #255
+ mul r6, r6, r11
+ mov r11, r6, lsr #8
+
+argb32constalpha_next_row
+
+ mov r7, r4
+
+argb32constalpha_next_pixel
+
+ ldr r9, [r2], #4 // load src pixel
+
+ // blend
+ uxtb16 r10, r9
+ uxtb16 r6, r9, ror #8
+ mla r10, r11, r10, r14
+ mla r9, r6, r11, r14
+ uxtab16 r10, r10, r10, ror #8
+ uxtab16 r9, r9, r9, ror #8
+ and r9, r9, r12
+ uxtab16 r8, r9, r10, ror #8
+
+ ldr r9, [r0] // load dst pixel
+
+ // blend
+ uxtb16 r10, r9
+ uxtb16 r6, r9, ror #8
+
+ // Negate r8 and extract src alpha
+ mvn r9, r8 // bitwise not
+ uxtb r9, r9, ror #24
+
+ mla r10, r9, r10, r14
+ mla r9, r6, r9, r14
+ uxtab16 r10, r10, r10, ror #8
+ uxtab16 r9, r9, r9, ror #8
+ and r9, r9, r12
+ uxtab16 r10, r9, r10, ror #8
+
+ uqadd8 r8, r10, r8
+
+ str r8, [r0], #4
+
+ subs r7, r7, #1
+ bgt argb32constalpha_next_pixel
+
+ add r0, r0, r1 // dest = dest + dbpl
+ add r2, r2, r3 // src = src + sbpl
+
+ subs r5, r5, #1
+ bgt argb32constalpha_next_row
+
+argb32_blend_exit
+
+ // Restore registers
+ ldmfd sp!, {r4-r12, lr}
+ bx lr
+
+ __END_ARM
+}
+
+void qt_blend_rgb32_on_rgb32_arm_simd(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha)
+{
+ if (const_alpha != 256) {
+ qt_blend_argb32_on_argb32_arm_simd(destPixels, dbpl, srcPixels, sbpl, w, h, const_alpha);
+ return;
+ }
+
+ const uint *src = (const uint *) srcPixels;
+ uint *dst = (uint *) destPixels;
+ if (w <= 64) {
+ for (int y=0; y<h; ++y) {
+ qt_memconvert(dst, src, w);
+ dst = (quint32 *)(((uchar *) dst) + dbpl);
+ src = (const quint32 *)(((const uchar *) src) + sbpl);
+ }
+ } else {
+ int len = w * 4;
+ for (int y=0; y<h; ++y) {
+ memcpy(dst, src, len);
+ dst = (quint32 *)(((uchar *) dst) + dbpl);
+ src = (const quint32 *)(((const uchar *) src) + sbpl);
+ }
+ }
+}
+
+#else // defined(Q_OS_SYMBIAN) && defined(Q_CC_RVCT)
+
+// TODO: add GNU assembler instructions and support for other platforms.
+// Default to C code for now
+
+void qt_blend_argb32_on_argb32_arm_simd(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha)
+{
+ const uint *src = (const uint *) srcPixels;
+ uint *dst = (uint *) destPixels;
+ if (const_alpha == 256) {
+ for (int y=0; y<h; ++y) {
+ for (int x=0; x<w; ++x) {
+ uint s = src[x];
+ if (s >= 0xff000000)
+ dst[x] = s;
+ else if (s != 0)
+ dst[x] = s + BYTE_MUL(dst[x], qAlpha(~s));
+ }
+ dst = (quint32 *)(((uchar *) dst) + dbpl);
+ src = (const quint32 *)(((const uchar *) src) + sbpl);
+ }
+ } else if (const_alpha != 0) {
+ const_alpha = (const_alpha * 255) >> 8;
+ for (int y=0; y<h; ++y) {
+ for (int x=0; x<w; ++x) {
+ uint s = BYTE_MUL(src[x], const_alpha);
+ dst[x] = s + BYTE_MUL(dst[x], qAlpha(~s));
+ }
+ dst = (quint32 *)(((uchar *) dst) + dbpl);
+ src = (const quint32 *)(((const uchar *) src) + sbpl);
+ }
+ }
+}
+
+void qt_blend_rgb32_on_rgb32_arm_simd(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha)
+{
+ if (const_alpha != 256) {
+ qt_blend_argb32_on_argb32_arm_simd(destPixels, dbpl, srcPixels, sbpl, w, h, const_alpha);
+ return;
+ }
+
+ const uint *src = (const uint *) srcPixels;
+ uint *dst = (uint *) destPixels;
+ if (w <= 64) {
+ for (int y=0; y<h; ++y) {
+ qt_memconvert(dst, src, w);
+ dst = (quint32 *)(((uchar *) dst) + dbpl);
+ src = (const quint32 *)(((const uchar *) src) + sbpl);
+ }
+ } else {
+ int len = w * 4;
+ for (int y=0; y<h; ++y) {
+ memcpy(dst, src, len);
+ dst = (quint32 *)(((uchar *) dst) + dbpl);
+ src = (const quint32 *)(((const uchar *) src) + sbpl);
+ }
+ }
+}
+
+#endif
+
+#endif // QT_HAVE_ARMV_SIMD
diff --git a/src/gui/painting/qdrawhelper_arm_simd_p.h b/src/gui/painting/qdrawhelper_arm_simd_p.h
new file mode 100644
index 0000000000..975a0244d2
--- /dev/null
+++ b/src/gui/painting/qdrawhelper_arm_simd_p.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDRAWHELPER_ARM_SIMD_P_H
+#define QDRAWHELPER_ARM_SIMD_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qdrawhelper_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#if defined(QT_HAVE_ARM_SIMD)
+
+void qt_blend_argb32_on_argb32_arm_simd(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha);
+
+void qt_blend_rgb32_on_rgb32_arm_simd(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha);
+
+#endif // QT_HAVE_ARM_SIMD
+
+QT_END_NAMESPACE
+
+#endif // QDRAWHELPER_ARM_SIMD_P_H
diff --git a/src/gui/painting/qdrawhelper_iwmmxt.cpp b/src/gui/painting/qdrawhelper_iwmmxt.cpp
new file mode 100644
index 0000000000..969224f1c8
--- /dev/null
+++ b/src/gui/painting/qdrawhelper_iwmmxt.cpp
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifdef QT_HAVE_IWMMXT
+
+#include <mmintrin.h>
+#if defined(Q_OS_WINCE)
+# include "qplatformdefs.h"
+#endif
+#if !defined(__IWMMXT__) && !defined(Q_OS_WINCE)
+# include <xmmintrin.h>
+#elif defined(Q_OS_WINCE_STD) && defined(_X86_)
+# pragma warning(disable: 4391)
+# include <xmmintrin.h>
+#endif
+
+#include <private/qdrawhelper_sse_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef _MM_SHUFFLE
+#define _MM_SHUFFLE(fp3,fp2,fp1,fp0) \
+ (((fp3) << 6) | ((fp2) << 4) | ((fp1) << 2) | (fp0))
+#endif
+
+struct QIWMMXTIntrinsics : public QMMXCommonIntrinsics
+{
+ static inline m64 alpha(m64 x) {
+ return _mm_shuffle_pi16 (x, _MM_SHUFFLE(3, 3, 3, 3));
+ }
+
+ static inline m64 _load_alpha(uint x, const m64 &mmx_0x0000) {
+ m64 t = _mm_unpacklo_pi8(_mm_cvtsi32_si64(x), mmx_0x0000);
+ return _mm_shuffle_pi16(t, _MM_SHUFFLE(0, 0, 0, 0));
+ }
+
+ static inline void end() {
+ }
+};
+
+CompositionFunctionSolid qt_functionForModeSolid_IWMMXT[numCompositionFunctions] = {
+ comp_func_solid_SourceOver<QIWMMXTIntrinsics>,
+ comp_func_solid_DestinationOver<QIWMMXTIntrinsics>,
+ comp_func_solid_Clear<QIWMMXTIntrinsics>,
+ comp_func_solid_Source<QIWMMXTIntrinsics>,
+ 0,
+ comp_func_solid_SourceIn<QIWMMXTIntrinsics>,
+ comp_func_solid_DestinationIn<QIWMMXTIntrinsics>,
+ comp_func_solid_SourceOut<QIWMMXTIntrinsics>,
+ comp_func_solid_DestinationOut<QIWMMXTIntrinsics>,
+ comp_func_solid_SourceAtop<QIWMMXTIntrinsics>,
+ comp_func_solid_DestinationAtop<QIWMMXTIntrinsics>,
+ comp_func_solid_XOR<QIWMMXTIntrinsics>,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // svg 1.2 modes
+ rasterop_solid_SourceOrDestination<QIWMMXTIntrinsics>,
+ rasterop_solid_SourceAndDestination<QIWMMXTIntrinsics>,
+ rasterop_solid_SourceXorDestination<QIWMMXTIntrinsics>,
+ rasterop_solid_NotSourceAndNotDestination<QIWMMXTIntrinsics>,
+ rasterop_solid_NotSourceOrNotDestination<QIWMMXTIntrinsics>,
+ rasterop_solid_NotSourceXorDestination<QIWMMXTIntrinsics>,
+ rasterop_solid_NotSource<QIWMMXTIntrinsics>,
+ rasterop_solid_NotSourceAndDestination<QIWMMXTIntrinsics>,
+ rasterop_solid_SourceAndNotDestination<QIWMMXTIntrinsics>
+};
+
+CompositionFunction qt_functionForMode_IWMMXT[] = {
+ comp_func_SourceOver<QIWMMXTIntrinsics>,
+ comp_func_DestinationOver<QIWMMXTIntrinsics>,
+ comp_func_Clear<QIWMMXTIntrinsics>,
+ comp_func_Source<QIWMMXTIntrinsics>,
+ comp_func_Destination,
+ comp_func_SourceIn<QIWMMXTIntrinsics>,
+ comp_func_DestinationIn<QIWMMXTIntrinsics>,
+ comp_func_SourceOut<QIWMMXTIntrinsics>,
+ comp_func_DestinationOut<QIWMMXTIntrinsics>,
+ comp_func_SourceAtop<QIWMMXTIntrinsics>,
+ comp_func_DestinationAtop<QIWMMXTIntrinsics>,
+ comp_func_XOR<QIWMMXTIntrinsics>,
+ comp_func_Plus,
+ comp_func_Multiply,
+ comp_func_Screen,
+ comp_func_Overlay,
+ comp_func_Darken,
+ comp_func_Lighten,
+ comp_func_ColorDodge,
+ comp_func_ColorBurn,
+ comp_func_HardLight,
+ comp_func_SoftLight,
+ comp_func_Difference,
+ comp_func_Exclusion,
+ rasterop_SourceOrDestination,
+ rasterop_SourceAndDestination,
+ rasterop_SourceXorDestination,
+ rasterop_NotSourceAndNotDestination,
+ rasterop_NotSourceOrNotDestination,
+ rasterop_NotSourceXorDestination,
+ rasterop_NotSource,
+ rasterop_NotSourceAndDestination,
+ rasterop_SourceAndNotDestination
+};
+
+void qt_blend_color_argb_iwmmxt(int count, const QSpan *spans, void *userData)
+{
+ qt_blend_color_argb_x86<QIWMMXTIntrinsics>(count, spans, userData,
+ (CompositionFunctionSolid*)qt_functionForModeSolid_IWMMXT);
+}
+
+#endif // QT_HAVE_IWMMXT
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qdrawhelper_mmx.cpp b/src/gui/painting/qdrawhelper_mmx.cpp
new file mode 100644
index 0000000000..dd1f3d41e3
--- /dev/null
+++ b/src/gui/painting/qdrawhelper_mmx.cpp
@@ -0,0 +1,155 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qdrawhelper_p.h>
+
+#if defined(QT_HAVE_MMX)
+
+#include <private/qdrawhelper_mmx_p.h>
+
+QT_BEGIN_NAMESPACE
+
+CompositionFunctionSolid qt_functionForModeSolid_MMX[numCompositionFunctions] = {
+ comp_func_solid_SourceOver<QMMXIntrinsics>,
+ comp_func_solid_DestinationOver<QMMXIntrinsics>,
+ comp_func_solid_Clear<QMMXIntrinsics>,
+ comp_func_solid_Source<QMMXIntrinsics>,
+ 0,
+ comp_func_solid_SourceIn<QMMXIntrinsics>,
+ comp_func_solid_DestinationIn<QMMXIntrinsics>,
+ comp_func_solid_SourceOut<QMMXIntrinsics>,
+ comp_func_solid_DestinationOut<QMMXIntrinsics>,
+ comp_func_solid_SourceAtop<QMMXIntrinsics>,
+ comp_func_solid_DestinationAtop<QMMXIntrinsics>,
+ comp_func_solid_XOR<QMMXIntrinsics>,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // svg 1.2 modes
+ rasterop_solid_SourceOrDestination<QMMXIntrinsics>,
+ rasterop_solid_SourceAndDestination<QMMXIntrinsics>,
+ rasterop_solid_SourceXorDestination<QMMXIntrinsics>,
+ rasterop_solid_NotSourceAndNotDestination<QMMXIntrinsics>,
+ rasterop_solid_NotSourceOrNotDestination<QMMXIntrinsics>,
+ rasterop_solid_NotSourceXorDestination<QMMXIntrinsics>,
+ rasterop_solid_NotSource<QMMXIntrinsics>,
+ rasterop_solid_NotSourceAndDestination<QMMXIntrinsics>,
+ rasterop_solid_SourceAndNotDestination<QMMXIntrinsics>
+};
+
+CompositionFunction qt_functionForMode_MMX[numCompositionFunctions] = {
+ comp_func_SourceOver<QMMXIntrinsics>,
+ comp_func_DestinationOver<QMMXIntrinsics>,
+ comp_func_Clear<QMMXIntrinsics>,
+ comp_func_Source<QMMXIntrinsics>,
+ comp_func_Destination,
+ comp_func_SourceIn<QMMXIntrinsics>,
+ comp_func_DestinationIn<QMMXIntrinsics>,
+ comp_func_SourceOut<QMMXIntrinsics>,
+ comp_func_DestinationOut<QMMXIntrinsics>,
+ comp_func_SourceAtop<QMMXIntrinsics>,
+ comp_func_DestinationAtop<QMMXIntrinsics>,
+ comp_func_XOR<QMMXIntrinsics>,
+ comp_func_Plus,
+ comp_func_Multiply,
+ comp_func_Screen,
+ comp_func_Overlay,
+ comp_func_Darken,
+ comp_func_Lighten,
+ comp_func_ColorDodge,
+ comp_func_ColorBurn,
+ comp_func_HardLight,
+ comp_func_SoftLight,
+ comp_func_Difference,
+ comp_func_Exclusion,
+ rasterop_SourceOrDestination,
+ rasterop_SourceAndDestination,
+ rasterop_SourceXorDestination,
+ rasterop_NotSourceAndNotDestination,
+ rasterop_NotSourceOrNotDestination,
+ rasterop_NotSourceXorDestination,
+ rasterop_NotSource,
+ rasterop_NotSourceAndDestination,
+ rasterop_SourceAndNotDestination
+};
+
+void qt_blend_color_argb_mmx(int count, const QSpan *spans, void *userData)
+{
+ qt_blend_color_argb_x86<QMMXIntrinsics>(count, spans, userData,
+ (CompositionFunctionSolid*)qt_functionForModeSolid_MMX);
+}
+
+
+void qt_blend_argb32_on_argb32_mmx(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha)
+{
+ const uint *src = (const uint *) srcPixels;
+ uint *dst = (uint *) destPixels;
+
+ uint ca = const_alpha - 1;
+
+ for (int y=0; y<h; ++y) {
+ comp_func_SourceOver<QMMXIntrinsics>(dst, src, w, ca);
+ dst = (quint32 *)(((uchar *) dst) + dbpl);
+ src = (const quint32 *)(((const uchar *) src) + sbpl);
+ }
+}
+
+void qt_blend_rgb32_on_rgb32_mmx(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha)
+{
+ const uint *src = (const uint *) srcPixels;
+ uint *dst = (uint *) destPixels;
+
+ uint ca = const_alpha - 1;
+
+ for (int y=0; y<h; ++y) {
+ comp_func_Source<QMMXIntrinsics>(dst, src, w, ca);
+ dst = (quint32 *)(((uchar *) dst) + dbpl);
+ src = (const quint32 *)(((const uchar *) src) + sbpl);
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_HAVE_MMX
+
diff --git a/src/gui/painting/qdrawhelper_mmx3dnow.cpp b/src/gui/painting/qdrawhelper_mmx3dnow.cpp
new file mode 100644
index 0000000000..12dde9e5bc
--- /dev/null
+++ b/src/gui/painting/qdrawhelper_mmx3dnow.cpp
@@ -0,0 +1,128 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qdrawhelper_x86_p.h>
+
+#ifdef QT_HAVE_3DNOW
+
+#include <private/qdrawhelper_mmx_p.h>
+#include <mm3dnow.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QMMX3DNOWIntrinsics : public QMMXCommonIntrinsics
+{
+ static inline void end() {
+ _m_femms();
+ }
+};
+
+CompositionFunctionSolid qt_functionForModeSolid_MMX3DNOW[numCompositionFunctions] = {
+ comp_func_solid_SourceOver<QMMX3DNOWIntrinsics>,
+ comp_func_solid_DestinationOver<QMMX3DNOWIntrinsics>,
+ comp_func_solid_Clear<QMMX3DNOWIntrinsics>,
+ comp_func_solid_Source<QMMX3DNOWIntrinsics>,
+ 0,
+ comp_func_solid_SourceIn<QMMX3DNOWIntrinsics>,
+ comp_func_solid_DestinationIn<QMMX3DNOWIntrinsics>,
+ comp_func_solid_SourceOut<QMMX3DNOWIntrinsics>,
+ comp_func_solid_DestinationOut<QMMX3DNOWIntrinsics>,
+ comp_func_solid_SourceAtop<QMMX3DNOWIntrinsics>,
+ comp_func_solid_DestinationAtop<QMMX3DNOWIntrinsics>,
+ comp_func_solid_XOR<QMMX3DNOWIntrinsics>,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // svg 1.2 modes
+ rasterop_solid_SourceOrDestination<QMMX3DNOWIntrinsics>,
+ rasterop_solid_SourceAndDestination<QMMX3DNOWIntrinsics>,
+ rasterop_solid_SourceXorDestination<QMMX3DNOWIntrinsics>,
+ rasterop_solid_NotSourceAndNotDestination<QMMX3DNOWIntrinsics>,
+ rasterop_solid_NotSourceOrNotDestination<QMMX3DNOWIntrinsics>,
+ rasterop_solid_NotSourceXorDestination<QMMX3DNOWIntrinsics>,
+ rasterop_solid_NotSource<QMMX3DNOWIntrinsics>,
+ rasterop_solid_NotSourceAndDestination<QMMX3DNOWIntrinsics>,
+ rasterop_solid_SourceAndNotDestination<QMMX3DNOWIntrinsics>
+};
+
+CompositionFunction qt_functionForMode_MMX3DNOW[numCompositionFunctions] = {
+ comp_func_SourceOver<QMMX3DNOWIntrinsics>,
+ comp_func_DestinationOver<QMMX3DNOWIntrinsics>,
+ comp_func_Clear<QMMX3DNOWIntrinsics>,
+ comp_func_Source<QMMX3DNOWIntrinsics>,
+ comp_func_Destination,
+ comp_func_SourceIn<QMMX3DNOWIntrinsics>,
+ comp_func_DestinationIn<QMMX3DNOWIntrinsics>,
+ comp_func_SourceOut<QMMX3DNOWIntrinsics>,
+ comp_func_DestinationOut<QMMX3DNOWIntrinsics>,
+ comp_func_SourceAtop<QMMX3DNOWIntrinsics>,
+ comp_func_DestinationAtop<QMMX3DNOWIntrinsics>,
+ comp_func_XOR<QMMX3DNOWIntrinsics>,
+ comp_func_Plus,
+ comp_func_Multiply,
+ comp_func_Screen,
+ comp_func_Overlay,
+ comp_func_Darken,
+ comp_func_Lighten,
+ comp_func_ColorDodge,
+ comp_func_ColorBurn,
+ comp_func_HardLight,
+ comp_func_SoftLight,
+ comp_func_Difference,
+ comp_func_Exclusion,
+ rasterop_SourceOrDestination,
+ rasterop_SourceAndDestination,
+ rasterop_SourceXorDestination,
+ rasterop_NotSourceAndNotDestination,
+ rasterop_NotSourceOrNotDestination,
+ rasterop_NotSourceXorDestination,
+ rasterop_NotSource,
+ rasterop_NotSourceAndDestination,
+ rasterop_SourceAndNotDestination
+};
+
+void qt_blend_color_argb_mmx3dnow(int count, const QSpan *spans, void *userData)
+{
+ qt_blend_color_argb_x86<QMMX3DNOWIntrinsics>(count, spans, userData,
+ (CompositionFunctionSolid*)qt_functionForModeSolid_MMX3DNOW);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_HAVE_3DNOW
+
diff --git a/src/gui/painting/qdrawhelper_mmx_p.h b/src/gui/painting/qdrawhelper_mmx_p.h
new file mode 100644
index 0000000000..06e945b882
--- /dev/null
+++ b/src/gui/painting/qdrawhelper_mmx_p.h
@@ -0,0 +1,892 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDRAWHELPER_MMX_P_H
+#define QDRAWHELPER_MMX_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qdrawhelper_p.h>
+#include <private/qdrawhelper_x86_p.h>
+#include <private/qpaintengine_raster_p.h>
+
+#ifdef QT_HAVE_MMX
+#include <mmintrin.h>
+#endif
+
+#define C_FF const m64 mmx_0x00ff = _mm_set1_pi16(0xff)
+#define C_80 const m64 mmx_0x0080 = _mm_set1_pi16(0x80)
+#define C_00 const m64 mmx_0x0000 = _mm_setzero_si64()
+
+#ifdef Q_CC_MSVC
+# pragma warning(disable: 4799) // No EMMS at end of function
+#endif
+
+typedef __m64 m64;
+
+QT_BEGIN_NAMESPACE
+
+struct QMMXCommonIntrinsics
+{
+ static inline m64 alpha(m64 x) {
+ x = _mm_unpackhi_pi16(x, x);
+ x = _mm_unpackhi_pi16(x, x);
+ return x;
+ }
+
+ static inline m64 _negate(const m64 &x, const m64 &mmx_0x00ff) {
+ return _mm_xor_si64(x, mmx_0x00ff);
+ }
+
+ static inline m64 add(const m64 &a, const m64 &b) {
+ return _mm_adds_pu16 (a, b);
+ }
+
+ static inline m64 _byte_mul(const m64 &a, const m64 &b,
+ const m64 &mmx_0x0080)
+ {
+ m64 res = _mm_mullo_pi16(a, b);
+ res = _mm_adds_pu16(res, mmx_0x0080);
+ res = _mm_adds_pu16(res, _mm_srli_pi16 (res, 8));
+ return _mm_srli_pi16(res, 8);
+ }
+
+ static inline m64 interpolate_pixel_256(const m64 &x, const m64 &a,
+ const m64 &y, const m64 &b)
+ {
+ m64 res = _mm_adds_pu16(_mm_mullo_pi16(x, a), _mm_mullo_pi16(y, b));
+ return _mm_srli_pi16(res, 8);
+ }
+
+ static inline m64 _interpolate_pixel_255(const m64 &x, const m64 &a,
+ const m64 &y, const m64 &b,
+ const m64 &mmx_0x0080)
+ {
+ m64 res = _mm_adds_pu16(_mm_mullo_pi16(x, a), _mm_mullo_pi16(y, b));
+ res = _mm_adds_pu16(res, mmx_0x0080);
+ res = _mm_adds_pu16(res, _mm_srli_pi16 (res, 8));
+ return _mm_srli_pi16(res, 8);
+ }
+
+ static inline m64 _premul(m64 x, const m64 &mmx_0x0080) {
+ m64 a = alpha(x);
+ return _byte_mul(x, a, mmx_0x0080);
+ }
+
+ static inline m64 _load(uint x, const m64 &mmx_0x0000) {
+ return _mm_unpacklo_pi8(_mm_cvtsi32_si64(x), mmx_0x0000);
+ }
+
+ static inline m64 _load_alpha(uint x, const m64 &) {
+ x |= (x << 16);
+ return _mm_set1_pi32(x);
+ }
+
+ static inline uint _store(const m64 &x, const m64 &mmx_0x0000) {
+ return _mm_cvtsi64_si32(_mm_packs_pu16(x, mmx_0x0000));
+ }
+};
+
+#define negate(x) _negate(x, mmx_0x00ff)
+#define byte_mul(a, b) _byte_mul(a, b, mmx_0x0080)
+#define interpolate_pixel_255(x, a, y, b) _interpolate_pixel_255(x, a, y, b, mmx_0x0080)
+#define premul(x) _premul(x, mmx_0x0080)
+#define load(x) _load(x, mmx_0x0000)
+#define load_alpha(x) _load_alpha(x, mmx_0x0000)
+#define store(x) _store(x, mmx_0x0000)
+
+/*
+ result = 0
+ d = d * cia
+*/
+#define comp_func_Clear_impl(dest, length, const_alpha)\
+{\
+ if (const_alpha == 255) {\
+ qt_memfill(static_cast<quint32*>(dest), quint32(0), length);\
+ } else {\
+ C_FF; C_80; C_00;\
+ m64 ia = MM::negate(MM::load_alpha(const_alpha));\
+ for (int i = 0; i < length; ++i) {\
+ dest[i] = MM::store(MM::byte_mul(MM::load(dest[i]), ia));\
+ }\
+ MM::end();\
+ }\
+}
+
+template <class MM>
+static void QT_FASTCALL comp_func_solid_Clear(uint *dest, int length, uint, uint const_alpha)
+{
+ comp_func_Clear_impl(dest, length, const_alpha);
+}
+
+template <class MM>
+static void QT_FASTCALL comp_func_Clear(uint *dest, const uint *, int length, uint const_alpha)
+{
+ comp_func_Clear_impl(dest, length, const_alpha);
+}
+
+/*
+ result = s
+ dest = s * ca + d * cia
+*/
+template <class MM>
+static void QT_FASTCALL comp_func_solid_Source(uint *dest, int length, uint src, uint const_alpha)
+{
+ if (const_alpha == 255) {
+ qt_memfill(static_cast<quint32*>(dest), quint32(src), length);
+ } else {
+ C_FF; C_80; C_00;
+ const m64 a = MM::load_alpha(const_alpha);
+ const m64 ia = MM::negate(a);
+ const m64 s = MM::byte_mul(MM::load(src), a);
+ for (int i = 0; i < length; ++i) {
+ dest[i] = MM::store(MM::add(s, MM::byte_mul(MM::load(dest[i]), ia)));
+ }
+ MM::end();
+ }
+}
+
+template <class MM>
+static void QT_FASTCALL comp_func_Source(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255) {
+ ::memcpy(dest, src, length * sizeof(uint));
+ } else {
+ C_FF; C_80; C_00;
+ const m64 a = MM::load_alpha(const_alpha);
+ const m64 ia = MM::negate(a);
+ for (int i = 0; i < length; ++i)
+ dest[i] = MM::store(MM::interpolate_pixel_255(MM::load(src[i]), a,
+ MM::load(dest[i]), ia));
+ }
+ MM::end();
+}
+
+/*
+ result = s + d * sia
+ dest = (s + d * sia) * ca + d * cia
+ = s * ca + d * (sia * ca + cia)
+ = s * ca + d * (1 - sa*ca)
+*/
+template <class MM>
+static void QT_FASTCALL comp_func_solid_SourceOver(uint *dest, int length, uint src, uint const_alpha)
+{
+ if ((const_alpha & qAlpha(src)) == 255) {
+ qt_memfill(static_cast<quint32*>(dest), quint32(src), length);
+ } else {
+ C_FF; C_80; C_00;
+ m64 s = MM::load(src);
+ if (const_alpha != 255) {
+ m64 ca = MM::load_alpha(const_alpha);
+ s = MM::byte_mul(s, ca);
+ }
+ m64 a = MM::negate(MM::alpha(s));
+ for (int i = 0; i < length; ++i)
+ dest[i] = MM::store(MM::add(s, MM::byte_mul(MM::load(dest[i]), a)));
+ MM::end();
+ }
+}
+
+template <class MM>
+static void QT_FASTCALL comp_func_SourceOver(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ C_FF; C_80; C_00;
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ const uint alphaMaskedSource = 0xff000000 & src[i];
+ if (alphaMaskedSource == 0)
+ continue;
+ if (alphaMaskedSource == 0xff000000) {
+ dest[i] = src[i];
+ } else {
+ m64 s = MM::load(src[i]);
+ m64 ia = MM::negate(MM::alpha(s));
+ dest[i] = MM::store(MM::add(s, MM::byte_mul(MM::load(dest[i]), ia)));
+ }
+ }
+ } else {
+ m64 ca = MM::load_alpha(const_alpha);
+ for (int i = 0; i < length; ++i) {
+ if ((0xff000000 & src[i]) == 0)
+ continue;
+ m64 s = MM::byte_mul(MM::load(src[i]), ca);
+ m64 ia = MM::negate(MM::alpha(s));
+ dest[i] = MM::store(MM::add(s, MM::byte_mul(MM::load(dest[i]), ia)));
+ }
+ }
+ MM::end();
+}
+
+/*
+ result = d + s * dia
+ dest = (d + s * dia) * ca + d * cia
+ = d + s * dia * ca
+*/
+template <class MM>
+static void QT_FASTCALL comp_func_solid_DestinationOver(uint *dest, int length, uint src, uint const_alpha)
+{
+ C_FF; C_80; C_00;
+ m64 s = MM::load(src);
+ if (const_alpha != 255)
+ s = MM::byte_mul(s, MM::load_alpha(const_alpha));
+
+ for (int i = 0; i < length; ++i) {
+ m64 d = MM::load(dest[i]);
+ m64 dia = MM::negate(MM::alpha(d));
+ dest[i] = MM::store(MM::add(d, MM::byte_mul(s, dia)));
+ }
+ MM::end();
+}
+
+template <class MM>
+static void QT_FASTCALL comp_func_DestinationOver(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ C_FF; C_80; C_00;
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ m64 d = MM::load(dest[i]);
+ m64 ia = MM::negate(MM::alpha(d));
+ dest[i] = MM::store(MM::add(d, MM::byte_mul(MM::load(src[i]), ia)));
+ }
+ } else {
+ m64 ca = MM::load_alpha(const_alpha);
+ for (int i = 0; i < length; ++i) {
+ m64 d = MM::load(dest[i]);
+ m64 dia = MM::negate(MM::alpha(d));
+ dia = MM::byte_mul(dia, ca);
+ dest[i] = MM::store(MM::add(d, MM::byte_mul(MM::load(src[i]), dia)));
+ }
+ }
+ MM::end();
+}
+
+/*
+ result = s * da
+ dest = s * da * ca + d * cia
+*/
+template <class MM>
+static void QT_FASTCALL comp_func_solid_SourceIn(uint *dest, int length, uint src, uint const_alpha)
+{
+ C_80; C_00;
+ if (const_alpha == 255) {
+ m64 s = MM::load(src);
+ for (int i = 0; i < length; ++i) {
+ m64 da = MM::alpha(MM::load(dest[i]));
+ dest[i] = MM::store(MM::byte_mul(s, da));
+ }
+ } else {
+ C_FF;
+ m64 s = MM::load(src);
+ m64 ca = MM::load_alpha(const_alpha);
+ s = MM::byte_mul(s, ca);
+ m64 cia = MM::negate(ca);
+ for (int i = 0; i < length; ++i) {
+ m64 d = MM::load(dest[i]);
+ dest[i] = MM::store(MM::interpolate_pixel_255(s, MM::alpha(d), d, cia));
+ }
+ }
+ MM::end();
+}
+
+template <class MM>
+static void QT_FASTCALL comp_func_SourceIn(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ C_FF; C_80; C_00;
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ m64 a = MM::alpha(MM::load(dest[i]));
+ dest[i] = MM::store(MM::byte_mul(MM::load(src[i]), a));
+ }
+ } else {
+ m64 ca = MM::load_alpha(const_alpha);
+ m64 cia = MM::negate(ca);
+ for (int i = 0; i < length; ++i) {
+ m64 d = MM::load(dest[i]);
+ m64 da = MM::byte_mul(MM::alpha(d), ca);
+ dest[i] = MM::store(MM::interpolate_pixel_255(
+ MM::load(src[i]), da, d, cia));
+ }
+ }
+ MM::end();
+}
+
+/*
+ result = d * sa
+ dest = d * sa * ca + d * cia
+ = d * (sa * ca + cia)
+*/
+template <class MM>
+static void QT_FASTCALL comp_func_solid_DestinationIn(uint *dest, int length, uint src, uint const_alpha)
+{
+ C_80; C_00;
+ m64 a = MM::alpha(MM::load(src));
+ if (const_alpha != 255) {
+ C_FF;
+ m64 ca = MM::load_alpha(const_alpha);
+ m64 cia = MM::negate(ca);
+ a = MM::byte_mul(a, ca);
+ a = MM::add(a, cia);
+ }
+ for (int i = 0; i < length; ++i)
+ dest[i] = MM::store(MM::byte_mul(MM::load(dest[i]), a));
+ MM::end();
+}
+
+template <class MM>
+static void QT_FASTCALL comp_func_DestinationIn(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ C_FF; C_80; C_00;
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ m64 a = MM::alpha(MM::load(src[i]));
+ dest[i] = MM::store(MM::byte_mul(MM::load(dest[i]), a));
+ }
+ } else {
+ m64 ca = MM::load_alpha(const_alpha);
+ m64 cia = MM::negate(ca);
+ for (int i = 0; i < length; ++i) {
+ m64 d = MM::load(dest[i]);
+ m64 a = MM::alpha(MM::load(src[i]));
+ a = MM::byte_mul(a, ca);
+ a = MM::add(a, cia);
+ dest[i] = MM::store(MM::byte_mul(d, a));
+ }
+ }
+ MM::end();
+}
+
+/*
+ result = s * dia
+ dest = s * dia * ca + d * cia
+*/
+template <class MM>
+static void QT_FASTCALL comp_func_solid_SourceOut(uint *dest, int length, uint src, uint const_alpha)
+{
+ C_FF; C_80; C_00;
+ m64 s = MM::load(src);
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ m64 dia = MM::negate(MM::alpha(MM::load(dest[i])));
+ dest[i] = MM::store(MM::byte_mul(s, dia));
+ }
+ } else {
+ m64 ca = MM::load_alpha(const_alpha);
+ m64 cia = MM::negate(ca);
+ s = MM::byte_mul(s, ca);
+ for (int i = 0; i < length; ++i) {
+ m64 d = MM::load(dest[i]);
+ dest[i] = MM::store(MM::interpolate_pixel_255(s, MM::negate(MM::alpha(d)), d, cia));
+ }
+ }
+ MM::end();
+}
+
+template <class MM>
+static void QT_FASTCALL comp_func_SourceOut(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ C_FF; C_80; C_00;
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ m64 ia = MM::negate(MM::alpha(MM::load(dest[i])));
+ dest[i] = MM::store(MM::byte_mul(MM::load(src[i]), ia));
+ }
+ } else {
+ m64 ca = MM::load_alpha(const_alpha);
+ m64 cia = MM::negate(ca);
+ for (int i = 0; i < length; ++i) {
+ m64 d = MM::load(dest[i]);
+ m64 dia = MM::byte_mul(MM::negate(MM::alpha(d)), ca);
+ dest[i] = MM::store(MM::interpolate_pixel_255(MM::load(src[i]), dia, d, cia));
+ }
+ }
+ MM::end();
+}
+
+/*
+ result = d * sia
+ dest = d * sia * ca + d * cia
+ = d * (sia * ca + cia)
+*/
+template <class MM>
+static void QT_FASTCALL comp_func_solid_DestinationOut(uint *dest, int length, uint src, uint const_alpha)
+{
+ C_FF; C_80; C_00;
+ m64 a = MM::negate(MM::alpha(MM::load(src)));
+ if (const_alpha != 255) {
+ m64 ca = MM::load_alpha(const_alpha);
+ a = MM::byte_mul(a, ca);
+ a = MM::add(a, MM::negate(ca));
+ }
+ for (int i = 0; i < length; ++i)
+ dest[i] = MM::store(MM::byte_mul(MM::load(dest[i]), a));
+ MM::end();
+}
+
+template <class MM>
+static void QT_FASTCALL comp_func_DestinationOut(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ C_FF; C_80; C_00;
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ m64 a = MM::negate(MM::alpha(MM::load(src[i])));
+ dest[i] = MM::store(MM::byte_mul(MM::load(dest[i]), a));
+ }
+ } else {
+ m64 ca = MM::load_alpha(const_alpha);
+ m64 cia = MM::negate(ca);
+ for (int i = 0; i < length; ++i) {
+ m64 d = MM::load(dest[i]);
+ m64 a = MM::negate(MM::alpha(MM::load(src[i])));
+ a = MM::byte_mul(a, ca);
+ a = MM::add(a, cia);
+ dest[i] = MM::store(MM::byte_mul(d, a));
+ }
+ }
+ MM::end();
+}
+
+/*
+ result = s*da + d*sia
+ dest = s*da*ca + d*sia*ca + d *cia
+ = s*ca * da + d * (sia*ca + cia)
+ = s*ca * da + d * (1 - sa*ca)
+*/
+template <class MM>
+static void QT_FASTCALL comp_func_solid_SourceAtop(uint *dest, int length, uint src, uint const_alpha)
+{
+ C_FF; C_80; C_00;
+ m64 s = MM::load(src);
+ if (const_alpha != 255) {
+ m64 ca = MM::load_alpha(const_alpha);
+ s = MM::byte_mul(s, ca);
+ }
+ m64 a = MM::negate(MM::alpha(s));
+ for (int i = 0; i < length; ++i) {
+ m64 d = MM::load(dest[i]);
+ dest[i] = MM::store(MM::interpolate_pixel_255(s, MM::alpha(d), d, a));
+ }
+ MM::end();
+}
+
+template <class MM>
+static void QT_FASTCALL comp_func_SourceAtop(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ C_FF; C_80; C_00;
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ m64 s = MM::load(src[i]);
+ m64 d = MM::load(dest[i]);
+ dest[i] = MM::store(MM::interpolate_pixel_255(s, MM::alpha(d), d,
+ MM::negate(MM::alpha(s))));
+ }
+ } else {
+ m64 ca = MM::load_alpha(const_alpha);
+ for (int i = 0; i < length; ++i) {
+ m64 s = MM::load(src[i]);
+ s = MM::byte_mul(s, ca);
+ m64 d = MM::load(dest[i]);
+ dest[i] = MM::store(MM::interpolate_pixel_255(s, MM::alpha(d), d,
+ MM::negate(MM::alpha(s))));
+ }
+ }
+ MM::end();
+}
+
+/*
+ result = d*sa + s*dia
+ dest = d*sa*ca + s*dia*ca + d *cia
+ = s*ca * dia + d * (sa*ca + cia)
+*/
+template <class MM>
+static void QT_FASTCALL comp_func_solid_DestinationAtop(uint *dest, int length, uint src, uint const_alpha)
+{
+ C_FF; C_80; C_00;
+ m64 s = MM::load(src);
+ m64 a = MM::alpha(s);
+ if (const_alpha != 255) {
+ m64 ca = MM::load_alpha(const_alpha);
+ s = MM::byte_mul(s, ca);
+ a = MM::alpha(s);
+ a = MM::add(a, MM::negate(ca));
+ }
+ for (int i = 0; i < length; ++i) {
+ m64 d = MM::load(dest[i]);
+ dest[i] = MM::store(MM::interpolate_pixel_255(s, MM::negate(MM::alpha(d)), d, a));
+ }
+ MM::end();
+}
+
+template <class MM>
+static void QT_FASTCALL comp_func_DestinationAtop(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ C_FF; C_80; C_00;
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ m64 s = MM::load(src[i]);
+ m64 d = MM::load(dest[i]);
+ dest[i] = MM::store(MM::interpolate_pixel_255(d, MM::alpha(s), s,
+ MM::negate(MM::alpha(d))));
+ }
+ } else {
+ m64 ca = MM::load_alpha(const_alpha);
+ for (int i = 0; i < length; ++i) {
+ m64 s = MM::load(src[i]);
+ s = MM::byte_mul(s, ca);
+ m64 d = MM::load(dest[i]);
+ m64 a = MM::alpha(s);
+ a = MM::add(a, MM::negate(ca));
+ dest[i] = MM::store(MM::interpolate_pixel_255(s, MM::negate(MM::alpha(d)),
+ d, a));
+ }
+ }
+ MM::end();
+}
+
+/*
+ result = d*sia + s*dia
+ dest = d*sia*ca + s*dia*ca + d *cia
+ = s*ca * dia + d * (sia*ca + cia)
+ = s*ca * dia + d * (1 - sa*ca)
+*/
+template <class MM>
+static void QT_FASTCALL comp_func_solid_XOR(uint *dest, int length, uint src, uint const_alpha)
+{
+ C_FF; C_80; C_00;
+ m64 s = MM::load(src);
+ if (const_alpha != 255) {
+ m64 ca = MM::load_alpha(const_alpha);
+ s = MM::byte_mul(s, ca);
+ }
+ m64 a = MM::negate(MM::alpha(s));
+ for (int i = 0; i < length; ++i) {
+ m64 d = MM::load(dest[i]);
+ dest[i] = MM::store(MM::interpolate_pixel_255(s, MM::negate(MM::alpha(d)),
+ d, a));
+ }
+ MM::end();
+}
+
+template <class MM>
+static void QT_FASTCALL comp_func_XOR(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ C_FF; C_80; C_00;
+ if (const_alpha == 255) {
+ for (int i = 0; i < length; ++i) {
+ m64 s = MM::load(src[i]);
+ m64 d = MM::load(dest[i]);
+ dest[i] = MM::store(MM::interpolate_pixel_255(s, MM::negate(MM::alpha(d)),
+ d, MM::negate(MM::alpha(s))));
+ }
+ } else {
+ m64 ca = MM::load_alpha(const_alpha);
+ for (int i = 0; i < length; ++i) {
+ m64 s = MM::load(src[i]);
+ s = MM::byte_mul(s, ca);
+ m64 d = MM::load(dest[i]);
+ dest[i] = MM::store(MM::interpolate_pixel_255(s, MM::negate(MM::alpha(d)),
+ d, MM::negate(MM::alpha(s))));
+ }
+ }
+ MM::end();
+}
+
+template <class MM>
+static void QT_FASTCALL rasterop_solid_SourceOrDestination(uint *dest,
+ int length,
+ uint color,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+
+ if ((quintptr)(dest) & 0x7) {
+ *dest++ |= color;
+ --length;
+ }
+
+ const int length64 = length / 2;
+ if (length64) {
+ __m64 *dst64 = reinterpret_cast<__m64*>(dest);
+ const __m64 color64 = _mm_set_pi32(color, color);
+
+ int n = (length64 + 3) / 4;
+ switch (length64 & 0x3) {
+ case 0: do { *dst64 = _mm_or_si64(*dst64, color64); ++dst64;
+ case 3: *dst64 = _mm_or_si64(*dst64, color64); ++dst64;
+ case 2: *dst64 = _mm_or_si64(*dst64, color64); ++dst64;
+ case 1: *dst64 = _mm_or_si64(*dst64, color64); ++dst64;
+ } while (--n > 0);
+ }
+ }
+
+ if (length & 0x1) {
+ dest[length - 1] |= color;
+ }
+
+ MM::end();
+}
+
+template <class MM>
+static void QT_FASTCALL rasterop_solid_SourceAndDestination(uint *dest,
+ int length,
+ uint color,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+
+ color |= 0xff000000;
+
+ if ((quintptr)(dest) & 0x7) { // align
+ *dest++ &= color;
+ --length;
+ }
+
+ const int length64 = length / 2;
+ if (length64) {
+ __m64 *dst64 = reinterpret_cast<__m64*>(dest);
+ const __m64 color64 = _mm_set_pi32(color, color);
+
+ int n = (length64 + 3) / 4;
+ switch (length64 & 0x3) {
+ case 0: do { *dst64 = _mm_and_si64(*dst64, color64); ++dst64;
+ case 3: *dst64 = _mm_and_si64(*dst64, color64); ++dst64;
+ case 2: *dst64 = _mm_and_si64(*dst64, color64); ++dst64;
+ case 1: *dst64 = _mm_and_si64(*dst64, color64); ++dst64;
+ } while (--n > 0);
+ }
+ }
+
+ if (length & 0x1) {
+ dest[length - 1] &= color;
+ }
+
+ MM::end();
+}
+
+template <class MM>
+static void QT_FASTCALL rasterop_solid_SourceXorDestination(uint *dest,
+ int length,
+ uint color,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+
+ color &= 0x00ffffff;
+
+ if ((quintptr)(dest) & 0x7) {
+ *dest++ ^= color;
+ --length;
+ }
+
+ const int length64 = length / 2;
+ if (length64) {
+ __m64 *dst64 = reinterpret_cast<__m64*>(dest);
+ const __m64 color64 = _mm_set_pi32(color, color);
+
+ int n = (length64 + 3) / 4;
+ switch (length64 & 0x3) {
+ case 0: do { *dst64 = _mm_xor_si64(*dst64, color64); ++dst64;
+ case 3: *dst64 = _mm_xor_si64(*dst64, color64); ++dst64;
+ case 2: *dst64 = _mm_xor_si64(*dst64, color64); ++dst64;
+ case 1: *dst64 = _mm_xor_si64(*dst64, color64); ++dst64;
+ } while (--n > 0);
+ }
+ }
+
+ if (length & 0x1) {
+ dest[length - 1] ^= color;
+ }
+
+ MM::end();
+}
+
+template <class MM>
+static void QT_FASTCALL rasterop_solid_SourceAndNotDestination(uint *dest,
+ int length,
+ uint color,
+ uint const_alpha)
+{
+
+ Q_UNUSED(const_alpha);
+
+ if ((quintptr)(dest) & 0x7) {
+ *dest = (color & ~(*dest)) | 0xff000000;
+ ++dest;
+ --length;
+ }
+
+ const int length64 = length / 2;
+ if (length64) {
+ __m64 *dst64 = reinterpret_cast<__m64*>(dest);
+ const __m64 color64 = _mm_set_pi32(color, color);
+ const m64 mmx_0xff000000 = _mm_set1_pi32(0xff000000);
+ __m64 tmp1, tmp2, tmp3, tmp4;
+
+ int n = (length64 + 3) / 4;
+ switch (length64 & 0x3) {
+ case 0: do { tmp1 = _mm_andnot_si64(*dst64, color64);
+ *dst64++ = _mm_or_si64(tmp1, mmx_0xff000000);
+ case 3: tmp2 = _mm_andnot_si64(*dst64, color64);
+ *dst64++ = _mm_or_si64(tmp2, mmx_0xff000000);
+ case 2: tmp3 = _mm_andnot_si64(*dst64, color64);
+ *dst64++ = _mm_or_si64(tmp3, mmx_0xff000000);
+ case 1: tmp4 = _mm_andnot_si64(*dst64, color64);
+ *dst64++ = _mm_or_si64(tmp4, mmx_0xff000000);
+ } while (--n > 0);
+ }
+ }
+
+ if (length & 0x1) {
+ dest[length - 1] = (color & ~(dest[length - 1])) | 0xff000000;
+ }
+
+ MM::end();
+}
+
+template <class MM>
+static void QT_FASTCALL rasterop_solid_NotSourceAndNotDestination(uint *dest,
+ int length,
+ uint color,
+ uint const_alpha)
+{
+ rasterop_solid_SourceAndNotDestination<MM>(dest, length,
+ ~color, const_alpha);
+}
+
+template <class MM>
+static void QT_FASTCALL rasterop_solid_NotSourceOrNotDestination(uint *dest,
+ int length,
+ uint color,
+ uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ color = ~color | 0xff000000;
+ while (length--) {
+ *dest = color | ~(*dest);
+ ++dest;
+ }
+}
+
+template <class MM>
+static void QT_FASTCALL rasterop_solid_NotSourceXorDestination(uint *dest,
+ int length,
+ uint color,
+ uint const_alpha)
+{
+ rasterop_solid_SourceXorDestination<MM>(dest, length, ~color, const_alpha);
+}
+
+template <class MM>
+static void QT_FASTCALL rasterop_solid_NotSource(uint *dest, int length,
+ uint color, uint const_alpha)
+{
+ Q_UNUSED(const_alpha);
+ qt_memfill((quint32*)dest, ~color | 0xff000000, length);
+}
+
+template <class MM>
+static void QT_FASTCALL rasterop_solid_NotSourceAndDestination(uint *dest,
+ int length,
+ uint color,
+ uint const_alpha)
+{
+ rasterop_solid_SourceAndDestination<MM>(dest, length,
+ ~color, const_alpha);
+}
+
+template <class MM>
+static inline void qt_blend_color_argb_x86(int count, const QSpan *spans,
+ void *userData,
+ CompositionFunctionSolid *solidFunc)
+{
+ QSpanData *data = reinterpret_cast<QSpanData *>(userData);
+ if (data->rasterBuffer->compositionMode == QPainter::CompositionMode_Source
+ || (data->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver
+ && qAlpha(data->solid.color) == 255)) {
+ // inline for performance
+ C_FF; C_80; C_00;
+ while (count--) {
+ uint *target = ((uint *)data->rasterBuffer->scanLine(spans->y)) + spans->x;
+ if (spans->coverage == 255) {
+ qt_memfill(static_cast<quint32*>(target), quint32(data->solid.color), spans->len);
+ } else {
+ // dest = s * ca + d * (1 - sa*ca) --> dest = s * ca + d * (1-ca)
+ m64 ca = MM::load_alpha(spans->coverage);
+ m64 s = MM::byte_mul(MM::load(data->solid.color), ca);
+ m64 ica = MM::negate(ca);
+ for (int i = 0; i < spans->len; ++i)
+ target[i] = MM::store(MM::add(s, MM::byte_mul(MM::load(target[i]), ica)));
+ }
+ ++spans;
+ }
+ MM::end();
+ return;
+ }
+ CompositionFunctionSolid func = solidFunc[data->rasterBuffer->compositionMode];
+ while (count--) {
+ uint *target = ((uint *)data->rasterBuffer->scanLine(spans->y)) + spans->x;
+ func(target, spans->len, data->solid.color, spans->coverage);
+ ++spans;
+ }
+}
+
+#ifdef QT_HAVE_MMX
+struct QMMXIntrinsics : public QMMXCommonIntrinsics
+{
+ static inline void end() {
+#if !defined(Q_OS_WINCE) || defined(_X86_)
+ _mm_empty();
+#endif
+ }
+};
+#endif // QT_HAVE_MMX
+
+QT_END_NAMESPACE
+
+#endif // QDRAWHELPER_MMX_P_H
diff --git a/src/gui/painting/qdrawhelper_neon.cpp b/src/gui/painting/qdrawhelper_neon.cpp
new file mode 100644
index 0000000000..debca37486
--- /dev/null
+++ b/src/gui/painting/qdrawhelper_neon.cpp
@@ -0,0 +1,961 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qdrawhelper_p.h>
+#include <private/qblendfunctions_p.h>
+#include <private/qmath_p.h>
+
+#ifdef QT_HAVE_NEON
+
+#include <private/qdrawhelper_neon_p.h>
+#include <private/qpaintengine_raster_p.h>
+#include <arm_neon.h>
+
+QT_BEGIN_NAMESPACE
+
+void qt_memfill32_neon(quint32 *dest, quint32 value, int count)
+{
+ const int epilogueSize = count % 16;
+ if (count >= 16) {
+ quint32 *const neonEnd = dest + count - epilogueSize;
+ register uint32x4_t valueVector1 asm ("q0") = vdupq_n_u32(value);
+ register uint32x4_t valueVector2 asm ("q1") = valueVector1;
+ while (dest != neonEnd) {
+ asm volatile (
+ "vst2.32 { d0, d1, d2, d3 }, [%[DST]] !\n\t"
+ "vst2.32 { d0, d1, d2, d3 }, [%[DST]] !\n\t"
+ : [DST]"+r" (dest)
+ : [VALUE1]"w"(valueVector1), [VALUE2]"w"(valueVector2)
+ : "memory"
+ );
+ }
+ }
+
+ switch (epilogueSize)
+ {
+ case 15: *dest++ = value;
+ case 14: *dest++ = value;
+ case 13: *dest++ = value;
+ case 12: *dest++ = value;
+ case 11: *dest++ = value;
+ case 10: *dest++ = value;
+ case 9: *dest++ = value;
+ case 8: *dest++ = value;
+ case 7: *dest++ = value;
+ case 6: *dest++ = value;
+ case 5: *dest++ = value;
+ case 4: *dest++ = value;
+ case 3: *dest++ = value;
+ case 2: *dest++ = value;
+ case 1: *dest++ = value;
+ }
+}
+
+static inline uint16x8_t qvdiv_255_u16(uint16x8_t x, uint16x8_t half)
+{
+ // result = (x + (x >> 8) + 0x80) >> 8
+
+ const uint16x8_t temp = vshrq_n_u16(x, 8); // x >> 8
+ const uint16x8_t sum_part = vaddq_u16(x, half); // x + 0x80
+ const uint16x8_t sum = vaddq_u16(temp, sum_part);
+
+ return vshrq_n_u16(sum, 8);
+}
+
+static inline uint16x8_t qvbyte_mul_u16(uint16x8_t x, uint16x8_t alpha, uint16x8_t half)
+{
+ // t = qRound(x * alpha / 255.0)
+
+ const uint16x8_t t = vmulq_u16(x, alpha); // t
+ return qvdiv_255_u16(t, half);
+}
+
+static inline uint16x8_t qvinterpolate_pixel_255(uint16x8_t x, uint16x8_t a, uint16x8_t y, uint16x8_t b, uint16x8_t half)
+{
+ // t = x * a + y * b
+
+ const uint16x8_t ta = vmulq_u16(x, a);
+ const uint16x8_t tb = vmulq_u16(y, b);
+
+ return qvdiv_255_u16(vaddq_u16(ta, tb), half);
+}
+
+static inline uint16x8_t qvsource_over_u16(uint16x8_t src16, uint16x8_t dst16, uint16x8_t half, uint16x8_t full)
+{
+ const uint16x4_t alpha16_high = vdup_lane_u16(vget_high_u16(src16), 3);
+ const uint16x4_t alpha16_low = vdup_lane_u16(vget_low_u16(src16), 3);
+
+ const uint16x8_t alpha16 = vsubq_u16(full, vcombine_u16(alpha16_low, alpha16_high));
+
+ return vaddq_u16(src16, qvbyte_mul_u16(dst16, alpha16, half));
+}
+
+extern "C" void
+pixman_composite_over_8888_0565_asm_neon (int32_t w,
+ int32_t h,
+ uint16_t *dst,
+ int32_t dst_stride,
+ uint32_t *src,
+ int32_t src_stride);
+
+extern "C" void
+pixman_composite_over_8888_8888_asm_neon (int32_t w,
+ int32_t h,
+ uint32_t *dst,
+ int32_t dst_stride,
+ uint32_t *src,
+ int32_t src_stride);
+
+extern "C" void
+pixman_composite_src_0565_8888_asm_neon (int32_t w,
+ int32_t h,
+ uint32_t *dst,
+ int32_t dst_stride,
+ uint16_t *src,
+ int32_t src_stride);
+
+extern "C" void
+pixman_composite_over_n_8_0565_asm_neon (int32_t w,
+ int32_t h,
+ uint16_t *dst,
+ int32_t dst_stride,
+ uint32_t src,
+ int32_t unused,
+ uint8_t *mask,
+ int32_t mask_stride);
+
+extern "C" void
+pixman_composite_scanline_over_asm_neon (int32_t w,
+ const uint32_t *dst,
+ const uint32_t *src);
+
+extern "C" void
+pixman_composite_src_0565_0565_asm_neon (int32_t w,
+ int32_t h,
+ uint16_t *dst,
+ int32_t dst_stride,
+ uint16_t *src,
+ int32_t src_stride);
+
+// qblendfunctions.cpp
+void qt_blend_argb32_on_rgb16_const_alpha(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha);
+
+void qt_blend_rgb16_on_argb32_neon(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha)
+{
+ dbpl /= 4;
+ sbpl /= 2;
+
+ quint32 *dst = (quint32 *) destPixels;
+ quint16 *src = (quint16 *) srcPixels;
+
+ if (const_alpha != 256) {
+ quint8 a = (255 * const_alpha) >> 8;
+ quint8 ia = 255 - a;
+
+ while (h--) {
+ for (int x=0; x<w; ++x)
+ dst[x] = INTERPOLATE_PIXEL_255(qt_colorConvert(src[x], dst[x]), a, dst[x], ia);
+ dst += dbpl;
+ src += sbpl;
+ }
+ return;
+ }
+
+ pixman_composite_src_0565_8888_asm_neon(w, h, dst, dbpl, src, sbpl);
+}
+
+// qblendfunctions.cpp
+void qt_blend_rgb16_on_rgb16(uchar *dst, int dbpl,
+ const uchar *src, int sbpl,
+ int w, int h,
+ int const_alpha);
+
+template <int N>
+static inline void scanLineBlit16(quint16 *dst, quint16 *src, int dstride)
+{
+ if (N >= 2) {
+ ((quint32 *)dst)[0] = ((quint32 *)src)[0];
+ __builtin_prefetch(dst + dstride, 1, 0);
+ }
+ for (int i = 1; i < N/2; ++i)
+ ((quint32 *)dst)[i] = ((quint32 *)src)[i];
+ if (N & 1)
+ dst[N-1] = src[N-1];
+}
+
+template <int Width>
+static inline void blockBlit16(quint16 *dst, quint16 *src, int dstride, int sstride, int h)
+{
+ union {
+ quintptr address;
+ quint16 *pointer;
+ } u;
+
+ u.pointer = dst;
+
+ if (u.address & 2) {
+ while (h--) {
+ // align dst
+ dst[0] = src[0];
+ if (Width > 1)
+ scanLineBlit16<Width-1>(dst + 1, src + 1, dstride);
+ dst += dstride;
+ src += sstride;
+ }
+ } else {
+ while (h--) {
+ scanLineBlit16<Width>(dst, src, dstride);
+
+ dst += dstride;
+ src += sstride;
+ }
+ }
+}
+
+void qt_blend_rgb16_on_rgb16_neon(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha)
+{
+ // testing show that the default memcpy is faster for widths 150 and up
+ if (const_alpha != 256 || w >= 150) {
+ qt_blend_rgb16_on_rgb16(destPixels, dbpl, srcPixels, sbpl, w, h, const_alpha);
+ return;
+ }
+
+ int dstride = dbpl / 2;
+ int sstride = sbpl / 2;
+
+ quint16 *dst = (quint16 *) destPixels;
+ quint16 *src = (quint16 *) srcPixels;
+
+ switch (w) {
+#define BLOCKBLIT(n) case n: blockBlit16<n>(dst, src, dstride, sstride, h); return;
+ BLOCKBLIT(1);
+ BLOCKBLIT(2);
+ BLOCKBLIT(3);
+ BLOCKBLIT(4);
+ BLOCKBLIT(5);
+ BLOCKBLIT(6);
+ BLOCKBLIT(7);
+ BLOCKBLIT(8);
+ BLOCKBLIT(9);
+ BLOCKBLIT(10);
+ BLOCKBLIT(11);
+ BLOCKBLIT(12);
+ BLOCKBLIT(13);
+ BLOCKBLIT(14);
+ BLOCKBLIT(15);
+#undef BLOCKBLIT
+ default:
+ break;
+ }
+
+ pixman_composite_src_0565_0565_asm_neon (w, h, dst, dstride, src, sstride);
+}
+
+extern "C" void blend_8_pixels_argb32_on_rgb16_neon(quint16 *dst, const quint32 *src, int const_alpha);
+
+void qt_blend_argb32_on_rgb16_neon(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha)
+{
+ quint16 *dst = (quint16 *) destPixels;
+ quint32 *src = (quint32 *) srcPixels;
+
+ if (const_alpha != 256) {
+ for (int y=0; y<h; ++y) {
+ int i = 0;
+ for (; i < w-7; i += 8)
+ blend_8_pixels_argb32_on_rgb16_neon(&dst[i], &src[i], const_alpha);
+
+ if (i < w) {
+ int tail = w - i;
+
+ quint16 dstBuffer[8];
+ quint32 srcBuffer[8];
+
+ for (int j = 0; j < tail; ++j) {
+ dstBuffer[j] = dst[i + j];
+ srcBuffer[j] = src[i + j];
+ }
+
+ blend_8_pixels_argb32_on_rgb16_neon(dstBuffer, srcBuffer, const_alpha);
+
+ for (int j = 0; j < tail; ++j)
+ dst[i + j] = dstBuffer[j];
+ }
+
+ dst = (quint16 *)(((uchar *) dst) + dbpl);
+ src = (quint32 *)(((uchar *) src) + sbpl);
+ }
+ return;
+ }
+
+ pixman_composite_over_8888_0565_asm_neon(w, h, dst, dbpl / 2, src, sbpl / 4);
+}
+
+void qt_blend_argb32_on_argb32_scanline_neon(uint *dest, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255) {
+ pixman_composite_scanline_over_asm_neon(length, dest, src);
+ } else {
+ qt_blend_argb32_on_argb32_neon((uchar *)dest, 4 * length, (uchar *)src, 4 * length, length, 1, (const_alpha * 256) / 255);
+ }
+}
+
+void qt_blend_argb32_on_argb32_neon(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha)
+{
+ const uint *src = (const uint *) srcPixels;
+ uint *dst = (uint *) destPixels;
+ uint16x8_t half = vdupq_n_u16(0x80);
+ uint16x8_t full = vdupq_n_u16(0xff);
+ if (const_alpha == 256) {
+ pixman_composite_over_8888_8888_asm_neon(w, h, (uint32_t *)destPixels, dbpl / 4, (uint32_t *)srcPixels, sbpl / 4);
+ } else if (const_alpha != 0) {
+ const_alpha = (const_alpha * 255) >> 8;
+ uint16x8_t const_alpha16 = vdupq_n_u16(const_alpha);
+ for (int y = 0; y < h; ++y) {
+ int x = 0;
+ for (; x < w-3; x += 4) {
+ if (src[x] | src[x+1] | src[x+2] | src[x+3]) {
+ uint32x4_t src32 = vld1q_u32((uint32_t *)&src[x]);
+ uint32x4_t dst32 = vld1q_u32((uint32_t *)&dst[x]);
+
+ const uint8x16_t src8 = vreinterpretq_u8_u32(src32);
+ const uint8x16_t dst8 = vreinterpretq_u8_u32(dst32);
+
+ const uint8x8_t src8_low = vget_low_u8(src8);
+ const uint8x8_t dst8_low = vget_low_u8(dst8);
+
+ const uint8x8_t src8_high = vget_high_u8(src8);
+ const uint8x8_t dst8_high = vget_high_u8(dst8);
+
+ const uint16x8_t src16_low = vmovl_u8(src8_low);
+ const uint16x8_t dst16_low = vmovl_u8(dst8_low);
+
+ const uint16x8_t src16_high = vmovl_u8(src8_high);
+ const uint16x8_t dst16_high = vmovl_u8(dst8_high);
+
+ const uint16x8_t srcalpha16_low = qvbyte_mul_u16(src16_low, const_alpha16, half);
+ const uint16x8_t srcalpha16_high = qvbyte_mul_u16(src16_high, const_alpha16, half);
+
+ const uint16x8_t result16_low = qvsource_over_u16(srcalpha16_low, dst16_low, half, full);
+ const uint16x8_t result16_high = qvsource_over_u16(srcalpha16_high, dst16_high, half, full);
+
+ const uint32x2_t result32_low = vreinterpret_u32_u8(vmovn_u16(result16_low));
+ const uint32x2_t result32_high = vreinterpret_u32_u8(vmovn_u16(result16_high));
+
+ vst1q_u32((uint32_t *)&dst[x], vcombine_u32(result32_low, result32_high));
+ }
+ }
+ for (; x<w; ++x) {
+ uint s = src[x];
+ if (s != 0) {
+ s = BYTE_MUL(s, const_alpha);
+ dst[x] = s + BYTE_MUL(dst[x], qAlpha(~s));
+ }
+ }
+ dst = (quint32 *)(((uchar *) dst) + dbpl);
+ src = (const quint32 *)(((const uchar *) src) + sbpl);
+ }
+ }
+}
+
+// qblendfunctions.cpp
+void qt_blend_rgb32_on_rgb32(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha);
+
+void qt_blend_rgb32_on_rgb32_neon(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha)
+{
+ if (const_alpha != 256) {
+ if (const_alpha != 0) {
+ const uint *src = (const uint *) srcPixels;
+ uint *dst = (uint *) destPixels;
+ uint16x8_t half = vdupq_n_u16(0x80);
+ const_alpha = (const_alpha * 255) >> 8;
+ int one_minus_const_alpha = 255 - const_alpha;
+ uint16x8_t const_alpha16 = vdupq_n_u16(const_alpha);
+ uint16x8_t one_minus_const_alpha16 = vdupq_n_u16(255 - const_alpha);
+ for (int y = 0; y < h; ++y) {
+ int x = 0;
+ for (; x < w-3; x += 4) {
+ uint32x4_t src32 = vld1q_u32((uint32_t *)&src[x]);
+ uint32x4_t dst32 = vld1q_u32((uint32_t *)&dst[x]);
+
+ const uint8x16_t src8 = vreinterpretq_u8_u32(src32);
+ const uint8x16_t dst8 = vreinterpretq_u8_u32(dst32);
+
+ const uint8x8_t src8_low = vget_low_u8(src8);
+ const uint8x8_t dst8_low = vget_low_u8(dst8);
+
+ const uint8x8_t src8_high = vget_high_u8(src8);
+ const uint8x8_t dst8_high = vget_high_u8(dst8);
+
+ const uint16x8_t src16_low = vmovl_u8(src8_low);
+ const uint16x8_t dst16_low = vmovl_u8(dst8_low);
+
+ const uint16x8_t src16_high = vmovl_u8(src8_high);
+ const uint16x8_t dst16_high = vmovl_u8(dst8_high);
+
+ const uint16x8_t result16_low = qvinterpolate_pixel_255(src16_low, const_alpha16, dst16_low, one_minus_const_alpha16, half);
+ const uint16x8_t result16_high = qvinterpolate_pixel_255(src16_high, const_alpha16, dst16_high, one_minus_const_alpha16, half);
+
+ const uint32x2_t result32_low = vreinterpret_u32_u8(vmovn_u16(result16_low));
+ const uint32x2_t result32_high = vreinterpret_u32_u8(vmovn_u16(result16_high));
+
+ vst1q_u32((uint32_t *)&dst[x], vcombine_u32(result32_low, result32_high));
+ }
+ for (; x<w; ++x) {
+ uint s = src[x];
+ s = BYTE_MUL(s, const_alpha);
+ dst[x] = INTERPOLATE_PIXEL_255(src[x], const_alpha, dst[x], one_minus_const_alpha);
+ }
+ dst = (quint32 *)(((uchar *) dst) + dbpl);
+ src = (const quint32 *)(((const uchar *) src) + sbpl);
+ }
+ }
+ } else {
+ qt_blend_rgb32_on_rgb32(destPixels, dbpl, srcPixels, sbpl, w, h, const_alpha);
+ }
+}
+
+void qt_alphamapblit_quint16_neon(QRasterBuffer *rasterBuffer,
+ int x, int y, quint32 color,
+ const uchar *bitmap,
+ int mapWidth, int mapHeight, int mapStride,
+ const QClipData *)
+{
+ quint16 *dest = reinterpret_cast<quint16*>(rasterBuffer->scanLine(y)) + x;
+ const int destStride = rasterBuffer->bytesPerLine() / sizeof(quint16);
+
+ uchar *mask = const_cast<uchar *>(bitmap);
+
+ pixman_composite_over_n_8_0565_asm_neon(mapWidth, mapHeight, dest, destStride, color, 0, mask, mapStride);
+}
+
+extern "C" void blend_8_pixels_rgb16_on_rgb16_neon(quint16 *dst, const quint16 *src, int const_alpha);
+
+template <typename SRC, typename BlendFunc>
+struct Blend_on_RGB16_SourceAndConstAlpha_Neon {
+ Blend_on_RGB16_SourceAndConstAlpha_Neon(BlendFunc blender, int const_alpha)
+ : m_index(0)
+ , m_blender(blender)
+ , m_const_alpha(const_alpha)
+ {
+ }
+
+ inline void write(quint16 *dst, quint32 src)
+ {
+ srcBuffer[m_index++] = src;
+
+ if (m_index == 8) {
+ m_blender(dst - 7, srcBuffer, m_const_alpha);
+ m_index = 0;
+ }
+ }
+
+ inline void flush(quint16 *dst)
+ {
+ if (m_index > 0) {
+ quint16 dstBuffer[8];
+ for (int i = 0; i < m_index; ++i)
+ dstBuffer[i] = dst[i - m_index];
+
+ m_blender(dstBuffer, srcBuffer, m_const_alpha);
+
+ for (int i = 0; i < m_index; ++i)
+ dst[i - m_index] = dstBuffer[i];
+
+ m_index = 0;
+ }
+ }
+
+ SRC srcBuffer[8];
+
+ int m_index;
+ BlendFunc m_blender;
+ int m_const_alpha;
+};
+
+template <typename SRC, typename BlendFunc>
+Blend_on_RGB16_SourceAndConstAlpha_Neon<SRC, BlendFunc>
+Blend_on_RGB16_SourceAndConstAlpha_Neon_create(BlendFunc blender, int const_alpha)
+{
+ return Blend_on_RGB16_SourceAndConstAlpha_Neon<SRC, BlendFunc>(blender, const_alpha);
+}
+
+void qt_scale_image_argb32_on_rgb16_neon(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ const QRectF &targetRect,
+ const QRectF &sourceRect,
+ const QRect &clip,
+ int const_alpha)
+{
+ if (const_alpha == 0)
+ return;
+
+ qt_scale_image_16bit<quint32>(destPixels, dbpl, srcPixels, sbpl, targetRect, sourceRect, clip,
+ Blend_on_RGB16_SourceAndConstAlpha_Neon_create<quint32>(blend_8_pixels_argb32_on_rgb16_neon, const_alpha));
+}
+
+void qt_scale_image_rgb16_on_rgb16(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ const QRectF &targetRect,
+ const QRectF &sourceRect,
+ const QRect &clip,
+ int const_alpha);
+
+void qt_scale_image_rgb16_on_rgb16_neon(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ const QRectF &targetRect,
+ const QRectF &sourceRect,
+ const QRect &clip,
+ int const_alpha)
+{
+ if (const_alpha == 0)
+ return;
+
+ if (const_alpha == 256) {
+ qt_scale_image_rgb16_on_rgb16(destPixels, dbpl, srcPixels, sbpl, targetRect, sourceRect, clip, const_alpha);
+ return;
+ }
+
+ qt_scale_image_16bit<quint16>(destPixels, dbpl, srcPixels, sbpl, targetRect, sourceRect, clip,
+ Blend_on_RGB16_SourceAndConstAlpha_Neon_create<quint16>(blend_8_pixels_rgb16_on_rgb16_neon, const_alpha));
+}
+
+extern void qt_transform_image_rgb16_on_rgb16(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ const QRectF &targetRect,
+ const QRectF &sourceRect,
+ const QRect &clip,
+ const QTransform &targetRectTransform,
+ int const_alpha);
+
+void qt_transform_image_rgb16_on_rgb16_neon(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ const QRectF &targetRect,
+ const QRectF &sourceRect,
+ const QRect &clip,
+ const QTransform &targetRectTransform,
+ int const_alpha)
+{
+ if (const_alpha == 0)
+ return;
+
+ if (const_alpha == 256) {
+ qt_transform_image_rgb16_on_rgb16(destPixels, dbpl, srcPixels, sbpl, targetRect, sourceRect, clip, targetRectTransform, const_alpha);
+ return;
+ }
+
+ qt_transform_image(reinterpret_cast<quint16 *>(destPixels), dbpl,
+ reinterpret_cast<const quint16 *>(srcPixels), sbpl, targetRect, sourceRect, clip, targetRectTransform,
+ Blend_on_RGB16_SourceAndConstAlpha_Neon_create<quint16>(blend_8_pixels_rgb16_on_rgb16_neon, const_alpha));
+}
+
+void qt_transform_image_argb32_on_rgb16_neon(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ const QRectF &targetRect,
+ const QRectF &sourceRect,
+ const QRect &clip,
+ const QTransform &targetRectTransform,
+ int const_alpha)
+{
+ if (const_alpha == 0)
+ return;
+
+ qt_transform_image(reinterpret_cast<quint16 *>(destPixels), dbpl,
+ reinterpret_cast<const quint32 *>(srcPixels), sbpl, targetRect, sourceRect, clip, targetRectTransform,
+ Blend_on_RGB16_SourceAndConstAlpha_Neon_create<quint32>(blend_8_pixels_argb32_on_rgb16_neon, const_alpha));
+}
+
+static inline void convert_8_pixels_rgb16_to_argb32(quint32 *dst, const quint16 *src)
+{
+ asm volatile (
+ "vld1.16 { d0, d1 }, [%[SRC]]\n\t"
+
+ /* convert 8 r5g6b5 pixel data from {d0, d1} to planar 8-bit format
+ and put data into d4 - red, d3 - green, d2 - blue */
+ "vshrn.u16 d4, q0, #8\n\t"
+ "vshrn.u16 d3, q0, #3\n\t"
+ "vsli.u16 q0, q0, #5\n\t"
+ "vsri.u8 d4, d4, #5\n\t"
+ "vsri.u8 d3, d3, #6\n\t"
+ "vshrn.u16 d2, q0, #2\n\t"
+
+ /* fill d5 - alpha with 0xff */
+ "mov r2, #255\n\t"
+ "vdup.8 d5, r2\n\t"
+
+ "vst4.8 { d2, d3, d4, d5 }, [%[DST]]"
+ : : [DST]"r" (dst), [SRC]"r" (src)
+ : "memory", "r2", "d0", "d1", "d2", "d3", "d4", "d5"
+ );
+}
+
+uint * QT_FASTCALL qt_destFetchRGB16_neon(uint *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length)
+{
+ const ushort *data = (const ushort *)rasterBuffer->scanLine(y) + x;
+
+ int i = 0;
+ for (; i < length - 7; i += 8)
+ convert_8_pixels_rgb16_to_argb32(&buffer[i], &data[i]);
+
+ if (i < length) {
+ quint16 srcBuffer[8];
+ quint32 dstBuffer[8];
+
+ int tail = length - i;
+ for (int j = 0; j < tail; ++j)
+ srcBuffer[j] = data[i + j];
+
+ convert_8_pixels_rgb16_to_argb32(dstBuffer, srcBuffer);
+
+ for (int j = 0; j < tail; ++j)
+ buffer[i + j] = dstBuffer[j];
+ }
+
+ return buffer;
+}
+
+static inline void convert_8_pixels_argb32_to_rgb16(quint16 *dst, const quint32 *src)
+{
+ asm volatile (
+ "vld4.8 { d0, d1, d2, d3 }, [%[SRC]]\n\t"
+
+ /* convert to r5g6b5 and store it into {d28, d29} */
+ "vshll.u8 q14, d2, #8\n\t"
+ "vshll.u8 q8, d1, #8\n\t"
+ "vshll.u8 q9, d0, #8\n\t"
+ "vsri.u16 q14, q8, #5\n\t"
+ "vsri.u16 q14, q9, #11\n\t"
+
+ "vst1.16 { d28, d29 }, [%[DST]]"
+ : : [DST]"r" (dst), [SRC]"r" (src)
+ : "memory", "d0", "d1", "d2", "d3", "d16", "d17", "d18", "d19", "d28", "d29"
+ );
+}
+
+void QT_FASTCALL qt_destStoreRGB16_neon(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length)
+{
+ quint16 *data = (quint16*)rasterBuffer->scanLine(y) + x;
+
+ int i = 0;
+ for (; i < length - 7; i += 8)
+ convert_8_pixels_argb32_to_rgb16(&data[i], &buffer[i]);
+
+ if (i < length) {
+ quint32 srcBuffer[8];
+ quint16 dstBuffer[8];
+
+ int tail = length - i;
+ for (int j = 0; j < tail; ++j)
+ srcBuffer[j] = buffer[i + j];
+
+ convert_8_pixels_argb32_to_rgb16(dstBuffer, srcBuffer);
+
+ for (int j = 0; j < tail; ++j)
+ data[i + j] = dstBuffer[j];
+ }
+}
+
+void QT_FASTCALL comp_func_solid_SourceOver_neon(uint *destPixels, int length, uint color, uint const_alpha)
+{
+ if ((const_alpha & qAlpha(color)) == 255) {
+ QT_MEMFILL_UINT(destPixels, length, color);
+ } else {
+ if (const_alpha != 255)
+ color = BYTE_MUL(color, const_alpha);
+
+ const quint32 minusAlphaOfColor = qAlpha(~color);
+ int x = 0;
+
+ uint32_t *dst = (uint32_t *) destPixels;
+ const uint32x4_t colorVector = vdupq_n_u32(color);
+ uint16x8_t half = vdupq_n_u16(0x80);
+ const uint16x8_t minusAlphaOfColorVector = vdupq_n_u16(minusAlphaOfColor);
+
+ for (; x < length-3; x += 4) {
+ uint32x4_t dstVector = vld1q_u32(&dst[x]);
+
+ const uint8x16_t dst8 = vreinterpretq_u8_u32(dstVector);
+
+ const uint8x8_t dst8_low = vget_low_u8(dst8);
+ const uint8x8_t dst8_high = vget_high_u8(dst8);
+
+ const uint16x8_t dst16_low = vmovl_u8(dst8_low);
+ const uint16x8_t dst16_high = vmovl_u8(dst8_high);
+
+ const uint16x8_t result16_low = qvbyte_mul_u16(dst16_low, minusAlphaOfColorVector, half);
+ const uint16x8_t result16_high = qvbyte_mul_u16(dst16_high, minusAlphaOfColorVector, half);
+
+ const uint32x2_t result32_low = vreinterpret_u32_u8(vmovn_u16(result16_low));
+ const uint32x2_t result32_high = vreinterpret_u32_u8(vmovn_u16(result16_high));
+
+ uint32x4_t blendedPixels = vcombine_u32(result32_low, result32_high);
+ uint32x4_t colorPlusBlendedPixels = vaddq_u32(colorVector, blendedPixels);
+ vst1q_u32(&dst[x], colorPlusBlendedPixels);
+ }
+
+ for (;x < length; ++x)
+ destPixels[x] = color + BYTE_MUL(destPixels[x], minusAlphaOfColor);
+ }
+}
+
+void QT_FASTCALL comp_func_Plus_neon(uint *dst, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255) {
+ uint *const end = dst + length;
+ uint *const neonEnd = end - 3;
+
+ while (dst < neonEnd) {
+ asm volatile (
+ "vld2.8 { d0, d1 }, [%[SRC]] !\n\t"
+ "vld2.8 { d2, d3 }, [%[DST]]\n\t"
+ "vqadd.u8 q0, q0, q1\n\t"
+ "vst2.8 { d0, d1 }, [%[DST]] !\n\t"
+ : [DST]"+r" (dst), [SRC]"+r" (src)
+ :
+ : "memory", "d0", "d1", "d2", "d3", "q0", "q1"
+ );
+ }
+
+ while (dst != end) {
+ *dst = comp_func_Plus_one_pixel(*dst, *src);
+ ++dst;
+ ++src;
+ }
+ } else {
+ int x = 0;
+ const int one_minus_const_alpha = 255 - const_alpha;
+ const uint16x8_t constAlphaVector = vdupq_n_u16(const_alpha);
+ const uint16x8_t oneMinusconstAlphaVector = vdupq_n_u16(one_minus_const_alpha);
+
+ const uint16x8_t half = vdupq_n_u16(0x80);
+ for (; x < length - 3; x += 4) {
+ const uint32x4_t src32 = vld1q_u32((uint32_t *)&src[x]);
+ const uint8x16_t src8 = vreinterpretq_u8_u32(src32);
+ uint8x16_t dst8 = vld1q_u8((uint8_t *)&dst[x]);
+ uint8x16_t result = vqaddq_u8(dst8, src8);
+
+ uint16x8_t result_low = vmovl_u8(vget_low_u8(result));
+ uint16x8_t result_high = vmovl_u8(vget_high_u8(result));
+
+ uint16x8_t dst_low = vmovl_u8(vget_low_u8(dst8));
+ uint16x8_t dst_high = vmovl_u8(vget_high_u8(dst8));
+
+ result_low = qvinterpolate_pixel_255(result_low, constAlphaVector, dst_low, oneMinusconstAlphaVector, half);
+ result_high = qvinterpolate_pixel_255(result_high, constAlphaVector, dst_high, oneMinusconstAlphaVector, half);
+
+ const uint32x2_t result32_low = vreinterpret_u32_u8(vmovn_u16(result_low));
+ const uint32x2_t result32_high = vreinterpret_u32_u8(vmovn_u16(result_high));
+ vst1q_u32((uint32_t *)&dst[x], vcombine_u32(result32_low, result32_high));
+ }
+
+ for (; x < length; ++x)
+ dst[x] = comp_func_Plus_one_pixel_const_alpha(dst[x], src[x], const_alpha, one_minus_const_alpha);
+ }
+}
+
+static const int tileSize = 32;
+
+extern "C" void qt_rotate90_16_neon(quint16 *dst, const quint16 *src, int sstride, int dstride, int count);
+
+void qt_memrotate90_16_neon(const uchar *srcPixels, int w, int h, int sstride, uchar *destPixels, int dstride)
+{
+ const ushort *src = (const ushort *)srcPixels;
+ ushort *dest = (ushort *)destPixels;
+
+ sstride /= sizeof(ushort);
+ dstride /= sizeof(ushort);
+
+ const int pack = sizeof(quint32) / sizeof(ushort);
+ const int unaligned =
+ qMin(uint((quintptr(dest) & (sizeof(quint32)-1)) / sizeof(ushort)), uint(h));
+ const int restX = w % tileSize;
+ const int restY = (h - unaligned) % tileSize;
+ const int unoptimizedY = restY % pack;
+ const int numTilesX = w / tileSize + (restX > 0);
+ const int numTilesY = (h - unaligned) / tileSize + (restY >= pack);
+
+ for (int tx = 0; tx < numTilesX; ++tx) {
+ const int startx = w - tx * tileSize - 1;
+ const int stopx = qMax(startx - tileSize, 0);
+
+ if (unaligned) {
+ for (int x = startx; x >= stopx; --x) {
+ ushort *d = dest + (w - x - 1) * dstride;
+ for (int y = 0; y < unaligned; ++y) {
+ *d++ = src[y * sstride + x];
+ }
+ }
+ }
+
+ for (int ty = 0; ty < numTilesY; ++ty) {
+ const int starty = ty * tileSize + unaligned;
+ const int stopy = qMin(starty + tileSize, h - unoptimizedY);
+
+ int x = startx;
+ // qt_rotate90_16_neon writes to eight rows, four pixels at a time
+ for (; x >= stopx + 7; x -= 8) {
+ ushort *d = dest + (w - x - 1) * dstride + starty;
+ const ushort *s = &src[starty * sstride + x - 7];
+ qt_rotate90_16_neon(d, s, sstride * 2, dstride * 2, stopy - starty);
+ }
+
+ for (; x >= stopx; --x) {
+ quint32 *d = reinterpret_cast<quint32*>(dest + (w - x - 1) * dstride + starty);
+ for (int y = starty; y < stopy; y += pack) {
+ quint32 c = src[y * sstride + x];
+ for (int i = 1; i < pack; ++i) {
+ const int shift = (sizeof(int) * 8 / pack * i);
+ const ushort color = src[(y + i) * sstride + x];
+ c |= color << shift;
+ }
+ *d++ = c;
+ }
+ }
+ }
+
+ if (unoptimizedY) {
+ const int starty = h - unoptimizedY;
+ for (int x = startx; x >= stopx; --x) {
+ ushort *d = dest + (w - x - 1) * dstride + starty;
+ for (int y = starty; y < h; ++y) {
+ *d++ = src[y * sstride + x];
+ }
+ }
+ }
+ }
+}
+
+extern "C" void qt_rotate270_16_neon(quint16 *dst, const quint16 *src, int sstride, int dstride, int count);
+
+void qt_memrotate270_16_neon(const uchar *srcPixels, int w, int h,
+ int sstride,
+ uchar *destPixels, int dstride)
+{
+ const ushort *src = (const ushort *)srcPixels;
+ ushort *dest = (ushort *)destPixels;
+
+ sstride /= sizeof(ushort);
+ dstride /= sizeof(ushort);
+
+ const int pack = sizeof(quint32) / sizeof(ushort);
+ const int unaligned =
+ qMin(uint((long(dest) & (sizeof(quint32)-1)) / sizeof(ushort)), uint(h));
+ const int restX = w % tileSize;
+ const int restY = (h - unaligned) % tileSize;
+ const int unoptimizedY = restY % pack;
+ const int numTilesX = w / tileSize + (restX > 0);
+ const int numTilesY = (h - unaligned) / tileSize + (restY >= pack);
+
+ for (int tx = 0; tx < numTilesX; ++tx) {
+ const int startx = tx * tileSize;
+ const int stopx = qMin(startx + tileSize, w);
+
+ if (unaligned) {
+ for (int x = startx; x < stopx; ++x) {
+ ushort *d = dest + x * dstride;
+ for (int y = h - 1; y >= h - unaligned; --y) {
+ *d++ = src[y * sstride + x];
+ }
+ }
+ }
+
+ for (int ty = 0; ty < numTilesY; ++ty) {
+ const int starty = h - 1 - unaligned - ty * tileSize;
+ const int stopy = qMax(starty - tileSize, unoptimizedY);
+
+ int x = startx;
+ // qt_rotate90_16_neon writes to eight rows, four pixels at a time
+ for (; x < stopx - 7; x += 8) {
+ ushort *d = dest + x * dstride + h - 1 - starty;
+ const ushort *s = &src[starty * sstride + x];
+ qt_rotate90_16_neon(d + 7 * dstride, s, -sstride * 2, -dstride * 2, starty - stopy);
+ }
+
+ for (; x < stopx; ++x) {
+ quint32 *d = reinterpret_cast<quint32*>(dest + x * dstride
+ + h - 1 - starty);
+ for (int y = starty; y > stopy; y -= pack) {
+ quint32 c = src[y * sstride + x];
+ for (int i = 1; i < pack; ++i) {
+ const int shift = (sizeof(int) * 8 / pack * i);
+ const ushort color = src[(y - i) * sstride + x];
+ c |= color << shift;
+ }
+ *d++ = c;
+ }
+ }
+ }
+ if (unoptimizedY) {
+ const int starty = unoptimizedY - 1;
+ for (int x = startx; x < stopx; ++x) {
+ ushort *d = dest + x * dstride + h - 1 - starty;
+ for (int y = starty; y >= 0; --y) {
+ *d++ = src[y * sstride + x];
+ }
+ }
+ }
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_HAVE_NEON
+
diff --git a/src/gui/painting/qdrawhelper_neon_asm.S b/src/gui/painting/qdrawhelper_neon_asm.S
new file mode 100644
index 0000000000..e8434fc8e7
--- /dev/null
+++ b/src/gui/painting/qdrawhelper_neon_asm.S
@@ -0,0 +1,297 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/* Prevent the stack from becoming executable for no reason... */
+#if defined(__linux__) && defined(__ELF__)
+.section .note.GNU-stack,"",%progbits
+#endif
+
+.text
+.fpu neon
+.arch armv7a
+.altmacro
+
+/* void blend_8_pixels_argb32_on_rgb16_neon(quint16 *dst, const quint32 *src, int const_alpha) */
+
+ .func blend_8_pixels_argb32_on_rgb16_neon
+ .global blend_8_pixels_argb32_on_rgb16_neon
+ /* For ELF format also set function visibility to hidden */
+#ifdef __ELF__
+ .hidden blend_8_pixels_argb32_on_rgb16_neon
+ .type blend_8_pixels_argb32_on_rgb16_neon, %function
+#endif
+blend_8_pixels_argb32_on_rgb16_neon:
+ vld4.8 { d0, d1, d2, d3 }, [r1]
+ vld1.16 { d4, d5 }, [r0]
+
+ cmp r2, #256
+ beq .blend_32_inner
+
+ vdup.8 d6, r2
+
+ /* multiply by const_alpha */
+ vmull.u8 q8, d6, d0
+ vmull.u8 q9, d6, d1
+ vmull.u8 q10, d6, d2
+ vmull.u8 q11, d6, d3
+
+ vshrn.u16 d0, q8, #8
+ vshrn.u16 d1, q9, #8
+ vshrn.u16 d2, q10, #8
+ vshrn.u16 d3, q11, #8
+
+.blend_32_inner:
+ /* convert 8 r5g6b5 pixel data from {d4, d5} to planar 8-bit format
+ and put data into d6 - red, d7 - green, d30 - blue */
+ vshrn.u16 d6, q2, #8
+ vshrn.u16 d7, q2, #3
+ vsli.u16 q2, q2, #5
+ vsri.u8 d6, d6, #5
+ vmvn.8 d3, d3
+ vsri.u8 d7, d7, #6
+ vshrn.u16 d30, q2, #2
+
+ pld [r0, #128]
+
+ /* now do alpha blending, storing results in 8-bit planar format
+ into d16 - red, d19 - green, d18 - blue */
+ vmull.u8 q10, d3, d6
+ vmull.u8 q11, d3, d7
+ vmull.u8 q12, d3, d30
+ vrshr.u16 q13, q10, #8
+ vrshr.u16 q3, q11, #8
+ vrshr.u16 q15, q12, #8
+ vraddhn.u16 d20, q10, q13
+ vraddhn.u16 d23, q11, q3
+ vraddhn.u16 d22, q12, q15
+ vqadd.u8 d16, d2, d20
+ vqadd.u8 q9, q0, q11
+ /* convert the result to r5g6b5 and store it into {d28, d29} */
+ vshll.u8 q14, d16, #8
+ vshll.u8 q8, d19, #8
+ vshll.u8 q9, d18, #8
+ vsri.u16 q14, q8, #5
+ vsri.u16 q14, q9, #11
+
+ vst1.16 { d28, d29 }, [r0]
+
+ bx lr
+
+ .endfunc
+
+/* void blend_8_pixels_rgb16_on_rgb16_neon(quint16 *dst, const quint16 *src, int const_alpha) */
+
+ .func blend_8_pixels_rgb16_on_rgb16_neon
+ .global blend_8_pixels_rgb16_on_rgb16_neon
+ /* For ELF format also set function visibility to hidden */
+#ifdef __ELF__
+ .hidden blend_8_pixels_rgb16_on_rgb16_neon
+ .type blend_8_pixels_rgb16_on_rgb16_neon, %function
+#endif
+blend_8_pixels_rgb16_on_rgb16_neon:
+ vld1.16 { d0, d1 }, [r0]
+ vld1.16 { d2, d3 }, [r1]
+
+ rsb r3, r2, #256
+ vdup.8 d4, r2
+ vdup.8 d5, r3
+
+ /* convert 8 r5g6b5 pixel data from {d0, d1} to planar 8-bit format
+ and put data into d6 - red, d7 - green, d30 - blue */
+ vshrn.u16 d6, q0, #8
+ vshrn.u16 d7, q0, #3
+ vsli.u16 q0, q0, #5
+ vsri.u8 d6, d6, #5
+ vsri.u8 d7, d7, #6
+ vshrn.u16 d30, q0, #2
+
+ /* same from {d2, d3} into {d26, d27, d28} */
+ vshrn.u16 d26, q1, #8
+ vshrn.u16 d27, q1, #3
+ vsli.u16 q1, q1, #5
+ vsri.u8 d26, d26, #5
+ vsri.u8 d27, d27, #6
+ vshrn.u16 d28, q1, #2
+
+ /* multiply dst by inv const_alpha */
+ vmull.u8 q10, d5, d6
+ vmull.u8 q11, d5, d7
+ vmull.u8 q12, d5, d30
+
+ vshrn.u16 d6, q10, #8
+ vshrn.u16 d7, q11, #8
+ vshrn.u16 d30, q12, #8
+
+ /* multiply src by const_alpha */
+ vmull.u8 q10, d4, d26
+ vmull.u8 q11, d4, d27
+ vmull.u8 q12, d4, d28
+
+ vshrn.u16 d26, q10, #8
+ vshrn.u16 d27, q11, #8
+ vshrn.u16 d28, q12, #8
+
+ /* preload dst + 128 */
+ pld [r0, #128]
+
+ /* add components, storing results in 8-bit planar format
+ into d16 - red, d19 - green, d18 - blue */
+ vadd.u8 d16, d26, d6
+ vadd.u8 d19, d27, d7
+ vadd.u8 d18, d28, d30
+
+ /* convert the result to r5g6b5 and store it into {d28, d29} */
+ vshll.u8 q14, d16, #8
+ vshll.u8 q8, d19, #8
+ vshll.u8 q9, d18, #8
+ vsri.u16 q14, q8, #5
+ vsri.u16 q14, q9, #11
+
+ vst1.16 { d28, d29 }, [r0]
+
+ bx lr
+
+ .endfunc
+
+/* void qt_rotate90_16_neon(quint16 *dst, const quint16 *src, int sstride, int dstride, int count) */
+ .func qt_rotate90_16_neon
+ .global qt_rotate90_16_neon
+ /* For ELF format also set function visibility to hidden */
+#ifdef __ELF__
+ .hidden qt_rotate90_16_neon
+ .type qt_rotate90_16_neon, %function
+#endif
+qt_rotate90_16_neon:
+ push { r4-r11, lr }
+ ldr r5, [sp, #(9*4)]
+
+ /* The preloads are the key to getting good performance */
+ pld [r1]
+
+ mov r4, r5, asr #2
+ add r6, r0, r3
+ add r7, r6, r3
+
+ add r8, r7, r3
+ add r9, r8, r3
+
+ pld [r1, r2]
+
+ add r10, r9, r3
+ add r11, r10, r3
+
+ add r3, r3, r11
+ and r5, r5, #3
+
+ pld [r1, r2, lsl #1]
+
+ cmp r4, #0
+ beq .rotate90_16_tail
+
+.rotate90_16_loop:
+ vld1.16 { q8 }, [r1], r2
+
+ pld [r1, r2, lsl #1]
+
+ vld1.16 { q9 }, [r1], r2
+ vld1.16 { q10 }, [r1], r2
+ vld1.16 { q11 }, [r1], r2
+
+ pld [r1]
+
+ /* Could have used four quad-word zips instead,
+ but those take three cycles as opposed to one. */
+ vzip.16 d16, d20
+ vzip.16 d17, d21
+
+ vzip.16 d18, d22
+
+ pld [r1, r2]
+
+ vzip.16 d19, d23
+
+ vzip.16 d16, d18
+ vzip.16 d17, d19
+
+ pld [r1, r2, lsl #1]
+
+ vzip.16 d20, d22
+ vzip.16 d21, d23
+
+ vst1.16 { d23 }, [r0]!
+ vst1.16 { d21 }, [r6]!
+ vst1.16 { d19 }, [r7]!
+ vst1.16 { d17 }, [r8]!
+ vst1.16 { d22 }, [r9]!
+ vst1.16 { d20 }, [r10]!
+ vst1.16 { d18 }, [r11]!
+ vst1.16 { d16 }, [r3]!
+
+ sub r4, r4, #1
+ cmp r4, #0
+ bne .rotate90_16_loop
+ b .rotate90_16_tail
+
+.rotate90_16_tail_loop:
+ sub r5, r5, #2
+
+ vld1.16 { q8 }, [r1], r2
+ vld1.16 { q9 }, [r1], r2
+
+ vzip.16 d16, d18
+ vzip.16 d17, d19
+
+ vst1.32 { d19[1] }, [r0]!
+ vst1.32 { d19[0] }, [r6]!
+ vst1.32 { d17[1] }, [r7]!
+ vst1.32 { d17[0] }, [r8]!
+ vst1.32 { d18[1] }, [r9]!
+ vst1.32 { d18[0] }, [r10]!
+ vst1.32 { d16[1] }, [r11]!
+ vst1.32 { d16[0] }, [r3]!
+
+.rotate90_16_tail:
+ cmp r5, #0
+ bgt .rotate90_16_tail_loop
+
+ pop { r4-r11, pc }
+
+ .endfunc
diff --git a/src/gui/painting/qdrawhelper_neon_p.h b/src/gui/painting/qdrawhelper_neon_p.h
new file mode 100644
index 0000000000..5db66932a1
--- /dev/null
+++ b/src/gui/painting/qdrawhelper_neon_p.h
@@ -0,0 +1,146 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDRAWHELPER_NEON_P_H
+#define QDRAWHELPER_NEON_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qdrawhelper_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifdef QT_HAVE_NEON
+
+void qt_blend_argb32_on_argb32_neon(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha);
+
+void qt_blend_rgb32_on_rgb32_neon(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha);
+
+void qt_blend_argb32_on_rgb16_neon(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha);
+
+void qt_blend_argb32_on_argb32_scanline_neon(uint *dest,
+ const uint *src,
+ int length,
+ uint const_alpha);
+
+void qt_blend_rgb16_on_argb32_neon(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha);
+
+void qt_blend_rgb16_on_rgb16_neon(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha);
+
+void qt_alphamapblit_quint16_neon(QRasterBuffer *rasterBuffer,
+ int x, int y, quint32 color,
+ const uchar *bitmap,
+ int mapWidth, int mapHeight, int mapStride,
+ const QClipData *clip);
+
+void qt_scale_image_argb32_on_rgb16_neon(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ const QRectF &targetRect,
+ const QRectF &sourceRect,
+ const QRect &clip,
+ int const_alpha);
+
+void qt_scale_image_rgb16_on_rgb16_neon(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ const QRectF &targetRect,
+ const QRectF &sourceRect,
+ const QRect &clip,
+ int const_alpha);
+
+void qt_transform_image_argb32_on_rgb16_neon(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ const QRectF &targetRect,
+ const QRectF &sourceRect,
+ const QRect &clip,
+ const QTransform &targetRectTransform,
+ int const_alpha);
+
+void qt_transform_image_rgb16_on_rgb16_neon(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ const QRectF &targetRect,
+ const QRectF &sourceRect,
+ const QRect &clip,
+ const QTransform &targetRectTransform,
+ int const_alpha);
+
+void qt_memfill32_neon(quint32 *dest, quint32 value, int count);
+void qt_memrotate90_16_neon(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl);
+void qt_memrotate270_16_neon(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl);
+
+uint * QT_FASTCALL qt_destFetchRGB16_neon(uint *buffer,
+ QRasterBuffer *rasterBuffer,
+ int x, int y, int length);
+
+void QT_FASTCALL qt_destStoreRGB16_neon(QRasterBuffer *rasterBuffer,
+ int x, int y, const uint *buffer, int length);
+
+void QT_FASTCALL comp_func_solid_SourceOver_neon(uint *destPixels, int length, uint color, uint const_alpha);
+void QT_FASTCALL comp_func_Plus_neon(uint *dst, const uint *src, int length, uint const_alpha);
+
+#endif // QT_HAVE_NEON
+
+QT_END_NAMESPACE
+
+#endif // QDRAWHELPER_NEON_P_H
diff --git a/src/gui/painting/qdrawhelper_p.h b/src/gui/painting/qdrawhelper_p.h
new file mode 100644
index 0000000000..d4e731bbb2
--- /dev/null
+++ b/src/gui/painting/qdrawhelper_p.h
@@ -0,0 +1,2033 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDRAWHELPER_P_H
+#define QDRAWHELPER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtCore/qglobal.h"
+#include "QtGui/qcolor.h"
+#include "QtGui/qpainter.h"
+#include "QtGui/qimage.h"
+#ifndef QT_FT_BEGIN_HEADER
+#define QT_FT_BEGIN_HEADER
+#define QT_FT_END_HEADER
+#endif
+#include "private/qrasterdefs_p.h"
+#include <private/qsimd_p.h>
+
+#ifdef Q_WS_QWS
+#include "QtGui/qscreen_qws.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#if defined(Q_CC_MSVC) && _MSCVER <= 1300 && !defined(Q_CC_INTEL)
+#define Q_STATIC_TEMPLATE_SPECIALIZATION static
+#else
+#define Q_STATIC_TEMPLATE_SPECIALIZATION
+#endif
+
+#if defined(Q_CC_RVCT)
+// RVCT doesn't like static template functions
+# define Q_STATIC_TEMPLATE_FUNCTION
+# define Q_STATIC_INLINE_FUNCTION static __forceinline
+#else
+# define Q_STATIC_TEMPLATE_FUNCTION static
+# define Q_STATIC_INLINE_FUNCTION static inline
+#endif
+
+static const uint AMASK = 0xff000000;
+static const uint RMASK = 0x00ff0000;
+static const uint GMASK = 0x0000ff00;
+static const uint BMASK = 0x000000ff;
+
+/*******************************************************************************
+ * QSpan
+ *
+ * duplicate definition of FT_Span
+ */
+typedef QT_FT_Span QSpan;
+
+struct QSolidData;
+struct QTextureData;
+struct QGradientData;
+struct QLinearGradientData;
+struct QRadialGradientData;
+struct QConicalGradientData;
+struct QSpanData;
+class QGradient;
+class QRasterBuffer;
+class QClipData;
+class QRasterPaintEngineState;
+
+typedef QT_FT_SpanFunc ProcessSpans;
+typedef void (*BitmapBlitFunc)(QRasterBuffer *rasterBuffer,
+ int x, int y, quint32 color,
+ const uchar *bitmap,
+ int mapWidth, int mapHeight, int mapStride);
+
+typedef void (*AlphamapBlitFunc)(QRasterBuffer *rasterBuffer,
+ int x, int y, quint32 color,
+ const uchar *bitmap,
+ int mapWidth, int mapHeight, int mapStride,
+ const QClipData *clip);
+
+typedef void (*AlphaRGBBlitFunc)(QRasterBuffer *rasterBuffer,
+ int x, int y, quint32 color,
+ const uint *rgbmask,
+ int mapWidth, int mapHeight, int mapStride,
+ const QClipData *clip);
+
+typedef void (*RectFillFunc)(QRasterBuffer *rasterBuffer,
+ int x, int y, int width, int height,
+ quint32 color);
+
+typedef void (*SrcOverBlendFunc)(uchar *destPixels, int dbpl,
+ const uchar *src, int spbl,
+ int w, int h,
+ int const_alpha);
+
+typedef void (*SrcOverScaleFunc)(uchar *destPixels, int dbpl,
+ const uchar *src, int spbl,
+ const QRectF &targetRect,
+ const QRectF &sourceRect,
+ const QRect &clipRect,
+ int const_alpha);
+
+typedef void (*SrcOverTransformFunc)(uchar *destPixels, int dbpl,
+ const uchar *src, int spbl,
+ const QRectF &targetRect,
+ const QRectF &sourceRect,
+ const QRect &clipRect,
+ const QTransform &targetRectTransform,
+ int const_alpha);
+
+typedef void (*MemRotateFunc)(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl);
+
+struct DrawHelper {
+ ProcessSpans blendColor;
+ ProcessSpans blendGradient;
+ BitmapBlitFunc bitmapBlit;
+ AlphamapBlitFunc alphamapBlit;
+ AlphaRGBBlitFunc alphaRGBBlit;
+ RectFillFunc fillRect;
+};
+
+extern SrcOverBlendFunc qBlendFunctions[QImage::NImageFormats][QImage::NImageFormats];
+extern SrcOverScaleFunc qScaleFunctions[QImage::NImageFormats][QImage::NImageFormats];
+extern SrcOverTransformFunc qTransformFunctions[QImage::NImageFormats][QImage::NImageFormats];
+extern MemRotateFunc qMemRotateFunctions[QImage::NImageFormats][3];
+
+extern DrawHelper qDrawHelper[QImage::NImageFormats];
+
+void qBlendTexture(int count, const QSpan *spans, void *userData);
+#if defined(Q_WS_QWS) && !defined(QT_NO_RASTERCALLBACKS)
+extern DrawHelper qDrawHelperCallback[QImage::NImageFormats];
+void qBlendTextureCallback(int count, const QSpan *spans, void *userData);
+#endif
+
+typedef void (QT_FASTCALL *CompositionFunction)(uint *dest, const uint *src, int length, uint const_alpha);
+typedef void (QT_FASTCALL *CompositionFunctionSolid)(uint *dest, int length, uint color, uint const_alpha);
+
+void qInitDrawhelperAsm();
+
+class QRasterPaintEngine;
+
+struct QSolidData
+{
+ uint color;
+};
+
+struct QLinearGradientData
+{
+ struct {
+ qreal x;
+ qreal y;
+ } origin;
+ struct {
+ qreal x;
+ qreal y;
+ } end;
+};
+
+struct QRadialGradientData
+{
+ struct {
+ qreal x;
+ qreal y;
+ } center;
+ struct {
+ qreal x;
+ qreal y;
+ } focal;
+ qreal radius;
+};
+
+struct QConicalGradientData
+{
+ struct {
+ qreal x;
+ qreal y;
+ } center;
+ qreal angle;
+};
+
+struct QGradientData
+{
+ QGradient::Spread spread;
+
+ union {
+ QLinearGradientData linear;
+ QRadialGradientData radial;
+ QConicalGradientData conical;
+ };
+
+#ifdef Q_WS_QWS
+#define GRADIENT_STOPTABLE_SIZE 256
+#else
+#define GRADIENT_STOPTABLE_SIZE 1024
+#endif
+
+ uint* colorTable; //[GRADIENT_STOPTABLE_SIZE];
+
+ uint alphaColor : 1;
+};
+
+struct QTextureData
+{
+ const uchar *imageData;
+ const uchar *scanLine(int y) const { return imageData + y*bytesPerLine; }
+
+ int width;
+ int height;
+ // clip rect
+ int x1;
+ int y1;
+ int x2;
+ int y2;
+ int bytesPerLine;
+ QImage::Format format;
+ const QVector<QRgb> *colorTable;
+ bool hasAlpha;
+ enum Type {
+ Plain,
+ Tiled
+ };
+ Type type;
+ int const_alpha;
+};
+
+struct QSpanData
+{
+ QSpanData() : tempImage(0) {}
+ ~QSpanData() { delete tempImage; }
+
+ QRasterBuffer *rasterBuffer;
+#ifdef Q_WS_QWS
+ QRasterPaintEngine *rasterEngine;
+#endif
+ ProcessSpans blend;
+ ProcessSpans unclipped_blend;
+ BitmapBlitFunc bitmapBlit;
+ AlphamapBlitFunc alphamapBlit;
+ AlphaRGBBlitFunc alphaRGBBlit;
+ RectFillFunc fillRect;
+ qreal m11, m12, m13, m21, m22, m23, m33, dx, dy; // inverse xform matrix
+ const QClipData *clip;
+ enum Type {
+ None,
+ Solid,
+ LinearGradient,
+ RadialGradient,
+ ConicalGradient,
+ Texture
+ } type : 8;
+ int txop : 8;
+ int fast_matrix : 1;
+ bool bilinear;
+ QImage *tempImage;
+ union {
+ QSolidData solid;
+ QGradientData gradient;
+ QTextureData texture;
+ };
+
+ void init(QRasterBuffer *rb, const QRasterPaintEngine *pe);
+ void setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode);
+ void setupMatrix(const QTransform &matrix, int bilinear);
+ void initTexture(const QImage *image, int alpha, QTextureData::Type = QTextureData::Plain, const QRect &sourceRect = QRect());
+ void adjustSpanMethods();
+};
+
+#if defined(Q_CC_RVCT)
+# pragma push
+# pragma arm
+#endif
+Q_STATIC_INLINE_FUNCTION uint INTERPOLATE_PIXEL_255(uint x, uint a, uint y, uint b) {
+ uint t = (x & 0xff00ff) * a + (y & 0xff00ff) * b;
+ t = (t + ((t >> 8) & 0xff00ff) + 0x800080) >> 8;
+ t &= 0xff00ff;
+
+ x = ((x >> 8) & 0xff00ff) * a + ((y >> 8) & 0xff00ff) * b;
+ x = (x + ((x >> 8) & 0xff00ff) + 0x800080);
+ x &= 0xff00ff00;
+ x |= t;
+ return x;
+}
+#if defined(Q_CC_RVCT)
+# pragma pop
+#endif
+
+#if QT_POINTER_SIZE == 8 // 64-bit versions
+
+Q_STATIC_INLINE_FUNCTION uint INTERPOLATE_PIXEL_256(uint x, uint a, uint y, uint b) {
+ quint64 t = (((quint64(x)) | ((quint64(x)) << 24)) & 0x00ff00ff00ff00ff) * a;
+ t += (((quint64(y)) | ((quint64(y)) << 24)) & 0x00ff00ff00ff00ff) * b;
+ t >>= 8;
+ t &= 0x00ff00ff00ff00ff;
+ return (uint(t)) | (uint(t >> 24));
+}
+
+Q_STATIC_INLINE_FUNCTION uint BYTE_MUL(uint x, uint a) {
+ quint64 t = (((quint64(x)) | ((quint64(x)) << 24)) & 0x00ff00ff00ff00ff) * a;
+ t = (t + ((t >> 8) & 0xff00ff00ff00ff) + 0x80008000800080) >> 8;
+ t &= 0x00ff00ff00ff00ff;
+ return (uint(t)) | (uint(t >> 24));
+}
+
+Q_STATIC_INLINE_FUNCTION uint PREMUL(uint x) {
+ uint a = x >> 24;
+ quint64 t = (((quint64(x)) | ((quint64(x)) << 24)) & 0x00ff00ff00ff00ff) * a;
+ t = (t + ((t >> 8) & 0xff00ff00ff00ff) + 0x80008000800080) >> 8;
+ t &= 0x000000ff00ff00ff;
+ return (uint(t)) | (uint(t >> 24)) | (a << 24);
+}
+
+#else // 32-bit versions
+
+Q_STATIC_INLINE_FUNCTION uint INTERPOLATE_PIXEL_256(uint x, uint a, uint y, uint b) {
+ uint t = (x & 0xff00ff) * a + (y & 0xff00ff) * b;
+ t >>= 8;
+ t &= 0xff00ff;
+
+ x = ((x >> 8) & 0xff00ff) * a + ((y >> 8) & 0xff00ff) * b;
+ x &= 0xff00ff00;
+ x |= t;
+ return x;
+}
+
+#if defined(Q_CC_RVCT)
+# pragma push
+# pragma arm
+#endif
+Q_STATIC_INLINE_FUNCTION uint BYTE_MUL(uint x, uint a) {
+ uint t = (x & 0xff00ff) * a;
+ t = (t + ((t >> 8) & 0xff00ff) + 0x800080) >> 8;
+ t &= 0xff00ff;
+
+ x = ((x >> 8) & 0xff00ff) * a;
+ x = (x + ((x >> 8) & 0xff00ff) + 0x800080);
+ x &= 0xff00ff00;
+ x |= t;
+ return x;
+}
+#if defined(Q_CC_RVCT)
+# pragma pop
+#endif
+
+Q_STATIC_INLINE_FUNCTION uint PREMUL(uint x) {
+ uint a = x >> 24;
+ uint t = (x & 0xff00ff) * a;
+ t = (t + ((t >> 8) & 0xff00ff) + 0x800080) >> 8;
+ t &= 0xff00ff;
+
+ x = ((x >> 8) & 0xff) * a;
+ x = (x + ((x >> 8) & 0xff) + 0x80);
+ x &= 0xff00;
+ x |= t | (a << 24);
+ return x;
+}
+#endif
+
+
+Q_STATIC_INLINE_FUNCTION uint BYTE_MUL_RGB16(uint x, uint a) {
+ a += 1;
+ uint t = (((x & 0x07e0)*a) >> 8) & 0x07e0;
+ t |= (((x & 0xf81f)*(a>>2)) >> 6) & 0xf81f;
+ return t;
+}
+
+Q_STATIC_INLINE_FUNCTION uint BYTE_MUL_RGB16_32(uint x, uint a) {
+ uint t = (((x & 0xf81f07e0) >> 5)*a) & 0xf81f07e0;
+ t |= (((x & 0x07e0f81f)*a) >> 5) & 0x07e0f81f;
+ return t;
+}
+
+#define INV_PREMUL(p) \
+ (qAlpha(p) == 0 ? 0 : \
+ ((qAlpha(p) << 24) \
+ | (((255*qRed(p))/ qAlpha(p)) << 16) \
+ | (((255*qGreen(p)) / qAlpha(p)) << 8) \
+ | ((255*qBlue(p)) / qAlpha(p))))
+
+template <class DST, class SRC>
+inline DST qt_colorConvert(SRC color, DST dummy)
+{
+ Q_UNUSED(dummy);
+ return DST(color);
+}
+
+
+template <>
+inline quint32 qt_colorConvert(quint16 color, quint32 dummy)
+{
+ Q_UNUSED(dummy);
+ const int r = (color & 0xf800);
+ const int g = (color & 0x07e0);
+ const int b = (color & 0x001f);
+ const int tr = (r >> 8) | (r >> 13);
+ const int tg = (g >> 3) | (g >> 9);
+ const int tb = (b << 3) | (b >> 2);
+
+ return qRgb(tr, tg, tb);
+}
+
+template <>
+inline quint16 qt_colorConvert(quint32 color, quint16 dummy)
+{
+ Q_UNUSED(dummy);
+ const int r = qRed(color) << 8;
+ const int g = qGreen(color) << 3;
+ const int b = qBlue(color) >> 3;
+
+ return (r & 0xf800) | (g & 0x07e0)| (b & 0x001f);
+}
+
+class quint32p
+{
+public:
+ inline quint32p(quint32 v) : data(PREMUL(v)) {}
+
+ inline operator quint32() const { return data; }
+
+ inline operator quint16() const
+ {
+ return qt_colorConvert<quint16, quint32>(data, 0);
+ }
+
+ Q_STATIC_INLINE_FUNCTION quint32p fromRawData(quint32 v)
+ {
+ quint32p p;
+ p.data = v;
+ return p;
+ }
+
+private:
+ quint32p() {}
+ quint32 data;
+} Q_PACKED;
+
+class qabgr8888
+{
+public:
+ inline qabgr8888(quint32 v)
+ {
+ data = qRgba(qBlue(v), qGreen(v), qRed(v), qAlpha(v));
+ }
+
+ inline bool operator==(const qabgr8888 &v) const { return data == v.data; }
+
+private:
+ quint32 data;
+} Q_PACKED;
+
+class qrgb565;
+
+class qargb8565
+{
+public:
+ Q_STATIC_INLINE_FUNCTION bool hasAlpha() { return true; }
+
+ inline qargb8565() {}
+ inline qargb8565(quint32 v);
+ inline explicit qargb8565(quint32p v);
+ inline qargb8565(const qargb8565 &v);
+ inline qargb8565(const qrgb565 &v);
+
+ inline operator quint32() const;
+ inline operator quint16() const;
+
+ inline quint8 alpha() const { return data[0]; }
+ inline qargb8565 truncedAlpha() {
+ data[0] &= 0xf8;
+ data[1] &= 0xdf;
+ return *this;
+ }
+ Q_STATIC_INLINE_FUNCTION quint8 alpha(quint8 a) { return (a + 1) >> 3; }
+ Q_STATIC_INLINE_FUNCTION quint8 ialpha(quint8 a) { return 0x20 - alpha(a); }
+
+ inline qargb8565 byte_mul(quint8 a) const;
+ inline qargb8565 operator+(qargb8565 v) const;
+ inline bool operator==(const qargb8565 &v) const;
+
+ inline quint32 rawValue() const;
+ inline quint16 rawValue16() const;
+
+private:
+ friend class qrgb565;
+
+ quint8 data[3];
+} Q_PACKED;
+
+class qrgb565
+{
+public:
+ Q_STATIC_INLINE_FUNCTION bool hasAlpha() { return false; }
+
+ qrgb565(int v = 0) : data(v) {}
+
+ inline explicit qrgb565(quint32p v);
+ inline explicit qrgb565(quint32 v);
+ inline explicit qrgb565(const qargb8565 &v);
+
+ inline operator quint32() const;
+ inline operator quint16() const;
+
+ inline qrgb565 operator+(qrgb565 v) const;
+
+ inline quint8 alpha() const { return 0xff; }
+ inline qrgb565 truncedAlpha() { return *this; }
+ Q_STATIC_INLINE_FUNCTION quint8 alpha(quint8 a) { return (a + 1) >> 3; }
+ Q_STATIC_INLINE_FUNCTION quint8 ialpha(quint8 a) { return 0x20 - alpha(a); }
+
+ inline qrgb565 byte_mul(quint8 a) const;
+
+ inline bool operator==(const qrgb565 &v) const;
+ inline quint16 rawValue() const { return data; }
+
+private:
+ friend class qargb8565;
+
+ quint16 data;
+} Q_PACKED;
+
+qargb8565::qargb8565(quint32 v)
+{
+ *this = qargb8565(quint32p(v));
+}
+
+qargb8565::qargb8565(quint32p v)
+{
+ data[0] = qAlpha(v);
+ const int r = qRed(v);
+ const int g = qGreen(v);
+ const int b = qBlue(v);
+ data[1] = ((g << 3) & 0xe0) | (b >> 3);
+ data[2] = (r & 0xf8) | (g >> 5);
+}
+
+qargb8565::qargb8565(const qargb8565 &v)
+{
+ data[0] = v.data[0];
+ data[1] = v.data[1];
+ data[2] = v.data[2];
+}
+
+qargb8565::qargb8565(const qrgb565 &v)
+{
+ data[0] = 0xff;
+ data[1] = v.data & 0xff;
+ data[2] = v.data >> 8;
+}
+
+qargb8565::operator quint32() const
+{
+ const quint16 rgb = (data[2] << 8) | data[1];
+ const int a = data[0];
+ const int r = (rgb & 0xf800);
+ const int g = (rgb & 0x07e0);
+ const int b = (rgb & 0x001f);
+ const int tr = qMin(a, (r >> 8) | (r >> 13));
+ const int tg = qMin(a, (g >> 3) | (g >> 9));
+ const int tb = qMin(a, (b << 3) | (b >> 2));
+ return qRgba(tr, tg, tb, data[0]);
+}
+
+qargb8565::operator quint16() const
+{
+ return (data[2] << 8) | data[1];
+}
+
+qargb8565 qargb8565::operator+(qargb8565 v) const
+{
+ qargb8565 t;
+ t.data[0] = data[0] + v.data[0];
+ const quint16 rgb = ((data[2] + v.data[2]) << 8)
+ + (data[1] + v.data[1]);
+ t.data[1] = rgb & 0xff;
+ t.data[2] = rgb >> 8;
+ return t;
+}
+
+qargb8565 qargb8565::byte_mul(quint8 a) const
+{
+ qargb8565 result;
+ result.data[0] = (data[0] * a) >> 5;
+
+ const quint16 x = (data[2] << 8) | data[1];
+ const quint16 t = ((((x & 0x07e0) >> 5) * a) & 0x07e0) |
+ ((((x & 0xf81f) * a) >> 5) & 0xf81f);
+ result.data[1] = t & 0xff;
+ result.data[2] = t >> 8;
+ return result;
+}
+
+bool qargb8565::operator==(const qargb8565 &v) const
+{
+ return data[0] == v.data[0]
+ && data[1] == v.data[1]
+ && data[2] == v.data[2];
+}
+
+quint32 qargb8565::rawValue() const
+{
+ return (data[2] << 16) | (data[1] << 8) | data[0];
+}
+
+quint16 qargb8565::rawValue16() const
+{
+ return (data[2] << 8) | data[1];
+}
+
+qrgb565::qrgb565(quint32p v)
+{
+ *this = qrgb565(quint32(v));
+}
+
+qrgb565::qrgb565(quint32 v)
+{
+ const int r = qRed(v) << 8;
+ const int g = qGreen(v) << 3;
+ const int b = qBlue(v) >> 3;
+
+ data = (r & 0xf800) | (g & 0x07e0)| (b & 0x001f);
+}
+
+qrgb565::qrgb565(const qargb8565 &v)
+{
+ data = (v.data[2] << 8) | v.data[1];
+}
+
+qrgb565::operator quint32() const
+{
+ const int r = (data & 0xf800);
+ const int g = (data & 0x07e0);
+ const int b = (data & 0x001f);
+ const int tr = (r >> 8) | (r >> 13);
+ const int tg = (g >> 3) | (g >> 9);
+ const int tb = (b << 3) | (b >> 2);
+ return qRgb(tr, tg, tb);
+}
+
+qrgb565::operator quint16() const
+{
+ return data;
+}
+
+qrgb565 qrgb565::operator+(qrgb565 v) const
+{
+ qrgb565 t;
+ t.data = data + v.data;
+ return t;
+}
+
+qrgb565 qrgb565::byte_mul(quint8 a) const
+{
+ qrgb565 result;
+ result.data = ((((data & 0x07e0) >> 5) * a) & 0x07e0) |
+ ((((data & 0xf81f) * a) >> 5) & 0xf81f);
+ return result;
+}
+
+bool qrgb565::operator==(const qrgb565 &v) const
+{
+ return data == v.data;
+}
+
+class qbgr565
+{
+public:
+ inline qbgr565(quint16 v)
+ {
+ data = ((v & 0x001f) << 11) |
+ (v & 0x07e0) |
+ ((v & 0xf800) >> 11);
+ }
+
+ inline bool operator==(const qbgr565 &v) const
+ {
+ return data == v.data;
+ }
+
+private:
+ quint16 data;
+} Q_PACKED;
+
+class qrgb555;
+
+class qargb8555
+{
+public:
+ Q_STATIC_INLINE_FUNCTION bool hasAlpha() { return true; }
+
+ qargb8555() {}
+ inline qargb8555(quint32 v);
+ inline explicit qargb8555(quint32p v);
+ inline qargb8555(const qargb8555 &v);
+ inline qargb8555(const qrgb555 &v);
+
+ inline operator quint32() const;
+
+ inline quint8 alpha() const { return data[0]; }
+ inline qargb8555 truncedAlpha() { data[0] &= 0xf8; return *this; }
+ Q_STATIC_INLINE_FUNCTION quint8 alpha(quint8 a) { return (a + 1) >> 3; }
+ Q_STATIC_INLINE_FUNCTION quint8 ialpha(quint8 a) { return 0x20 - alpha(a); }
+
+ inline qargb8555 operator+(qargb8555 v) const;
+ inline qargb8555 byte_mul(quint8 a) const;
+
+ inline bool operator==(const qargb8555 &v) const;
+
+ inline quint32 rawValue() const;
+
+private:
+ friend class qrgb555;
+ quint8 data[3];
+} Q_PACKED;
+
+class qrgb555
+{
+public:
+ Q_STATIC_INLINE_FUNCTION bool hasAlpha() { return false; }
+
+ inline qrgb555(int v = 0) : data(v) {}
+
+ inline explicit qrgb555(quint32p v) { *this = qrgb555(quint32(v)); }
+
+ inline explicit qrgb555(quint32 v)
+ {
+ const int r = qRed(v) << 7;
+ const int g = qGreen(v) << 2;
+ const int b = qBlue(v) >> 3;
+
+ data = (r & 0x7c00) | (g & 0x03e0) | (b & 0x001f);
+ }
+
+ inline explicit qrgb555(quint16 v)
+ {
+ data = ((v >> 1) & (0x7c00 | 0x03e0)) |
+ (v & 0x001f);
+ }
+
+ inline explicit qrgb555(const qargb8555 &v);
+
+ inline operator quint32() const
+ {
+ const int r = (data & 0x7c00);
+ const int g = (data & 0x03e0);
+ const int b = (data & 0x001f);
+ const int tr = (r >> 7) | (r >> 12);
+ const int tg = (g >> 2) | (g >> 7);
+ const int tb = (b << 3) | (b >> 2);
+
+ return qRgb(tr, tg, tb);
+ }
+
+ inline operator quint16() const
+ {
+ const int r = ((data & 0x7c00) << 1) & 0xf800;
+ const int g = (((data & 0x03e0) << 1) | ((data >> 4) & 0x0020)) & 0x07e0;
+ const int b = (data & 0x001f);
+
+ return r | g | b;
+ }
+
+ inline qrgb555 operator+(qrgb555 v) const;
+ inline qrgb555 byte_mul(quint8 a) const;
+
+ inline quint8 alpha() const { return 0xff; }
+ inline qrgb555 truncedAlpha() { return *this; }
+ Q_STATIC_INLINE_FUNCTION quint8 alpha(quint8 a) { return (a + 1) >> 3; }
+ Q_STATIC_INLINE_FUNCTION quint8 ialpha(quint8 a) { return 0x20 - alpha(a); }
+
+ inline bool operator==(const qrgb555 &v) const { return v.data == data; }
+ inline bool operator!=(const qrgb555 &v) const { return v.data != data; }
+
+ inline quint16 rawValue() const { return data; }
+
+private:
+ friend class qargb8555;
+ friend class qbgr555;
+ quint16 data;
+
+} Q_PACKED;
+
+qrgb555::qrgb555(const qargb8555 &v)
+{
+ data = (v.data[2] << 8) | v.data[1];
+}
+
+qrgb555 qrgb555::operator+(qrgb555 v) const
+{
+ qrgb555 t;
+ t.data = data + v.data;
+ return t;
+}
+
+qrgb555 qrgb555::byte_mul(quint8 a) const
+{
+ quint16 t = (((data & 0x3e0) * a) >> 5) & 0x03e0;
+ t |= (((data & 0x7c1f) * a) >> 5) & 0x7c1f;
+
+ qrgb555 result;
+ result.data = t;
+ return result;
+}
+
+class qbgr555
+{
+public:
+ inline qbgr555(quint32 v) { *this = qbgr555(qrgb555(v)); }
+
+ inline qbgr555(qrgb555 v)
+ {
+ data = ((v.data & 0x001f) << 10) |
+ (v.data & 0x03e0) |
+ ((v.data & 0x7c00) >> 10);
+ }
+
+ inline bool operator==(const qbgr555 &v) const
+ {
+ return data == v.data;
+ }
+
+private:
+ quint16 data;
+} Q_PACKED;
+
+qargb8555::qargb8555(quint32 v)
+{
+ v = quint32p(v);
+ data[0] = qAlpha(v);
+ const int r = qRed(v);
+ const int g = qGreen(v);
+ const int b = qBlue(v);
+ data[1] = ((g << 2) & 0xe0) | (b >> 3);
+ data[2] = ((r >> 1) & 0x7c) | (g >> 6);
+
+}
+
+qargb8555::qargb8555(quint32p v)
+{
+ data[0] = qAlpha(v);
+ const int r = qRed(v);
+ const int g = qGreen(v);
+ const int b = qBlue(v);
+ data[1] = ((g << 2) & 0xe0) | (b >> 3);
+ data[2] = ((r >> 1) & 0x7c) | (g >> 6);
+}
+
+qargb8555::qargb8555(const qargb8555 &v)
+{
+ data[0] = v.data[0];
+ data[1] = v.data[1];
+ data[2] = v.data[2];
+}
+
+qargb8555::qargb8555(const qrgb555 &v)
+{
+ data[0] = 0xff;
+ data[1] = v.data & 0xff;
+ data[2] = v.data >> 8;
+}
+
+qargb8555::operator quint32() const
+{
+ const quint16 rgb = (data[2] << 8) | data[1];
+ const int r = (rgb & 0x7c00);
+ const int g = (rgb & 0x03e0);
+ const int b = (rgb & 0x001f);
+ const int tr = (r >> 7) | (r >> 12);
+ const int tg = (g >> 2) | (g >> 7);
+ const int tb = (b << 3) | (b >> 2);
+
+ return qRgba(tr, tg, tb, data[0]);
+}
+
+bool qargb8555::operator==(const qargb8555 &v) const
+{
+ return data[0] == v.data[0]
+ && data[1] == v.data[1]
+ && data[2] == v.data[2];
+}
+
+quint32 qargb8555::rawValue() const
+{
+ return (data[2] << 16) | (data[1] << 8) | data[0];
+}
+
+qargb8555 qargb8555::operator+(qargb8555 v) const
+{
+ qargb8555 t;
+ t.data[0] = data[0] + v.data[0];
+ const quint16 rgb = ((data[2] + v.data[2]) << 8)
+ + (data[1] + v.data[1]);
+ t.data[1] = rgb & 0xff;
+ t.data[2] = rgb >> 8;
+ return t;
+}
+
+qargb8555 qargb8555::byte_mul(quint8 a) const
+{
+ qargb8555 result;
+ result.data[0] = (data[0] * a) >> 5;
+
+ const quint16 x = (data[2] << 8) | data[1];
+ quint16 t = (((x & 0x3e0) * a) >> 5) & 0x03e0;
+ t |= (((x & 0x7c1f) * a) >> 5) & 0x7c1f;
+ result.data[1] = t & 0xff;
+ result.data[2] = t >> 8;
+ return result;
+
+}
+
+class qrgb666;
+
+class qargb6666
+{
+public:
+ Q_STATIC_INLINE_FUNCTION bool hasAlpha() { return true; }
+
+ inline qargb6666() {}
+ inline qargb6666(quint32 v) { *this = qargb6666(quint32p(v)); }
+ inline explicit qargb6666(quint32p v);
+ inline qargb6666(const qargb6666 &v);
+ inline qargb6666(const qrgb666 &v);
+
+ inline operator quint32 () const;
+
+ inline quint8 alpha() const;
+ inline qargb6666 truncedAlpha() { return *this; }
+ Q_STATIC_INLINE_FUNCTION quint8 alpha(quint8 a) { return (a + 1) >> 2; }
+ Q_STATIC_INLINE_FUNCTION quint8 ialpha(quint8 a) { return (255 - a + 1) >> 2; }
+
+ inline qargb6666 byte_mul(quint8 a) const;
+ inline qargb6666 operator+(qargb6666 v) const;
+ inline bool operator==(const qargb6666 &v) const;
+
+ inline quint32 rawValue() const;
+
+private:
+ friend class qrgb666;
+ quint8 data[3];
+
+} Q_PACKED;
+
+class qrgb666
+{
+public:
+ Q_STATIC_INLINE_FUNCTION bool hasAlpha() { return false; }
+
+ inline qrgb666() {}
+ inline qrgb666(quint32 v);
+ inline qrgb666(const qargb6666 &v);
+
+ inline operator quint32 () const;
+
+ inline quint8 alpha() const { return 0xff; }
+ inline qrgb666 truncedAlpha() { return *this; }
+ Q_STATIC_INLINE_FUNCTION quint8 alpha(quint8 a) { return (a + 1) >> 2; }
+ Q_STATIC_INLINE_FUNCTION quint8 ialpha(quint8 a) { return (255 - a + 1) >> 2; }
+
+ inline qrgb666 operator+(qrgb666 v) const;
+ inline qrgb666 byte_mul(quint8 a) const;
+
+ inline bool operator==(const qrgb666 &v) const;
+ inline bool operator!=(const qrgb666 &v) const { return !(*this == v); }
+
+ inline quint32 rawValue() const
+ {
+ return (data[2] << 16) | (data[1] << 8) | data[0];
+ }
+
+private:
+ friend class qargb6666;
+
+ quint8 data[3];
+} Q_PACKED;
+
+qrgb666::qrgb666(quint32 v)
+{
+ const uchar b = qBlue(v);
+ const uchar g = qGreen(v);
+ const uchar r = qRed(v);
+ const uint p = (b >> 2) | ((g >> 2) << 6) | ((r >> 2) << 12);
+ data[0] = qBlue(p);
+ data[1] = qGreen(p);
+ data[2] = qRed(p);
+}
+
+qrgb666::qrgb666(const qargb6666 &v)
+{
+ data[0] = v.data[0];
+ data[1] = v.data[1];
+ data[2] = v.data[2] & 0x03;
+}
+
+qrgb666::operator quint32 () const
+{
+ const uchar r = (data[2] << 6) | ((data[1] & 0xf0) >> 2) | (data[2] & 0x3);
+ const uchar g = (data[1] << 4) | ((data[0] & 0xc0) >> 4) | ((data[1] & 0x0f) >> 2);
+ const uchar b = (data[0] << 2) | ((data[0] & 0x3f) >> 4);
+ return qRgb(r, g, b);
+}
+
+qrgb666 qrgb666::operator+(qrgb666 v) const
+{
+ const quint32 x1 = (data[2] << 16) | (data[1] << 8) | data[0];
+ const quint32 x2 = (v.data[2] << 16) | (v.data[1] << 8) | v.data[0];
+ const quint32 t = x1 + x2;
+ qrgb666 r;
+ r.data[0] = t & 0xff;
+ r.data[1] = (t >> 8) & 0xff;
+ r.data[2] = (t >> 16) & 0xff;
+ return r;
+}
+
+qrgb666 qrgb666::byte_mul(quint8 a) const
+{
+ const quint32 x = (data[2] << 16) | (data[1] << 8) | data[0];
+ const quint32 t = ((((x & 0x03f03f) * a) >> 6) & 0x03f03f) |
+ ((((x & 0x000fc0) * a) >> 6) & 0x000fc0);
+
+ qrgb666 r;
+ r.data[0] = t & 0xff;
+ r.data[1] = (t >> 8) & 0xff;
+ r.data[2] = (t >> 16) & 0xff;
+ return r;
+}
+
+bool qrgb666::operator==(const qrgb666 &v) const
+{
+ return (data[0] == v.data[0] &&
+ data[1] == v.data[1] &&
+ data[2] == v.data[2]);
+}
+
+qargb6666::qargb6666(quint32p v)
+{
+ const quint8 b = qBlue(v) >> 2;
+ const quint8 g = qGreen(v) >> 2;
+ const quint8 r = qRed(v) >> 2;
+ const quint8 a = qAlpha(v) >> 2;
+ const uint p = (a << 18) | (r << 12) | (g << 6) | b;
+ data[0] = qBlue(p);
+ data[1] = qGreen(p);
+ data[2] = qRed(p);
+}
+
+qargb6666::qargb6666(const qargb6666 &v)
+{
+ data[0] = v.data[0];
+ data[1] = v.data[1];
+ data[2] = v.data[2];
+}
+
+qargb6666::qargb6666(const qrgb666 &v)
+{
+ data[0] = v.data[0];
+ data[1] = v.data[1];
+ data[2] = (v.data[2] | 0xfc);
+}
+
+qargb6666::operator quint32 () const
+{
+ const quint8 r = (data[2] << 6) | ((data[1] & 0xf0) >> 2) | (data[2] & 0x3);
+ const quint8 g = (data[1] << 4) | ((data[0] & 0xc0) >> 4) | ((data[1] & 0x0f) >> 2);
+ const quint8 b = (data[0] << 2) | ((data[0] & 0x3f) >> 4);
+ const quint8 a = (data[2] & 0xfc) | (data[2] >> 6);
+ return qRgba(r, g, b, a);
+}
+
+qargb6666 qargb6666::operator+(qargb6666 v) const
+{
+ const quint32 x1 = (data[2] << 16) | (data[1] << 8) | data[0];
+ const quint32 x2 = (v.data[2] << 16) | (v.data[1] << 8) | v.data[0];
+ const quint32 t = x1 + x2;
+ qargb6666 r;
+ r.data[0] = t & 0xff;
+ r.data[1] = (t >> 8) & 0xff;
+ r.data[2] = (t >> 16) & 0xff;
+ return r;
+}
+
+quint8 qargb6666::alpha() const
+{
+ return (data[2] & 0xfc) | (data[2] >> 6);
+}
+
+inline qargb6666 qargb6666::byte_mul(quint8 a) const
+{
+ const quint32 x = (data[2] << 16) | (data[1] << 8) | data[0];
+ const quint32 t = ((((x & 0x03f03f) * a) >> 6) & 0x03f03f) |
+ ((((x & 0xfc0fc0) * a) >> 6) & 0xfc0fc0);
+
+ qargb6666 r;
+ r.data[0] = t & 0xff;
+ r.data[1] = (t >> 8) & 0xff;
+ r.data[2] = (t >> 16) & 0xff;
+ return r;
+}
+
+bool qargb6666::operator==(const qargb6666 &v) const
+{
+ return data[0] == v.data[0]
+ && data[1] == v.data[1]
+ && data[2] == v.data[2];
+}
+
+quint32 qargb6666::rawValue() const
+{
+ return (data[2] << 16) | (data[1] << 8) | data[0];
+}
+
+class qrgb888
+{
+public:
+ Q_STATIC_INLINE_FUNCTION bool hasAlpha() { return false; }
+
+ inline qrgb888() {}
+ inline qrgb888(quint32 v);
+
+ inline operator quint32() const;
+
+ inline quint8 alpha() const { return 0xff; }
+ inline qrgb888 truncedAlpha() { return *this; }
+ Q_STATIC_INLINE_FUNCTION quint8 alpha(quint8 a) { return a; }
+ Q_STATIC_INLINE_FUNCTION quint8 ialpha(quint8 a) { return 255 - a; }
+
+ inline qrgb888 byte_mul(quint8 a) const;
+ inline qrgb888 operator+(qrgb888 v) const;
+ inline bool operator==(qrgb888 v) const;
+
+ inline quint32 rawValue() const;
+
+private:
+ uchar data[3];
+
+} Q_PACKED;
+
+qrgb888::qrgb888(quint32 v)
+{
+ data[0] = qRed(v);
+ data[1] = qGreen(v);
+ data[2] = qBlue(v);
+}
+
+qrgb888::operator quint32() const
+{
+ return qRgb(data[0], data[1], data[2]);
+}
+
+qrgb888 qrgb888::operator+(qrgb888 v) const
+{
+ qrgb888 t = *this;
+ t.data[0] += v.data[0];
+ t.data[1] += v.data[1];
+ t.data[2] += v.data[2];
+ return t;
+}
+
+qrgb888 qrgb888::byte_mul(quint8 a) const
+{
+ quint32 x(*this);
+
+ quint32 t = (x & 0xff00ff) * a;
+ t = (t + ((t >> 8) & 0xff00ff) + 0x800080) >> 8;
+ t &= 0xff00ff;
+
+ x = ((x >> 8) & 0xff00ff) * a;
+ x = (x + ((x >> 8) & 0xff00ff) + 0x800080);
+ x &= 0xff00ff00;
+ x |= t;
+ return qrgb888(x);
+}
+
+bool qrgb888::operator==(qrgb888 v) const
+{
+ return (data[0] == v.data[0] &&
+ data[1] == v.data[1] &&
+ data[2] == v.data[2]);
+}
+
+quint32 qrgb888::rawValue() const
+{
+ return (data[2] << 16) | (data[1] << 8) | data[0];
+}
+
+template <>
+inline qrgb888 qt_colorConvert(quint32 color, qrgb888 dummy)
+{
+ Q_UNUSED(dummy);
+ return qrgb888(color);
+}
+
+template <>
+inline quint32 qt_colorConvert(qrgb888 color, quint32 dummy)
+{
+ Q_UNUSED(dummy);
+ return quint32(color);
+}
+
+#ifdef QT_QWS_DEPTH_8
+template <>
+inline quint8 qt_colorConvert(quint32 color, quint8 dummy)
+{
+ Q_UNUSED(dummy);
+
+ uchar r = ((qRed(color) & 0xf8) + 0x19) / 0x33;
+ uchar g = ((qGreen(color) &0xf8) + 0x19) / 0x33;
+ uchar b = ((qBlue(color) &0xf8) + 0x19) / 0x33;
+
+ return r*6*6 + g*6 + b;
+}
+
+template <>
+inline quint8 qt_colorConvert(quint16 color, quint8 dummy)
+{
+ Q_UNUSED(dummy);
+
+ uchar r = (color & 0xf800) >> (11-3);
+ uchar g = (color & 0x07c0) >> (6-3);
+ uchar b = (color & 0x001f) << 3;
+
+ uchar tr = (r + 0x19) / 0x33;
+ uchar tg = (g + 0x19) / 0x33;
+ uchar tb = (b + 0x19) / 0x33;
+
+ return tr*6*6 + tg*6 + tb;
+}
+
+#endif // QT_QWS_DEPTH_8
+
+// hw: endianess??
+class quint24
+{
+public:
+ inline quint24(quint32 v)
+ {
+ data[0] = qBlue(v);
+ data[1] = qGreen(v);
+ data[2] = qRed(v);
+ }
+
+ inline operator quint32 ()
+ {
+ return qRgb(data[2], data[1], data[0]);
+ }
+
+ inline bool operator==(const quint24 &v) const
+ {
+ return data[0] == v.data[0]
+ && data[1] == v.data[1]
+ && data[2] == v.data[2];
+ }
+
+private:
+ uchar data[3];
+} Q_PACKED;
+
+template <>
+inline quint24 qt_colorConvert(quint32 color, quint24 dummy)
+{
+ Q_UNUSED(dummy);
+ return quint24(color);
+}
+
+// hw: endianess??
+class quint18
+{
+public:
+ inline quint18(quint32 v)
+ {
+ uchar b = qBlue(v);
+ uchar g = qGreen(v);
+ uchar r = qRed(v);
+ uint p = (b >> 2) | ((g >> 2) << 6) | ((r >> 2) << 12);
+ data[0] = qBlue(p);
+ data[1] = qGreen(p);
+ data[2] = qRed(p);
+ }
+
+ inline operator quint32 ()
+ {
+ const uchar r = (data[2] << 6) | ((data[1] & 0xf0) >> 2) | (data[2] & 0x3);
+ const uchar g = (data[1] << 4) | ((data[0] & 0xc0) >> 4) | ((data[1] & 0x0f) >> 2);
+ const uchar b = (data[0] << 2) | ((data[0] & 0x3f) >> 4);
+ return qRgb(r, g, b);
+ }
+
+private:
+ uchar data[3];
+} Q_PACKED;
+
+template <>
+inline quint18 qt_colorConvert(quint32 color, quint18 dummy)
+{
+ Q_UNUSED(dummy);
+ return quint18(color);
+}
+
+class qrgb444;
+
+class qargb4444
+{
+public:
+ Q_STATIC_INLINE_FUNCTION bool hasAlpha() { return true; }
+
+ inline qargb4444() {}
+ inline qargb4444(quint32 v) { *this = qargb4444(quint32p(v)); }
+ inline explicit qargb4444(quint32p v);
+ inline qargb4444(const qrgb444 &v);
+
+ inline operator quint32() const;
+ inline operator quint8() const;
+
+ inline qargb4444 operator+(qargb4444 v) const;
+
+ inline quint8 alpha() const { return ((data & 0xf000) >> 8) | ((data & 0xf000) >> 12); }
+ inline qargb4444 truncedAlpha() { return *this; }
+ Q_STATIC_INLINE_FUNCTION quint8 alpha(quint8 a) { return (a + 1) >> 4; }
+ Q_STATIC_INLINE_FUNCTION quint8 ialpha(quint8 a) { return 0x10 - alpha(a); }
+ inline qargb4444 byte_mul(quint8 a) const;
+
+ inline bool operator==(const qargb4444 &v) const { return data == v.data; }
+
+ inline quint16 rawValue() const { return data; }
+
+private:
+ friend class qrgb444;
+ quint16 data;
+
+} Q_PACKED;
+
+class qrgb444
+{
+public:
+ Q_STATIC_INLINE_FUNCTION bool hasAlpha() { return false; }
+
+ inline qrgb444() {}
+ inline qrgb444(quint32 v);
+ inline explicit qrgb444(qargb4444 v);
+
+ inline operator quint32() const;
+ inline operator quint8() const;
+
+ inline qrgb444 operator+(qrgb444 v) const;
+ inline quint8 alpha() const { return 0xff; }
+ inline qrgb444 truncedAlpha() { return *this; }
+ Q_STATIC_INLINE_FUNCTION quint8 alpha(quint8 a) { return (a + 1) >> 4; }
+ Q_STATIC_INLINE_FUNCTION quint8 ialpha(quint8 a) { return 0x10 - alpha(a); }
+ inline qrgb444 byte_mul(quint8 a) const;
+
+ inline bool operator==(const qrgb444 &v) const { return data == v.data; }
+ inline bool operator!=(const qrgb444 &v) const { return data != v.data; }
+
+ inline quint16 rawValue() const { return data; }
+
+private:
+ friend class qargb4444;
+ quint16 data;
+
+} Q_PACKED;
+
+
+qargb4444::qargb4444(quint32p color)
+{
+ quint32 v = color;
+ v &= 0xf0f0f0f0;
+ const int a = qAlpha(v) << 8;
+ const int r = qRed(v) << 4;
+ const int g = qGreen(v);
+ const int b = qBlue(v) >> 4;
+
+ data = a | r | g | b;
+}
+
+qargb4444::qargb4444(const qrgb444 &v)
+{
+ data = v.data | 0xf000;
+}
+
+qargb4444::operator quint32() const
+{
+ const int a = (data & 0xf000);
+ const int r = (data & 0x0f00);
+ const int g = (data & 0x00f0);
+ const int b = (data & 0x000f);
+ const int ta = (a >> 8) | (a >> 12);
+ const int tr = (r >> 4) | (r >> 8);
+ const int tg = g | (g >> 4);
+ const int tb = (b << 4) | b;
+
+ return qRgba(tr, tg, tb, ta);
+}
+
+qargb4444::operator quint8() const
+{
+ // hw: optimize!
+ return qt_colorConvert<quint8, quint32>(operator quint32(), 0);
+}
+
+qargb4444 qargb4444::operator+(qargb4444 v) const
+{
+ qargb4444 t;
+ t.data = data + v.data;
+ return t;
+}
+
+qargb4444 qargb4444::byte_mul(quint8 a) const
+{
+ quint16 t = (((data & 0xf0f0) * a) >> 4) & 0xf0f0;
+ t |= (((data & 0x0f0f) * a) >> 4) & 0x0f0f;
+
+ qargb4444 result;
+ result.data = t;
+ return result;
+}
+
+qrgb444::qrgb444(quint32 v)
+{
+ v &= 0xf0f0f0f0;
+ const int r = qRed(v) << 4;
+ const int g = qGreen(v);
+ const int b = qBlue(v) >> 4;
+
+ data = r | g | b;
+}
+
+qrgb444::qrgb444(qargb4444 v)
+{
+ data = v.data & 0x0fff;
+}
+
+qrgb444::operator quint32() const
+{
+ const int r = (data & 0x0f00);
+ const int g = (data & 0x00f0);
+ const int b = (data & 0x000f);
+ const int tr = (r >> 4) | (r >> 8);
+ const int tg = g | (g >> 4);
+ const int tb = (b << 4) | b;
+
+ return qRgb(tr, tg, tb);
+}
+
+qrgb444::operator quint8() const
+{
+ // hw: optimize!
+ return qt_colorConvert<quint8, quint32>(operator quint32(), 0);
+}
+
+qrgb444 qrgb444::operator+(qrgb444 v) const
+{
+ qrgb444 t;
+ t.data = data + v.data;
+ return t;
+}
+
+qrgb444 qrgb444::byte_mul(quint8 a) const
+{
+ quint16 t = (((data & 0xf0f0) * a) >> 4) & 0xf0f0;
+ t |= (((data & 0x0f0f) * a) >> 4) & 0x0f0f;
+
+ qrgb444 result;
+ result.data = t;
+ return result;
+}
+
+#ifdef QT_QWS_DEPTH_GENERIC
+
+struct qrgb
+{
+public:
+ static int bpp;
+ static int len_red;
+ static int len_green;
+ static int len_blue;
+ static int len_alpha;
+ static int off_red;
+ static int off_green;
+ static int off_blue;
+ static int off_alpha;
+} Q_PACKED;
+
+template <typename SRC>
+Q_STATIC_TEMPLATE_FUNCTION inline quint32 qt_convertToRgb(SRC color);
+
+template <>
+inline quint32 qt_convertToRgb(quint32 color)
+{
+ const int r = qRed(color) >> (8 - qrgb::len_red);
+ const int g = qGreen(color) >> (8 - qrgb::len_green);
+ const int b = qBlue(color) >> (8 - qrgb::len_blue);
+ const int a = qAlpha(color) >> (8 - qrgb::len_alpha);
+ const quint32 v = (r << qrgb::off_red)
+ | (g << qrgb::off_green)
+ | (b << qrgb::off_blue)
+ | (a << qrgb::off_alpha);
+
+ return v;
+}
+
+template <>
+inline quint32 qt_convertToRgb(quint16 color)
+{
+ return qt_convertToRgb(qt_colorConvert<quint32, quint16>(color, 0));
+}
+
+class qrgb_generic16
+{
+public:
+ inline qrgb_generic16(quint32 color)
+ {
+ const int r = qRed(color) >> (8 - qrgb::len_red);
+ const int g = qGreen(color) >> (8 - qrgb::len_green);
+ const int b = qBlue(color) >> (8 - qrgb::len_blue);
+ const int a = qAlpha(color) >> (8 - qrgb::len_alpha);
+ data = (r << qrgb::off_red)
+ | (g << qrgb::off_green)
+ | (b << qrgb::off_blue)
+ | (a << qrgb::off_alpha);
+ }
+
+ inline operator quint16 () { return data; }
+ inline quint32 operator<<(int shift) const { return data << shift; }
+
+private:
+ quint16 data;
+} Q_PACKED;
+
+template <>
+inline qrgb_generic16 qt_colorConvert(quint32 color, qrgb_generic16 dummy)
+{
+ Q_UNUSED(dummy);
+ return qrgb_generic16(color);
+}
+
+template <>
+inline qrgb_generic16 qt_colorConvert(quint16 color, qrgb_generic16 dummy)
+{
+ Q_UNUSED(dummy);
+ return qrgb_generic16(qt_colorConvert<quint32, quint16>(color, 0));
+}
+
+#endif // QT_QWS_DEPTH_GENERIC
+
+template <class T>
+void qt_memfill(T *dest, T value, int count);
+
+template<> inline void qt_memfill(quint32 *dest, quint32 color, int count)
+{
+ extern void (*qt_memfill32)(quint32 *dest, quint32 value, int count);
+ qt_memfill32(dest, color, count);
+}
+
+template<> inline void qt_memfill(quint16 *dest, quint16 color, int count)
+{
+ extern void (*qt_memfill16)(quint16 *dest, quint16 value, int count);
+ qt_memfill16(dest, color, count);
+}
+
+template<> inline void qt_memfill(quint8 *dest, quint8 color, int count)
+{
+ memset(dest, color, count);
+}
+
+template <class T>
+inline void qt_memfill(T *dest, T value, int count)
+{
+ if (!count)
+ return;
+
+ int n = (count + 7) / 8;
+ switch (count & 0x07)
+ {
+ case 0: do { *dest++ = value;
+ case 7: *dest++ = value;
+ case 6: *dest++ = value;
+ case 5: *dest++ = value;
+ case 4: *dest++ = value;
+ case 3: *dest++ = value;
+ case 2: *dest++ = value;
+ case 1: *dest++ = value;
+ } while (--n > 0);
+ }
+}
+
+template <class T>
+inline void qt_rectfill(T *dest, T value,
+ int x, int y, int width, int height, int stride)
+{
+ char *d = reinterpret_cast<char*>(dest + x) + y * stride;
+ if (uint(stride) == (width * sizeof(T))) {
+ qt_memfill(reinterpret_cast<T*>(d), value, width * height);
+ } else {
+ for (int j = 0; j < height; ++j) {
+ dest = reinterpret_cast<T*>(d);
+ qt_memfill(dest, value, width);
+ d += stride;
+ }
+ }
+}
+
+template <class DST, class SRC>
+inline void qt_memconvert(DST *dest, const SRC *src, int count)
+{
+ if (sizeof(DST) == 1) {
+ while (count) {
+ int n = 1;
+ const SRC color = *src++;
+ const DST dstColor = qt_colorConvert<DST, SRC>(color, 0);
+ while (--count && (*src == color || dstColor == qt_colorConvert<DST, SRC>(*src, 0))) {
+ ++n;
+ ++src;
+ }
+ qt_memfill(dest, dstColor, n);
+ dest += n;
+ }
+ } else {
+ /* Duff's device */
+ int n = (count + 7) / 8;
+ switch (count & 0x07)
+ {
+ case 0: do { *dest++ = qt_colorConvert<DST, SRC>(*src++, 0);
+ case 7: *dest++ = qt_colorConvert<DST, SRC>(*src++, 0);
+ case 6: *dest++ = qt_colorConvert<DST, SRC>(*src++, 0);
+ case 5: *dest++ = qt_colorConvert<DST, SRC>(*src++, 0);
+ case 4: *dest++ = qt_colorConvert<DST, SRC>(*src++, 0);
+ case 3: *dest++ = qt_colorConvert<DST, SRC>(*src++, 0);
+ case 2: *dest++ = qt_colorConvert<DST, SRC>(*src++, 0);
+ case 1: *dest++ = qt_colorConvert<DST, SRC>(*src++, 0);
+ } while (--n > 0);
+ }
+ }
+}
+
+#define QT_TRIVIAL_MEMCONVERT_IMPL(T) \
+ template <> \
+ inline void qt_memconvert(T *dest, const T *src, int count) \
+ { \
+ memcpy(dest, src, count * sizeof(T)); \
+ }
+QT_TRIVIAL_MEMCONVERT_IMPL(quint32)
+QT_TRIVIAL_MEMCONVERT_IMPL(qrgb888)
+QT_TRIVIAL_MEMCONVERT_IMPL(qargb6666)
+QT_TRIVIAL_MEMCONVERT_IMPL(qrgb666)
+QT_TRIVIAL_MEMCONVERT_IMPL(quint16)
+QT_TRIVIAL_MEMCONVERT_IMPL(qrgb565)
+QT_TRIVIAL_MEMCONVERT_IMPL(qargb8565)
+QT_TRIVIAL_MEMCONVERT_IMPL(qargb8555)
+QT_TRIVIAL_MEMCONVERT_IMPL(qrgb555)
+QT_TRIVIAL_MEMCONVERT_IMPL(qargb4444)
+QT_TRIVIAL_MEMCONVERT_IMPL(qrgb444)
+#undef QT_TRIVIAL_MEMCONVERT_IMPL
+
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+template <>
+inline void qt_memconvert(qrgb666 *dest, const quint32 *src, int count)
+{
+ if (count < 3) {
+ switch (count) {
+ case 2: *dest++ = qrgb666(*src++);
+ case 1: *dest = qrgb666(*src);
+ }
+ return;
+ }
+
+ const int align = (quintptr(dest) & 3);
+ switch (align) {
+ case 1: *dest++ = qrgb666(*src++); --count;
+ case 2: *dest++ = qrgb666(*src++); --count;
+ case 3: *dest++ = qrgb666(*src++); --count;
+ }
+
+ quint32 *dest32 = reinterpret_cast<quint32*>(dest);
+ int sourceCount = count >> 2;
+ while (sourceCount--) {
+ dest32[0] = ((src[1] & 0x00000c00) << 20)
+ | ((src[1] & 0x000000fc) << 22)
+ | ((src[0] & 0x00fc0000) >> 6)
+ | ((src[0] & 0x0000fc00) >> 4)
+ | ((src[0] & 0x000000fc) >> 2);
+ dest32[1] = ((src[2] & 0x003c0000) << 10)
+ | ((src[2] & 0x0000fc00) << 12)
+ | ((src[2] & 0x000000fc) << 14)
+ | ((src[1] & 0x00fc0000) >> 14)
+ | ((src[1] & 0x0000f000) >> 12);
+ dest32[2] = ((src[3] & 0x00fc0000) << 2)
+ | ((src[3] & 0x0000fc00) << 4)
+ | ((src[3] & 0x000000fc) << 6)
+ | ((src[2] & 0x00c00000) >> 22);
+ dest32 += 3;
+ src += 4;
+ }
+
+ dest = reinterpret_cast<qrgb666*>(dest32);
+ switch (count & 3) {
+ case 3: *dest++ = qrgb666(*src++);
+ case 2: *dest++ = qrgb666(*src++);
+ case 1: *dest = qrgb666(*src);
+ }
+}
+#endif // Q_BYTE_ORDER
+
+template <class T>
+inline void qt_rectcopy(T *dest, const T *src,
+ int x, int y, int width, int height,
+ int dstStride, int srcStride)
+{
+ char *d = (char*)(dest + x) + y * dstStride;
+ const char *s = (char*)(src);
+ for (int i = 0; i < height; ++i) {
+ ::memcpy(d, s, width * sizeof(T));
+ d += dstStride;
+ s += srcStride;
+ }
+}
+
+template <class DST, class SRC>
+inline void qt_rectconvert(DST *dest, const SRC *src,
+ int x, int y, int width, int height,
+ int dstStride, int srcStride)
+{
+ char *d = (char*)(dest + x) + y * dstStride;
+ const char *s = (char*)(src);
+ for (int i = 0; i < height; ++i) {
+ qt_memconvert<DST,SRC>((DST*)d, (const SRC*)s, width);
+ d += dstStride;
+ s += srcStride;
+ }
+}
+
+#define QT_RECTCONVERT_TRIVIAL_IMPL(T) \
+ template <> \
+ inline void qt_rectconvert(T *dest, const T *src, \
+ int x, int y, int width, int height, \
+ int dstStride, int srcStride) \
+ { \
+ qt_rectcopy(dest, src, x, y, width, height, dstStride, srcStride); \
+ }
+QT_RECTCONVERT_TRIVIAL_IMPL(quint32)
+QT_RECTCONVERT_TRIVIAL_IMPL(qrgb888)
+QT_RECTCONVERT_TRIVIAL_IMPL(qargb6666)
+QT_RECTCONVERT_TRIVIAL_IMPL(qrgb666)
+QT_RECTCONVERT_TRIVIAL_IMPL(qrgb565)
+QT_RECTCONVERT_TRIVIAL_IMPL(qargb8565)
+QT_RECTCONVERT_TRIVIAL_IMPL(quint16)
+QT_RECTCONVERT_TRIVIAL_IMPL(qargb8555)
+QT_RECTCONVERT_TRIVIAL_IMPL(qrgb555)
+QT_RECTCONVERT_TRIVIAL_IMPL(qargb4444)
+QT_RECTCONVERT_TRIVIAL_IMPL(qrgb444)
+#undef QT_RECTCONVERT_TRIVIAL_IMPL
+
+#ifdef QT_QWS_DEPTH_GENERIC
+template <> void qt_rectconvert(qrgb *dest, const quint32 *src,
+ int x, int y, int width, int height,
+ int dstStride, int srcStride);
+
+template <> void qt_rectconvert(qrgb *dest, const quint16 *src,
+ int x, int y, int width, int height,
+ int dstStride, int srcStride);
+#endif // QT_QWS_DEPTH_GENERIC
+
+#define QT_MEMFILL_UINT(dest, length, color) \
+ qt_memfill<quint32>(dest, color, length);
+
+#define QT_MEMFILL_USHORT(dest, length, color) \
+ qt_memfill<quint16>(dest, color, length);
+
+#define QT_MEMCPY_REV_UINT(dest, src, length) \
+do { \
+ /* Duff's device */ \
+ uint *_d = (uint*)(dest) + length; \
+ const uint *_s = (uint*)(src) + length; \
+ register int n = ((length) + 7) / 8; \
+ switch ((length) & 0x07) \
+ { \
+ case 0: do { *--_d = *--_s; \
+ case 7: *--_d = *--_s; \
+ case 6: *--_d = *--_s; \
+ case 5: *--_d = *--_s; \
+ case 4: *--_d = *--_s; \
+ case 3: *--_d = *--_s; \
+ case 2: *--_d = *--_s; \
+ case 1: *--_d = *--_s; \
+ } while (--n > 0); \
+ } \
+} while (0)
+
+#define QT_MEMCPY_USHORT(dest, src, length) \
+do { \
+ /* Duff's device */ \
+ ushort *_d = (ushort*)(dest); \
+ const ushort *_s = (ushort*)(src); \
+ register int n = ((length) + 7) / 8; \
+ switch ((length) & 0x07) \
+ { \
+ case 0: do { *_d++ = *_s++; \
+ case 7: *_d++ = *_s++; \
+ case 6: *_d++ = *_s++; \
+ case 5: *_d++ = *_s++; \
+ case 4: *_d++ = *_s++; \
+ case 3: *_d++ = *_s++; \
+ case 2: *_d++ = *_s++; \
+ case 1: *_d++ = *_s++; \
+ } while (--n > 0); \
+ } \
+} while (0)
+
+#if defined(Q_CC_RVCT)
+# pragma push
+# pragma arm
+#endif
+Q_STATIC_INLINE_FUNCTION int qt_div_255(int x) { return (x + (x>>8) + 0x80) >> 8; }
+#if defined(Q_CC_RVCT)
+# pragma pop
+#endif
+
+inline ushort qConvertRgb32To16(uint c)
+{
+ return (((c) >> 3) & 0x001f)
+ | (((c) >> 5) & 0x07e0)
+ | (((c) >> 8) & 0xf800);
+}
+
+#if defined(Q_WS_QWS) || (QT_VERSION >= 0x040400)
+inline quint32 qConvertRgb32To16x2(quint64 c)
+{
+ c = (((c) >> 3) & Q_UINT64_C(0x001f0000001f))
+ | (((c) >> 5) & Q_UINT64_C(0x07e0000007e0))
+ | (((c) >> 8) & Q_UINT64_C(0xf8000000f800));
+ return c | (c >> 16);
+}
+#endif
+
+inline QRgb qConvertRgb16To32(uint c)
+{
+ return 0xff000000
+ | ((((c) << 3) & 0xf8) | (((c) >> 2) & 0x7))
+ | ((((c) << 5) & 0xfc00) | (((c) >> 1) & 0x300))
+ | ((((c) << 8) & 0xf80000) | (((c) << 3) & 0x70000));
+}
+
+inline int qRed565(quint16 rgb) {
+ const int r = (rgb & 0xf800);
+ return (r >> 8) | (r >> 13);
+}
+
+inline int qGreen565(quint16 rgb) {
+ const int g = (rgb & 0x07e0);
+ return (g >> 3) | (g >> 9);
+}
+
+inline int qBlue565(quint16 rgb) {
+ const int b = (rgb & 0x001f);
+ return (b << 3) | (b >> 2);
+}
+
+const uint qt_bayer_matrix[16][16] = {
+ { 0x1, 0xc0, 0x30, 0xf0, 0xc, 0xcc, 0x3c, 0xfc,
+ 0x3, 0xc3, 0x33, 0xf3, 0xf, 0xcf, 0x3f, 0xff},
+ { 0x80, 0x40, 0xb0, 0x70, 0x8c, 0x4c, 0xbc, 0x7c,
+ 0x83, 0x43, 0xb3, 0x73, 0x8f, 0x4f, 0xbf, 0x7f},
+ { 0x20, 0xe0, 0x10, 0xd0, 0x2c, 0xec, 0x1c, 0xdc,
+ 0x23, 0xe3, 0x13, 0xd3, 0x2f, 0xef, 0x1f, 0xdf},
+ { 0xa0, 0x60, 0x90, 0x50, 0xac, 0x6c, 0x9c, 0x5c,
+ 0xa3, 0x63, 0x93, 0x53, 0xaf, 0x6f, 0x9f, 0x5f},
+ { 0x8, 0xc8, 0x38, 0xf8, 0x4, 0xc4, 0x34, 0xf4,
+ 0xb, 0xcb, 0x3b, 0xfb, 0x7, 0xc7, 0x37, 0xf7},
+ { 0x88, 0x48, 0xb8, 0x78, 0x84, 0x44, 0xb4, 0x74,
+ 0x8b, 0x4b, 0xbb, 0x7b, 0x87, 0x47, 0xb7, 0x77},
+ { 0x28, 0xe8, 0x18, 0xd8, 0x24, 0xe4, 0x14, 0xd4,
+ 0x2b, 0xeb, 0x1b, 0xdb, 0x27, 0xe7, 0x17, 0xd7},
+ { 0xa8, 0x68, 0x98, 0x58, 0xa4, 0x64, 0x94, 0x54,
+ 0xab, 0x6b, 0x9b, 0x5b, 0xa7, 0x67, 0x97, 0x57},
+ { 0x2, 0xc2, 0x32, 0xf2, 0xe, 0xce, 0x3e, 0xfe,
+ 0x1, 0xc1, 0x31, 0xf1, 0xd, 0xcd, 0x3d, 0xfd},
+ { 0x82, 0x42, 0xb2, 0x72, 0x8e, 0x4e, 0xbe, 0x7e,
+ 0x81, 0x41, 0xb1, 0x71, 0x8d, 0x4d, 0xbd, 0x7d},
+ { 0x22, 0xe2, 0x12, 0xd2, 0x2e, 0xee, 0x1e, 0xde,
+ 0x21, 0xe1, 0x11, 0xd1, 0x2d, 0xed, 0x1d, 0xdd},
+ { 0xa2, 0x62, 0x92, 0x52, 0xae, 0x6e, 0x9e, 0x5e,
+ 0xa1, 0x61, 0x91, 0x51, 0xad, 0x6d, 0x9d, 0x5d},
+ { 0xa, 0xca, 0x3a, 0xfa, 0x6, 0xc6, 0x36, 0xf6,
+ 0x9, 0xc9, 0x39, 0xf9, 0x5, 0xc5, 0x35, 0xf5},
+ { 0x8a, 0x4a, 0xba, 0x7a, 0x86, 0x46, 0xb6, 0x76,
+ 0x89, 0x49, 0xb9, 0x79, 0x85, 0x45, 0xb5, 0x75},
+ { 0x2a, 0xea, 0x1a, 0xda, 0x26, 0xe6, 0x16, 0xd6,
+ 0x29, 0xe9, 0x19, 0xd9, 0x25, 0xe5, 0x15, 0xd5},
+ { 0xaa, 0x6a, 0x9a, 0x5a, 0xa6, 0x66, 0x96, 0x56,
+ 0xa9, 0x69, 0x99, 0x59, 0xa5, 0x65, 0x95, 0x55}
+};
+
+#define ARGB_COMBINE_ALPHA(argb, alpha) \
+ ((((argb >> 24) * alpha) >> 8) << 24) | (argb & 0x00ffffff)
+
+
+#if QT_POINTER_SIZE == 8 // 64-bit versions
+#define AMIX(mask) (qMin(((qint64(s)&mask) + (qint64(d)&mask)), qint64(mask)))
+#define MIX(mask) (qMin(((qint64(s)&mask) + (qint64(d)&mask)), qint64(mask)))
+#else // 32 bits
+// The mask for alpha can overflow over 32 bits
+#define AMIX(mask) quint32(qMin(((qint64(s)&mask) + (qint64(d)&mask)), qint64(mask)))
+#define MIX(mask) (qMin(((quint32(s)&mask) + (quint32(d)&mask)), quint32(mask)))
+#endif
+
+inline int comp_func_Plus_one_pixel_const_alpha(uint d, const uint s, const uint const_alpha, const uint one_minus_const_alpha)
+{
+ const int result = (AMIX(AMASK) | MIX(RMASK) | MIX(GMASK) | MIX(BMASK));
+ return INTERPOLATE_PIXEL_255(result, const_alpha, d, one_minus_const_alpha);
+}
+
+inline int comp_func_Plus_one_pixel(uint d, const uint s)
+{
+ const int result = (AMIX(AMASK) | MIX(RMASK) | MIX(GMASK) | MIX(BMASK));
+ return result;
+}
+
+#undef MIX
+#undef AMIX
+
+// prototypes of all the composition functions
+void QT_FASTCALL comp_func_SourceOver(uint *dest, const uint *src, int length, uint const_alpha);
+void QT_FASTCALL comp_func_DestinationOver(uint *dest, const uint *src, int length, uint const_alpha);
+void QT_FASTCALL comp_func_Clear(uint *dest, const uint *, int length, uint const_alpha);
+void QT_FASTCALL comp_func_Source(uint *dest, const uint *src, int length, uint const_alpha);
+void QT_FASTCALL comp_func_Destination(uint *, const uint *, int, uint);
+void QT_FASTCALL comp_func_SourceIn(uint *dest, const uint *src, int length, uint const_alpha);
+void QT_FASTCALL comp_func_DestinationIn(uint *dest, const uint *src, int length, uint const_alpha);
+void QT_FASTCALL comp_func_SourceOut(uint *dest, const uint *src, int length, uint const_alpha);
+void QT_FASTCALL comp_func_DestinationOut(uint *dest, const uint *src, int length, uint const_alpha);
+void QT_FASTCALL comp_func_SourceAtop(uint *dest, const uint *src, int length, uint const_alpha);
+void QT_FASTCALL comp_func_DestinationAtop(uint *dest, const uint *src, int length, uint const_alpha);
+void QT_FASTCALL comp_func_XOR(uint *dest, const uint *src, int length, uint const_alpha);
+void QT_FASTCALL comp_func_Plus(uint *dest, const uint *src, int length, uint const_alpha);
+void QT_FASTCALL comp_func_Multiply(uint *dest, const uint *src, int length, uint const_alpha);
+void QT_FASTCALL comp_func_Screen(uint *dest, const uint *src, int length, uint const_alpha);
+void QT_FASTCALL comp_func_Overlay(uint *dest, const uint *src, int length, uint const_alpha);
+void QT_FASTCALL comp_func_Darken(uint *dest, const uint *src, int length, uint const_alpha);
+void QT_FASTCALL comp_func_Lighten(uint *dest, const uint *src, int length, uint const_alpha);
+void QT_FASTCALL comp_func_ColorDodge(uint *dest, const uint *src, int length, uint const_alpha);
+void QT_FASTCALL comp_func_ColorBurn(uint *dest, const uint *src, int length, uint const_alpha);
+void QT_FASTCALL comp_func_HardLight(uint *dest, const uint *src, int length, uint const_alpha);
+void QT_FASTCALL comp_func_SoftLight(uint *dest, const uint *src, int length, uint const_alpha);
+void QT_FASTCALL comp_func_Difference(uint *dest, const uint *src, int length, uint const_alpha);
+void QT_FASTCALL comp_func_Exclusion(uint *dest, const uint *src, int length, uint const_alpha);
+void QT_FASTCALL rasterop_SourceOrDestination(uint *dest, const uint *src, int length, uint const_alpha);
+void QT_FASTCALL rasterop_SourceAndDestination(uint *dest, const uint *src, int length, uint const_alpha);
+void QT_FASTCALL rasterop_SourceXorDestination(uint *dest, const uint *src, int length, uint const_alpha);
+void QT_FASTCALL rasterop_NotSourceAndNotDestination(uint *dest, const uint *src, int length, uint const_alpha);
+void QT_FASTCALL rasterop_NotSourceOrNotDestination(uint *dest, const uint *src, int length, uint const_alpha);
+void QT_FASTCALL rasterop_NotSourceXorDestination(uint *dest, const uint *src, int length, uint const_alpha);
+void QT_FASTCALL rasterop_NotSource(uint *dest, const uint *src, int length, uint const_alpha);
+void QT_FASTCALL rasterop_NotSourceAndDestination(uint *dest, const uint *src, int length, uint const_alpha);
+void QT_FASTCALL rasterop_SourceAndNotDestination(uint *dest, const uint *src, int length, uint const_alpha);
+
+// prototypes of all the solid composition functions
+void QT_FASTCALL comp_func_solid_SourceOver(uint *dest, int length, uint color, uint const_alpha);
+void QT_FASTCALL comp_func_solid_DestinationOver(uint *dest, int length, uint color, uint const_alpha);
+void QT_FASTCALL comp_func_solid_Clear(uint *dest, int length, uint color, uint const_alpha);
+void QT_FASTCALL comp_func_solid_Source(uint *dest, int length, uint color, uint const_alpha);
+void QT_FASTCALL comp_func_solid_Destination(uint *dest, int length, uint color, uint const_alpha);
+void QT_FASTCALL comp_func_solid_SourceIn(uint *dest, int length, uint color, uint const_alpha);
+void QT_FASTCALL comp_func_solid_DestinationIn(uint *dest, int length, uint color, uint const_alpha);
+void QT_FASTCALL comp_func_solid_SourceOut(uint *dest, int length, uint color, uint const_alpha);
+void QT_FASTCALL comp_func_solid_DestinationOut(uint *dest, int length, uint color, uint const_alpha);
+void QT_FASTCALL comp_func_solid_SourceAtop(uint *dest, int length, uint color, uint const_alpha);
+void QT_FASTCALL comp_func_solid_DestinationAtop(uint *dest, int length, uint color, uint const_alpha);
+void QT_FASTCALL comp_func_solid_XOR(uint *dest, int length, uint color, uint const_alpha);
+void QT_FASTCALL comp_func_solid_Plus(uint *dest, int length, uint color, uint const_alpha);
+void QT_FASTCALL comp_func_solid_Multiply(uint *dest, int length, uint color, uint const_alpha);
+void QT_FASTCALL comp_func_solid_Screen(uint *dest, int length, uint color, uint const_alpha);
+void QT_FASTCALL comp_func_solid_Overlay(uint *dest, int length, uint color, uint const_alpha);
+void QT_FASTCALL comp_func_solid_Darken(uint *dest, int length, uint color, uint const_alpha);
+void QT_FASTCALL comp_func_solid_Lighten(uint *dest, int length, uint color, uint const_alpha);
+void QT_FASTCALL comp_func_solid_ColorDodge(uint *dest, int length, uint color, uint const_alpha);
+void QT_FASTCALL comp_func_solid_ColorBurn(uint *dest, int length, uint color, uint const_alpha);
+void QT_FASTCALL comp_func_solid_HardLight(uint *dest, int length, uint color, uint const_alpha);
+void QT_FASTCALL comp_func_solid_SoftLight(uint *dest, int length, uint color, uint const_alpha);
+void QT_FASTCALL comp_func_solid_Difference(uint *dest, int length, uint color, uint const_alpha);
+void QT_FASTCALL comp_func_solid_Exclusion(uint *dest, int length, uint color, uint const_alpha);
+void QT_FASTCALL rasterop_solid_SourceOrDestination(uint *dest, int length, uint color, uint const_alpha);
+void QT_FASTCALL rasterop_solid_SourceAndDestination(uint *dest, int length, uint color, uint const_alpha);
+void QT_FASTCALL rasterop_solid_SourceXorDestination(uint *dest, int length, uint color, uint const_alpha);
+void QT_FASTCALL rasterop_solid_NotSourceAndNotDestination(uint *dest, int length, uint color, uint const_alpha);
+void QT_FASTCALL rasterop_solid_NotSourceOrNotDestination(uint *dest, int length, uint color, uint const_alpha);
+void QT_FASTCALL rasterop_solid_NotSourceXorDestination(uint *dest, int length, uint color, uint const_alpha);
+void QT_FASTCALL rasterop_solid_NotSource(uint *dest, int length, uint color, uint const_alpha);
+void QT_FASTCALL rasterop_solid_NotSourceAndDestination(uint *dest, int length, uint color, uint const_alpha);
+void QT_FASTCALL rasterop_solid_SourceAndNotDestination(uint *dest, int length, uint color, uint const_alpha);
+
+QT_END_NAMESPACE
+
+#endif // QDRAWHELPER_P_H
diff --git a/src/gui/painting/qdrawhelper_sse.cpp b/src/gui/painting/qdrawhelper_sse.cpp
new file mode 100644
index 0000000000..7f7aee650a
--- /dev/null
+++ b/src/gui/painting/qdrawhelper_sse.cpp
@@ -0,0 +1,168 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qdrawhelper_p.h>
+
+#ifdef QT_HAVE_SSE
+
+#include <private/qdrawhelper_sse_p.h>
+
+QT_BEGIN_NAMESPACE
+
+CompositionFunctionSolid qt_functionForModeSolid_SSE[numCompositionFunctions] = {
+ comp_func_solid_SourceOver<QSSEIntrinsics>,
+ comp_func_solid_DestinationOver<QSSEIntrinsics>,
+ comp_func_solid_Clear<QSSEIntrinsics>,
+ comp_func_solid_Source<QSSEIntrinsics>,
+ 0,
+ comp_func_solid_SourceIn<QSSEIntrinsics>,
+ comp_func_solid_DestinationIn<QSSEIntrinsics>,
+ comp_func_solid_SourceOut<QSSEIntrinsics>,
+ comp_func_solid_DestinationOut<QSSEIntrinsics>,
+ comp_func_solid_SourceAtop<QSSEIntrinsics>,
+ comp_func_solid_DestinationAtop<QSSEIntrinsics>,
+ comp_func_solid_XOR<QSSEIntrinsics>,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // svg 1.2 modes
+ rasterop_solid_SourceOrDestination<QMMXIntrinsics>,
+ rasterop_solid_SourceAndDestination<QMMXIntrinsics>,
+ rasterop_solid_SourceXorDestination<QMMXIntrinsics>,
+ rasterop_solid_NotSourceAndNotDestination<QMMXIntrinsics>,
+ rasterop_solid_NotSourceOrNotDestination<QMMXIntrinsics>,
+ rasterop_solid_NotSourceXorDestination<QMMXIntrinsics>,
+ rasterop_solid_NotSource<QMMXIntrinsics>,
+ rasterop_solid_NotSourceAndDestination<QMMXIntrinsics>,
+ rasterop_solid_SourceAndNotDestination<QMMXIntrinsics>
+};
+
+CompositionFunction qt_functionForMode_SSE[numCompositionFunctions] = {
+ comp_func_SourceOver<QSSEIntrinsics>,
+ comp_func_DestinationOver<QSSEIntrinsics>,
+ comp_func_Clear<QSSEIntrinsics>,
+ comp_func_Source<QSSEIntrinsics>,
+ comp_func_Destination,
+ comp_func_SourceIn<QSSEIntrinsics>,
+ comp_func_DestinationIn<QSSEIntrinsics>,
+ comp_func_SourceOut<QSSEIntrinsics>,
+ comp_func_DestinationOut<QSSEIntrinsics>,
+ comp_func_SourceAtop<QSSEIntrinsics>,
+ comp_func_DestinationAtop<QSSEIntrinsics>,
+ comp_func_XOR<QSSEIntrinsics>,
+ comp_func_Plus,
+ comp_func_Multiply,
+ comp_func_Screen,
+ comp_func_Overlay,
+ comp_func_Darken,
+ comp_func_Lighten,
+ comp_func_ColorDodge,
+ comp_func_ColorBurn,
+ comp_func_HardLight,
+ comp_func_SoftLight,
+ comp_func_Difference,
+ comp_func_Exclusion,
+ rasterop_SourceOrDestination,
+ rasterop_SourceAndDestination,
+ rasterop_SourceXorDestination,
+ rasterop_NotSourceAndNotDestination,
+ rasterop_NotSourceOrNotDestination,
+ rasterop_NotSourceXorDestination,
+ rasterop_NotSource,
+ rasterop_NotSourceAndDestination,
+ rasterop_SourceAndNotDestination
+};
+
+void qt_blend_color_argb_sse(int count, const QSpan *spans, void *userData)
+{
+ qt_blend_color_argb_x86<QSSEIntrinsics>(count, spans, userData,
+ (CompositionFunctionSolid*)qt_functionForModeSolid_SSE);
+}
+
+void qt_memfill32_sse(quint32 *dest, quint32 value, int count)
+{
+ return qt_memfill32_sse_template<QSSEIntrinsics>(dest, value, count);
+}
+
+void qt_bitmapblit16_sse(QRasterBuffer *rasterBuffer, int x, int y,
+ quint32 color,
+ const uchar *src,
+ int width, int height, int stride)
+{
+ return qt_bitmapblit16_sse_template<QSSEIntrinsics>(rasterBuffer, x,y,
+ color, src, width,
+ height, stride);
+}
+
+void qt_blend_argb32_on_argb32_sse(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha)
+{
+ const uint *src = (const uint *) srcPixels;
+ uint *dst = (uint *) destPixels;
+
+ uint ca = const_alpha - 1;
+
+ for (int y=0; y<h; ++y) {
+ comp_func_SourceOver<QSSEIntrinsics>(dst, src, w, ca);
+ dst = (quint32 *)(((uchar *) dst) + dbpl);
+ src = (const quint32 *)(((const uchar *) src) + sbpl);
+ }
+}
+
+void qt_blend_rgb32_on_rgb32_sse(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha)
+{
+ const uint *src = (const uint *) srcPixels;
+ uint *dst = (uint *) destPixels;
+
+ uint ca = const_alpha - 1;
+
+ for (int y=0; y<h; ++y) {
+ comp_func_Source<QSSEIntrinsics>(dst, src, w, ca);
+ dst = (quint32 *)(((uchar *) dst) + dbpl);
+ src = (const quint32 *)(((const uchar *) src) + sbpl);
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_HAVE_SSE
diff --git a/src/gui/painting/qdrawhelper_sse2.cpp b/src/gui/painting/qdrawhelper_sse2.cpp
new file mode 100644
index 0000000000..aad6bc9254
--- /dev/null
+++ b/src/gui/painting/qdrawhelper_sse2.cpp
@@ -0,0 +1,496 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qdrawhelper_x86_p.h>
+
+#ifdef QT_HAVE_SSE2
+
+#include <private/qdrawingprimitive_sse2_p.h>
+#include <private/qpaintengine_raster_p.h>
+
+QT_BEGIN_NAMESPACE
+
+void qt_blend_argb32_on_argb32_sse2(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha)
+{
+ const quint32 *src = (const quint32 *) srcPixels;
+ quint32 *dst = (quint32 *) destPixels;
+ if (const_alpha == 256) {
+ const __m128i alphaMask = _mm_set1_epi32(0xff000000);
+ const __m128i nullVector = _mm_set1_epi32(0);
+ const __m128i half = _mm_set1_epi16(0x80);
+ const __m128i one = _mm_set1_epi16(0xff);
+ const __m128i colorMask = _mm_set1_epi32(0x00ff00ff);
+ for (int y = 0; y < h; ++y) {
+ BLEND_SOURCE_OVER_ARGB32_SSE2(dst, src, w, nullVector, half, one, colorMask, alphaMask);
+ dst = (quint32 *)(((uchar *) dst) + dbpl);
+ src = (const quint32 *)(((const uchar *) src) + sbpl);
+ }
+ } else if (const_alpha != 0) {
+ // dest = (s + d * sia) * ca + d * cia
+ // = s * ca + d * (sia * ca + cia)
+ // = s * ca + d * (1 - sa*ca)
+ const_alpha = (const_alpha * 255) >> 8;
+ const __m128i nullVector = _mm_set1_epi32(0);
+ const __m128i half = _mm_set1_epi16(0x80);
+ const __m128i one = _mm_set1_epi16(0xff);
+ const __m128i colorMask = _mm_set1_epi32(0x00ff00ff);
+ const __m128i constAlphaVector = _mm_set1_epi16(const_alpha);
+ for (int y = 0; y < h; ++y) {
+ BLEND_SOURCE_OVER_ARGB32_WITH_CONST_ALPHA_SSE2(dst, src, w, nullVector, half, one, colorMask, constAlphaVector)
+ dst = (quint32 *)(((uchar *) dst) + dbpl);
+ src = (const quint32 *)(((const uchar *) src) + sbpl);
+ }
+ }
+}
+
+// qblendfunctions.cpp
+void qt_blend_rgb32_on_rgb32(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha);
+
+void qt_blend_rgb32_on_rgb32_sse2(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha)
+{
+ const quint32 *src = (const quint32 *) srcPixels;
+ quint32 *dst = (quint32 *) destPixels;
+ if (const_alpha != 256) {
+ if (const_alpha != 0) {
+ const __m128i nullVector = _mm_set1_epi32(0);
+ const __m128i half = _mm_set1_epi16(0x80);
+ const __m128i colorMask = _mm_set1_epi32(0x00ff00ff);
+
+ const_alpha = (const_alpha * 255) >> 8;
+ int one_minus_const_alpha = 255 - const_alpha;
+ const __m128i constAlphaVector = _mm_set1_epi16(const_alpha);
+ const __m128i oneMinusConstAlpha = _mm_set1_epi16(one_minus_const_alpha);
+ for (int y = 0; y < h; ++y) {
+ int x = 0;
+
+ // First, align dest to 16 bytes:
+ ALIGNMENT_PROLOGUE_16BYTES(dst, x, w) {
+ quint32 s = src[x];
+ s = BYTE_MUL(s, const_alpha);
+ dst[x] = INTERPOLATE_PIXEL_255(src[x], const_alpha, dst[x], one_minus_const_alpha);
+ }
+
+ for (; x < w-3; x += 4) {
+ __m128i srcVector = _mm_loadu_si128((__m128i *)&src[x]);
+ if (_mm_movemask_epi8(_mm_cmpeq_epi32(srcVector, nullVector)) != 0xffff) {
+ const __m128i dstVector = _mm_load_si128((__m128i *)&dst[x]);
+ __m128i result;
+ INTERPOLATE_PIXEL_255_SSE2(result, srcVector, dstVector, constAlphaVector, oneMinusConstAlpha, colorMask, half);
+ _mm_store_si128((__m128i *)&dst[x], result);
+ }
+ }
+ for (; x<w; ++x) {
+ quint32 s = src[x];
+ s = BYTE_MUL(s, const_alpha);
+ dst[x] = INTERPOLATE_PIXEL_255(src[x], const_alpha, dst[x], one_minus_const_alpha);
+ }
+ dst = (quint32 *)(((uchar *) dst) + dbpl);
+ src = (const quint32 *)(((const uchar *) src) + sbpl);
+ }
+ }
+ } else {
+ qt_blend_rgb32_on_rgb32(destPixels, dbpl, srcPixels, sbpl, w, h, const_alpha);
+ }
+}
+
+void QT_FASTCALL comp_func_SourceOver_sse2(uint *destPixels, const uint *srcPixels, int length, uint const_alpha)
+{
+ Q_ASSERT(const_alpha < 256);
+
+ const quint32 *src = (const quint32 *) srcPixels;
+ quint32 *dst = (quint32 *) destPixels;
+
+ const __m128i nullVector = _mm_set1_epi32(0);
+ const __m128i half = _mm_set1_epi16(0x80);
+ const __m128i one = _mm_set1_epi16(0xff);
+ const __m128i colorMask = _mm_set1_epi32(0x00ff00ff);
+ if (const_alpha == 255) {
+ const __m128i alphaMask = _mm_set1_epi32(0xff000000);
+ BLEND_SOURCE_OVER_ARGB32_SSE2(dst, src, length, nullVector, half, one, colorMask, alphaMask);
+ } else {
+ const __m128i constAlphaVector = _mm_set1_epi16(const_alpha);
+ BLEND_SOURCE_OVER_ARGB32_WITH_CONST_ALPHA_SSE2(dst, src, length, nullVector, half, one, colorMask, constAlphaVector);
+ }
+}
+
+void QT_FASTCALL comp_func_Plus_sse2(uint *dst, const uint *src, int length, uint const_alpha)
+{
+ int x = 0;
+
+ if (const_alpha == 255) {
+ // 1) Prologue: align destination on 16 bytes
+ ALIGNMENT_PROLOGUE_16BYTES(dst, x, length)
+ dst[x] = comp_func_Plus_one_pixel(dst[x], src[x]);
+
+ // 2) composition with SSE2
+ for (; x < length - 3; x += 4) {
+ const __m128i srcVector = _mm_loadu_si128((__m128i *)&src[x]);
+ const __m128i dstVector = _mm_load_si128((__m128i *)&dst[x]);
+
+ const __m128i result = _mm_adds_epu8(srcVector, dstVector);
+ _mm_store_si128((__m128i *)&dst[x], result);
+ }
+
+ // 3) Epilogue:
+ for (; x < length; ++x)
+ dst[x] = comp_func_Plus_one_pixel(dst[x], src[x]);
+ } else {
+ const int one_minus_const_alpha = 255 - const_alpha;
+ const __m128i constAlphaVector = _mm_set1_epi16(const_alpha);
+ const __m128i oneMinusConstAlpha = _mm_set1_epi16(one_minus_const_alpha);
+
+ // 1) Prologue: align destination on 16 bytes
+ ALIGNMENT_PROLOGUE_16BYTES(dst, x, length)
+ dst[x] = comp_func_Plus_one_pixel_const_alpha(dst[x], src[x], const_alpha, one_minus_const_alpha);
+
+ const __m128i half = _mm_set1_epi16(0x80);
+ const __m128i colorMask = _mm_set1_epi32(0x00ff00ff);
+ // 2) composition with SSE2
+ for (; x < length - 3; x += 4) {
+ const __m128i srcVector = _mm_loadu_si128((__m128i *)&src[x]);
+ const __m128i dstVector = _mm_load_si128((__m128i *)&dst[x]);
+
+ __m128i result = _mm_adds_epu8(srcVector, dstVector);
+ INTERPOLATE_PIXEL_255_SSE2(result, result, dstVector, constAlphaVector, oneMinusConstAlpha, colorMask, half)
+ _mm_store_si128((__m128i *)&dst[x], result);
+ }
+
+ // 3) Epilogue:
+ for (; x < length; ++x)
+ dst[x] = comp_func_Plus_one_pixel_const_alpha(dst[x], src[x], const_alpha, one_minus_const_alpha);
+ }
+}
+
+void QT_FASTCALL comp_func_Source_sse2(uint *dst, const uint *src, int length, uint const_alpha)
+{
+ if (const_alpha == 255) {
+ ::memcpy(dst, src, length * sizeof(uint));
+ } else {
+ const int ialpha = 255 - const_alpha;
+
+ int x = 0;
+
+ // 1) prologue, align on 16 bytes
+ ALIGNMENT_PROLOGUE_16BYTES(dst, x, length)
+ dst[x] = INTERPOLATE_PIXEL_255(src[x], const_alpha, dst[x], ialpha);
+
+ // 2) interpolate pixels with SSE2
+ const __m128i half = _mm_set1_epi16(0x80);
+ const __m128i colorMask = _mm_set1_epi32(0x00ff00ff);
+ const __m128i constAlphaVector = _mm_set1_epi16(const_alpha);
+ const __m128i oneMinusConstAlpha = _mm_set1_epi16(ialpha);
+ for (; x < length - 3; x += 4) {
+ const __m128i srcVector = _mm_loadu_si128((__m128i *)&src[x]);
+ __m128i dstVector = _mm_load_si128((__m128i *)&dst[x]);
+ INTERPOLATE_PIXEL_255_SSE2(dstVector, srcVector, dstVector, constAlphaVector, oneMinusConstAlpha, colorMask, half)
+ _mm_store_si128((__m128i *)&dst[x], dstVector);
+ }
+
+ // 3) Epilogue
+ for (; x < length; ++x)
+ dst[x] = INTERPOLATE_PIXEL_255(src[x], const_alpha, dst[x], ialpha);
+ }
+}
+
+void qt_memfill32_sse2(quint32 *dest, quint32 value, int count)
+{
+ if (count < 7) {
+ switch (count) {
+ case 6: *dest++ = value;
+ case 5: *dest++ = value;
+ case 4: *dest++ = value;
+ case 3: *dest++ = value;
+ case 2: *dest++ = value;
+ case 1: *dest = value;
+ }
+ return;
+ };
+
+ const int align = (quintptr)(dest) & 0xf;
+ switch (align) {
+ case 4: *dest++ = value; --count;
+ case 8: *dest++ = value; --count;
+ case 12: *dest++ = value; --count;
+ }
+
+ int count128 = count / 4;
+ __m128i *dst128 = reinterpret_cast<__m128i*>(dest);
+ const __m128i value128 = _mm_set_epi32(value, value, value, value);
+
+ int n = (count128 + 3) / 4;
+ switch (count128 & 0x3) {
+ case 0: do { _mm_stream_si128(dst128++, value128);
+ case 3: _mm_stream_si128(dst128++, value128);
+ case 2: _mm_stream_si128(dst128++, value128);
+ case 1: _mm_stream_si128(dst128++, value128);
+ } while (--n > 0);
+ }
+
+ const int rest = count & 0x3;
+ if (rest) {
+ switch (rest) {
+ case 3: dest[count - 3] = value;
+ case 2: dest[count - 2] = value;
+ case 1: dest[count - 1] = value;
+ }
+ }
+}
+
+void QT_FASTCALL comp_func_solid_SourceOver_sse2(uint *destPixels, int length, uint color, uint const_alpha)
+{
+ if ((const_alpha & qAlpha(color)) == 255) {
+ qt_memfill32_sse2(destPixels, color, length);
+ } else {
+ if (const_alpha != 255)
+ color = BYTE_MUL(color, const_alpha);
+
+ const quint32 minusAlphaOfColor = qAlpha(~color);
+ int x = 0;
+
+ quint32 *dst = (quint32 *) destPixels;
+ const __m128i colorVector = _mm_set1_epi32(color);
+ const __m128i colorMask = _mm_set1_epi32(0x00ff00ff);
+ const __m128i half = _mm_set1_epi16(0x80);
+ const __m128i minusAlphaOfColorVector = _mm_set1_epi16(minusAlphaOfColor);
+
+ ALIGNMENT_PROLOGUE_16BYTES(dst, x, length)
+ destPixels[x] = color + BYTE_MUL(destPixels[x], minusAlphaOfColor);
+
+ for (; x < length-3; x += 4) {
+ __m128i dstVector = _mm_load_si128((__m128i *)&dst[x]);
+ BYTE_MUL_SSE2(dstVector, dstVector, minusAlphaOfColorVector, colorMask, half);
+ dstVector = _mm_add_epi8(colorVector, dstVector);
+ _mm_store_si128((__m128i *)&dst[x], dstVector);
+ }
+ for (;x < length; ++x)
+ destPixels[x] = color + BYTE_MUL(destPixels[x], minusAlphaOfColor);
+ }
+}
+
+CompositionFunctionSolid qt_functionForModeSolid_onlySSE2[numCompositionFunctions] = {
+ comp_func_solid_SourceOver_sse2,
+ comp_func_solid_DestinationOver,
+ comp_func_solid_Clear,
+ comp_func_solid_Source,
+ comp_func_solid_Destination,
+ comp_func_solid_SourceIn,
+ comp_func_solid_DestinationIn,
+ comp_func_solid_SourceOut,
+ comp_func_solid_DestinationOut,
+ comp_func_solid_SourceAtop,
+ comp_func_solid_DestinationAtop,
+ comp_func_solid_XOR,
+ comp_func_solid_Plus,
+ comp_func_solid_Multiply,
+ comp_func_solid_Screen,
+ comp_func_solid_Overlay,
+ comp_func_solid_Darken,
+ comp_func_solid_Lighten,
+ comp_func_solid_ColorDodge,
+ comp_func_solid_ColorBurn,
+ comp_func_solid_HardLight,
+ comp_func_solid_SoftLight,
+ comp_func_solid_Difference,
+ comp_func_solid_Exclusion,
+ rasterop_solid_SourceOrDestination,
+ rasterop_solid_SourceAndDestination,
+ rasterop_solid_SourceXorDestination,
+ rasterop_solid_NotSourceAndNotDestination,
+ rasterop_solid_NotSourceOrNotDestination,
+ rasterop_solid_NotSourceXorDestination,
+ rasterop_solid_NotSource,
+ rasterop_solid_NotSourceAndDestination,
+ rasterop_solid_SourceAndNotDestination
+};
+
+CompositionFunction qt_functionForMode_onlySSE2[numCompositionFunctions] = {
+ comp_func_SourceOver_sse2,
+ comp_func_DestinationOver,
+ comp_func_Clear,
+ comp_func_Source_sse2,
+ comp_func_Destination,
+ comp_func_SourceIn,
+ comp_func_DestinationIn,
+ comp_func_SourceOut,
+ comp_func_DestinationOut,
+ comp_func_SourceAtop,
+ comp_func_DestinationAtop,
+ comp_func_XOR,
+ comp_func_Plus_sse2,
+ comp_func_Multiply,
+ comp_func_Screen,
+ comp_func_Overlay,
+ comp_func_Darken,
+ comp_func_Lighten,
+ comp_func_ColorDodge,
+ comp_func_ColorBurn,
+ comp_func_HardLight,
+ comp_func_SoftLight,
+ comp_func_Difference,
+ comp_func_Exclusion,
+ rasterop_SourceOrDestination,
+ rasterop_SourceAndDestination,
+ rasterop_SourceXorDestination,
+ rasterop_NotSourceAndNotDestination,
+ rasterop_NotSourceOrNotDestination,
+ rasterop_NotSourceXorDestination,
+ rasterop_NotSource,
+ rasterop_NotSourceAndDestination,
+ rasterop_SourceAndNotDestination
+};
+
+void qt_memfill16_sse2(quint16 *dest, quint16 value, int count)
+{
+ if (count < 3) {
+ switch (count) {
+ case 2: *dest++ = value;
+ case 1: *dest = value;
+ }
+ return;
+ }
+
+ const int align = (quintptr)(dest) & 0x3;
+ switch (align) {
+ case 2: *dest++ = value; --count;
+ }
+
+ const quint32 value32 = (value << 16) | value;
+ qt_memfill32_sse2(reinterpret_cast<quint32*>(dest), value32, count / 2);
+
+ if (count & 0x1)
+ dest[count - 1] = value;
+}
+
+void qt_bitmapblit32_sse2(QRasterBuffer *rasterBuffer, int x, int y,
+ quint32 color,
+ const uchar *src, int width, int height, int stride)
+{
+ quint32 *dest = reinterpret_cast<quint32*>(rasterBuffer->scanLine(y)) + x;
+ const int destStride = rasterBuffer->bytesPerLine() / sizeof(quint32);
+
+ const __m128i c128 = _mm_set1_epi32(color);
+ const __m128i maskmask1 = _mm_set_epi32(0x10101010, 0x20202020,
+ 0x40404040, 0x80808080);
+ const __m128i maskadd1 = _mm_set_epi32(0x70707070, 0x60606060,
+ 0x40404040, 0x00000000);
+
+ if (width > 4) {
+ const __m128i maskmask2 = _mm_set_epi32(0x01010101, 0x02020202,
+ 0x04040404, 0x08080808);
+ const __m128i maskadd2 = _mm_set_epi32(0x7f7f7f7f, 0x7e7e7e7e,
+ 0x7c7c7c7c, 0x78787878);
+ while (height--) {
+ for (int x = 0; x < width; x += 8) {
+ const quint8 s = src[x >> 3];
+ if (!s)
+ continue;
+ __m128i mask1 = _mm_set1_epi8(s);
+ __m128i mask2 = mask1;
+
+ mask1 = _mm_and_si128(mask1, maskmask1);
+ mask1 = _mm_add_epi8(mask1, maskadd1);
+ _mm_maskmoveu_si128(c128, mask1, (char*)(dest + x));
+ mask2 = _mm_and_si128(mask2, maskmask2);
+ mask2 = _mm_add_epi8(mask2, maskadd2);
+ _mm_maskmoveu_si128(c128, mask2, (char*)(dest + x + 4));
+ }
+ dest += destStride;
+ src += stride;
+ }
+ } else {
+ while (height--) {
+ const quint8 s = *src;
+ if (s) {
+ __m128i mask1 = _mm_set1_epi8(s);
+ mask1 = _mm_and_si128(mask1, maskmask1);
+ mask1 = _mm_add_epi8(mask1, maskadd1);
+ _mm_maskmoveu_si128(c128, mask1, (char*)(dest));
+ }
+ dest += destStride;
+ src += stride;
+ }
+ }
+}
+
+void qt_bitmapblit16_sse2(QRasterBuffer *rasterBuffer, int x, int y,
+ quint32 color,
+ const uchar *src, int width, int height, int stride)
+{
+ const quint16 c = qt_colorConvert<quint16, quint32>(color, 0);
+ quint16 *dest = reinterpret_cast<quint16*>(rasterBuffer->scanLine(y)) + x;
+ const int destStride = rasterBuffer->bytesPerLine() / sizeof(quint16);
+
+ const __m128i c128 = _mm_set1_epi16(c);
+#if defined(Q_CC_MSVC)
+# pragma warning(disable: 4309) // truncation of constant value
+#endif
+ const __m128i maskmask = _mm_set_epi16(0x0101, 0x0202, 0x0404, 0x0808,
+ 0x1010, 0x2020, 0x4040, 0x8080);
+ const __m128i maskadd = _mm_set_epi16(0x7f7f, 0x7e7e, 0x7c7c, 0x7878,
+ 0x7070, 0x6060, 0x4040, 0x0000);
+
+ while (height--) {
+ for (int x = 0; x < width; x += 8) {
+ const quint8 s = src[x >> 3];
+ if (!s)
+ continue;
+ __m128i mask = _mm_set1_epi8(s);
+ mask = _mm_and_si128(mask, maskmask);
+ mask = _mm_add_epi8(mask, maskadd);
+ _mm_maskmoveu_si128(c128, mask, (char*)(dest + x));
+ }
+ dest += destStride;
+ src += stride;
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_HAVE_SSE2
diff --git a/src/gui/painting/qdrawhelper_sse3dnow.cpp b/src/gui/painting/qdrawhelper_sse3dnow.cpp
new file mode 100644
index 0000000000..fd351ed2bf
--- /dev/null
+++ b/src/gui/painting/qdrawhelper_sse3dnow.cpp
@@ -0,0 +1,143 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qdrawhelper_x86_p.h>
+
+#if defined(QT_HAVE_3DNOW) && defined(QT_HAVE_SSE)
+
+#include <private/qdrawhelper_sse_p.h>
+#include <mm3dnow.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QSSE3DNOWIntrinsics : public QSSEIntrinsics
+{
+ static inline void end() {
+ _m_femms();
+ }
+};
+
+CompositionFunctionSolid qt_functionForModeSolid_SSE3DNOW[numCompositionFunctions] = {
+ comp_func_solid_SourceOver<QSSE3DNOWIntrinsics>,
+ comp_func_solid_DestinationOver<QSSE3DNOWIntrinsics>,
+ comp_func_solid_Clear<QSSE3DNOWIntrinsics>,
+ comp_func_solid_Source<QSSE3DNOWIntrinsics>,
+ 0,
+ comp_func_solid_SourceIn<QSSE3DNOWIntrinsics>,
+ comp_func_solid_DestinationIn<QSSE3DNOWIntrinsics>,
+ comp_func_solid_SourceOut<QSSE3DNOWIntrinsics>,
+ comp_func_solid_DestinationOut<QSSE3DNOWIntrinsics>,
+ comp_func_solid_SourceAtop<QSSE3DNOWIntrinsics>,
+ comp_func_solid_DestinationAtop<QSSE3DNOWIntrinsics>,
+ comp_func_solid_XOR<QSSE3DNOWIntrinsics>,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // svg 1.2 modes
+ rasterop_solid_SourceOrDestination<QSSE3DNOWIntrinsics>,
+ rasterop_solid_SourceAndDestination<QSSE3DNOWIntrinsics>,
+ rasterop_solid_SourceXorDestination<QSSE3DNOWIntrinsics>,
+ rasterop_solid_NotSourceAndNotDestination<QSSE3DNOWIntrinsics>,
+ rasterop_solid_NotSourceOrNotDestination<QSSE3DNOWIntrinsics>,
+ rasterop_solid_NotSourceXorDestination<QSSE3DNOWIntrinsics>,
+ rasterop_solid_NotSource<QSSE3DNOWIntrinsics>,
+ rasterop_solid_NotSourceAndDestination<QSSE3DNOWIntrinsics>,
+ rasterop_solid_SourceAndNotDestination<QSSE3DNOWIntrinsics>
+};
+
+CompositionFunction qt_functionForMode_SSE3DNOW[numCompositionFunctions] = {
+ comp_func_SourceOver<QSSE3DNOWIntrinsics>,
+ comp_func_DestinationOver<QSSE3DNOWIntrinsics>,
+ comp_func_Clear<QSSE3DNOWIntrinsics>,
+ comp_func_Source<QSSE3DNOWIntrinsics>,
+ comp_func_Destination,
+ comp_func_SourceIn<QSSE3DNOWIntrinsics>,
+ comp_func_DestinationIn<QSSE3DNOWIntrinsics>,
+ comp_func_SourceOut<QSSE3DNOWIntrinsics>,
+ comp_func_DestinationOut<QSSE3DNOWIntrinsics>,
+ comp_func_SourceAtop<QSSE3DNOWIntrinsics>,
+ comp_func_DestinationAtop<QSSE3DNOWIntrinsics>,
+ comp_func_XOR<QSSE3DNOWIntrinsics>,
+ comp_func_Plus,
+ comp_func_Multiply,
+ comp_func_Screen,
+ comp_func_Overlay,
+ comp_func_Darken,
+ comp_func_Lighten,
+ comp_func_ColorDodge,
+ comp_func_ColorBurn,
+ comp_func_HardLight,
+ comp_func_SoftLight,
+ comp_func_Difference,
+ comp_func_Exclusion,
+ rasterop_SourceOrDestination,
+ rasterop_SourceAndDestination,
+ rasterop_SourceXorDestination,
+ rasterop_NotSourceAndNotDestination,
+ rasterop_NotSourceOrNotDestination,
+ rasterop_NotSourceXorDestination,
+ rasterop_NotSource,
+ rasterop_NotSourceAndDestination,
+ rasterop_SourceAndNotDestination
+};
+
+void qt_blend_color_argb_sse3dnow(int count, const QSpan *spans, void *userData)
+{
+ qt_blend_color_argb_x86<QSSE3DNOWIntrinsics>(count, spans, userData,
+ (CompositionFunctionSolid*)qt_functionForModeSolid_SSE3DNOW);
+}
+
+void qt_memfill32_sse3dnow(quint32 *dest, quint32 value, int count)
+{
+ return qt_memfill32_sse_template<QSSE3DNOWIntrinsics>(dest, value, count);
+}
+
+
+void qt_bitmapblit16_sse3dnow(QRasterBuffer *rasterBuffer, int x, int y,
+ quint32 color,
+ const uchar *src,
+ int width, int height, int stride)
+{
+ return qt_bitmapblit16_sse_template<QSSE3DNOWIntrinsics>(rasterBuffer, x,y,
+ color, src, width,
+ height, stride);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_HAVE_3DNOW && QT_HAVE_SSE
diff --git a/src/gui/painting/qdrawhelper_sse_p.h b/src/gui/painting/qdrawhelper_sse_p.h
new file mode 100644
index 0000000000..fff1e07152
--- /dev/null
+++ b/src/gui/painting/qdrawhelper_sse_p.h
@@ -0,0 +1,182 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDRAWHELPER_SSE_P_H
+#define QDRAWHELPER_SSE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qdrawhelper_mmx_p.h>
+
+#ifdef QT_HAVE_SSE
+
+#ifdef QT_LINUXBASE
+// this is an evil hack - the posix_memalign declaration in LSB
+// is wrong - see http://bugs.linuxbase.org/show_bug.cgi?id=2431
+# define posix_memalign _lsb_hack_posix_memalign
+# include <xmmintrin.h>
+# undef posix_memalign
+#else
+# include <xmmintrin.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifndef _MM_SHUFFLE
+#define _MM_SHUFFLE(fp3,fp2,fp1,fp0) \
+ (((fp3) << 6) | ((fp2) << 4) | ((fp1) << 2) | (fp0))
+#endif
+
+struct QSSEIntrinsics : public QMMXIntrinsics
+{
+ static inline m64 alpha(m64 x) {
+ return _mm_shuffle_pi16 (x, _MM_SHUFFLE(3, 3, 3, 3));
+ }
+
+ static inline m64 _load_alpha(uint x, const m64 &mmx_0x0000) {
+ m64 t = _mm_unpacklo_pi8(_mm_cvtsi32_si64(x), mmx_0x0000);
+ return _mm_shuffle_pi16 (t, _MM_SHUFFLE(0, 0, 0, 0));
+ }
+};
+
+template <class MM>
+inline void qt_memfill32_sse_template(quint32 *dest, quint32 value, int count)
+{
+ if (count < 7) {
+ switch (count) {
+ case 6: *dest++ = value;
+ case 5: *dest++ = value;
+ case 4: *dest++ = value;
+ case 3: *dest++ = value;
+ case 2: *dest++ = value;
+ case 1: *dest = value;
+ }
+ return;
+ };
+
+ __m64 *dst64 = reinterpret_cast<__m64*>(dest);
+ const __m64 value64 = _mm_set_pi32(value, value);
+ int count64 = count / 2;
+
+ int n = (count64 + 3) / 4;
+ switch (count64 & 0x3) {
+ case 0: do { _mm_stream_pi(dst64++, value64);
+ case 3: _mm_stream_pi(dst64++, value64);
+ case 2: _mm_stream_pi(dst64++, value64);
+ case 1: _mm_stream_pi(dst64++, value64);
+ } while (--n > 0);
+ }
+
+ if (count & 0x1)
+ dest[count - 1] = value;
+
+ MM::end();
+}
+
+template <class MM>
+inline void qt_bitmapblit16_sse_template(QRasterBuffer *rasterBuffer,
+ int x, int y,
+ quint32 color,
+ const uchar *src,
+ int width, int height, int stride)
+{
+ const quint16 c = qt_colorConvert<quint16, quint32>(color, 0);
+ quint16 *dest = reinterpret_cast<quint16*>(rasterBuffer->scanLine(y)) + x;
+ const int destStride = rasterBuffer->bytesPerLine() / sizeof(quint16);
+
+ const __m64 c64 = _mm_set1_pi16(c);
+#ifdef Q_CC_MSVC
+# pragma warning(disable: 4309) // truncation of constant value
+#endif
+ const __m64 maskmask1 = _mm_set_pi16(0x1010, 0x2020, 0x4040, 0x8080);
+ const __m64 maskadd1 = _mm_set_pi16(0x7070, 0x6060, 0x4040, 0x0000);
+
+ if (width > 4) {
+ const __m64 maskmask2 = _mm_set_pi16(0x0101, 0x0202, 0x0404, 0x0808);
+ const __m64 maskadd2 = _mm_set_pi16(0x7f7f, 0x7e7e, 0x7c7c, 0x7878);
+
+ while (height--) {
+ for (int x = 0; x < width; x += 8) {
+ const quint8 s = src[x >> 3];
+ if (!s)
+ continue;
+ __m64 mask1 = _mm_set1_pi8(s);
+ __m64 mask2 = mask1;
+ mask1 = _m_pand(mask1, maskmask1);
+ mask1 = _mm_add_pi16(mask1, maskadd1);
+ _mm_maskmove_si64(c64, mask1, (char*)(dest + x));
+ mask2 = _m_pand(mask2, maskmask2);
+ mask2 = _mm_add_pi16(mask2, maskadd2);
+ _mm_maskmove_si64(c64, mask2, (char*)(dest + x + 4));
+ }
+ dest += destStride;
+ src += stride;
+ }
+ } else {
+ while (height--) {
+ const quint8 s = *src;
+ if (s) {
+ __m64 mask1 = _mm_set1_pi8(s);
+ mask1 = _m_pand(mask1, maskmask1);
+ mask1 = _mm_add_pi16(mask1, maskadd1);
+ _mm_maskmove_si64(c64, mask1, (char*)(dest));
+ }
+ dest += destStride;
+ src += stride;
+ }
+ }
+
+ MM::end();
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_HAVE_SSE
+#endif // QDRAWHELPER_SSE_P_H
diff --git a/src/gui/painting/qdrawhelper_ssse3.cpp b/src/gui/painting/qdrawhelper_ssse3.cpp
new file mode 100644
index 0000000000..e392de2541
--- /dev/null
+++ b/src/gui/painting/qdrawhelper_ssse3.cpp
@@ -0,0 +1,183 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qdrawhelper_x86_p.h>
+
+#ifdef QT_HAVE_SSSE3
+
+#include <private/qdrawingprimitive_sse2_p.h>
+
+QT_BEGIN_NAMESPACE
+
+inline static void blend_pixel(quint32 &dst, const quint32 src)
+{
+ if (src >= 0xff000000)
+ dst = src;
+ else if (src != 0)
+ dst = src + BYTE_MUL(dst, qAlpha(~src));
+}
+
+
+/* The instruction palignr uses direct arguments, so we have to generate the code fo the different
+ shift (4, 8, 12). Checking the alignment inside the loop is unfortunatelly way too slow.
+ */
+#define BLENDING_LOOP(palignrOffset, length)\
+ for (; x < length-3; x += 4) { \
+ const __m128i srcVectorLastLoaded = _mm_load_si128((__m128i *)&src[x - minusOffsetToAlignSrcOn16Bytes + 4]);\
+ const __m128i srcVector = _mm_alignr_epi8(srcVectorLastLoaded, srcVectorPrevLoaded, palignrOffset); \
+ const __m128i srcVectorAlpha = _mm_and_si128(srcVector, alphaMask); \
+ if (_mm_movemask_epi8(_mm_cmpeq_epi32(srcVectorAlpha, alphaMask)) == 0xffff) { \
+ _mm_store_si128((__m128i *)&dst[x], srcVector); \
+ } else if (_mm_movemask_epi8(_mm_cmpeq_epi32(srcVectorAlpha, nullVector)) != 0xffff) { \
+ __m128i alphaChannel = _mm_shuffle_epi8(srcVector, alphaShuffleMask); \
+ alphaChannel = _mm_sub_epi16(one, alphaChannel); \
+ const __m128i dstVector = _mm_load_si128((__m128i *)&dst[x]); \
+ __m128i destMultipliedByOneMinusAlpha; \
+ BYTE_MUL_SSE2(destMultipliedByOneMinusAlpha, dstVector, alphaChannel, colorMask, half); \
+ const __m128i result = _mm_add_epi8(srcVector, destMultipliedByOneMinusAlpha); \
+ _mm_store_si128((__m128i *)&dst[x], result); \
+ } \
+ srcVectorPrevLoaded = srcVectorLastLoaded;\
+ }
+
+
+// Basically blend src over dst with the const alpha defined as constAlphaVector.
+// nullVector, half, one, colorMask are constant across the whole image/texture, and should be defined as:
+//const __m128i nullVector = _mm_set1_epi32(0);
+//const __m128i half = _mm_set1_epi16(0x80);
+//const __m128i one = _mm_set1_epi16(0xff);
+//const __m128i colorMask = _mm_set1_epi32(0x00ff00ff);
+//const __m128i alphaMask = _mm_set1_epi32(0xff000000);
+//
+// The computation being done is:
+// result = s + d * (1-alpha)
+// with shortcuts if fully opaque or fully transparent.
+#define BLEND_SOURCE_OVER_ARGB32_SSSE3(dst, src, length, nullVector, half, one, colorMask, alphaMask) { \
+ int x = 0; \
+\
+ /* First, get dst aligned. */ \
+ ALIGNMENT_PROLOGUE_16BYTES(dst, x, length) { \
+ blend_pixel(dst[x], src[x]); \
+ } \
+\
+ const int minusOffsetToAlignSrcOn16Bytes = (reinterpret_cast<quintptr>(&(src[x])) >> 2) & 0x3;\
+\
+ if (!minusOffsetToAlignSrcOn16Bytes) {\
+ /* src is aligned, usual algorithm but with aligned operations.\
+ See the SSE2 version for more documentation on the algorithm itself. */\
+ const __m128i alphaShuffleMask = _mm_set_epi8(0xff,15,0xff,15,0xff,11,0xff,11,0xff,7,0xff,7,0xff,3,0xff,3);\
+ for (; x < length-3; x += 4) { \
+ const __m128i srcVector = _mm_load_si128((__m128i *)&src[x]); \
+ const __m128i srcVectorAlpha = _mm_and_si128(srcVector, alphaMask); \
+ if (_mm_movemask_epi8(_mm_cmpeq_epi32(srcVectorAlpha, alphaMask)) == 0xffff) { \
+ _mm_store_si128((__m128i *)&dst[x], srcVector); \
+ } else if (_mm_movemask_epi8(_mm_cmpeq_epi32(srcVectorAlpha, nullVector)) != 0xffff) { \
+ __m128i alphaChannel = _mm_shuffle_epi8(srcVector, alphaShuffleMask); \
+ alphaChannel = _mm_sub_epi16(one, alphaChannel); \
+ const __m128i dstVector = _mm_load_si128((__m128i *)&dst[x]); \
+ __m128i destMultipliedByOneMinusAlpha; \
+ BYTE_MUL_SSE2(destMultipliedByOneMinusAlpha, dstVector, alphaChannel, colorMask, half); \
+ const __m128i result = _mm_add_epi8(srcVector, destMultipliedByOneMinusAlpha); \
+ _mm_store_si128((__m128i *)&dst[x], result); \
+ } \
+ } /* end for() */\
+ } else if ((length - x) >= 8) {\
+ /* We use two vectors to extract the src: prevLoaded for the first pixels, lastLoaded for the current pixels. */\
+ __m128i srcVectorPrevLoaded = _mm_load_si128((__m128i *)&src[x - minusOffsetToAlignSrcOn16Bytes]);\
+ const int palignrOffset = minusOffsetToAlignSrcOn16Bytes << 2;\
+\
+ const __m128i alphaShuffleMask = _mm_set_epi8(0xff,15,0xff,15,0xff,11,0xff,11,0xff,7,0xff,7,0xff,3,0xff,3);\
+ switch (palignrOffset) {\
+ case 4:\
+ BLENDING_LOOP(4, length)\
+ break;\
+ case 8:\
+ BLENDING_LOOP(8, length)\
+ break;\
+ case 12:\
+ BLENDING_LOOP(12, length)\
+ break;\
+ }\
+ }\
+ for (; x < length; ++x) \
+ blend_pixel(dst[x], src[x]); \
+}
+
+void qt_blend_argb32_on_argb32_ssse3(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha)
+{
+ const quint32 *src = (const quint32 *) srcPixels;
+ quint32 *dst = (quint32 *) destPixels;
+ if (const_alpha == 256) {
+ const __m128i alphaMask = _mm_set1_epi32(0xff000000);
+ const __m128i nullVector = _mm_setzero_si128();
+ const __m128i half = _mm_set1_epi16(0x80);
+ const __m128i one = _mm_set1_epi16(0xff);
+ const __m128i colorMask = _mm_set1_epi32(0x00ff00ff);
+
+ for (int y = 0; y < h; ++y) {
+ BLEND_SOURCE_OVER_ARGB32_SSSE3(dst, src, w, nullVector, half, one, colorMask, alphaMask);
+ dst = (quint32 *)(((uchar *) dst) + dbpl);
+ src = (const quint32 *)(((const uchar *) src) + sbpl);
+ }
+ } else if (const_alpha != 0) {
+ // dest = (s + d * sia) * ca + d * cia
+ // = s * ca + d * (sia * ca + cia)
+ // = s * ca + d * (1 - sa*ca)
+ const_alpha = (const_alpha * 255) >> 8;
+ const __m128i nullVector = _mm_setzero_si128();
+ const __m128i half = _mm_set1_epi16(0x80);
+ const __m128i one = _mm_set1_epi16(0xff);
+ const __m128i colorMask = _mm_set1_epi32(0x00ff00ff);
+ const __m128i constAlphaVector = _mm_set1_epi16(const_alpha);
+ for (int y = 0; y < h; ++y) {
+ BLEND_SOURCE_OVER_ARGB32_WITH_CONST_ALPHA_SSE2(dst, src, w, nullVector, half, one, colorMask, constAlphaVector)
+ dst = (quint32 *)(((uchar *) dst) + dbpl);
+ src = (const quint32 *)(((const uchar *) src) + sbpl);
+ }
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_HAVE_SSSE3
diff --git a/src/gui/painting/qdrawhelper_x86_p.h b/src/gui/painting/qdrawhelper_x86_p.h
new file mode 100644
index 0000000000..496ca8d141
--- /dev/null
+++ b/src/gui/painting/qdrawhelper_x86_p.h
@@ -0,0 +1,141 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDRAWHELPER_X86_P_H
+#define QDRAWHELPER_X86_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qdrawhelper_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifdef QT_HAVE_MMX
+extern CompositionFunction qt_functionForMode_MMX[];
+extern CompositionFunctionSolid qt_functionForModeSolid_MMX[];
+void qt_blend_color_argb_mmx(int count, const QSpan *spans, void *userData);
+#endif
+
+#ifdef QT_HAVE_MMXEXT
+void qt_memfill32_mmxext(quint32 *dest, quint32 value, int count);
+void qt_bitmapblit16_mmxext(QRasterBuffer *rasterBuffer, int x, int y,
+ quint32 color, const uchar *src,
+ int width, int height, int stride);
+#endif
+
+#ifdef QT_HAVE_3DNOW
+#if defined(QT_HAVE_MMX) || !defined(QT_HAVE_SSE)
+extern CompositionFunction qt_functionForMode_MMX3DNOW[];
+extern CompositionFunctionSolid qt_functionForModeSolid_MMX3DNOW[];
+
+void qt_blend_color_argb_mmx3dnow(int count, const QSpan *spans,
+ void *userData);
+#endif // MMX
+
+#ifdef QT_HAVE_SSE
+extern CompositionFunction qt_functionForMode_SSE3DNOW[];
+extern CompositionFunctionSolid qt_functionForModeSolid_SSE3DNOW[];
+
+void qt_memfill32_sse3dnow(quint32 *dest, quint32 value, int count);
+void qt_bitmapblit16_sse3dnow(QRasterBuffer *rasterBuffer, int x, int y,
+ quint32 color,
+ const uchar *src, int width, int height,
+ int stride);
+void qt_blend_color_argb_sse3dnow(int count, const QSpan *spans,
+ void *userData);
+#endif // SSE
+#endif // QT_HAVE_3DNOW
+
+#ifdef QT_HAVE_SSE
+void qt_memfill32_sse(quint32 *dest, quint32 value, int count);
+void qt_bitmapblit16_sse(QRasterBuffer *rasterBuffer, int x, int y,
+ quint32 color,
+ const uchar *src, int width, int height, int stride);
+
+void qt_blend_color_argb_sse(int count, const QSpan *spans, void *userData);
+
+extern CompositionFunction qt_functionForMode_SSE[];
+extern CompositionFunctionSolid qt_functionForModeSolid_SSE[];
+#endif // QT_HAVE_SSE
+
+#ifdef QT_HAVE_SSE2
+void qt_memfill32_sse2(quint32 *dest, quint32 value, int count);
+void qt_memfill16_sse2(quint16 *dest, quint16 value, int count);
+void qt_bitmapblit32_sse2(QRasterBuffer *rasterBuffer, int x, int y,
+ quint32 color,
+ const uchar *src, int width, int height, int stride);
+void qt_bitmapblit16_sse2(QRasterBuffer *rasterBuffer, int x, int y,
+ quint32 color,
+ const uchar *src, int width, int height, int stride);
+void qt_blend_argb32_on_argb32_sse2(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha);
+void qt_blend_rgb32_on_rgb32_sse2(uchar *destPixels, int dbpl,
+ const uchar *srcPixels, int sbpl,
+ int w, int h,
+ int const_alpha);
+
+extern CompositionFunction qt_functionForMode_onlySSE2[];
+extern CompositionFunctionSolid qt_functionForModeSolid_onlySSE2[];
+#endif // QT_HAVE_SSE2
+
+#ifdef QT_HAVE_IWMMXT
+void qt_blend_color_argb_iwmmxt(int count, const QSpan *spans, void *userData);
+
+extern CompositionFunction qt_functionForMode_IWMMXT[];
+extern CompositionFunctionSolid qt_functionForModeSolid_IWMMXT[];
+#endif
+
+static const int numCompositionFunctions = 33;
+
+QT_END_NAMESPACE
+
+#endif // QDRAWHELPER_X86_P_H
diff --git a/src/gui/painting/qdrawingprimitive_sse2_p.h b/src/gui/painting/qdrawingprimitive_sse2_p.h
new file mode 100644
index 0000000000..d8793c3900
--- /dev/null
+++ b/src/gui/painting/qdrawingprimitive_sse2_p.h
@@ -0,0 +1,241 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDRAWINGPRIMITIVE_SSE2_P_H
+#define QDRAWINGPRIMITIVE_SSE2_P_H
+
+#include <private/qsimd_p.h>
+
+#ifdef QT_HAVE_SSE2
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+/*
+ * Multiply the components of pixelVector by alphaChannel
+ * Each 32bits components of alphaChannel must be in the form 0x00AA00AA
+ * colorMask must have 0x00ff00ff on each 32 bits component
+ * half must have the value 128 (0x80) for each 32 bits compnent
+ */
+#define BYTE_MUL_SSE2(result, pixelVector, alphaChannel, colorMask, half) \
+{ \
+ /* 1. separate the colors in 2 vectors so each color is on 16 bits \
+ (in order to be multiplied by the alpha \
+ each 32 bit of dstVectorAG are in the form 0x00AA00GG \
+ each 32 bit of dstVectorRB are in the form 0x00RR00BB */\
+ __m128i pixelVectorAG = _mm_srli_epi16(pixelVector, 8); \
+ __m128i pixelVectorRB = _mm_and_si128(pixelVector, colorMask); \
+ \
+ /* 2. multiply the vectors by the alpha channel */\
+ pixelVectorAG = _mm_mullo_epi16(pixelVectorAG, alphaChannel); \
+ pixelVectorRB = _mm_mullo_epi16(pixelVectorRB, alphaChannel); \
+ \
+ /* 3. divide by 255, that's the tricky part. \
+ we do it like for BYTE_MUL(), with bit shift: X/255 ~= (X + X/256 + rounding)/256 */ \
+ /** so first (X + X/256 + rounding) */\
+ pixelVectorRB = _mm_add_epi16(pixelVectorRB, _mm_srli_epi16(pixelVectorRB, 8)); \
+ pixelVectorRB = _mm_add_epi16(pixelVectorRB, half); \
+ pixelVectorAG = _mm_add_epi16(pixelVectorAG, _mm_srli_epi16(pixelVectorAG, 8)); \
+ pixelVectorAG = _mm_add_epi16(pixelVectorAG, half); \
+ \
+ /** second divide by 256 */\
+ pixelVectorRB = _mm_srli_epi16(pixelVectorRB, 8); \
+ /** for AG, we could >> 8 to divide followed by << 8 to put the \
+ bytes in the correct position. By masking instead, we execute \
+ only one instruction */\
+ pixelVectorAG = _mm_andnot_si128(colorMask, pixelVectorAG); \
+ \
+ /* 4. combine the 2 pairs of colors */ \
+ result = _mm_or_si128(pixelVectorAG, pixelVectorRB); \
+}
+
+/*
+ * Each 32bits components of alphaChannel must be in the form 0x00AA00AA
+ * oneMinusAlphaChannel must be 255 - alpha for each 32 bits component
+ * colorMask must have 0x00ff00ff on each 32 bits component
+ * half must have the value 128 (0x80) for each 32 bits compnent
+ */
+#define INTERPOLATE_PIXEL_255_SSE2(result, srcVector, dstVector, alphaChannel, oneMinusAlphaChannel, colorMask, half) { \
+ /* interpolate AG */\
+ __m128i srcVectorAG = _mm_srli_epi16(srcVector, 8); \
+ __m128i dstVectorAG = _mm_srli_epi16(dstVector, 8); \
+ __m128i srcVectorAGalpha = _mm_mullo_epi16(srcVectorAG, alphaChannel); \
+ __m128i dstVectorAGoneMinusAlphalpha = _mm_mullo_epi16(dstVectorAG, oneMinusAlphaChannel); \
+ __m128i finalAG = _mm_add_epi16(srcVectorAGalpha, dstVectorAGoneMinusAlphalpha); \
+ finalAG = _mm_add_epi16(finalAG, _mm_srli_epi16(finalAG, 8)); \
+ finalAG = _mm_add_epi16(finalAG, half); \
+ finalAG = _mm_andnot_si128(colorMask, finalAG); \
+ \
+ /* interpolate RB */\
+ __m128i srcVectorRB = _mm_and_si128(srcVector, colorMask); \
+ __m128i dstVectorRB = _mm_and_si128(dstVector, colorMask); \
+ __m128i srcVectorRBalpha = _mm_mullo_epi16(srcVectorRB, alphaChannel); \
+ __m128i dstVectorRBoneMinusAlphalpha = _mm_mullo_epi16(dstVectorRB, oneMinusAlphaChannel); \
+ __m128i finalRB = _mm_add_epi16(srcVectorRBalpha, dstVectorRBoneMinusAlphalpha); \
+ finalRB = _mm_add_epi16(finalRB, _mm_srli_epi16(finalRB, 8)); \
+ finalRB = _mm_add_epi16(finalRB, half); \
+ finalRB = _mm_srli_epi16(finalRB, 8); \
+ \
+ /* combine */\
+ result = _mm_or_si128(finalAG, finalRB); \
+}
+
+// Basically blend src over dst with the const alpha defined as constAlphaVector.
+// nullVector, half, one, colorMask are constant across the whole image/texture, and should be defined as:
+//const __m128i nullVector = _mm_set1_epi32(0);
+//const __m128i half = _mm_set1_epi16(0x80);
+//const __m128i one = _mm_set1_epi16(0xff);
+//const __m128i colorMask = _mm_set1_epi32(0x00ff00ff);
+//const __m128i alphaMask = _mm_set1_epi32(0xff000000);
+//
+// The computation being done is:
+// result = s + d * (1-alpha)
+// with shortcuts if fully opaque or fully transparent.
+#define BLEND_SOURCE_OVER_ARGB32_SSE2(dst, src, length, nullVector, half, one, colorMask, alphaMask) { \
+ int x = 0; \
+\
+ /* First, get dst aligned. */ \
+ ALIGNMENT_PROLOGUE_16BYTES(dst, x, length) { \
+ uint s = src[x]; \
+ if (s >= 0xff000000) \
+ dst[x] = s; \
+ else if (s != 0) \
+ dst[x] = s + BYTE_MUL(dst[x], qAlpha(~s)); \
+ } \
+\
+ for (; x < length-3; x += 4) { \
+ const __m128i srcVector = _mm_loadu_si128((__m128i *)&src[x]); \
+ const __m128i srcVectorAlpha = _mm_and_si128(srcVector, alphaMask); \
+ if (_mm_movemask_epi8(_mm_cmpeq_epi32(srcVectorAlpha, alphaMask)) == 0xffff) { \
+ /* all opaque */ \
+ _mm_store_si128((__m128i *)&dst[x], srcVector); \
+ } else if (_mm_movemask_epi8(_mm_cmpeq_epi32(srcVectorAlpha, nullVector)) != 0xffff) { \
+ /* not fully transparent */ \
+ /* extract the alpha channel on 2 x 16 bits */ \
+ /* so we have room for the multiplication */ \
+ /* each 32 bits will be in the form 0x00AA00AA */ \
+ /* with A being the 1 - alpha */ \
+ __m128i alphaChannel = _mm_srli_epi32(srcVector, 24); \
+ alphaChannel = _mm_or_si128(alphaChannel, _mm_slli_epi32(alphaChannel, 16)); \
+ alphaChannel = _mm_sub_epi16(one, alphaChannel); \
+ \
+ const __m128i dstVector = _mm_load_si128((__m128i *)&dst[x]); \
+ __m128i destMultipliedByOneMinusAlpha; \
+ BYTE_MUL_SSE2(destMultipliedByOneMinusAlpha, dstVector, alphaChannel, colorMask, half); \
+ \
+ /* result = s + d * (1-alpha) */\
+ const __m128i result = _mm_add_epi8(srcVector, destMultipliedByOneMinusAlpha); \
+ _mm_store_si128((__m128i *)&dst[x], result); \
+ } \
+ } \
+ for (; x < length; ++x) { \
+ uint s = src[x]; \
+ if (s >= 0xff000000) \
+ dst[x] = s; \
+ else if (s != 0) \
+ dst[x] = s + BYTE_MUL(dst[x], qAlpha(~s)); \
+ } \
+}
+
+// Basically blend src over dst with the const alpha defined as constAlphaVector.
+// nullVector, half, one, colorMask are constant across the whole image/texture, and should be defined as:
+//const __m128i nullVector = _mm_set1_epi32(0);
+//const __m128i half = _mm_set1_epi16(0x80);
+//const __m128i one = _mm_set1_epi16(0xff);
+//const __m128i colorMask = _mm_set1_epi32(0x00ff00ff);
+//
+// The computation being done is:
+// dest = (s + d * sia) * ca + d * cia
+// = s * ca + d * (sia * ca + cia)
+// = s * ca + d * (1 - sa*ca)
+#define BLEND_SOURCE_OVER_ARGB32_WITH_CONST_ALPHA_SSE2(dst, src, length, nullVector, half, one, colorMask, constAlphaVector) \
+{ \
+ int x = 0; \
+\
+ ALIGNMENT_PROLOGUE_16BYTES(dst, x, length) { \
+ quint32 s = src[x]; \
+ if (s != 0) { \
+ s = BYTE_MUL(s, const_alpha); \
+ dst[x] = s + BYTE_MUL(dst[x], qAlpha(~s)); \
+ } \
+ } \
+\
+ for (; x < length-3; x += 4) { \
+ __m128i srcVector = _mm_loadu_si128((__m128i *)&src[x]); \
+ if (_mm_movemask_epi8(_mm_cmpeq_epi32(srcVector, nullVector)) != 0xffff) { \
+ BYTE_MUL_SSE2(srcVector, srcVector, constAlphaVector, colorMask, half); \
+\
+ __m128i alphaChannel = _mm_srli_epi32(srcVector, 24); \
+ alphaChannel = _mm_or_si128(alphaChannel, _mm_slli_epi32(alphaChannel, 16)); \
+ alphaChannel = _mm_sub_epi16(one, alphaChannel); \
+ \
+ const __m128i dstVector = _mm_load_si128((__m128i *)&dst[x]); \
+ __m128i destMultipliedByOneMinusAlpha; \
+ BYTE_MUL_SSE2(destMultipliedByOneMinusAlpha, dstVector, alphaChannel, colorMask, half); \
+ \
+ const __m128i result = _mm_add_epi8(srcVector, destMultipliedByOneMinusAlpha); \
+ _mm_store_si128((__m128i *)&dst[x], result); \
+ } \
+ } \
+ for (; x < length; ++x) { \
+ quint32 s = src[x]; \
+ if (s != 0) { \
+ s = BYTE_MUL(s, const_alpha); \
+ dst[x] = s + BYTE_MUL(dst[x], qAlpha(~s)); \
+ } \
+ } \
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_HAVE_SSE2
+
+#endif // QDRAWINGPRIMITIVE_SSE2_P_H
diff --git a/src/gui/painting/qdrawutil.cpp b/src/gui/painting/qdrawutil.cpp
new file mode 100644
index 0000000000..89474d9679
--- /dev/null
+++ b/src/gui/painting/qdrawutil.cpp
@@ -0,0 +1,1360 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdrawutil.h"
+#include "qbitmap.h"
+#include "qpixmapcache.h"
+#include "qapplication.h"
+#include "qpainter.h"
+#include "qpalette.h"
+#include <private/qpaintengineex_p.h>
+#include <qvarlengtharray.h>
+#include <qmath.h>
+#include <private/qstylehelper_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \headerfile <qdrawutil.h>
+ \title Drawing Utility Functions
+
+ \sa QPainter
+*/
+
+/*!
+ \fn void qDrawShadeLine(QPainter *painter, int x1, int y1, int x2, int y2,
+ const QPalette &palette, bool sunken,
+ int lineWidth, int midLineWidth)
+ \relates <qdrawutil.h>
+
+ Draws a horizontal (\a y1 == \a y2) or vertical (\a x1 == \a x2)
+ shaded line using the given \a painter. Note that nothing is
+ drawn if \a y1 != \a y2 and \a x1 != \a x2 (i.e. the line is
+ neither horizontal nor vertical).
+
+ The provided \a palette specifies the shading colors (\l
+ {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l
+ {QPalette::mid()}{middle} colors). The given \a lineWidth
+ specifies the line width for each of the lines; it is not the
+ total line width. The given \a midLineWidth specifies the width of
+ a middle line drawn in the QPalette::mid() color.
+
+ The line appears sunken if \a sunken is true, otherwise raised.
+
+ \warning This function does not look at QWidget::style() or
+ QApplication::style(). Use the drawing functions in QStyle to
+ make widgets that follow the current GUI style.
+
+
+ Alternatively you can use a QFrame widget and apply the
+ QFrame::setFrameStyle() function to display a shaded line:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qdrawutil.cpp 0
+
+ \sa qDrawShadeRect(), qDrawShadePanel(), QStyle
+*/
+
+void qDrawShadeLine(QPainter *p, int x1, int y1, int x2, int y2,
+ const QPalette &pal, bool sunken,
+ int lineWidth, int midLineWidth)
+{
+ if (!(p && lineWidth >= 0 && midLineWidth >= 0)) {
+ qWarning("qDrawShadeLine: Invalid parameters");
+ return;
+ }
+ int tlw = lineWidth*2 + midLineWidth; // total line width
+ QPen oldPen = p->pen(); // save pen
+ if (sunken)
+ p->setPen(pal.color(QPalette::Dark));
+ else
+ p->setPen(pal.light().color());
+ QPolygon a;
+ int i;
+ if (y1 == y2) { // horizontal line
+ int y = y1 - tlw/2;
+ if (x1 > x2) { // swap x1 and x2
+ int t = x1;
+ x1 = x2;
+ x2 = t;
+ }
+ x2--;
+ for (i=0; i<lineWidth; i++) { // draw top shadow
+ a.setPoints(3, x1+i, y+tlw-1-i,
+ x1+i, y+i,
+ x2-i, y+i);
+ p->drawPolyline(a);
+ }
+ if (midLineWidth > 0) {
+ p->setPen(pal.mid().color());
+ for (i=0; i<midLineWidth; i++) // draw lines in the middle
+ p->drawLine(x1+lineWidth, y+lineWidth+i,
+ x2-lineWidth, y+lineWidth+i);
+ }
+ if (sunken)
+ p->setPen(pal.light().color());
+ else
+ p->setPen(pal.dark().color());
+ for (i=0; i<lineWidth; i++) { // draw bottom shadow
+ a.setPoints(3, x1+i, y+tlw-i-1,
+ x2-i, y+tlw-i-1,
+ x2-i, y+i+1);
+ p->drawPolyline(a);
+ }
+ }
+ else if (x1 == x2) { // vertical line
+ int x = x1 - tlw/2;
+ if (y1 > y2) { // swap y1 and y2
+ int t = y1;
+ y1 = y2;
+ y2 = t;
+ }
+ y2--;
+ for (i=0; i<lineWidth; i++) { // draw left shadow
+ a.setPoints(3, x+i, y2,
+ x+i, y1+i,
+ x+tlw-1, y1+i);
+ p->drawPolyline(a);
+ }
+ if (midLineWidth > 0) {
+ p->setPen(pal.mid().color());
+ for (i=0; i<midLineWidth; i++) // draw lines in the middle
+ p->drawLine(x+lineWidth+i, y1+lineWidth, x+lineWidth+i, y2);
+ }
+ if (sunken)
+ p->setPen(pal.light().color());
+ else
+ p->setPen(pal.dark().color());
+ for (i=0; i<lineWidth; i++) { // draw right shadow
+ a.setPoints(3, x+lineWidth, y2-i,
+ x+tlw-i-1, y2-i,
+ x+tlw-i-1, y1+lineWidth);
+ p->drawPolyline(a);
+ }
+ }
+ p->setPen(oldPen);
+}
+
+/*!
+ \fn void qDrawShadeRect(QPainter *painter, int x, int y, int width, int height,
+ const QPalette &palette, bool sunken,
+ int lineWidth, int midLineWidth,
+ const QBrush *fill)
+ \relates <qdrawutil.h>
+
+ Draws the shaded rectangle beginning at (\a x, \a y) with the
+ given \a width and \a height using the provided \a painter.
+
+ The provide \a palette specifies the shading colors (\l
+ {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l
+ {QPalette::mid()}{middle} colors. The given \a lineWidth
+ specifies the line width for each of the lines; it is not the
+ total line width. The \a midLineWidth specifies the width of a
+ middle line drawn in the QPalette::mid() color. The rectangle's
+ interior is filled with the \a fill brush unless \a fill is 0.
+
+ The rectangle appears sunken if \a sunken is true, otherwise
+ raised.
+
+ \warning This function does not look at QWidget::style() or
+ QApplication::style(). Use the drawing functions in QStyle to make
+ widgets that follow the current GUI style.
+
+ Alternatively you can use a QFrame widget and apply the
+ QFrame::setFrameStyle() function to display a shaded rectangle:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qdrawutil.cpp 1
+
+ \sa qDrawShadeLine(), qDrawShadePanel(), qDrawPlainRect(), QStyle
+*/
+
+void qDrawShadeRect(QPainter *p, int x, int y, int w, int h,
+ const QPalette &pal, bool sunken,
+ int lineWidth, int midLineWidth,
+ const QBrush *fill)
+{
+ if (w == 0 || h == 0)
+ return;
+ if (! (w > 0 && h > 0 && lineWidth >= 0 && midLineWidth >= 0)) {
+ qWarning("qDrawShadeRect: Invalid parameters");
+ return;
+ }
+ QPen oldPen = p->pen();
+ if (sunken)
+ p->setPen(pal.dark().color());
+ else
+ p->setPen(pal.light().color());
+ int x1=x, y1=y, x2=x+w-1, y2=y+h-1;
+
+ if (lineWidth == 1 && midLineWidth == 0) {// standard shade rectangle
+ p->drawRect(x1, y1, w-2, h-2);
+ if (sunken)
+ p->setPen(pal.light().color());
+ else
+ p->setPen(pal.dark().color());
+ QLineF lines[4] = { QLineF(x1+1, y1+1, x2-2, y1+1),
+ QLineF(x1+1, y1+2, x1+1, y2-2),
+ QLineF(x1, y2, x2, y2),
+ QLineF(x2,y1, x2,y2-1) };
+ p->drawLines(lines, 4); // draw bottom/right lines
+ } else { // more complicated
+ int m = lineWidth+midLineWidth;
+ int i, j=0, k=m;
+ for (i=0; i<lineWidth; i++) { // draw top shadow
+ QLineF lines[4] = { QLineF(x1+i, y2-i, x1+i, y1+i),
+ QLineF(x1+i, y1+i, x2-i, y1+i),
+ QLineF(x1+k, y2-k, x2-k, y2-k),
+ QLineF(x2-k, y2-k, x2-k, y1+k) };
+ p->drawLines(lines, 4);
+ k++;
+ }
+ p->setPen(pal.mid().color());
+ j = lineWidth*2;
+ for (i=0; i<midLineWidth; i++) { // draw lines in the middle
+ p->drawRect(x1+lineWidth+i, y1+lineWidth+i, w-j-1, h-j-1);
+ j += 2;
+ }
+ if (sunken)
+ p->setPen(pal.light().color());
+ else
+ p->setPen(pal.dark().color());
+ k = m;
+ for (i=0; i<lineWidth; i++) { // draw bottom shadow
+ QLineF lines[4] = { QLineF(x1+1+i, y2-i, x2-i, y2-i),
+ QLineF(x2-i, y2-i, x2-i, y1+i+1),
+ QLineF(x1+k, y2-k, x1+k, y1+k),
+ QLineF(x1+k, y1+k, x2-k, y1+k) };
+ p->drawLines(lines, 4);
+ k++;
+ }
+ }
+ if (fill) {
+ QBrush oldBrush = p->brush();
+ int tlw = lineWidth + midLineWidth;
+ p->setPen(Qt::NoPen);
+ p->setBrush(*fill);
+ p->drawRect(x+tlw, y+tlw, w-2*tlw, h-2*tlw);
+ p->setBrush(oldBrush);
+ }
+ p->setPen(oldPen); // restore pen
+}
+
+
+/*!
+ \fn void qDrawShadePanel(QPainter *painter, int x, int y, int width, int height,
+ const QPalette &palette, bool sunken,
+ int lineWidth, const QBrush *fill)
+ \relates <qdrawutil.h>
+
+ Draws the shaded panel beginning at (\a x, \a y) with the given \a
+ width and \a height using the provided \a painter and the given \a
+ lineWidth.
+
+ The given \a palette specifies the shading colors (\l
+ {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l
+ {QPalette::mid()}{middle} colors). The panel's interior is filled
+ with the \a fill brush unless \a fill is 0.
+
+ The panel appears sunken if \a sunken is true, otherwise raised.
+
+ \warning This function does not look at QWidget::style() or
+ QApplication::style(). Use the drawing functions in QStyle to make
+ widgets that follow the current GUI style.
+
+ Alternatively you can use a QFrame widget and apply the
+ QFrame::setFrameStyle() function to display a shaded panel:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qdrawutil.cpp 2
+
+ \sa qDrawWinPanel(), qDrawShadeLine(), qDrawShadeRect(), QStyle
+*/
+
+void qDrawShadePanel(QPainter *p, int x, int y, int w, int h,
+ const QPalette &pal, bool sunken,
+ int lineWidth, const QBrush *fill)
+{
+ if (w == 0 || h == 0)
+ return;
+ if (!(w > 0 && h > 0 && lineWidth >= 0)) {
+ qWarning("qDrawShadePanel: Invalid parameters");
+ }
+ QColor shade = pal.dark().color();
+ QColor light = pal.light().color();
+ if (fill) {
+ if (fill->color() == shade)
+ shade = pal.shadow().color();
+ if (fill->color() == light)
+ light = pal.midlight().color();
+ }
+ QPen oldPen = p->pen(); // save pen
+ QVector<QLineF> lines;
+ lines.reserve(2*lineWidth);
+
+ if (sunken)
+ p->setPen(shade);
+ else
+ p->setPen(light);
+ int x1, y1, x2, y2;
+ int i;
+ x1 = x;
+ y1 = y2 = y;
+ x2 = x+w-2;
+ for (i=0; i<lineWidth; i++) { // top shadow
+ lines << QLineF(x1, y1++, x2--, y2++);
+ }
+ x2 = x1;
+ y1 = y+h-2;
+ for (i=0; i<lineWidth; i++) { // left shado
+ lines << QLineF(x1++, y1, x2++, y2--);
+ }
+ p->drawLines(lines);
+ lines.clear();
+ if (sunken)
+ p->setPen(light);
+ else
+ p->setPen(shade);
+ x1 = x;
+ y1 = y2 = y+h-1;
+ x2 = x+w-1;
+ for (i=0; i<lineWidth; i++) { // bottom shadow
+ lines << QLineF(x1++, y1--, x2, y2--);
+ }
+ x1 = x2;
+ y1 = y;
+ y2 = y+h-lineWidth-1;
+ for (i=0; i<lineWidth; i++) { // right shadow
+ lines << QLineF(x1--, y1++, x2--, y2);
+ }
+ p->drawLines(lines);
+ if (fill) // fill with fill color
+ p->fillRect(x+lineWidth, y+lineWidth, w-lineWidth*2, h-lineWidth*2, *fill);
+ p->setPen(oldPen); // restore pen
+}
+
+
+/*!
+ \internal
+ This function draws a rectangle with two pixel line width.
+ It is called from qDrawWinButton() and qDrawWinPanel().
+
+ c1..c4 and fill are used:
+
+ 1 1 1 1 1 2
+ 1 3 3 3 4 2
+ 1 3 F F 4 2
+ 1 3 F F 4 2
+ 1 4 4 4 4 2
+ 2 2 2 2 2 2
+*/
+
+static void qDrawWinShades(QPainter *p,
+ int x, int y, int w, int h,
+ const QColor &c1, const QColor &c2,
+ const QColor &c3, const QColor &c4,
+ const QBrush *fill)
+{
+ if (w < 2 || h < 2) // can't do anything with that
+ return;
+ QPen oldPen = p->pen();
+ QPoint a[3] = { QPoint(x, y+h-2), QPoint(x, y), QPoint(x+w-2, y) };
+ p->setPen(c1);
+ p->drawPolyline(a, 3);
+ QPoint b[3] = { QPoint(x, y+h-1), QPoint(x+w-1, y+h-1), QPoint(x+w-1, y) };
+ p->setPen(c2);
+ p->drawPolyline(b, 3);
+ if (w > 4 && h > 4) {
+ QPoint c[3] = { QPoint(x+1, y+h-3), QPoint(x+1, y+1), QPoint(x+w-3, y+1) };
+ p->setPen(c3);
+ p->drawPolyline(c, 3);
+ QPoint d[3] = { QPoint(x+1, y+h-2), QPoint(x+w-2, y+h-2), QPoint(x+w-2, y+1) };
+ p->setPen(c4);
+ p->drawPolyline(d, 3);
+ if (fill)
+ p->fillRect(QRect(x+2, y+2, w-4, h-4), *fill);
+ }
+ p->setPen(oldPen);
+}
+
+
+/*!
+ \fn void qDrawWinButton(QPainter *painter, int x, int y, int width, int height,
+ const QPalette &palette, bool sunken,
+ const QBrush *fill)
+ \relates <qdrawutil.h>
+
+ Draws the Windows-style button specified by the given point (\a x,
+ \a y}, \a width and \a height using the provided \a painter with a
+ line width of 2 pixels. The button's interior is filled with the
+ \a{fill} brush unless \a fill is 0.
+
+ The given \a palette specifies the shading colors (\l
+ {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l
+ {QPalette::mid()}{middle} colors).
+
+ The button appears sunken if \a sunken is true, otherwise raised.
+
+ \warning This function does not look at QWidget::style() or
+ QApplication::style()-> Use the drawing functions in QStyle to make
+ widgets that follow the current GUI style.
+
+ \sa qDrawWinPanel(), QStyle
+*/
+
+void qDrawWinButton(QPainter *p, int x, int y, int w, int h,
+ const QPalette &pal, bool sunken,
+ const QBrush *fill)
+{
+ if (sunken)
+ qDrawWinShades(p, x, y, w, h,
+ pal.shadow().color(), pal.light().color(), pal.dark().color(),
+ pal.button().color(), fill);
+ else
+ qDrawWinShades(p, x, y, w, h,
+ pal.light().color(), pal.shadow().color(), pal.button().color(),
+ pal.dark().color(), fill);
+}
+
+/*!
+ \fn void qDrawWinPanel(QPainter *painter, int x, int y, int width, int height,
+ const QPalette &palette, bool sunken,
+ const QBrush *fill)
+ \relates <qdrawutil.h>
+
+ Draws the Windows-style panel specified by the given point(\a x,
+ \a y), \a width and \a height using the provided \a painter with a
+ line width of 2 pixels. The button's interior is filled with the
+ \a fill brush unless \a fill is 0.
+
+ The given \a palette specifies the shading colors. The panel
+ appears sunken if \a sunken is true, otherwise raised.
+
+ \warning This function does not look at QWidget::style() or
+ QApplication::style(). Use the drawing functions in QStyle to make
+ widgets that follow the current GUI style.
+
+ Alternatively you can use a QFrame widget and apply the
+ QFrame::setFrameStyle() function to display a shaded panel:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qdrawutil.cpp 3
+
+ \sa qDrawShadePanel(), qDrawWinButton(), QStyle
+*/
+
+void qDrawWinPanel(QPainter *p, int x, int y, int w, int h,
+ const QPalette &pal, bool sunken,
+ const QBrush *fill)
+{
+ if (sunken)
+ qDrawWinShades(p, x, y, w, h,
+ pal.dark().color(), pal.light().color(), pal.shadow().color(),
+ pal.midlight().color(), fill);
+ else
+ qDrawWinShades(p, x, y, w, h,
+ pal.light().color(), pal.shadow().color(), pal.midlight().color(),
+ pal.dark().color(), fill);
+}
+
+/*!
+ \fn void qDrawPlainRect(QPainter *painter, int x, int y, int width, int height, const QColor &lineColor,
+ int lineWidth, const QBrush *fill)
+ \relates <qdrawutil.h>
+
+ Draws the plain rectangle beginning at (\a x, \a y) with the given
+ \a width and \a height, using the specified \a painter, \a lineColor
+ and \a lineWidth. The rectangle's interior is filled with the \a
+ fill brush unless \a fill is 0.
+
+ \warning This function does not look at QWidget::style() or
+ QApplication::style(). Use the drawing functions in QStyle to make
+ widgets that follow the current GUI style.
+
+ Alternatively you can use a QFrame widget and apply the
+ QFrame::setFrameStyle() function to display a plain rectangle:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qdrawutil.cpp 4
+
+ \sa qDrawShadeRect(), QStyle
+*/
+
+void qDrawPlainRect(QPainter *p, int x, int y, int w, int h, const QColor &c,
+ int lineWidth, const QBrush *fill)
+{
+ if (w == 0 || h == 0)
+ return;
+ if (!(w > 0 && h > 0 && lineWidth >= 0)) {
+ qWarning("qDrawPlainRect: Invalid parameters");
+ }
+ QPen oldPen = p->pen();
+ QBrush oldBrush = p->brush();
+ p->setPen(c);
+ p->setBrush(Qt::NoBrush);
+ for (int i=0; i<lineWidth; i++)
+ p->drawRect(x+i, y+i, w-i*2 - 1, h-i*2 - 1);
+ if (fill) { // fill with fill color
+ p->setPen(Qt::NoPen);
+ p->setBrush(*fill);
+ p->drawRect(x+lineWidth, y+lineWidth, w-lineWidth*2, h-lineWidth*2);
+ }
+ p->setPen(oldPen);
+ p->setBrush(oldBrush);
+}
+
+/*****************************************************************************
+ Overloaded functions.
+ *****************************************************************************/
+
+/*!
+ \fn void qDrawShadeLine(QPainter *painter, const QPoint &p1, const QPoint &p2,
+ const QPalette &palette, bool sunken, int lineWidth, int midLineWidth)
+ \relates <qdrawutil.h>
+ \overload
+
+ Draws a horizontal or vertical shaded line between \a p1 and \a p2
+ using the given \a painter. Note that nothing is drawn if the line
+ between the points would be neither horizontal nor vertical.
+
+ The provided \a palette specifies the shading colors (\l
+ {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l
+ {QPalette::mid()}{middle} colors). The given \a lineWidth
+ specifies the line width for each of the lines; it is not the
+ total line width. The given \a midLineWidth specifies the width of
+ a middle line drawn in the QPalette::mid() color.
+
+ The line appears sunken if \a sunken is true, otherwise raised.
+
+ \warning This function does not look at QWidget::style() or
+ QApplication::style(). Use the drawing functions in QStyle to
+ make widgets that follow the current GUI style.
+
+
+ Alternatively you can use a QFrame widget and apply the
+ QFrame::setFrameStyle() function to display a shaded line:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qdrawutil.cpp 5
+
+ \sa qDrawShadeRect(), qDrawShadePanel(), QStyle
+*/
+
+void qDrawShadeLine(QPainter *p, const QPoint &p1, const QPoint &p2,
+ const QPalette &pal, bool sunken,
+ int lineWidth, int midLineWidth)
+{
+ qDrawShadeLine(p, p1.x(), p1.y(), p2.x(), p2.y(), pal, sunken,
+ lineWidth, midLineWidth);
+}
+
+/*!
+ \fn void qDrawShadeRect(QPainter *painter, const QRect &rect, const QPalette &palette,
+ bool sunken, int lineWidth, int midLineWidth, const QBrush *fill)
+ \relates <qdrawutil.h>
+ \overload
+
+ Draws the shaded rectangle specified by \a rect using the given \a painter.
+
+ The provide \a palette specifies the shading colors (\l
+ {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l
+ {QPalette::mid()}{middle} colors. The given \a lineWidth
+ specifies the line width for each of the lines; it is not the
+ total line width. The \a midLineWidth specifies the width of a
+ middle line drawn in the QPalette::mid() color. The rectangle's
+ interior is filled with the \a fill brush unless \a fill is 0.
+
+ The rectangle appears sunken if \a sunken is true, otherwise
+ raised.
+
+ \warning This function does not look at QWidget::style() or
+ QApplication::style(). Use the drawing functions in QStyle to make
+ widgets that follow the current GUI style.
+
+ Alternatively you can use a QFrame widget and apply the
+ QFrame::setFrameStyle() function to display a shaded rectangle:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qdrawutil.cpp 6
+
+ \sa qDrawShadeLine(), qDrawShadePanel(), qDrawPlainRect(), QStyle
+*/
+
+void qDrawShadeRect(QPainter *p, const QRect &r,
+ const QPalette &pal, bool sunken,
+ int lineWidth, int midLineWidth,
+ const QBrush *fill)
+{
+ qDrawShadeRect(p, r.x(), r.y(), r.width(), r.height(), pal, sunken,
+ lineWidth, midLineWidth, fill);
+}
+
+/*!
+ \fn void qDrawShadePanel(QPainter *painter, const QRect &rect, const QPalette &palette,
+ bool sunken, int lineWidth, const QBrush *fill)
+ \relates <qdrawutil.h>
+ \overload
+
+ Draws the shaded panel at the rectangle specified by \a rect using the
+ given \a painter and the given \a lineWidth.
+
+ The given \a palette specifies the shading colors (\l
+ {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l
+ {QPalette::mid()}{middle} colors). The panel's interior is filled
+ with the \a fill brush unless \a fill is 0.
+
+ The panel appears sunken if \a sunken is true, otherwise raised.
+
+ \warning This function does not look at QWidget::style() or
+ QApplication::style(). Use the drawing functions in QStyle to make
+ widgets that follow the current GUI style.
+
+ Alternatively you can use a QFrame widget and apply the
+ QFrame::setFrameStyle() function to display a shaded panel:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qdrawutil.cpp 7
+
+ \sa qDrawWinPanel(), qDrawShadeLine(), qDrawShadeRect(), QStyle
+*/
+
+void qDrawShadePanel(QPainter *p, const QRect &r,
+ const QPalette &pal, bool sunken,
+ int lineWidth, const QBrush *fill)
+{
+ qDrawShadePanel(p, r.x(), r.y(), r.width(), r.height(), pal, sunken,
+ lineWidth, fill);
+}
+
+/*!
+ \fn void qDrawWinButton(QPainter *painter, const QRect &rect, const QPalette &palette,
+ bool sunken, const QBrush *fill)
+ \relates <qdrawutil.h>
+ \overload
+
+ Draws the Windows-style button at the rectangle specified by \a rect using
+ the given \a painter with a line width of 2 pixels. The button's interior
+ is filled with the \a{fill} brush unless \a fill is 0.
+
+ The given \a palette specifies the shading colors (\l
+ {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l
+ {QPalette::mid()}{middle} colors).
+
+ The button appears sunken if \a sunken is true, otherwise raised.
+
+ \warning This function does not look at QWidget::style() or
+ QApplication::style()-> Use the drawing functions in QStyle to make
+ widgets that follow the current GUI style.
+
+ \sa qDrawWinPanel(), QStyle
+*/
+
+void qDrawWinButton(QPainter *p, const QRect &r,
+ const QPalette &pal, bool sunken, const QBrush *fill)
+{
+ qDrawWinButton(p, r.x(), r.y(), r.width(), r.height(), pal, sunken, fill);
+}
+
+/*!
+ \fn void qDrawWinPanel(QPainter *painter, const QRect &rect, const QPalette &palette,
+ bool sunken, const QBrush *fill)
+ \overload
+
+ Draws the Windows-style panel at the rectangle specified by \a rect using
+ the given \a painter with a line width of 2 pixels. The button's interior
+ is filled with the \a fill brush unless \a fill is 0.
+
+ The given \a palette specifies the shading colors. The panel
+ appears sunken if \a sunken is true, otherwise raised.
+
+ \warning This function does not look at QWidget::style() or
+ QApplication::style(). Use the drawing functions in QStyle to make
+ widgets that follow the current GUI style.
+
+ Alternatively you can use a QFrame widget and apply the
+ QFrame::setFrameStyle() function to display a shaded panel:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qdrawutil.cpp 8
+
+ \sa qDrawShadePanel(), qDrawWinButton(), QStyle
+*/
+
+void qDrawWinPanel(QPainter *p, const QRect &r,
+ const QPalette &pal, bool sunken, const QBrush *fill)
+{
+ qDrawWinPanel(p, r.x(), r.y(), r.width(), r.height(), pal, sunken, fill);
+}
+
+/*!
+ \fn void qDrawPlainRect(QPainter *painter, const QRect &rect, const QColor &lineColor, int lineWidth, const QBrush *fill)
+ \relates <qdrawutil.h>
+ \overload
+
+ Draws the plain rectangle specified by \a rect using the given \a painter,
+ \a lineColor and \a lineWidth. The rectangle's interior is filled with the
+ \a fill brush unless \a fill is 0.
+
+ \warning This function does not look at QWidget::style() or
+ QApplication::style(). Use the drawing functions in QStyle to make
+ widgets that follow the current GUI style.
+
+ Alternatively you can use a QFrame widget and apply the
+ QFrame::setFrameStyle() function to display a plain rectangle:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qdrawutil.cpp 9
+
+ \sa qDrawShadeRect(), QStyle
+*/
+
+void qDrawPlainRect(QPainter *p, const QRect &r, const QColor &c,
+ int lineWidth, const QBrush *fill)
+{
+ qDrawPlainRect(p, r.x(), r.y(), r.width(), r.height(), c,
+ lineWidth, fill);
+}
+
+#ifdef QT3_SUPPORT
+static void qDrawWinArrow(QPainter *p, Qt::ArrowType type, bool down,
+ int x, int y, int w, int h,
+ const QPalette &pal, bool enabled)
+{
+ QPolygon a; // arrow polygon
+ switch (type) {
+ case Qt::UpArrow:
+ a.setPoints(7, -3,1, 3,1, -2,0, 2,0, -1,-1, 1,-1, 0,-2);
+ break;
+ case Qt::DownArrow:
+ a.setPoints(7, -3,-1, 3,-1, -2,0, 2,0, -1,1, 1,1, 0,2);
+ break;
+ case Qt::LeftArrow:
+ a.setPoints(7, 1,-3, 1,3, 0,-2, 0,2, -1,-1, -1,1, -2,0);
+ break;
+ case Qt::RightArrow:
+ a.setPoints(7, -1,-3, -1,3, 0,-2, 0,2, 1,-1, 1,1, 2,0);
+ break;
+ default:
+ break;
+ }
+ if (a.isEmpty())
+ return;
+
+ if (down) {
+ x++;
+ y++;
+ }
+
+ QPen savePen = p->pen(); // save current pen
+ if (down)
+ p->setBrushOrigin(p->brushOrigin() + QPoint(1,1));
+ p->fillRect(x, y, w, h, pal.brush(QPalette::Button));
+ if (down)
+ p->setBrushOrigin(p->brushOrigin() - QPoint(1,1));
+ if (enabled) {
+ a.translate(x+w/2, y+h/2);
+ p->setPen(pal.foreground().color());
+ p->drawLine(a.at(0), a.at(1));
+ p->drawLine(a.at(2), a.at(2));
+ p->drawPoint(a[6]);
+ } else {
+ a.translate(x+w/2+1, y+h/2+1);
+ p->setPen(pal.light().color());
+ p->drawLine(a.at(0), a.at(1));
+ p->drawLine(a.at(2), a.at(2));
+ p->drawPoint(a[6]);
+ a.translate(-1, -1);
+ p->setPen(pal.mid().color());
+ p->drawLine(a.at(0), a.at(1));
+ p->drawLine(a.at(2), a.at(2));
+ p->drawPoint(a[6]);
+ }
+ p->setPen(savePen); // restore pen
+}
+#endif // QT3_SUPPORT
+
+#if defined(Q_CC_MSVC)
+#pragma warning(disable: 4244)
+#endif
+
+#ifdef QT3_SUPPORT
+#ifndef QT_NO_STYLE_MOTIF
+// motif arrows look the same whether they are used or not
+// is this correct?
+static void qDrawMotifArrow(QPainter *p, Qt::ArrowType type, bool down,
+ int x, int y, int w, int h,
+ const QPalette &pal, bool)
+{
+ QPolygon bFill; // fill polygon
+ QPolygon bTop; // top shadow.
+ QPolygon bBot; // bottom shadow.
+ QPolygon bLeft; // left shadow.
+ QTransform matrix; // xform matrix
+ bool vertical = type == Qt::UpArrow || type == Qt::DownArrow;
+ bool horizontal = !vertical;
+ int dim = w < h ? w : h;
+ int colspec = 0x0000; // color specification array
+
+ if (dim < 2) // too small arrow
+ return;
+
+ if (dim > 3) {
+ if (dim > 6)
+ bFill.resize(dim & 1 ? 3 : 4);
+ bTop.resize((dim/2)*2);
+ bBot.resize(dim & 1 ? dim + 1 : dim);
+ bLeft.resize(dim > 4 ? 4 : 2);
+ bLeft.putPoints(0, 2, 0,0, 0,dim-1);
+ if (dim > 4)
+ bLeft.putPoints(2, 2, 1,2, 1,dim-3);
+ bTop.putPoints(0, 4, 1,0, 1,1, 2,1, 3,1);
+ bBot.putPoints(0, 4, 1,dim-1, 1,dim-2, 2,dim-2, 3,dim-2);
+
+ for(int i=0; i<dim/2-2 ; i++) {
+ bTop.putPoints(i*2+4, 2, 2+i*2,2+i, 5+i*2, 2+i);
+ bBot.putPoints(i*2+4, 2, 2+i*2,dim-3-i, 5+i*2,dim-3-i);
+ }
+ if (dim & 1) // odd number size: extra line
+ bBot.putPoints(dim-1, 2, dim-3,dim/2, dim-1,dim/2);
+ if (dim > 6) { // dim>6: must fill interior
+ bFill.putPoints(0, 2, 1,dim-3, 1,2);
+ if (dim & 1) // if size is an odd number
+ bFill.setPoint(2, dim - 3, dim / 2);
+ else
+ bFill.putPoints(2, 2, dim-4,dim/2-1, dim-4,dim/2);
+ }
+ }
+ else {
+ if (dim == 3) { // 3x3 arrow pattern
+ bLeft.setPoints(4, 0,0, 0,2, 1,1, 1,1);
+ bTop .setPoints(2, 1,0, 1,0);
+ bBot .setPoints(2, 1,2, 2,1);
+ }
+ else { // 2x2 arrow pattern
+ bLeft.setPoints(2, 0,0, 0,1);
+ bTop .setPoints(2, 1,0, 1,0);
+ bBot .setPoints(2, 1,1, 1,1);
+ }
+ }
+
+ if (type == Qt::UpArrow || type == Qt::LeftArrow) {
+ matrix.translate(x, y);
+ if (vertical) {
+ matrix.translate(0, h - 1);
+ matrix.rotate(-90);
+ } else {
+ matrix.translate(w - 1, h - 1);
+ matrix.rotate(180);
+ }
+ if (down)
+ colspec = horizontal ? 0x2334 : 0x2343;
+ else
+ colspec = horizontal ? 0x1443 : 0x1434;
+ }
+ else if (type == Qt::DownArrow || type == Qt::RightArrow) {
+ matrix.translate(x, y);
+ if (vertical) {
+ matrix.translate(w-1, 0);
+ matrix.rotate(90);
+ }
+ if (down)
+ colspec = horizontal ? 0x2443 : 0x2434;
+ else
+ colspec = horizontal ? 0x1334 : 0x1343;
+ }
+
+ const QColor *cols[5];
+ cols[0] = 0;
+ cols[1] = &pal.button().color();
+ cols[2] = &pal.mid().color();
+ cols[3] = &pal.light().color();
+ cols[4] = &pal.dark().color();
+#define CMID *cols[(colspec>>12) & 0xf]
+#define CLEFT *cols[(colspec>>8) & 0xf]
+#define CTOP *cols[(colspec>>4) & 0xf]
+#define CBOT *cols[colspec & 0xf]
+
+ QPen savePen = p->pen(); // save current pen
+ QBrush saveBrush = p->brush(); // save current brush
+ QTransform wxm = p->transform();
+ QPen pen(Qt::NoPen);
+ const QBrush &brush = pal.brush(QPalette::Button);
+
+ p->setPen(pen);
+ p->setBrush(brush);
+ p->setTransform(matrix, true); // set transformation matrix
+ p->drawPolygon(bFill); // fill arrow
+ p->setBrush(Qt::NoBrush); // don't fill
+
+ p->setPen(CLEFT);
+ p->drawLines(bLeft);
+ p->setPen(CTOP);
+ p->drawLines(bTop);
+ p->setPen(CBOT);
+ p->drawLines(bBot);
+
+ p->setTransform(wxm);
+ p->setBrush(saveBrush); // restore brush
+ p->setPen(savePen); // restore pen
+
+#undef CMID
+#undef CLEFT
+#undef CTOP
+#undef CBOT
+}
+#endif // QT_NO_STYLE_MOTIF
+
+QRect qItemRect(QPainter *p, Qt::GUIStyle gs,
+ int x, int y, int w, int h,
+ int flags,
+ bool enabled,
+ const QPixmap *pixmap,
+ const QString& text, int len)
+{
+ QRect result;
+
+ if (pixmap) {
+ if ((flags & Qt::AlignVCenter) == Qt::AlignVCenter)
+ y += h/2 - pixmap->height()/2;
+ else if ((flags & Qt::AlignBottom) == Qt::AlignBottom)
+ y += h - pixmap->height();
+ if ((flags & Qt::AlignRight) == Qt::AlignRight)
+ x += w - pixmap->width();
+ else if ((flags & Qt::AlignHCenter) == Qt::AlignHCenter)
+ x += w/2 - pixmap->width()/2;
+ else if ((flags & Qt::AlignLeft) != Qt::AlignLeft && QApplication::isRightToLeft())
+ x += w - pixmap->width();
+ result = QRect(x, y, pixmap->width(), pixmap->height());
+ } else if (!text.isNull() && p) {
+ result = p->boundingRect(QRect(x, y, w, h), flags, text.left(len));
+ if (gs == Qt::WindowsStyle && !enabled) {
+ result.setWidth(result.width()+1);
+ result.setHeight(result.height()+1);
+ }
+ } else {
+ result = QRect(x, y, w, h);
+ }
+
+ return result;
+}
+
+void qDrawArrow(QPainter *p, Qt::ArrowType type, Qt::GUIStyle style, bool down,
+ int x, int y, int w, int h,
+ const QPalette &pal, bool enabled)
+{
+ switch (style) {
+ case Qt::WindowsStyle:
+ qDrawWinArrow(p, type, down, x, y, w, h, pal, enabled);
+ break;
+#ifndef QT_NO_STYLE_MOTIF
+ case Qt::MotifStyle:
+ qDrawMotifArrow(p, type, down, x, y, w, h, pal, enabled);
+ break;
+#endif
+ default:
+ qWarning("qDrawArrow: Requested unsupported GUI style");
+ }
+}
+
+void qDrawItem(QPainter *p, Qt::GUIStyle gs,
+ int x, int y, int w, int h,
+ int flags,
+ const QPalette &pal, bool enabled,
+ const QPixmap *pixmap,
+ const QString& text, int len , const QColor* penColor)
+{
+ p->setPen(penColor?*penColor:pal.foreground().color());
+ if (pixmap) {
+ QPixmap pm(*pixmap);
+ bool clip = (flags & Qt::TextDontClip) == 0;
+ if (clip) {
+ if (pm.width() < w && pm.height() < h)
+ clip = false;
+ else
+ p->setClipRect(x, y, w, h);
+ }
+ if ((flags & Qt::AlignVCenter) == Qt::AlignVCenter)
+ y += h/2 - pm.height()/2;
+ else if ((flags & Qt::AlignBottom) == Qt::AlignBottom)
+ y += h - pm.height();
+ if ((flags & Qt::AlignRight) == Qt::AlignRight)
+ x += w - pm.width();
+ else if ((flags & Qt::AlignHCenter) == Qt::AlignHCenter)
+ x += w/2 - pm.width()/2;
+ else if (((flags & Qt::AlignLeft) != Qt::AlignLeft) && QApplication::isRightToLeft()) // Qt::AlignAuto && rightToLeft
+ x += w - pm.width();
+
+ if (!enabled) {
+ if (pm.hasAlphaChannel()) { // pixmap with a mask
+ pm = pm.mask();
+ } else if (pm.depth() == 1) { // monochrome pixmap, no mask
+ ;
+#ifndef QT_NO_IMAGE_HEURISTIC_MASK
+ } else { // color pixmap, no mask
+ QString k = QLatin1Literal("$qt-drawitem")
+ % HexString<qint64>(pm.cacheKey());
+
+ if (!QPixmapCache::find(k, pm)) {
+ pm = pm.createHeuristicMask();
+ pm.setMask((QBitmap&)pm);
+ QPixmapCache::insert(k, pm);
+ }
+#endif
+ }
+ if (gs == Qt::WindowsStyle) {
+ p->setPen(pal.light().color());
+ p->drawPixmap(x+1, y+1, pm);
+ p->setPen(pal.text().color());
+ }
+ }
+ p->drawPixmap(x, y, pm);
+ if (clip)
+ p->setClipping(false);
+ } else if (!text.isNull()) {
+ if (gs == Qt::WindowsStyle && !enabled) {
+ p->setPen(pal.light().color());
+ p->drawText(x+1, y+1, w, h, flags, text.left(len));
+ p->setPen(pal.text().color());
+ }
+ p->drawText(x, y, w, h, flags, text.left(len));
+ }
+}
+
+#endif
+
+/*!
+ \class QTileRules
+ \since 4.6
+
+ Holds the rules used to draw a pixmap or image split into nine segments,
+ similar to \l{http://www.w3.org/TR/css3-background/}{CSS3 border-images}.
+
+ \sa Qt::TileRule, QMargins
+*/
+
+/*! \fn QTileRules::QTileRules(Qt::TileRule horizontalRule, Qt::TileRule verticalRule)
+ Constructs a QTileRules with the given \a horizontalRule and
+ \a verticalRule.
+ */
+
+/*! \fn QTileRules::QTileRules(Qt::TileRule rule)
+ Constructs a QTileRules with the given \a rule used for both
+ the horizontal rule and the vertical rule.
+ */
+
+/*!
+ \fn void qDrawBorderPixmap(QPainter *painter, const QRect &target, const QMargins &margins, const QPixmap &pixmap)
+ \relates <qdrawutil.h>
+ \since 4.6
+ \overload
+
+ \brief The qDrawBorderPixmap function is for drawing a pixmap into
+ the margins of a rectangle.
+
+ Draws the given \a pixmap into the given \a target rectangle, using the
+ given \a painter. The pixmap will be split into nine segments and drawn
+ according to the \a margins structure.
+*/
+
+typedef QVarLengthArray<QPainter::PixmapFragment, 16> QPixmapFragmentsArray;
+
+/*!
+ \since 4.6
+
+ Draws the indicated \a sourceRect rectangle from the given \a pixmap into
+ the given \a targetRect rectangle, using the given \a painter. The pixmap
+ will be split into nine segments according to the given \a targetMargins
+ and \a sourceMargins structures. Finally, the pixmap will be drawn
+ according to the given \a rules.
+
+ This function is used to draw a scaled pixmap, similar to
+ \l{http://www.w3.org/TR/css3-background/}{CSS3 border-images}
+
+ \sa Qt::TileRule, QTileRules, QMargins
+*/
+
+void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargins &targetMargins,
+ const QPixmap &pixmap, const QRect &sourceRect,const QMargins &sourceMargins,
+ const QTileRules &rules, QDrawBorderPixmap::DrawingHints hints)
+{
+ QPainter::PixmapFragment d;
+ d.opacity = 1.0;
+ d.rotation = 0.0;
+
+ QPixmapFragmentsArray opaqueData;
+ QPixmapFragmentsArray translucentData;
+
+ // source center
+ const int sourceCenterTop = sourceRect.top() + sourceMargins.top();
+ const int sourceCenterLeft = sourceRect.left() + sourceMargins.left();
+ const int sourceCenterBottom = sourceRect.bottom() - sourceMargins.bottom() + 1;
+ const int sourceCenterRight = sourceRect.right() - sourceMargins.right() + 1;
+ const int sourceCenterWidth = sourceCenterRight - sourceCenterLeft;
+ const int sourceCenterHeight = sourceCenterBottom - sourceCenterTop;
+ // target center
+ const int targetCenterTop = targetRect.top() + targetMargins.top();
+ const int targetCenterLeft = targetRect.left() + targetMargins.left();
+ const int targetCenterBottom = targetRect.bottom() - targetMargins.bottom() + 1;
+ const int targetCenterRight = targetRect.right() - targetMargins.right() + 1;
+ const int targetCenterWidth = targetCenterRight - targetCenterLeft;
+ const int targetCenterHeight = targetCenterBottom - targetCenterTop;
+
+ QVarLengthArray<qreal, 16> xTarget; // x-coordinates of target rectangles
+ QVarLengthArray<qreal, 16> yTarget; // y-coordinates of target rectangles
+
+ int columns = 3;
+ int rows = 3;
+ if (rules.horizontal != Qt::StretchTile && sourceCenterWidth != 0)
+ columns = qMax(3, 2 + qCeil(targetCenterWidth / qreal(sourceCenterWidth)));
+ if (rules.vertical != Qt::StretchTile && sourceCenterHeight != 0)
+ rows = qMax(3, 2 + qCeil(targetCenterHeight / qreal(sourceCenterHeight)));
+
+ xTarget.resize(columns + 1);
+ yTarget.resize(rows + 1);
+
+ bool oldAA = painter->testRenderHint(QPainter::Antialiasing);
+ if (painter->paintEngine()->type() != QPaintEngine::OpenGL
+ && painter->paintEngine()->type() != QPaintEngine::OpenGL2
+ && oldAA && painter->combinedTransform().type() != QTransform::TxNone) {
+ painter->setRenderHint(QPainter::Antialiasing, false);
+ }
+
+ xTarget[0] = targetRect.left();
+ xTarget[1] = targetCenterLeft;
+ xTarget[columns - 1] = targetCenterRight;
+ xTarget[columns] = targetRect.left() + targetRect.width();
+
+ yTarget[0] = targetRect.top();
+ yTarget[1] = targetCenterTop;
+ yTarget[rows - 1] = targetCenterBottom;
+ yTarget[rows] = targetRect.top() + targetRect.height();
+
+ qreal dx = targetCenterWidth;
+ qreal dy = targetCenterHeight;
+
+ switch (rules.horizontal) {
+ case Qt::StretchTile:
+ dx = targetCenterWidth;
+ break;
+ case Qt::RepeatTile:
+ dx = sourceCenterWidth;
+ break;
+ case Qt::RoundTile:
+ dx = targetCenterWidth / qreal(columns - 2);
+ break;
+ }
+
+ for (int i = 2; i < columns - 1; ++i)
+ xTarget[i] = xTarget[i - 1] + dx;
+
+ switch (rules.vertical) {
+ case Qt::StretchTile:
+ dy = targetCenterHeight;
+ break;
+ case Qt::RepeatTile:
+ dy = sourceCenterHeight;
+ break;
+ case Qt::RoundTile:
+ dy = targetCenterHeight / qreal(rows - 2);
+ break;
+ }
+
+ for (int i = 2; i < rows - 1; ++i)
+ yTarget[i] = yTarget[i - 1] + dy;
+
+ // corners
+ if (targetMargins.top() > 0 && targetMargins.left() > 0 && sourceMargins.top() > 0 && sourceMargins.left() > 0) { // top left
+ d.x = (0.5 * (xTarget[1] + xTarget[0]));
+ d.y = (0.5 * (yTarget[1] + yTarget[0]));
+ d.sourceLeft = sourceRect.left();
+ d.sourceTop = sourceRect.top();
+ d.width = sourceMargins.left();
+ d.height = sourceMargins.top();
+ d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width;
+ d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height;
+ if (hints & QDrawBorderPixmap::OpaqueTopLeft)
+ opaqueData.append(d);
+ else
+ translucentData.append(d);
+ }
+ if (targetMargins.top() > 0 && targetMargins.right() > 0 && sourceMargins.top() > 0 && sourceMargins.right() > 0) { // top right
+ d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1]));
+ d.y = (0.5 * (yTarget[1] + yTarget[0]));
+ d.sourceLeft = sourceCenterRight;
+ d.sourceTop = sourceRect.top();
+ d.width = sourceMargins.right();
+ d.height = sourceMargins.top();
+ d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width;
+ d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height;
+ if (hints & QDrawBorderPixmap::OpaqueTopRight)
+ opaqueData.append(d);
+ else
+ translucentData.append(d);
+ }
+ if (targetMargins.bottom() > 0 && targetMargins.left() > 0 && sourceMargins.bottom() > 0 && sourceMargins.left() > 0) { // bottom left
+ d.x = (0.5 * (xTarget[1] + xTarget[0]));
+ d.y =(0.5 * (yTarget[rows] + yTarget[rows - 1]));
+ d.sourceLeft = sourceRect.left();
+ d.sourceTop = sourceCenterBottom;
+ d.width = sourceMargins.left();
+ d.height = sourceMargins.bottom();
+ d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width;
+ d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height;
+ if (hints & QDrawBorderPixmap::OpaqueBottomLeft)
+ opaqueData.append(d);
+ else
+ translucentData.append(d);
+ }
+ if (targetMargins.bottom() > 0 && targetMargins.right() > 0 && sourceMargins.bottom() > 0 && sourceMargins.right() > 0) { // bottom right
+ d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1]));
+ d.y = (0.5 * (yTarget[rows] + yTarget[rows - 1]));
+ d.sourceLeft = sourceCenterRight;
+ d.sourceTop = sourceCenterBottom;
+ d.width = sourceMargins.right();
+ d.height = sourceMargins.bottom();
+ d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width;
+ d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height;
+ if (hints & QDrawBorderPixmap::OpaqueBottomRight)
+ opaqueData.append(d);
+ else
+ translucentData.append(d);
+ }
+
+ // horizontal edges
+ if (targetCenterWidth > 0 && sourceCenterWidth > 0) {
+ if (targetMargins.top() > 0 && sourceMargins.top() > 0) { // top
+ QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueTop ? opaqueData : translucentData;
+ d.sourceLeft = sourceCenterLeft;
+ d.sourceTop = sourceRect.top();
+ d.width = sourceCenterWidth;
+ d.height = sourceMargins.top();
+ d.y = (0.5 * (yTarget[1] + yTarget[0]));
+ d.scaleX = dx / d.width;
+ d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height;
+ for (int i = 1; i < columns - 1; ++i) {
+ d.x = (0.5 * (xTarget[i + 1] + xTarget[i]));
+ data.append(d);
+ }
+ if (rules.horizontal == Qt::RepeatTile)
+ data[data.size() - 1].width = ((xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX);
+ }
+ if (targetMargins.bottom() > 0 && sourceMargins.bottom() > 0) { // bottom
+ QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueBottom ? opaqueData : translucentData;
+ d.sourceLeft = sourceCenterLeft;
+ d.sourceTop = sourceCenterBottom;
+ d.width = sourceCenterWidth;
+ d.height = sourceMargins.bottom();
+ d.y = (0.5 * (yTarget[rows] + yTarget[rows - 1]));
+ d.scaleX = dx / d.width;
+ d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height;
+ for (int i = 1; i < columns - 1; ++i) {
+ d.x = (0.5 * (xTarget[i + 1] + xTarget[i]));
+ data.append(d);
+ }
+ if (rules.horizontal == Qt::RepeatTile)
+ data[data.size() - 1].width = ((xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX);
+ }
+ }
+
+ // vertical edges
+ if (targetCenterHeight > 0 && sourceCenterHeight > 0) {
+ if (targetMargins.left() > 0 && sourceMargins.left() > 0) { // left
+ QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueLeft ? opaqueData : translucentData;
+ d.sourceLeft = sourceRect.left();
+ d.sourceTop = sourceCenterTop;
+ d.width = sourceMargins.left();
+ d.height = sourceCenterHeight;
+ d.x = (0.5 * (xTarget[1] + xTarget[0]));
+ d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width;
+ d.scaleY = dy / d.height;
+ for (int i = 1; i < rows - 1; ++i) {
+ d.y = (0.5 * (yTarget[i + 1] + yTarget[i]));
+ data.append(d);
+ }
+ if (rules.vertical == Qt::RepeatTile)
+ data[data.size() - 1].height = ((yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY);
+ }
+ if (targetMargins.right() > 0 && sourceMargins.right() > 0) { // right
+ QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueRight ? opaqueData : translucentData;
+ d.sourceLeft = sourceCenterRight;
+ d.sourceTop = sourceCenterTop;
+ d.width = sourceMargins.right();
+ d.height = sourceCenterHeight;
+ d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1]));
+ d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width;
+ d.scaleY = dy / d.height;
+ for (int i = 1; i < rows - 1; ++i) {
+ d.y = (0.5 * (yTarget[i + 1] + yTarget[i]));
+ data.append(d);
+ }
+ if (rules.vertical == Qt::RepeatTile)
+ data[data.size() - 1].height = ((yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY);
+ }
+ }
+
+ // center
+ if (targetCenterWidth > 0 && targetCenterHeight > 0 && sourceCenterWidth > 0 && sourceCenterHeight > 0) {
+ QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueCenter ? opaqueData : translucentData;
+ d.sourceLeft = sourceCenterLeft;
+ d.sourceTop = sourceCenterTop;
+ d.width = sourceCenterWidth;
+ d.height = sourceCenterHeight;
+ d.scaleX = dx / d.width;
+ d.scaleY = dy / d.height;
+
+ qreal repeatWidth = (xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX;
+ qreal repeatHeight = (yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY;
+
+ for (int j = 1; j < rows - 1; ++j) {
+ d.y = (0.5 * (yTarget[j + 1] + yTarget[j]));
+ for (int i = 1; i < columns - 1; ++i) {
+ d.x = (0.5 * (xTarget[i + 1] + xTarget[i]));
+ data.append(d);
+ }
+ if (rules.horizontal == Qt::RepeatTile)
+ data[data.size() - 1].width = repeatWidth;
+ }
+ if (rules.vertical == Qt::RepeatTile) {
+ for (int i = 1; i < columns - 1; ++i)
+ data[data.size() - i].height = repeatHeight;
+ }
+ }
+
+ if (opaqueData.size())
+ painter->drawPixmapFragments(opaqueData.data(), opaqueData.size(), pixmap, QPainter::OpaqueHint);
+ if (translucentData.size())
+ painter->drawPixmapFragments(translucentData.data(), translucentData.size(), pixmap);
+
+ if (oldAA)
+ painter->setRenderHint(QPainter::Antialiasing, true);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qdrawutil.h b/src/gui/painting/qdrawutil.h
new file mode 100644
index 0000000000..20b558d411
--- /dev/null
+++ b/src/gui/painting/qdrawutil.h
@@ -0,0 +1,195 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDRAWUTIL_H
+#define QDRAWUTIL_H
+
+#include <QtCore/qnamespace.h>
+#include <QtCore/qstring.h> // char*->QString conversion
+#include <QtCore/qmargins.h>
+#include <QtGui/qpixmap.h>
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QPainter;
+#ifndef QT3_SUPPORT
+class QColorGroup;
+#endif
+class QPalette;
+class QPoint;
+class QColor;
+class QBrush;
+class QRect;
+
+//
+// Standard shade drawing
+//
+
+Q_GUI_EXPORT void qDrawShadeLine(QPainter *p, int x1, int y1, int x2, int y2,
+ const QPalette &pal, bool sunken = true,
+ int lineWidth = 1, int midLineWidth = 0);
+
+Q_GUI_EXPORT void qDrawShadeLine(QPainter *p, const QPoint &p1, const QPoint &p2,
+ const QPalette &pal, bool sunken = true,
+ int lineWidth = 1, int midLineWidth = 0);
+
+Q_GUI_EXPORT void qDrawShadeRect(QPainter *p, int x, int y, int w, int h,
+ const QPalette &pal, bool sunken = false,
+ int lineWidth = 1, int midLineWidth = 0,
+ const QBrush *fill = 0);
+
+Q_GUI_EXPORT void qDrawShadeRect(QPainter *p, const QRect &r,
+ const QPalette &pal, bool sunken = false,
+ int lineWidth = 1, int midLineWidth = 0,
+ const QBrush *fill = 0);
+
+Q_GUI_EXPORT void qDrawShadePanel(QPainter *p, int x, int y, int w, int h,
+ const QPalette &pal, bool sunken = false,
+ int lineWidth = 1, const QBrush *fill = 0);
+
+Q_GUI_EXPORT void qDrawShadePanel(QPainter *p, const QRect &r,
+ const QPalette &pal, bool sunken = false,
+ int lineWidth = 1, const QBrush *fill = 0);
+
+Q_GUI_EXPORT void qDrawWinButton(QPainter *p, int x, int y, int w, int h,
+ const QPalette &pal, bool sunken = false,
+ const QBrush *fill = 0);
+
+Q_GUI_EXPORT void qDrawWinButton(QPainter *p, const QRect &r,
+ const QPalette &pal, bool sunken = false,
+ const QBrush *fill = 0);
+
+Q_GUI_EXPORT void qDrawWinPanel(QPainter *p, int x, int y, int w, int h,
+ const QPalette &pal, bool sunken = false,
+ const QBrush *fill = 0);
+
+Q_GUI_EXPORT void qDrawWinPanel(QPainter *p, const QRect &r,
+ const QPalette &pal, bool sunken = false,
+ const QBrush *fill = 0);
+
+Q_GUI_EXPORT void qDrawPlainRect(QPainter *p, int x, int y, int w, int h, const QColor &,
+ int lineWidth = 1, const QBrush *fill = 0);
+
+Q_GUI_EXPORT void qDrawPlainRect(QPainter *p, const QRect &r, const QColor &,
+ int lineWidth = 1, const QBrush *fill = 0);
+
+
+#ifdef QT3_SUPPORT
+//
+// Use QStyle::itemRect(), QStyle::drawItem() and QStyle::drawArrow() instead.
+//
+Q_GUI_EXPORT QT3_SUPPORT QRect qItemRect(QPainter *p, Qt::GUIStyle gs, int x, int y, int w, int h,
+ int flags, bool enabled,
+ const QPixmap *pixmap, const QString& text, int len=-1);
+
+Q_GUI_EXPORT QT3_SUPPORT void qDrawItem(QPainter *p, Qt::GUIStyle gs, int x, int y, int w, int h,
+ int flags, const QPalette &pal, bool enabled,
+ const QPixmap *pixmap, const QString& text,
+ int len=-1, const QColor* penColor = 0);
+
+Q_GUI_EXPORT QT3_SUPPORT void qDrawArrow(QPainter *p, Qt::ArrowType type, Qt::GUIStyle style, bool down,
+ int x, int y, int w, int h,
+ const QPalette &pal, bool enabled);
+#endif
+
+struct QTileRules
+{
+ inline QTileRules(Qt::TileRule horizontalRule, Qt::TileRule verticalRule)
+ : horizontal(horizontalRule), vertical(verticalRule) {}
+ inline QTileRules(Qt::TileRule rule = Qt::StretchTile)
+ : horizontal(rule), vertical(rule) {}
+ Qt::TileRule horizontal;
+ Qt::TileRule vertical;
+};
+
+#ifndef Q_QDOC
+// For internal use only.
+namespace QDrawBorderPixmap
+{
+ enum DrawingHint
+ {
+ OpaqueTopLeft = 0x0001,
+ OpaqueTop = 0x0002,
+ OpaqueTopRight = 0x0004,
+ OpaqueLeft = 0x0008,
+ OpaqueCenter = 0x0010,
+ OpaqueRight = 0x0020,
+ OpaqueBottomLeft = 0x0040,
+ OpaqueBottom = 0x0080,
+ OpaqueBottomRight = 0x0100,
+ OpaqueCorners = OpaqueTopLeft | OpaqueTopRight | OpaqueBottomLeft | OpaqueBottomRight,
+ OpaqueEdges = OpaqueTop | OpaqueLeft | OpaqueRight | OpaqueBottom,
+ OpaqueFrame = OpaqueCorners | OpaqueEdges,
+ OpaqueAll = OpaqueCenter | OpaqueFrame
+ };
+
+ Q_DECLARE_FLAGS(DrawingHints, DrawingHint)
+}
+#endif
+
+Q_GUI_EXPORT void qDrawBorderPixmap(QPainter *painter,
+ const QRect &targetRect,
+ const QMargins &targetMargins,
+ const QPixmap &pixmap,
+ const QRect &sourceRect,
+ const QMargins &sourceMargins,
+ const QTileRules &rules = QTileRules()
+#ifndef Q_QDOC
+ , QDrawBorderPixmap::DrawingHints hints = 0
+#endif
+ );
+
+inline void qDrawBorderPixmap(QPainter *painter,
+ const QRect &target,
+ const QMargins &margins,
+ const QPixmap &pixmap)
+{
+ qDrawBorderPixmap(painter, target, margins, pixmap, pixmap.rect(), margins);
+}
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QDRAWUTIL_H
diff --git a/src/gui/painting/qemulationpaintengine.cpp b/src/gui/painting/qemulationpaintengine.cpp
new file mode 100644
index 0000000000..6fec3f3641
--- /dev/null
+++ b/src/gui/painting/qemulationpaintengine.cpp
@@ -0,0 +1,289 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qemulationpaintengine_p.h>
+#include <private/qpainter_p.h>
+#include <private/qtextengine_p.h>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+QEmulationPaintEngine::QEmulationPaintEngine(QPaintEngineEx *engine)
+ : real_engine(engine)
+{
+ QPaintEngine::state = real_engine->state();
+}
+
+
+QPaintEngine::Type QEmulationPaintEngine::type() const
+{
+ return real_engine->type();
+}
+
+bool QEmulationPaintEngine::begin(QPaintDevice *)
+{
+ return true;
+}
+
+bool QEmulationPaintEngine::end()
+{
+ return true;
+}
+
+
+QPainterState *QEmulationPaintEngine::createState(QPainterState *orig) const
+{
+ return real_engine->createState(orig);
+}
+
+void QEmulationPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
+{
+ QPainterState *s = state();
+
+ if (s->bgMode == Qt::OpaqueMode) {
+ Qt::BrushStyle style = brush.style();
+ if (style >= Qt::Dense1Pattern && style <= Qt::DiagCrossPattern)
+ real_engine->fill(path, s->bgBrush);
+ }
+
+ Qt::BrushStyle style = qbrush_style(brush);
+ if (style >= Qt::LinearGradientPattern && style <= Qt::ConicalGradientPattern) {
+ const QGradient *g = brush.gradient();
+
+ if (g->coordinateMode() > QGradient::LogicalMode) {
+ if (g->coordinateMode() == QGradient::StretchToDeviceMode) {
+ QBrush copy = brush;
+ QTransform mat = copy.transform();
+ mat.scale(real_engine->painter()->device()->width(), real_engine->painter()->device()->height());
+ copy.setTransform(mat);
+ real_engine->fill(path, copy);
+ return;
+ } else if (g->coordinateMode() == QGradient::ObjectBoundingMode) {
+ QBrush copy = brush;
+ QTransform mat = copy.transform();
+ QRectF r = path.controlPointRect();
+ mat.translate(r.x(), r.y());
+ mat.scale(r.width(), r.height());
+ copy.setTransform(mat);
+ real_engine->fill(path, copy);
+ return;
+ }
+ }
+ }
+
+ real_engine->fill(path, brush);
+}
+
+void QEmulationPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
+{
+ QPainterState *s = state();
+
+ if (s->bgMode == Qt::OpaqueMode && pen.style() > Qt::SolidLine) {
+ QPen bgPen = pen;
+ bgPen.setBrush(s->bgBrush);
+ bgPen.setStyle(Qt::SolidLine);
+ real_engine->stroke(path, bgPen);
+ }
+
+ QBrush brush = pen.brush();
+ QPen copy = pen;
+ Qt::BrushStyle style = qbrush_style(brush);
+ if (style >= Qt::LinearGradientPattern && style <= Qt::ConicalGradientPattern) {
+ const QGradient *g = brush.gradient();
+
+ if (g->coordinateMode() > QGradient::LogicalMode) {
+ if (g->coordinateMode() == QGradient::StretchToDeviceMode) {
+ QTransform mat = brush.transform();
+ mat.scale(real_engine->painter()->device()->width(), real_engine->painter()->device()->height());
+ brush.setTransform(mat);
+ copy.setBrush(brush);
+ real_engine->stroke(path, copy);
+ return;
+ } else if (g->coordinateMode() == QGradient::ObjectBoundingMode) {
+ QTransform mat = brush.transform();
+ QRectF r = path.controlPointRect();
+ mat.translate(r.x(), r.y());
+ mat.scale(r.width(), r.height());
+ brush.setTransform(mat);
+ copy.setBrush(brush);
+ real_engine->stroke(path, copy);
+ return;
+ }
+ }
+ }
+
+ real_engine->stroke(path, pen);
+}
+
+void QEmulationPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
+{
+ real_engine->clip(path, op);
+}
+
+void QEmulationPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
+{
+ if (state()->bgMode == Qt::OpaqueMode && pm.isQBitmap())
+ fillBGRect(r);
+ real_engine->drawPixmap(r, pm, sr);
+}
+
+void QEmulationPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
+{
+ if (state()->bgMode == Qt::OpaqueMode) {
+ const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
+ QRectF rect(p.x(), p.y() - ti.ascent.toReal(), ti.width.toReal(), (ti.ascent + ti.descent + 1).toReal());
+ fillBGRect(rect);
+ }
+
+ QPainterState *s = state();
+ Qt::BrushStyle style = qbrush_style(s->pen.brush());
+ if (style >= Qt::LinearGradientPattern && style <= Qt::ConicalGradientPattern)
+ {
+ QPen savedPen = s->pen;
+ QGradient g = *s->pen.brush().gradient();
+
+ if (g.coordinateMode() > QGradient::LogicalMode) {
+ QTransform mat = s->pen.brush().transform();
+ if (g.coordinateMode() == QGradient::StretchToDeviceMode) {
+ mat.scale(real_engine->painter()->device()->width(), real_engine->painter()->device()->height());
+ } else if (g.coordinateMode() == QGradient::ObjectBoundingMode) {
+ const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
+ QRectF r(p.x(), p.y() - ti.ascent.toReal(), ti.width.toReal(), (ti.ascent + ti.descent + 1).toReal());
+ mat.translate(r.x(), r.y());
+ mat.scale(r.width(), r.height());
+ }
+ g.setCoordinateMode(QGradient::LogicalMode);
+ QBrush brush(g);
+ brush.setTransform(mat);
+ s->pen.setBrush(brush);
+ penChanged();
+ real_engine->drawTextItem(p, textItem);
+ s->pen = savedPen;
+ penChanged();
+ return;
+ }
+ }
+
+ real_engine->drawTextItem(p, textItem);
+}
+
+void QEmulationPaintEngine::drawStaticTextItem(QStaticTextItem *item)
+{
+ real_engine->drawStaticTextItem(item);
+}
+
+void QEmulationPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s)
+{
+ if (state()->bgMode == Qt::OpaqueMode && pixmap.isQBitmap())
+ fillBGRect(r);
+ real_engine->drawTiledPixmap(r, pixmap, s);
+}
+
+void QEmulationPaintEngine::drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, Qt::ImageConversionFlags flags)
+{
+ real_engine->drawImage(r, pm, sr, flags);
+}
+
+void QEmulationPaintEngine::clipEnabledChanged()
+{
+ real_engine->clipEnabledChanged();
+}
+
+void QEmulationPaintEngine::penChanged()
+{
+ real_engine->penChanged();
+}
+
+void QEmulationPaintEngine::brushChanged()
+{
+ real_engine->brushChanged();
+}
+
+void QEmulationPaintEngine::brushOriginChanged()
+{
+ real_engine->brushOriginChanged();
+}
+
+void QEmulationPaintEngine::opacityChanged()
+{
+ real_engine->opacityChanged();
+}
+
+void QEmulationPaintEngine::compositionModeChanged()
+{
+ real_engine->compositionModeChanged();
+}
+
+void QEmulationPaintEngine::renderHintsChanged()
+{
+ real_engine->renderHintsChanged();
+}
+
+void QEmulationPaintEngine::transformChanged()
+{
+ real_engine->transformChanged();
+}
+
+void QEmulationPaintEngine::setState(QPainterState *s)
+{
+ QPaintEngine::state = s;
+ real_engine->setState(s);
+}
+
+void QEmulationPaintEngine::beginNativePainting()
+{
+ real_engine->beginNativePainting();
+}
+
+void QEmulationPaintEngine::endNativePainting()
+{
+ real_engine->endNativePainting();
+}
+
+void QEmulationPaintEngine::fillBGRect(const QRectF &r)
+{
+ qreal pts[] = { r.x(), r.y(), r.x() + r.width(), r.y(),
+ r.x() + r.width(), r.y() + r.height(), r.x(), r.y() + r.height() };
+ QVectorPath vp(pts, 4, 0, QVectorPath::RectangleHint);
+ real_engine->fill(vp, state()->bgBrush);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qemulationpaintengine_p.h b/src/gui/painting/qemulationpaintengine_p.h
new file mode 100644
index 0000000000..20c399074d
--- /dev/null
+++ b/src/gui/painting/qemulationpaintengine_p.h
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QEMULATIONPAINTENGINE_P_H
+#define QEMULATIONPAINTENGINE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qpaintengineex_p.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QEmulationPaintEngine : public QPaintEngineEx
+{
+public:
+ QEmulationPaintEngine(QPaintEngineEx *engine);
+
+ virtual bool begin(QPaintDevice *pdev);
+ virtual bool end();
+
+ virtual Type type() const;
+ virtual QPainterState *createState(QPainterState *orig) const;
+
+ virtual void fill(const QVectorPath &path, const QBrush &brush);
+ virtual void stroke(const QVectorPath &path, const QPen &pen);
+ virtual void clip(const QVectorPath &path, Qt::ClipOperation op);
+
+ virtual void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
+ virtual void drawTextItem(const QPointF &p, const QTextItem &textItem);
+ virtual void drawStaticTextItem(QStaticTextItem *item);
+ virtual void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s);
+ virtual void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, Qt::ImageConversionFlags flags);
+
+ virtual void clipEnabledChanged();
+ virtual void penChanged();
+ virtual void brushChanged();
+ virtual void brushOriginChanged();
+ virtual void opacityChanged();
+ virtual void compositionModeChanged();
+ virtual void renderHintsChanged();
+ virtual void transformChanged();
+
+ virtual void setState(QPainterState *s);
+
+ virtual void beginNativePainting();
+ virtual void endNativePainting();
+
+ virtual uint flags() const {return QPaintEngineEx::IsEmulationEngine | QPaintEngineEx::DoNotEmulate;}
+
+ inline QPainterState *state() { return (QPainterState *)QPaintEngine::state; }
+ inline const QPainterState *state() const { return (const QPainterState *)QPaintEngine::state; }
+
+ QPaintEngineEx *real_engine;
+private:
+ void fillBGRect(const QRectF &r);
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/gui/painting/qfixed_p.h b/src/gui/painting/qfixed_p.h
new file mode 100644
index 0000000000..cd96f9a0ef
--- /dev/null
+++ b/src/gui/painting/qfixed_p.h
@@ -0,0 +1,221 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFIXED_P_H
+#define QFIXED_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtCore/qdebug.h"
+#include "QtCore/qpoint.h"
+#include "QtCore/qsize.h"
+
+QT_BEGIN_NAMESPACE
+
+struct QFixed {
+public:
+ QFixed() : val(0) {}
+ QFixed(int i) : val(i<<6) {}
+ QFixed(long i) : val(i<<6) {}
+ QFixed &operator=(int i) { val = (i<<6); return *this; }
+ QFixed &operator=(long i) { val = (i<<6); return *this; }
+
+ static QFixed fromReal(qreal r) { QFixed f; f.val = (int)(r*qreal(64)); return f; }
+ static QFixed fromFixed(int fixed) { QFixed f; f.val = fixed; return f; }
+
+ inline int value() const { return val; }
+ inline void setValue(int value) { val = value; }
+
+ inline int toInt() const { return (((val)+32) & -64)>>6; }
+ inline qreal toReal() const { return ((qreal)val)/(qreal)64; }
+
+ inline int truncate() const { return val>>6; }
+ inline QFixed round() const { QFixed f; f.val = ((val)+32) & -64; return f; }
+ inline QFixed floor() const { QFixed f; f.val = (val) & -64; return f; }
+ inline QFixed ceil() const { QFixed f; f.val = (val+63) & -64; return f; }
+
+ inline QFixed operator+(int i) const { QFixed f; f.val = (val + (i<<6)); return f; }
+ inline QFixed operator+(uint i) const { QFixed f; f.val = (val + (i<<6)); return f; }
+ inline QFixed operator+(const QFixed &other) const { QFixed f; f.val = (val + other.val); return f; }
+ inline QFixed &operator+=(int i) { val += (i<<6); return *this; }
+ inline QFixed &operator+=(uint i) { val += (i<<6); return *this; }
+ inline QFixed &operator+=(const QFixed &other) { val += other.val; return *this; }
+ inline QFixed operator-(int i) const { QFixed f; f.val = (val - (i<<6)); return f; }
+ inline QFixed operator-(uint i) const { QFixed f; f.val = (val - (i<<6)); return f; }
+ inline QFixed operator-(const QFixed &other) const { QFixed f; f.val = (val - other.val); return f; }
+ inline QFixed &operator-=(int i) { val -= (i<<6); return *this; }
+ inline QFixed &operator-=(uint i) { val -= (i<<6); return *this; }
+ inline QFixed &operator-=(const QFixed &other) { val -= other.val; return *this; }
+ inline QFixed operator-() const { QFixed f; f.val = -val; return f; }
+
+ inline bool operator==(const QFixed &other) const { return val == other.val; }
+ inline bool operator!=(const QFixed &other) const { return val != other.val; }
+ inline bool operator<(const QFixed &other) const { return val < other.val; }
+ inline bool operator>(const QFixed &other) const { return val > other.val; }
+ inline bool operator<=(const QFixed &other) const { return val <= other.val; }
+ inline bool operator>=(const QFixed &other) const { return val >= other.val; }
+ inline bool operator!() const { return !val; }
+
+ inline QFixed &operator/=(int x) { val /= x; return *this; }
+ inline QFixed &operator/=(const QFixed &o) {
+ if (o.val == 0) {
+ val = 0x7FFFFFFFL;
+ } else {
+ bool neg = false;
+ qint64 a = val;
+ qint64 b = o.val;
+ if (a < 0) { a = -a; neg = true; }
+ if (b < 0) { b = -b; neg = !neg; }
+
+ int res = (int)(((a << 6) + (b >> 1)) / b);
+
+ val = (neg ? -res : res);
+ }
+ return *this;
+ }
+ inline QFixed operator/(int d) const { QFixed f; f.val = val/d; return f; }
+ inline QFixed operator/(QFixed b) const { QFixed f = *this; return (f /= b); }
+ inline QFixed operator>>(int d) const { QFixed f = *this; f.val >>= d; return f; }
+ inline QFixed &operator*=(int i) { val *= i; return *this; }
+ inline QFixed &operator*=(uint i) { val *= i; return *this; }
+ inline QFixed &operator*=(const QFixed &o) {
+ bool neg = false;
+ qint64 a = val;
+ qint64 b = o.val;
+ if (a < 0) { a = -a; neg = true; }
+ if (b < 0) { b = -b; neg = !neg; }
+
+ int res = (int)((a * b + 0x20L) >> 6);
+ val = neg ? -res : res;
+ return *this;
+ }
+ inline QFixed operator*(int i) const { QFixed f = *this; return (f *= i); }
+ inline QFixed operator*(uint i) const { QFixed f = *this; return (f *= i); }
+ inline QFixed operator*(const QFixed &o) const { QFixed f = *this; return (f *= o); }
+
+private:
+ QFixed(qreal i) : val((int)(i*qreal(64))) {}
+ QFixed &operator=(qreal i) { val = (int)(i*qreal(64)); return *this; }
+ inline QFixed operator+(qreal i) const { QFixed f; f.val = (val + (int)(i*qreal(64))); return f; }
+ inline QFixed &operator+=(qreal i) { val += (int)(i*64); return *this; }
+ inline QFixed operator-(qreal i) const { QFixed f; f.val = (val - (int)(i*qreal(64))); return f; }
+ inline QFixed &operator-=(qreal i) { val -= (int)(i*64); return *this; }
+ inline QFixed &operator/=(qreal r) { val = (int)(val/r); return *this; }
+ inline QFixed operator/(qreal d) const { QFixed f; f.val = (int)(val/d); return f; }
+ inline QFixed &operator*=(qreal d) { val = (int) (val*d); return *this; }
+ inline QFixed operator*(qreal d) const { QFixed f = *this; return (f *= d); }
+ int val;
+};
+Q_DECLARE_TYPEINFO(QFixed, Q_PRIMITIVE_TYPE);
+
+#define QFIXED_MAX (INT_MAX/256)
+
+inline int qRound(const QFixed &f) { return f.toInt(); }
+inline int qFloor(const QFixed &f) { return f.floor().truncate(); }
+
+inline QFixed operator*(int i, const QFixed &d) { return d*i; }
+inline QFixed operator+(int i, const QFixed &d) { return d+i; }
+inline QFixed operator-(int i, const QFixed &d) { return -(d-i); }
+inline QFixed operator*(uint i, const QFixed &d) { return d*i; }
+inline QFixed operator+(uint i, const QFixed &d) { return d+i; }
+inline QFixed operator-(uint i, const QFixed &d) { return -(d-i); }
+// inline QFixed operator*(qreal d, const QFixed &d2) { return d2*d; }
+
+inline bool operator==(const QFixed &f, int i) { return f.value() == (i<<6); }
+inline bool operator==(int i, const QFixed &f) { return f.value() == (i<<6); }
+inline bool operator!=(const QFixed &f, int i) { return f.value() != (i<<6); }
+inline bool operator!=(int i, const QFixed &f) { return f.value() != (i<<6); }
+inline bool operator<=(const QFixed &f, int i) { return f.value() <= (i<<6); }
+inline bool operator<=(int i, const QFixed &f) { return (i<<6) <= f.value(); }
+inline bool operator>=(const QFixed &f, int i) { return f.value() >= (i<<6); }
+inline bool operator>=(int i, const QFixed &f) { return (i<<6) >= f.value(); }
+inline bool operator<(const QFixed &f, int i) { return f.value() < (i<<6); }
+inline bool operator<(int i, const QFixed &f) { return (i<<6) < f.value(); }
+inline bool operator>(const QFixed &f, int i) { return f.value() > (i<<6); }
+inline bool operator>(int i, const QFixed &f) { return (i<<6) > f.value(); }
+
+#ifndef QT_NO_DEBUG_STREAM
+inline QDebug &operator<<(QDebug &dbg, const QFixed &f)
+{ return dbg << f.toReal(); }
+#endif
+
+struct QFixedPoint {
+ QFixed x;
+ QFixed y;
+ inline QFixedPoint() {}
+ inline QFixedPoint(const QFixed &_x, const QFixed &_y) : x(_x), y(_y) {}
+ QPointF toPointF() const { return QPointF(x.toReal(), y.toReal()); }
+ static QFixedPoint fromPointF(const QPointF &p) {
+ return QFixedPoint(QFixed::fromReal(p.x()), QFixed::fromReal(p.y()));
+ }
+};
+Q_DECLARE_TYPEINFO(QFixedPoint, Q_PRIMITIVE_TYPE);
+
+inline QFixedPoint operator-(const QFixedPoint &p1, const QFixedPoint &p2)
+{ return QFixedPoint(p1.x - p2.x, p1.y - p2.y); }
+inline QFixedPoint operator+(const QFixedPoint &p1, const QFixedPoint &p2)
+{ return QFixedPoint(p1.x + p2.x, p1.y + p2.y); }
+
+struct QFixedSize {
+ QFixed width;
+ QFixed height;
+ QSizeF toSizeF() const { return QSizeF(width.toReal(), height.toReal()); }
+ static QFixedSize fromSizeF(const QSizeF &s) {
+ QFixedSize size;
+ size.width = QFixed::fromReal(s.width());
+ size.height = QFixed::fromReal(s.height());
+ return size;
+ }
+};
+Q_DECLARE_TYPEINFO(QFixedSize, Q_PRIMITIVE_TYPE);
+
+QT_END_NAMESPACE
+
+#endif // QTEXTENGINE_P_H
diff --git a/src/gui/painting/qgraphicssystem.cpp b/src/gui/painting/qgraphicssystem.cpp
new file mode 100644
index 0000000000..171ef46f98
--- /dev/null
+++ b/src/gui/painting/qgraphicssystem.cpp
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qgraphicssystem_p.h"
+
+#ifdef Q_WS_X11
+# include <private/qpixmap_x11_p.h>
+#endif
+#if defined(Q_WS_WIN)
+# include <private/qpixmap_raster_p.h>
+#endif
+#ifdef Q_WS_MAC
+# include <private/qpixmap_mac_p.h>
+#endif
+#ifdef Q_WS_QPA
+# include <QtGui/private/qapplication_p.h>
+#endif
+#ifdef Q_OS_SYMBIAN
+# include <private/qpixmap_s60_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+QGraphicsSystem::~QGraphicsSystem()
+{
+}
+
+QPixmapData *QGraphicsSystem::createDefaultPixmapData(QPixmapData::PixelType type)
+{
+#ifdef Q_WS_QWS
+ Q_UNUSED(type);
+#endif
+#if defined(Q_WS_X11)
+ return new QX11PixmapData(type);
+#elif defined(Q_WS_WIN)
+ return new QRasterPixmapData(type);
+#elif defined(Q_WS_MAC)
+ return new QMacPixmapData(type);
+#elif defined(Q_WS_QPA)
+ return QApplicationPrivate::platformIntegration()->createPixmapData(type);
+#elif defined(Q_OS_SYMBIAN)
+ return new QS60PixmapData(type);
+#elif !defined(Q_WS_QWS)
+#error QGraphicsSystem::createDefaultPixmapData() not implemented
+#endif
+ return 0;
+}
+
+QPixmapData *QGraphicsSystem::createPixmapData(QPixmapData *origin)
+{
+ return createPixmapData(origin->pixelType());
+}
+
+void QGraphicsSystem::releaseCachedResources()
+{
+ // Do nothing here
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qgraphicssystem_mac.cpp b/src/gui/painting/qgraphicssystem_mac.cpp
new file mode 100644
index 0000000000..4f0be1f86a
--- /dev/null
+++ b/src/gui/painting/qgraphicssystem_mac.cpp
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qgraphicssystem_mac_p.h"
+
+#include <private/qpixmap_mac_p.h>
+#include <private/qwindowsurface_mac_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QPixmapData *QCoreGraphicsSystem::createPixmapData(QPixmapData::PixelType type) const
+{
+ return new QMacPixmapData(type);
+}
+
+QWindowSurface *QCoreGraphicsSystem::createWindowSurface(QWidget *widget) const
+{
+ return new QMacWindowSurface(widget);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qgraphicssystem_mac_p.h b/src/gui/painting/qgraphicssystem_mac_p.h
new file mode 100644
index 0000000000..4d2eae84d7
--- /dev/null
+++ b/src/gui/painting/qgraphicssystem_mac_p.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSSYSTEM_MAC_P_H
+#define QGRAPHICSSYSTEM_MAC_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qgraphicssystem_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q_GUI_EXPORT QCoreGraphicsSystem : public QGraphicsSystem
+{
+public:
+ QPixmapData *createPixmapData(QPixmapData::PixelType type) const;
+ QWindowSurface *createWindowSurface(QWidget *widget) const;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/painting/qgraphicssystem_p.h b/src/gui/painting/qgraphicssystem_p.h
new file mode 100644
index 0000000000..0f99a311a6
--- /dev/null
+++ b/src/gui/painting/qgraphicssystem_p.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSSYSTEM_P_H
+#define QGRAPHICSSYSTEM_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qpixmapdata_p.h"
+#include "private/qwindowsurface_p.h"
+#include "private/qpaintengine_blitter_p.h"
+
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPixmapFilter;
+class QBlittable;
+
+class Q_GUI_EXPORT QGraphicsSystem
+{
+public:
+ virtual QPixmapData *createPixmapData(QPixmapData::PixelType type) const = 0;
+ virtual QPixmapData *createPixmapData(QPixmapData *origin);
+ virtual QWindowSurface *createWindowSurface(QWidget *widget) const = 0;
+
+ virtual ~QGraphicsSystem();
+
+ //### Remove this & change qpixmap.cpp & qbitmap.cpp once every platform is gaurenteed
+ // to have a graphics system.
+ static QPixmapData *createDefaultPixmapData(QPixmapData::PixelType type);
+
+ virtual void releaseCachedResources();
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/painting/qgraphicssystem_qws.cpp b/src/gui/painting/qgraphicssystem_qws.cpp
new file mode 100644
index 0000000000..40f9ad4839
--- /dev/null
+++ b/src/gui/painting/qgraphicssystem_qws.cpp
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qscreen_qws.h>
+#include "qgraphicssystem_qws_p.h"
+#include <private/qpixmap_raster_p.h>
+#include <private/qwindowsurface_qws_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QPixmapData *QWSGraphicsSystem::createPixmapData(QPixmapData::PixelType type) const
+{
+ if (screen->pixmapDataFactory())
+ return screen->pixmapDataFactory()->create(type); //### For 4.4 compatibility
+ else
+ return new QRasterPixmapData(type);
+}
+
+QWindowSurface *QWSGraphicsSystem::createWindowSurface(QWidget *widget) const
+{
+ return screen->createSurface(widget);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qgraphicssystem_qws_p.h b/src/gui/painting/qgraphicssystem_qws_p.h
new file mode 100644
index 0000000000..92b92628cd
--- /dev/null
+++ b/src/gui/painting/qgraphicssystem_qws_p.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSSYSTEM_QWS_P_H
+#define QGRAPHICSSYSTEM_QWS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qgraphicssystem_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q_GUI_EXPORT QWSGraphicsSystem : public QGraphicsSystem
+{
+public:
+
+ QWSGraphicsSystem()
+ : screen(QScreen::instance()) {}
+
+ QWSGraphicsSystem(QScreen* s)
+ : screen(s) {}
+
+ virtual QPixmapData *createPixmapData(QPixmapData::PixelType type) const;
+ QWindowSurface *createWindowSurface(QWidget *widget) const;
+
+private:
+ QScreen* screen;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/painting/qgraphicssystem_raster.cpp b/src/gui/painting/qgraphicssystem_raster.cpp
new file mode 100644
index 0000000000..69bf588292
--- /dev/null
+++ b/src/gui/painting/qgraphicssystem_raster.cpp
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qgraphicssystem_raster_p.h"
+
+#ifdef Q_OS_SYMBIAN
+#include "private/qpixmap_s60_p.h"
+#include "private/qwindowsurface_s60_p.h"
+#else
+#include "private/qpixmap_raster_p.h"
+#include "private/qwindowsurface_raster_p.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+QPixmapData *QRasterGraphicsSystem::createPixmapData(QPixmapData::PixelType type) const
+{
+#ifdef Q_OS_SYMBIAN
+ return new QS60PixmapData(type);
+#else
+ return new QRasterPixmapData(type);
+#endif
+}
+
+QWindowSurface *QRasterGraphicsSystem::createWindowSurface(QWidget *widget) const
+{
+#ifdef Q_OS_SYMBIAN
+ return new QS60WindowSurface(widget);
+#else
+ return new QRasterWindowSurface(widget);
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qgraphicssystem_raster_p.h b/src/gui/painting/qgraphicssystem_raster_p.h
new file mode 100644
index 0000000000..a66c56903f
--- /dev/null
+++ b/src/gui/painting/qgraphicssystem_raster_p.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSSYSTEM_RASTER_P_H
+#define QGRAPHICSSYSTEM_RASTER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qgraphicssystem_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QRasterGraphicsSystem : public QGraphicsSystem
+{
+public:
+ QPixmapData *createPixmapData(QPixmapData::PixelType type) const;
+ QWindowSurface *createWindowSurface(QWidget *widget) const;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/painting/qgraphicssystem_runtime.cpp b/src/gui/painting/qgraphicssystem_runtime.cpp
new file mode 100644
index 0000000000..d1dd7ef392
--- /dev/null
+++ b/src/gui/painting/qgraphicssystem_runtime.cpp
@@ -0,0 +1,426 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qgraphicssystem_runtime_p.h>
+#include <private/qgraphicssystem_raster_p.h>
+#include <private/qgraphicssystemfactory_p.h>
+#include <private/qapplication_p.h>
+#include <private/qwidget_p.h>
+#include <QtCore/QDebug>
+#include <QtCore/QTimer>
+#include <QtGui/QBitmap>
+
+QT_BEGIN_NAMESPACE
+
+static int qt_pixmap_serial = 0;
+
+#define READBACK(f) \
+ f \
+ readBackInfo();
+
+
+class QDeferredGraphicsSystemChange : public QObject
+{
+ Q_OBJECT
+
+public:
+ QDeferredGraphicsSystemChange(QRuntimeGraphicsSystem *gs, const QString& graphicsSystemName)
+ : m_graphicsSystem(gs), m_graphicsSystemName(graphicsSystemName)
+ {
+ }
+
+ void launch()
+ {
+ QTimer::singleShot(0, this, SLOT(doChange()));
+ }
+
+private slots:
+
+ void doChange()
+ {
+ m_graphicsSystem->setGraphicsSystem(m_graphicsSystemName);
+ deleteLater();
+ }
+
+private:
+
+ QRuntimeGraphicsSystem *m_graphicsSystem;
+ QString m_graphicsSystemName;
+};
+
+QRuntimePixmapData::QRuntimePixmapData(const QRuntimeGraphicsSystem *gs, PixelType type)
+ : QPixmapData(type, RuntimeClass), m_graphicsSystem(gs)
+{
+ setSerialNumber(++qt_pixmap_serial);
+}
+
+QRuntimePixmapData::~QRuntimePixmapData()
+{
+ if (QApplicationPrivate::graphics_system)
+ m_graphicsSystem->removePixmapData(this);
+ delete m_data;
+}
+
+void QRuntimePixmapData::readBackInfo()
+{
+ w = m_data->width();
+ h = m_data->height();
+ d = m_data->depth();
+ is_null = m_data->isNull();
+}
+
+
+QPixmapData *QRuntimePixmapData::createCompatiblePixmapData() const
+{
+ QRuntimePixmapData *rtData = new QRuntimePixmapData(m_graphicsSystem, pixelType());
+ rtData->m_data = m_data->createCompatiblePixmapData();
+ return rtData;
+}
+
+
+void QRuntimePixmapData::resize(int width, int height)
+{
+ READBACK(
+ m_data->resize(width, height);
+ )
+}
+
+
+void QRuntimePixmapData::fromImage(const QImage &image,
+ Qt::ImageConversionFlags flags)
+{
+ READBACK(
+ m_data->fromImage(image, flags);
+ )
+}
+
+
+bool QRuntimePixmapData::fromFile(const QString &filename, const char *format,
+ Qt::ImageConversionFlags flags)
+{
+ bool success(false);
+ READBACK(
+ success = m_data->fromFile(filename, format, flags);
+ )
+ return success;
+}
+
+bool QRuntimePixmapData::fromData(const uchar *buffer, uint len, const char *format,
+ Qt::ImageConversionFlags flags)
+{
+ bool success(false);
+ READBACK(
+ success = m_data->fromData(buffer, len, format, flags);
+ )
+ return success;
+}
+
+
+void QRuntimePixmapData::copy(const QPixmapData *data, const QRect &rect)
+{
+ if (data->runtimeData()) {
+ READBACK(
+ m_data->copy(data->runtimeData(), rect);
+ )
+ } else {
+ READBACK(
+ m_data->copy(data, rect);
+ )
+ }
+}
+
+bool QRuntimePixmapData::scroll(int dx, int dy, const QRect &rect)
+{
+ return m_data->scroll(dx, dy, rect);
+}
+
+
+int QRuntimePixmapData::metric(QPaintDevice::PaintDeviceMetric metric) const
+{
+ return m_data->metric(metric);
+}
+
+void QRuntimePixmapData::fill(const QColor &color)
+{
+ return m_data->fill(color);
+}
+
+QBitmap QRuntimePixmapData::mask() const
+{
+ return m_data->mask();
+}
+
+void QRuntimePixmapData::setMask(const QBitmap &mask)
+{
+ READBACK(
+ m_data->setMask(mask);
+ )
+}
+
+bool QRuntimePixmapData::hasAlphaChannel() const
+{
+ return m_data->hasAlphaChannel();
+}
+
+QPixmap QRuntimePixmapData::transformed(const QTransform &matrix,
+ Qt::TransformationMode mode) const
+{
+ return m_data->transformed(matrix, mode);
+}
+
+void QRuntimePixmapData::setAlphaChannel(const QPixmap &alphaChannel)
+{
+ READBACK(
+ m_data->setAlphaChannel(alphaChannel);
+ )
+}
+
+QPixmap QRuntimePixmapData::alphaChannel() const
+{
+ return m_data->alphaChannel();
+}
+
+QImage QRuntimePixmapData::toImage() const
+{
+ return m_data->toImage();
+}
+
+QPaintEngine* QRuntimePixmapData::paintEngine() const
+{
+ return m_data->paintEngine();
+}
+
+QImage* QRuntimePixmapData::buffer()
+{
+ return m_data->buffer();
+}
+
+#if defined(Q_OS_SYMBIAN)
+void* QRuntimePixmapData::toNativeType(NativeType type)
+{
+ return m_data->toNativeType(type);
+}
+
+void QRuntimePixmapData::fromNativeType(void *pixmap, NativeType type)
+{
+ m_data->fromNativeType(pixmap, type);
+ readBackInfo();
+}
+#endif
+
+QPixmapData* QRuntimePixmapData::runtimeData() const
+{
+ return m_data;
+}
+
+QRuntimeWindowSurface::QRuntimeWindowSurface(const QRuntimeGraphicsSystem *gs, QWidget *window)
+ : QWindowSurface(window), m_graphicsSystem(gs)
+{
+
+}
+
+QRuntimeWindowSurface::~QRuntimeWindowSurface()
+{
+ if (QApplicationPrivate::graphics_system)
+ m_graphicsSystem->removeWindowSurface(this);
+}
+
+QPaintDevice *QRuntimeWindowSurface::paintDevice()
+{
+ return m_windowSurface->paintDevice();
+}
+
+void QRuntimeWindowSurface::flush(QWidget *widget, const QRegion &region,
+ const QPoint &offset)
+{
+ m_windowSurface->flush(widget, region, offset);
+
+ int destroyPolicy = m_graphicsSystem->windowSurfaceDestroyPolicy();
+ if(m_pendingWindowSurface &&
+ destroyPolicy == QRuntimeGraphicsSystem::DestroyAfterFirstFlush) {
+#ifdef QT_DEBUG
+ qDebug() << "QRuntimeWindowSurface::flush() - destroy pending window surface";
+#endif
+ m_pendingWindowSurface.reset();
+ }
+}
+
+void QRuntimeWindowSurface::setGeometry(const QRect &rect)
+{
+ QWindowSurface::setGeometry(rect);
+ m_windowSurface->setGeometry(rect);
+}
+
+bool QRuntimeWindowSurface::scroll(const QRegion &area, int dx, int dy)
+{
+ return m_windowSurface->scroll(area, dx, dy);
+}
+
+void QRuntimeWindowSurface::beginPaint(const QRegion &rgn)
+{
+ m_windowSurface->beginPaint(rgn);
+}
+
+void QRuntimeWindowSurface::endPaint(const QRegion &rgn)
+{
+ m_windowSurface->endPaint(rgn);
+}
+
+QImage* QRuntimeWindowSurface::buffer(const QWidget *widget)
+{
+ return m_windowSurface->buffer(widget);
+}
+
+QPixmap QRuntimeWindowSurface::grabWidget(const QWidget *widget, const QRect& rectangle) const
+{
+ return m_windowSurface->grabWidget(widget, rectangle);
+}
+
+QPoint QRuntimeWindowSurface::offset(const QWidget *widget) const
+{
+ return m_windowSurface->offset(widget);
+}
+
+QWindowSurface::WindowSurfaceFeatures QRuntimeWindowSurface::features() const
+{
+ return m_windowSurface->features();
+}
+
+QRuntimeGraphicsSystem::QRuntimeGraphicsSystem()
+ : m_windowSurfaceDestroyPolicy(DestroyImmediately),
+ m_graphicsSystem(0)
+{
+ QApplicationPrivate::runtime_graphics_system = true;
+
+#ifdef QT_DEFAULT_RUNTIME_SYSTEM
+ m_graphicsSystemName = QLatin1String(QT_DEFAULT_RUNTIME_SYSTEM);
+ if (m_graphicsSystemName.isNull())
+#endif
+ m_graphicsSystemName = QLatin1String("raster");
+
+#ifdef Q_OS_SYMBIAN
+ m_windowSurfaceDestroyPolicy = DestroyAfterFirstFlush;
+#endif
+
+ m_graphicsSystem = QGraphicsSystemFactory::create(m_graphicsSystemName);
+
+ QApplicationPrivate::graphics_system_name = QLatin1String("runtime");
+}
+
+
+QPixmapData *QRuntimeGraphicsSystem::createPixmapData(QPixmapData::PixelType type) const
+{
+ Q_ASSERT(m_graphicsSystem);
+ QPixmapData *data = m_graphicsSystem->createPixmapData(type);
+
+ QRuntimePixmapData *rtData = new QRuntimePixmapData(this, type);
+ rtData->m_data = data;
+ m_pixmapDatas << rtData;
+
+ return rtData;
+}
+
+QWindowSurface *QRuntimeGraphicsSystem::createWindowSurface(QWidget *widget) const
+{
+ Q_ASSERT(m_graphicsSystem);
+ QRuntimeWindowSurface *rtSurface = new QRuntimeWindowSurface(this, widget);
+ rtSurface->m_windowSurface.reset(m_graphicsSystem->createWindowSurface(widget));
+ widget->setWindowSurface(rtSurface);
+ m_windowSurfaces << rtSurface;
+ return rtSurface;
+}
+
+void QRuntimeGraphicsSystem::setGraphicsSystem(const QString &name)
+{
+ if (m_graphicsSystemName == name)
+ return;
+#ifdef QT_DEBUG
+ qDebug() << "QRuntimeGraphicsSystem::setGraphicsSystem( " << name << " )";
+#endif
+ QGraphicsSystem *oldSystem = m_graphicsSystem;
+ m_graphicsSystem = QGraphicsSystemFactory::create(name);
+ m_graphicsSystemName = name;
+
+ Q_ASSERT(m_graphicsSystem);
+
+ m_pendingGraphicsSystemName = QString();
+
+ for (int i = 0; i < m_pixmapDatas.size(); ++i) {
+ QRuntimePixmapData *proxy = m_pixmapDatas.at(i);
+ QPixmapData *newData = m_graphicsSystem->createPixmapData(proxy->m_data);
+ newData->fromImage(proxy->m_data->toImage(), Qt::NoOpaqueDetection);
+ delete proxy->m_data;
+ proxy->m_data = newData;
+ proxy->readBackInfo();
+ }
+
+ for (int i = 0; i < m_windowSurfaces.size(); ++i) {
+ QRuntimeWindowSurface *proxy = m_windowSurfaces.at(i);
+ QWidget *widget = proxy->m_windowSurface->window();
+
+ if(m_windowSurfaceDestroyPolicy == DestroyAfterFirstFlush)
+ proxy->m_pendingWindowSurface.reset(proxy->m_windowSurface.take());
+
+ QWindowSurface *newWindowSurface = m_graphicsSystem->createWindowSurface(widget);
+ newWindowSurface->setGeometry(proxy->geometry());
+
+ proxy->m_windowSurface.reset(newWindowSurface);
+ qt_widget_private(widget)->invalidateBuffer(widget->rect());
+ }
+
+ delete oldSystem;
+}
+
+void QRuntimeGraphicsSystem::removePixmapData(QRuntimePixmapData *pixmapData) const
+{
+ int index = m_pixmapDatas.lastIndexOf(pixmapData);
+ m_pixmapDatas.removeAt(index);
+}
+
+void QRuntimeGraphicsSystem::removeWindowSurface(QRuntimeWindowSurface *windowSurface) const
+{
+ int index = m_windowSurfaces.lastIndexOf(windowSurface);
+ m_windowSurfaces.removeAt(index);
+}
+
+#include "qgraphicssystem_runtime.moc"
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qgraphicssystem_runtime_p.h b/src/gui/painting/qgraphicssystem_runtime_p.h
new file mode 100644
index 0000000000..26d3777ad9
--- /dev/null
+++ b/src/gui/painting/qgraphicssystem_runtime_p.h
@@ -0,0 +1,187 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSSYSTEM_RUNTIME_P_H
+#define QGRAPHICSSYSTEM_RUNTIME_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qgraphicssystem_p.h"
+
+#include <private/qpixmapdata_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QRuntimeGraphicsSystem;
+
+class Q_GUI_EXPORT QRuntimePixmapData : public QPixmapData {
+public:
+ QRuntimePixmapData(const QRuntimeGraphicsSystem *gs, PixelType type);
+ ~QRuntimePixmapData();
+
+ virtual QPixmapData *createCompatiblePixmapData() const;
+ virtual void resize(int width, int height);
+ virtual void fromImage(const QImage &image,
+ Qt::ImageConversionFlags flags);
+
+ virtual bool fromFile(const QString &filename, const char *format,
+ Qt::ImageConversionFlags flags);
+ virtual bool fromData(const uchar *buffer, uint len, const char *format,
+ Qt::ImageConversionFlags flags);
+
+ virtual void copy(const QPixmapData *data, const QRect &rect);
+ virtual bool scroll(int dx, int dy, const QRect &rect);
+
+ virtual int metric(QPaintDevice::PaintDeviceMetric metric) const;
+ virtual void fill(const QColor &color);
+ virtual QBitmap mask() const;
+ virtual void setMask(const QBitmap &mask);
+ virtual bool hasAlphaChannel() const;
+ virtual QPixmap transformed(const QTransform &matrix,
+ Qt::TransformationMode mode) const;
+ virtual void setAlphaChannel(const QPixmap &alphaChannel);
+ virtual QPixmap alphaChannel() const;
+ virtual QImage toImage() const;
+ virtual QPaintEngine *paintEngine() const;
+
+ virtual QImage *buffer();
+
+ void readBackInfo();
+
+ QPixmapData *m_data;
+
+#if defined(Q_OS_SYMBIAN)
+ void* toNativeType(NativeType type);
+ void fromNativeType(void* pixmap, NativeType type);
+#endif
+
+ virtual QPixmapData *runtimeData() const;
+
+private:
+ const QRuntimeGraphicsSystem *m_graphicsSystem;
+
+};
+
+class QRuntimeWindowSurface : public QWindowSurface {
+public:
+ QRuntimeWindowSurface(const QRuntimeGraphicsSystem *gs, QWidget *window);
+ ~QRuntimeWindowSurface();
+
+ virtual QPaintDevice *paintDevice();
+ virtual void flush(QWidget *widget, const QRegion &region,
+ const QPoint &offset);
+ virtual void setGeometry(const QRect &rect);
+
+ virtual bool scroll(const QRegion &area, int dx, int dy);
+
+ virtual void beginPaint(const QRegion &);
+ virtual void endPaint(const QRegion &);
+
+ virtual QImage* buffer(const QWidget *widget);
+ virtual QPixmap grabWidget(const QWidget *widget, const QRect& rectangle = QRect()) const;
+
+ virtual QPoint offset(const QWidget *widget) const;
+
+ virtual WindowSurfaceFeatures features() const;
+
+ QScopedPointer<QWindowSurface> m_windowSurface;
+ QScopedPointer<QWindowSurface> m_pendingWindowSurface;
+
+private:
+ const QRuntimeGraphicsSystem *m_graphicsSystem;
+};
+
+class QRuntimeGraphicsSystem : public QGraphicsSystem
+{
+public:
+
+ enum WindowSurfaceDestroyPolicy
+ {
+ DestroyImmediately,
+ DestroyAfterFirstFlush
+ };
+
+public:
+ QRuntimeGraphicsSystem();
+
+ QPixmapData *createPixmapData(QPixmapData::PixelType type) const;
+ QWindowSurface *createWindowSurface(QWidget *widget) const;
+
+ void removePixmapData(QRuntimePixmapData *pixmapData) const;
+ void removeWindowSurface(QRuntimeWindowSurface *windowSurface) const;
+
+ void setGraphicsSystem(const QString &name);
+ QString graphicsSystemName() const { return m_graphicsSystemName; }
+
+ void setWindowSurfaceDestroyPolicy(WindowSurfaceDestroyPolicy policy)
+ {
+ m_windowSurfaceDestroyPolicy = policy;
+ }
+
+ int windowSurfaceDestroyPolicy() const { return m_windowSurfaceDestroyPolicy; }
+
+
+private:
+ int m_windowSurfaceDestroyPolicy;
+ QGraphicsSystem *m_graphicsSystem;
+ mutable QList<QRuntimePixmapData *> m_pixmapDatas;
+ mutable QList<QRuntimeWindowSurface *> m_windowSurfaces;
+ QString m_graphicsSystemName;
+
+ QString m_pendingGraphicsSystemName;
+
+ friend class QRuntimePixmapData;
+ friend class QRuntimeWindowSurface;
+ friend class QMeeGoGraphicsSystem;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/painting/qgraphicssystemfactory.cpp b/src/gui/painting/qgraphicssystemfactory.cpp
new file mode 100644
index 0000000000..62a60d77c0
--- /dev/null
+++ b/src/gui/painting/qgraphicssystemfactory.cpp
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qgraphicssystemfactory_p.h"
+#include "qgraphicssystemplugin_p.h"
+#include "private/qfactoryloader_p.h"
+#include "qmutex.h"
+
+#include "qapplication.h"
+#include <private/qapplication_p.h>
+#include "qgraphicssystem_raster_p.h"
+#include "qgraphicssystem_runtime_p.h"
+#include "qdebug.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_LIBRARY
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
+ (QGraphicsSystemFactoryInterface_iid, QLatin1String("/graphicssystems"), Qt::CaseInsensitive))
+#endif
+
+QGraphicsSystem *QGraphicsSystemFactory::create(const QString& key)
+{
+ QGraphicsSystem *ret = 0;
+ QString system = key.toLower();
+
+#if defined (QT_GRAPHICSSYSTEM_OPENGL)
+ if (system.isEmpty()) {
+ system = QLatin1String("opengl");
+ }
+#elif defined (QT_GRAPHICSSYSTEM_OPENVG)
+ if (system.isEmpty()) {
+ system = QLatin1String("openvg");
+ }
+#elif defined (QT_GRAPHICSSYSTEM_RUNTIME)
+ if (system.isEmpty()) {
+ system = QLatin1String("runtime");
+ }
+#elif defined (QT_GRAPHICSSYSTEM_RASTER) && !defined(Q_WS_WIN) && !defined(Q_OS_SYMBIAN) || defined(Q_WS_X11)
+ if (system.isEmpty()) {
+ system = QLatin1String("raster");
+ }
+#endif
+
+ QApplicationPrivate::graphics_system_name = system;
+ if (system == QLatin1String("raster"))
+ return new QRasterGraphicsSystem;
+ else if (system == QLatin1String("runtime"))
+ return new QRuntimeGraphicsSystem;
+ else if (system.isEmpty() || system == QLatin1String("native"))
+ return 0;
+
+#ifndef QT_NO_LIBRARY
+ if (!ret) {
+ if (QGraphicsSystemFactoryInterface *factory = qobject_cast<QGraphicsSystemFactoryInterface*>(loader()->instance(system)))
+ ret = factory->create(system);
+ }
+#endif
+
+ if (!ret)
+ qWarning() << "Unable to load graphicssystem" << system;
+
+ return ret;
+}
+
+/*!
+ Returns the list of valid keys, i.e. the keys this factory can
+ create styles for.
+
+ \sa create()
+*/
+QStringList QGraphicsSystemFactory::keys()
+{
+#ifndef QT_NO_LIBRARY
+ QStringList list = loader()->keys();
+#else
+ QStringList list;
+#endif
+ if (!list.contains(QLatin1String("Raster")))
+ list << QLatin1String("raster");
+ return list;
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/gui/painting/qgraphicssystemfactory_p.h b/src/gui/painting/qgraphicssystemfactory_p.h
new file mode 100644
index 0000000000..8a2e2b14e0
--- /dev/null
+++ b/src/gui/painting/qgraphicssystemfactory_p.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSSYSTEMFACTORY_H
+#define QGRAPHICSSYSTEMFACTORY_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qstringlist.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QGraphicsSystem;
+
+class QGraphicsSystemFactory
+{
+public:
+ static QStringList keys();
+ static QGraphicsSystem *create(const QString&);
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QGRAPHICSSYSTEMFACTORY_H
+
diff --git a/src/gui/painting/qgraphicssystemplugin.cpp b/src/gui/painting/qgraphicssystemplugin.cpp
new file mode 100644
index 0000000000..8eb1278373
--- /dev/null
+++ b/src/gui/painting/qgraphicssystemplugin.cpp
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qgraphicssystemplugin_p.h"
+#include "qgraphicssystem_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QGraphicsSystemPlugin::QGraphicsSystemPlugin(QObject *parent)
+ : QObject(parent)
+{
+}
+
+QGraphicsSystemPlugin::~QGraphicsSystemPlugin()
+{
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qgraphicssystemplugin_p.h b/src/gui/painting/qgraphicssystemplugin_p.h
new file mode 100644
index 0000000000..1dafe82243
--- /dev/null
+++ b/src/gui/painting/qgraphicssystemplugin_p.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSSYSTEMPLUGIN_H
+#define QGRAPHICSSYSTEMPLUGIN_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qplugin.h>
+#include <QtCore/qfactoryinterface.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QGraphicsSystem;
+
+struct QGraphicsSystemFactoryInterface : public QFactoryInterface
+{
+ virtual QGraphicsSystem *create(const QString &key) = 0;
+};
+
+#define QGraphicsSystemFactoryInterface_iid "com.trolltech.Qt.QGraphicsSystemFactoryInterface"
+
+Q_DECLARE_INTERFACE(QGraphicsSystemFactoryInterface, QGraphicsSystemFactoryInterface_iid)
+
+class Q_GUI_EXPORT QGraphicsSystemPlugin : public QObject, public QGraphicsSystemFactoryInterface
+{
+ Q_OBJECT
+ Q_INTERFACES(QGraphicsSystemFactoryInterface:QFactoryInterface)
+public:
+ explicit QGraphicsSystemPlugin(QObject *parent = 0);
+ ~QGraphicsSystemPlugin();
+
+ virtual QStringList keys() const = 0;
+ virtual QGraphicsSystem *create(const QString &key) = 0;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QGRAPHICSSYSTEMEPLUGIN_H
diff --git a/src/gui/painting/qgrayraster.c b/src/gui/painting/qgrayraster.c
new file mode 100644
index 0000000000..e5e8ba4fa1
--- /dev/null
+++ b/src/gui/painting/qgrayraster.c
@@ -0,0 +1,1942 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/***************************************************************************/
+/* */
+/* qgrayraster.c, derived from ftgrays.c */
+/* */
+/* A new `perfect' anti-aliasing renderer (body). */
+/* */
+/* Copyright 2000-2001, 2002, 2003 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, ../../3rdparty/freetype/docs/FTL.TXT. By continuing to use, */
+/* modify, or distribute this file you indicate that you have read */
+/* the license and understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* This file can be compiled without the rest of the FreeType engine, by */
+ /* defining the _STANDALONE_ macro when compiling it. You also need to */
+ /* put the files `ftgrays.h' and `ftimage.h' into the current */
+ /* compilation directory. Typically, you could do something like */
+ /* */
+ /* - copy `src/smooth/ftgrays.c' (this file) to your current directory */
+ /* */
+ /* - copy `include/freetype/ftimage.h' and `src/smooth/ftgrays.h' to the */
+ /* same directory */
+ /* */
+ /* - compile `ftgrays' with the _STANDALONE_ macro defined, as in */
+ /* */
+ /* cc -c -D_STANDALONE_ ftgrays.c */
+ /* */
+ /* The renderer can be initialized with a call to */
+ /* `qt_ft_gray_raster.raster_new'; an anti-aliased bitmap can be generated */
+ /* with a call to `qt_ft_gray_raster.raster_render'. */
+ /* */
+ /* See the comments and documentation in the file `ftimage.h' for more */
+ /* details on how the raster works. */
+ /* */
+ /*************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* This is a new anti-aliasing scan-converter for FreeType 2. The */
+ /* algorithm used here is _very_ different from the one in the standard */
+ /* `ftraster' module. Actually, `ftgrays' computes the _exact_ */
+ /* coverage of the outline on each pixel cell. */
+ /* */
+ /* It is based on ideas that I initially found in Raph Levien's */
+ /* excellent LibArt graphics library (see http://www.levien.com/libart */
+ /* for more information, though the web pages do not tell anything */
+ /* about the renderer; you'll have to dive into the source code to */
+ /* understand how it works). */
+ /* */
+ /* Note, however, that this is a _very_ different implementation */
+ /* compared to Raph's. Coverage information is stored in a very */
+ /* different way, and I don't use sorted vector paths. Also, it doesn't */
+ /* use floating point values. */
+ /* */
+ /* This renderer has the following advantages: */
+ /* */
+ /* - It doesn't need an intermediate bitmap. Instead, one can supply a */
+ /* callback function that will be called by the renderer to draw gray */
+ /* spans on any target surface. You can thus do direct composition on */
+ /* any kind of bitmap, provided that you give the renderer the right */
+ /* callback. */
+ /* */
+ /* - A perfect anti-aliaser, i.e., it computes the _exact_ coverage on */
+ /* each pixel cell. */
+ /* */
+ /* - It performs a single pass on the outline (the `standard' FT2 */
+ /* renderer makes two passes). */
+ /* */
+ /* - It can easily be modified to render to _any_ number of gray levels */
+ /* cheaply. */
+ /* */
+ /* - For small (< 20) pixel sizes, it is faster than the standard */
+ /* renderer. */
+ /* */
+ /*************************************************************************/
+
+/* experimental support for gamma correction within the rasterizer */
+#define xxxGRAYS_USE_GAMMA
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro QT_FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the QT_FT_TRACE() and QT_FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef QT_FT_COMPONENT
+#define QT_FT_COMPONENT trace_smooth
+
+
+#define ErrRaster_MemoryOverflow -4
+
+#if defined(VXWORKS)
+# include <vxWorksCommon.h> /* needed for setjmp.h */
+#endif
+#include <string.h> /* for qt_ft_memcpy() */
+#include <setjmp.h>
+#include <limits.h>
+
+#define QT_FT_UINT_MAX UINT_MAX
+
+#define qt_ft_memset memset
+
+#define qt_ft_setjmp setjmp
+#define qt_ft_longjmp longjmp
+#define qt_ft_jmp_buf jmp_buf
+
+#define ErrRaster_Invalid_Mode -2
+#define ErrRaster_Invalid_Outline -1
+#define ErrRaster_Invalid_Argument -3
+#define ErrRaster_Memory_Overflow -4
+#define ErrRaster_OutOfMemory -6
+
+#define QT_FT_BEGIN_HEADER
+#define QT_FT_END_HEADER
+
+#include <private/qrasterdefs_p.h>
+#include <private/qgrayraster_p.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+
+ /* This macro is used to indicate that a function parameter is unused. */
+ /* Its purpose is simply to reduce compiler warnings. Note also that */
+ /* simply defining it as `(void)x' doesn't avoid warnings with certain */
+ /* ANSI compilers (e.g. LCC). */
+#define QT_FT_UNUSED( x ) (x) = (x)
+
+ /* Disable the tracing mechanism for simplicity -- developers can */
+ /* activate it easily by redefining these two macros. */
+#ifndef QT_FT_ERROR
+#define QT_FT_ERROR( x ) do ; while ( 0 ) /* nothing */
+#endif
+
+#ifndef QT_FT_TRACE
+#define QT_FT_TRACE( x ) do ; while ( 0 ) /* nothing */
+#endif
+
+#ifndef QT_FT_MEM_SET
+#define QT_FT_MEM_SET( d, s, c ) qt_ft_memset( d, s, c )
+#endif
+
+#ifndef QT_FT_MEM_ZERO
+#define QT_FT_MEM_ZERO( dest, count ) QT_FT_MEM_SET( dest, 0, count )
+#endif
+
+ /* define this to dump debugging information */
+#define xxxDEBUG_GRAYS
+
+
+#define RAS_ARG PWorker worker
+#define RAS_ARG_ PWorker worker,
+
+#define RAS_VAR worker
+#define RAS_VAR_ worker,
+
+#define ras (*worker)
+
+
+ /* must be at least 6 bits! */
+#define PIXEL_BITS 8
+
+#define ONE_PIXEL ( 1L << PIXEL_BITS )
+#define PIXEL_MASK ( -1L << PIXEL_BITS )
+#define TRUNC( x ) ( (TCoord)( (x) >> PIXEL_BITS ) )
+#define SUBPIXELS( x ) ( (TPos)(x) << PIXEL_BITS )
+#define FLOOR( x ) ( (x) & -ONE_PIXEL )
+#define CEILING( x ) ( ( (x) + ONE_PIXEL - 1 ) & -ONE_PIXEL )
+#define ROUND( x ) ( ( (x) + ONE_PIXEL / 2 ) & -ONE_PIXEL )
+
+#if PIXEL_BITS >= 6
+#define UPSCALE( x ) ( (x) << ( PIXEL_BITS - 6 ) )
+#define DOWNSCALE( x ) ( (x) >> ( PIXEL_BITS - 6 ) )
+#else
+#define UPSCALE( x ) ( (x) >> ( 6 - PIXEL_BITS ) )
+#define DOWNSCALE( x ) ( (x) << ( 6 - PIXEL_BITS ) )
+#endif
+
+ /*************************************************************************/
+ /* */
+ /* TYPE DEFINITIONS */
+ /* */
+
+ /* don't change the following types to QT_FT_Int or QT_FT_Pos, since we might */
+ /* need to define them to "float" or "double" when experimenting with */
+ /* new algorithms */
+
+ typedef int TCoord; /* integer scanline/pixel coordinate */
+ typedef int TPos; /* sub-pixel coordinate */
+
+ /* determine the type used to store cell areas. This normally takes at */
+ /* least PIXEL_BITS*2 + 1 bits. On 16-bit systems, we need to use */
+ /* `long' instead of `int', otherwise bad things happen */
+
+#if PIXEL_BITS <= 7
+
+ typedef int TArea;
+
+#else /* PIXEL_BITS >= 8 */
+
+ /* approximately determine the size of integers using an ANSI-C header */
+#if QT_FT_UINT_MAX == 0xFFFFU
+ typedef long TArea;
+#else
+ typedef int TArea;
+#endif
+
+#endif /* PIXEL_BITS >= 8 */
+
+
+ /* maximal number of gray spans in a call to the span callback */
+#define QT_FT_MAX_GRAY_SPANS 256
+
+
+ typedef struct TCell_* PCell;
+
+ typedef struct TCell_
+ {
+ int x;
+ int cover;
+ TArea area;
+ PCell next;
+
+ } TCell;
+
+
+ typedef struct TWorker_
+ {
+ TCoord ex, ey;
+ TPos min_ex, max_ex;
+ TPos min_ey, max_ey;
+ TPos count_ex, count_ey;
+
+ TArea area;
+ int cover;
+ int invalid;
+
+ PCell cells;
+ int max_cells;
+ int num_cells;
+
+ TCoord cx, cy;
+ TPos x, y;
+
+ TPos last_ey;
+
+ QT_FT_Vector bez_stack[32 * 3 + 1];
+ int lev_stack[32];
+
+ QT_FT_Outline outline;
+ QT_FT_Bitmap target;
+ QT_FT_BBox clip_box;
+
+ QT_FT_Span gray_spans[QT_FT_MAX_GRAY_SPANS];
+ int num_gray_spans;
+
+ QT_FT_Raster_Span_Func render_span;
+ void* render_span_data;
+
+ int band_size;
+ int band_shoot;
+ int conic_level;
+ int cubic_level;
+
+ qt_ft_jmp_buf jump_buffer;
+
+ void* buffer;
+ long buffer_size;
+
+ PCell* ycells;
+ int ycount;
+
+ int skip_spans;
+ } TWorker, *PWorker;
+
+
+ typedef struct TRaster_
+ {
+ void* buffer;
+ long buffer_size;
+ long buffer_allocated_size;
+ int band_size;
+ void* memory;
+ PWorker worker;
+
+ } TRaster, *PRaster;
+
+ int q_gray_rendered_spans(TRaster *raster)
+ {
+ if ( raster && raster->worker )
+ return raster->worker->skip_spans > 0 ? 0 : -raster->worker->skip_spans;
+ return 0;
+ }
+
+ /*************************************************************************/
+ /* */
+ /* Initialize the cells table. */
+ /* */
+ static void
+ gray_init_cells( RAS_ARG_ void* buffer,
+ long byte_size )
+ {
+ ras.buffer = buffer;
+ ras.buffer_size = byte_size;
+
+ ras.ycells = (PCell*) buffer;
+ ras.cells = NULL;
+ ras.max_cells = 0;
+ ras.num_cells = 0;
+ ras.area = 0;
+ ras.cover = 0;
+ ras.invalid = 1;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* Compute the outline bounding box. */
+ /* */
+ static void
+ gray_compute_cbox( RAS_ARG )
+ {
+ QT_FT_Outline* outline = &ras.outline;
+ QT_FT_Vector* vec = outline->points;
+ QT_FT_Vector* limit = vec + outline->n_points;
+
+
+ if ( outline->n_points <= 0 )
+ {
+ ras.min_ex = ras.max_ex = 0;
+ ras.min_ey = ras.max_ey = 0;
+ return;
+ }
+
+ ras.min_ex = ras.max_ex = vec->x;
+ ras.min_ey = ras.max_ey = vec->y;
+
+ vec++;
+
+ for ( ; vec < limit; vec++ )
+ {
+ TPos x = vec->x;
+ TPos y = vec->y;
+
+
+ if ( x < ras.min_ex ) ras.min_ex = x;
+ if ( x > ras.max_ex ) ras.max_ex = x;
+ if ( y < ras.min_ey ) ras.min_ey = y;
+ if ( y > ras.max_ey ) ras.max_ey = y;
+ }
+
+ /* truncate the bounding box to integer pixels */
+ ras.min_ex = ras.min_ex >> 6;
+ ras.min_ey = ras.min_ey >> 6;
+ ras.max_ex = ( ras.max_ex + 63 ) >> 6;
+ ras.max_ey = ( ras.max_ey + 63 ) >> 6;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* Record the current cell in the table. */
+ /* */
+ static void
+ gray_record_cell( RAS_ARG )
+ {
+ PCell *pcell, cell;
+ int x = ras.ex;
+
+ if ( ras.invalid || !( ras.area | ras.cover ) )
+ return;
+
+ if ( x > ras.max_ex )
+ x = ras.max_ex;
+
+ pcell = &ras.ycells[ras.ey];
+
+ for (;;)
+ {
+ cell = *pcell;
+ if ( cell == NULL || cell->x > x )
+ break;
+
+ if ( cell->x == x ) {
+ cell->area += ras.area;
+ cell->cover += ras.cover;
+ return;
+ }
+
+ pcell = &cell->next;
+ }
+
+ if ( ras.num_cells >= ras.max_cells )
+ qt_ft_longjmp( ras.jump_buffer, 1 );
+
+ cell = ras.cells + ras.num_cells++;
+ cell->x = x;
+ cell->area = ras.area;
+ cell->cover = ras.cover;
+
+ cell->next = *pcell;
+ *pcell = cell;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* Set the current cell to a new position. */
+ /* */
+ static void
+ gray_set_cell( RAS_ARG_ TCoord ex,
+ TCoord ey )
+ {
+ /* Move the cell pointer to a new position. We set the `invalid' */
+ /* flag to indicate that the cell isn't part of those we're interested */
+ /* in during the render phase. This means that: */
+ /* */
+ /* . the new vertical position must be within min_ey..max_ey-1. */
+ /* . the new horizontal position must be strictly less than max_ex */
+ /* */
+ /* Note that if a cell is to the left of the clipping region, it is */
+ /* actually set to the (min_ex-1) horizontal position. */
+
+ /* All cells that are on the left of the clipping region go to the */
+ /* min_ex - 1 horizontal position. */
+ ey -= ras.min_ey;
+
+ if ( ex > ras.max_ex )
+ ex = ras.max_ex;
+
+ ex -= ras.min_ex;
+ if ( ex < 0 )
+ ex = -1;
+
+ /* are we moving to a different cell ? */
+ if ( ex != ras.ex || ey != ras.ey )
+ {
+ /* record the current one if it is valid */
+ if ( !ras.invalid )
+ gray_record_cell( RAS_VAR );
+
+ ras.area = 0;
+ ras.cover = 0;
+ }
+
+ ras.ex = ex;
+ ras.ey = ey;
+ ras.invalid = ( (unsigned)ey >= (unsigned)ras.count_ey ||
+ ex >= ras.count_ex );
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* Start a new contour at a given cell. */
+ /* */
+ static void
+ gray_start_cell( RAS_ARG_ TCoord ex,
+ TCoord ey )
+ {
+ if ( ex > ras.max_ex )
+ ex = (TCoord)( ras.max_ex );
+
+ if ( ex < ras.min_ex )
+ ex = (TCoord)( ras.min_ex - 1 );
+
+ ras.area = 0;
+ ras.cover = 0;
+ ras.ex = ex - ras.min_ex;
+ ras.ey = ey - ras.min_ey;
+ ras.last_ey = SUBPIXELS( ey );
+ ras.invalid = 0;
+
+ gray_set_cell( RAS_VAR_ ex, ey );
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* Render a scanline as one or more cells. */
+ /* */
+ static void
+ gray_render_scanline( RAS_ARG_ TCoord ey,
+ TPos x1,
+ TCoord y1,
+ TPos x2,
+ TCoord y2 )
+ {
+ TCoord ex1, ex2, fx1, fx2, delta;
+ int p, first, dx;
+ int incr, lift, mod, rem;
+
+
+ dx = x2 - x1;
+
+ ex1 = TRUNC( x1 );
+ ex2 = TRUNC( x2 );
+ fx1 = (TCoord)( x1 - SUBPIXELS( ex1 ) );
+ fx2 = (TCoord)( x2 - SUBPIXELS( ex2 ) );
+
+ /* trivial case. Happens often */
+ if ( y1 == y2 )
+ {
+ gray_set_cell( RAS_VAR_ ex2, ey );
+ return;
+ }
+
+ /* everything is located in a single cell. That is easy! */
+ /* */
+ if ( ex1 == ex2 )
+ {
+ delta = y2 - y1;
+ ras.area += (TArea)( fx1 + fx2 ) * delta;
+ ras.cover += delta;
+ return;
+ }
+
+ /* ok, we'll have to render a run of adjacent cells on the same */
+ /* scanline... */
+ /* */
+ p = ( ONE_PIXEL - fx1 ) * ( y2 - y1 );
+ first = ONE_PIXEL;
+ incr = 1;
+
+ if ( dx < 0 )
+ {
+ p = fx1 * ( y2 - y1 );
+ first = 0;
+ incr = -1;
+ dx = -dx;
+ }
+
+ delta = (TCoord)( p / dx );
+ mod = (TCoord)( p % dx );
+ if ( mod < 0 )
+ {
+ delta--;
+ mod += (TCoord)dx;
+ }
+
+ ras.area += (TArea)( fx1 + first ) * delta;
+ ras.cover += delta;
+
+ ex1 += incr;
+ gray_set_cell( RAS_VAR_ ex1, ey );
+ y1 += delta;
+
+ if ( ex1 != ex2 )
+ {
+ p = ONE_PIXEL * ( y2 - y1 + delta );
+ lift = (TCoord)( p / dx );
+ rem = (TCoord)( p % dx );
+ if ( rem < 0 )
+ {
+ lift--;
+ rem += (TCoord)dx;
+ }
+
+ mod -= (int)dx;
+
+ while ( ex1 != ex2 )
+ {
+ delta = lift;
+ mod += rem;
+ if ( mod >= 0 )
+ {
+ mod -= (TCoord)dx;
+ delta++;
+ }
+
+ ras.area += (TArea)ONE_PIXEL * delta;
+ ras.cover += delta;
+ y1 += delta;
+ ex1 += incr;
+ gray_set_cell( RAS_VAR_ ex1, ey );
+ }
+ }
+
+ delta = y2 - y1;
+ ras.area += (TArea)( fx2 + ONE_PIXEL - first ) * delta;
+ ras.cover += delta;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* Render a given line as a series of scanlines. */
+ /* */
+ static void
+ gray_render_line( RAS_ARG_ TPos to_x,
+ TPos to_y )
+ {
+ TCoord ey1, ey2, fy1, fy2;
+ TPos dx, dy, x, x2;
+ int p, first;
+ int delta, rem, mod, lift, incr;
+
+
+ ey1 = TRUNC( ras.last_ey );
+ ey2 = TRUNC( to_y ); /* if (ey2 >= ras.max_ey) ey2 = ras.max_ey-1; */
+ fy1 = (TCoord)( ras.y - ras.last_ey );
+ fy2 = (TCoord)( to_y - SUBPIXELS( ey2 ) );
+
+ dx = to_x - ras.x;
+ dy = to_y - ras.y;
+
+ /* XXX: we should do something about the trivial case where dx == 0, */
+ /* as it happens very often! */
+
+ /* perform vertical clipping */
+ {
+ TCoord min, max;
+
+
+ min = ey1;
+ max = ey2;
+ if ( ey1 > ey2 )
+ {
+ min = ey2;
+ max = ey1;
+ }
+ if ( min >= ras.max_ey || max < ras.min_ey )
+ goto End;
+ }
+
+ /* everything is on a single scanline */
+ if ( ey1 == ey2 )
+ {
+ gray_render_scanline( RAS_VAR_ ey1, ras.x, fy1, to_x, fy2 );
+ goto End;
+ }
+
+ /* vertical line - avoid calling gray_render_scanline */
+ incr = 1;
+
+ if ( dx == 0 )
+ {
+ TCoord ex = TRUNC( ras.x );
+ TCoord two_fx = (TCoord)( ( ras.x - SUBPIXELS( ex ) ) << 1 );
+ TPos area;
+
+
+ first = ONE_PIXEL;
+ if ( dy < 0 )
+ {
+ first = 0;
+ incr = -1;
+ }
+
+ delta = (int)( first - fy1 );
+ ras.area += (TArea)two_fx * delta;
+ ras.cover += delta;
+ ey1 += incr;
+
+ gray_set_cell( &ras, ex, ey1 );
+
+ delta = (int)( first + first - ONE_PIXEL );
+ area = (TArea)two_fx * delta;
+ while ( ey1 != ey2 )
+ {
+ ras.area += area;
+ ras.cover += delta;
+ ey1 += incr;
+
+ gray_set_cell( &ras, ex, ey1 );
+ }
+
+ delta = (int)( fy2 - ONE_PIXEL + first );
+ ras.area += (TArea)two_fx * delta;
+ ras.cover += delta;
+
+ goto End;
+ }
+
+ /* ok, we have to render several scanlines */
+ p = ( ONE_PIXEL - fy1 ) * dx;
+ first = ONE_PIXEL;
+ incr = 1;
+
+ if ( dy < 0 )
+ {
+ p = fy1 * dx;
+ first = 0;
+ incr = -1;
+ dy = -dy;
+ }
+
+ delta = (int)( p / dy );
+ mod = (int)( p % dy );
+ if ( mod < 0 )
+ {
+ delta--;
+ mod += (TCoord)dy;
+ }
+
+ x = ras.x + delta;
+ gray_render_scanline( RAS_VAR_ ey1, ras.x, fy1, x, (TCoord)first );
+
+ ey1 += incr;
+ gray_set_cell( RAS_VAR_ TRUNC( x ), ey1 );
+
+ if ( ey1 != ey2 )
+ {
+ p = ONE_PIXEL * dx;
+ lift = (int)( p / dy );
+ rem = (int)( p % dy );
+ if ( rem < 0 )
+ {
+ lift--;
+ rem += (int)dy;
+ }
+ mod -= (int)dy;
+
+ while ( ey1 != ey2 )
+ {
+ delta = lift;
+ mod += rem;
+ if ( mod >= 0 )
+ {
+ mod -= (int)dy;
+ delta++;
+ }
+
+ x2 = x + delta;
+ gray_render_scanline( RAS_VAR_ ey1, x,
+ (TCoord)( ONE_PIXEL - first ), x2,
+ (TCoord)first );
+ x = x2;
+
+ ey1 += incr;
+ gray_set_cell( RAS_VAR_ TRUNC( x ), ey1 );
+ }
+ }
+
+ gray_render_scanline( RAS_VAR_ ey1, x,
+ (TCoord)( ONE_PIXEL - first ), to_x,
+ fy2 );
+
+ End:
+ ras.x = to_x;
+ ras.y = to_y;
+ ras.last_ey = SUBPIXELS( ey2 );
+ }
+
+
+ static void
+ gray_split_conic( QT_FT_Vector* base )
+ {
+ TPos a, b;
+
+
+ base[4].x = base[2].x;
+ b = base[1].x;
+ a = base[3].x = ( base[2].x + b ) / 2;
+ b = base[1].x = ( base[0].x + b ) / 2;
+ base[2].x = ( a + b ) / 2;
+
+ base[4].y = base[2].y;
+ b = base[1].y;
+ a = base[3].y = ( base[2].y + b ) / 2;
+ b = base[1].y = ( base[0].y + b ) / 2;
+ base[2].y = ( a + b ) / 2;
+ }
+
+
+ static void
+ gray_render_conic( RAS_ARG_ const QT_FT_Vector* control,
+ const QT_FT_Vector* to )
+ {
+ TPos dx, dy;
+ int top, level;
+ int* levels;
+ QT_FT_Vector* arc;
+
+
+ dx = DOWNSCALE( ras.x ) + to->x - ( control->x << 1 );
+ if ( dx < 0 )
+ dx = -dx;
+ dy = DOWNSCALE( ras.y ) + to->y - ( control->y << 1 );
+ if ( dy < 0 )
+ dy = -dy;
+ if ( dx < dy )
+ dx = dy;
+
+ level = 1;
+ dx = dx / ras.conic_level;
+ while ( dx > 0 )
+ {
+ dx >>= 2;
+ level++;
+ }
+
+ /* a shortcut to speed things up */
+ if ( level <= 1 )
+ {
+ /* we compute the mid-point directly in order to avoid */
+ /* calling gray_split_conic() */
+ TPos to_x, to_y, mid_x, mid_y;
+
+
+ to_x = UPSCALE( to->x );
+ to_y = UPSCALE( to->y );
+ mid_x = ( ras.x + to_x + 2 * UPSCALE( control->x ) ) / 4;
+ mid_y = ( ras.y + to_y + 2 * UPSCALE( control->y ) ) / 4;
+
+ gray_render_line( RAS_VAR_ mid_x, mid_y );
+ gray_render_line( RAS_VAR_ to_x, to_y );
+
+ return;
+ }
+
+ arc = ras.bez_stack;
+ levels = ras.lev_stack;
+ top = 0;
+ levels[0] = level;
+
+ arc[0].x = UPSCALE( to->x );
+ arc[0].y = UPSCALE( to->y );
+ arc[1].x = UPSCALE( control->x );
+ arc[1].y = UPSCALE( control->y );
+ arc[2].x = ras.x;
+ arc[2].y = ras.y;
+
+ while ( top >= 0 )
+ {
+ level = levels[top];
+ if ( level > 1 )
+ {
+ /* check that the arc crosses the current band */
+ TPos min, max, y;
+
+
+ min = max = arc[0].y;
+
+ y = arc[1].y;
+ if ( y < min ) min = y;
+ if ( y > max ) max = y;
+
+ y = arc[2].y;
+ if ( y < min ) min = y;
+ if ( y > max ) max = y;
+
+ if ( TRUNC( min ) >= ras.max_ey || TRUNC( max ) < ras.min_ey )
+ goto Draw;
+
+ gray_split_conic( arc );
+ arc += 2;
+ top++;
+ levels[top] = levels[top - 1] = level - 1;
+ continue;
+ }
+
+ Draw:
+ {
+ TPos to_x, to_y, mid_x, mid_y;
+
+
+ to_x = arc[0].x;
+ to_y = arc[0].y;
+ mid_x = ( ras.x + to_x + 2 * arc[1].x ) / 4;
+ mid_y = ( ras.y + to_y + 2 * arc[1].y ) / 4;
+
+ gray_render_line( RAS_VAR_ mid_x, mid_y );
+ gray_render_line( RAS_VAR_ to_x, to_y );
+
+ top--;
+ arc -= 2;
+ }
+ }
+
+ return;
+ }
+
+
+ static void
+ gray_split_cubic( QT_FT_Vector* base )
+ {
+ TPos a, b, c, d;
+
+
+ base[6].x = base[3].x;
+ c = base[1].x;
+ d = base[2].x;
+ base[1].x = a = ( base[0].x + c ) / 2;
+ base[5].x = b = ( base[3].x + d ) / 2;
+ c = ( c + d ) / 2;
+ base[2].x = a = ( a + c ) / 2;
+ base[4].x = b = ( b + c ) / 2;
+ base[3].x = ( a + b ) / 2;
+
+ base[6].y = base[3].y;
+ c = base[1].y;
+ d = base[2].y;
+ base[1].y = a = ( base[0].y + c ) / 2;
+ base[5].y = b = ( base[3].y + d ) / 2;
+ c = ( c + d ) / 2;
+ base[2].y = a = ( a + c ) / 2;
+ base[4].y = b = ( b + c ) / 2;
+ base[3].y = ( a + b ) / 2;
+ }
+
+
+ static void
+ gray_render_cubic( RAS_ARG_ const QT_FT_Vector* control1,
+ const QT_FT_Vector* control2,
+ const QT_FT_Vector* to )
+ {
+ TPos dx, dy, da, db;
+ int top, level;
+ int* levels;
+ QT_FT_Vector* arc;
+
+
+ dx = DOWNSCALE( ras.x ) + to->x - ( control1->x << 1 );
+ if ( dx < 0 )
+ dx = -dx;
+ dy = DOWNSCALE( ras.y ) + to->y - ( control1->y << 1 );
+ if ( dy < 0 )
+ dy = -dy;
+ if ( dx < dy )
+ dx = dy;
+ da = dx;
+
+ dx = DOWNSCALE( ras.x ) + to->x - 3 * ( control1->x + control2->x );
+ if ( dx < 0 )
+ dx = -dx;
+ dy = DOWNSCALE( ras.y ) + to->y - 3 * ( control1->y + control2->y );
+ if ( dy < 0 )
+ dy = -dy;
+ if ( dx < dy )
+ dx = dy;
+ db = dx;
+
+ level = 1;
+ da = da / ras.cubic_level;
+ db = db / ras.conic_level;
+ while ( da > 0 || db > 0 )
+ {
+ da >>= 2;
+ db >>= 3;
+ level++;
+ }
+
+ if ( level <= 1 )
+ {
+ TPos to_x, to_y, mid_x, mid_y;
+
+
+ to_x = UPSCALE( to->x );
+ to_y = UPSCALE( to->y );
+ mid_x = ( ras.x + to_x +
+ 3 * UPSCALE( control1->x + control2->x ) ) / 8;
+ mid_y = ( ras.y + to_y +
+ 3 * UPSCALE( control1->y + control2->y ) ) / 8;
+
+ gray_render_line( RAS_VAR_ mid_x, mid_y );
+ gray_render_line( RAS_VAR_ to_x, to_y );
+ return;
+ }
+
+ arc = ras.bez_stack;
+ arc[0].x = UPSCALE( to->x );
+ arc[0].y = UPSCALE( to->y );
+ arc[1].x = UPSCALE( control2->x );
+ arc[1].y = UPSCALE( control2->y );
+ arc[2].x = UPSCALE( control1->x );
+ arc[2].y = UPSCALE( control1->y );
+ arc[3].x = ras.x;
+ arc[3].y = ras.y;
+
+ levels = ras.lev_stack;
+ top = 0;
+ levels[0] = level;
+
+ while ( top >= 0 )
+ {
+ level = levels[top];
+ if ( level > 1 )
+ {
+ /* check that the arc crosses the current band */
+ TPos min, max, y;
+
+
+ min = max = arc[0].y;
+ y = arc[1].y;
+ if ( y < min ) min = y;
+ if ( y > max ) max = y;
+ y = arc[2].y;
+ if ( y < min ) min = y;
+ if ( y > max ) max = y;
+ y = arc[3].y;
+ if ( y < min ) min = y;
+ if ( y > max ) max = y;
+ if ( TRUNC( min ) >= ras.max_ey || TRUNC( max ) < 0 )
+ goto Draw;
+ gray_split_cubic( arc );
+ arc += 3;
+ top ++;
+ levels[top] = levels[top - 1] = level - 1;
+ continue;
+ }
+
+ Draw:
+ {
+ TPos to_x, to_y, mid_x, mid_y;
+
+
+ to_x = arc[0].x;
+ to_y = arc[0].y;
+ mid_x = ( ras.x + to_x + 3 * ( arc[1].x + arc[2].x ) ) / 8;
+ mid_y = ( ras.y + to_y + 3 * ( arc[1].y + arc[2].y ) ) / 8;
+
+ gray_render_line( RAS_VAR_ mid_x, mid_y );
+ gray_render_line( RAS_VAR_ to_x, to_y );
+ top --;
+ arc -= 3;
+ }
+ }
+
+ return;
+ }
+
+
+
+ static int
+ gray_move_to( const QT_FT_Vector* to,
+ PWorker worker )
+ {
+ TPos x, y;
+
+
+ /* record current cell, if any */
+ gray_record_cell( worker );
+
+ /* start to a new position */
+ x = UPSCALE( to->x );
+ y = UPSCALE( to->y );
+
+ gray_start_cell( worker, TRUNC( x ), TRUNC( y ) );
+
+ worker->x = x;
+ worker->y = y;
+ return 0;
+ }
+
+
+ static int
+ gray_line_to( const QT_FT_Vector* to,
+ PWorker worker )
+ {
+ gray_render_line( worker, UPSCALE( to->x ), UPSCALE( to->y ) );
+ return 0;
+ }
+
+
+ static int
+ gray_conic_to( const QT_FT_Vector* control,
+ const QT_FT_Vector* to,
+ PWorker worker )
+ {
+ gray_render_conic( worker, control, to );
+ return 0;
+ }
+
+
+ static int
+ gray_cubic_to( const QT_FT_Vector* control1,
+ const QT_FT_Vector* control2,
+ const QT_FT_Vector* to,
+ PWorker worker )
+ {
+ gray_render_cubic( worker, control1, control2, to );
+ return 0;
+ }
+
+
+ static void
+ gray_render_span( int count,
+ const QT_FT_Span* spans,
+ PWorker worker )
+ {
+ unsigned char* p;
+ QT_FT_Bitmap* map = &worker->target;
+
+ for ( ; count > 0; count--, spans++ )
+ {
+ unsigned char coverage = spans->coverage;
+
+ /* first of all, compute the scanline offset */
+ p = (unsigned char*)map->buffer - spans->y * map->pitch;
+ if ( map->pitch >= 0 )
+ p += ( map->rows - 1 ) * map->pitch;
+
+
+ if ( coverage )
+ {
+ /* For small-spans it is faster to do it by ourselves than
+ * calling `memset'. This is mainly due to the cost of the
+ * function call.
+ */
+ if ( spans->len >= 8 )
+ QT_FT_MEM_SET( p + spans->x, (unsigned char)coverage, spans->len );
+ else
+ {
+ unsigned char* q = p + spans->x;
+
+
+ switch ( spans->len )
+ {
+ case 7: *q++ = (unsigned char)coverage;
+ case 6: *q++ = (unsigned char)coverage;
+ case 5: *q++ = (unsigned char)coverage;
+ case 4: *q++ = (unsigned char)coverage;
+ case 3: *q++ = (unsigned char)coverage;
+ case 2: *q++ = (unsigned char)coverage;
+ case 1: *q = (unsigned char)coverage;
+ default:
+ ;
+ }
+ }
+ }
+ }
+ }
+
+
+ static void
+ gray_hline( RAS_ARG_ TCoord x,
+ TCoord y,
+ TPos area,
+ int acount )
+ {
+ QT_FT_Span* span;
+ int coverage;
+ int skip;
+
+
+ /* compute the coverage line's coverage, depending on the */
+ /* outline fill rule */
+ /* */
+ /* the coverage percentage is area/(PIXEL_BITS*PIXEL_BITS*2) */
+ /* */
+ coverage = (int)( area >> ( PIXEL_BITS * 2 + 1 - 8 ) );
+ /* use range 0..256 */
+ if ( coverage < 0 )
+ coverage = -coverage;
+
+ if ( ras.outline.flags & QT_FT_OUTLINE_EVEN_ODD_FILL )
+ {
+ coverage &= 511;
+
+ if ( coverage > 256 )
+ coverage = 512 - coverage;
+ else if ( coverage == 256 )
+ coverage = 255;
+ }
+ else
+ {
+ /* normal non-zero winding rule */
+ if ( coverage >= 256 )
+ coverage = 255;
+ }
+
+ y += (TCoord)ras.min_ey;
+ x += (TCoord)ras.min_ex;
+
+ /* QT_FT_Span.x is a 16-bit short, so limit our coordinates appropriately */
+ if ( x >= 32768 )
+ x = 32767;
+
+ if ( coverage )
+ {
+ /* see whether we can add this span to the current list */
+ span = ras.gray_spans + ras.num_gray_spans - 1;
+ if ( ras.num_gray_spans > 0 &&
+ span->y == y &&
+ (int)span->x + span->len == (int)x &&
+ span->coverage == coverage )
+ {
+ span->len = (unsigned short)( span->len + acount );
+ return;
+ }
+
+ if ( ras.num_gray_spans >= QT_FT_MAX_GRAY_SPANS )
+ {
+ if ( ras.render_span && ras.num_gray_spans > ras.skip_spans )
+ {
+ skip = ras.skip_spans > 0 ? ras.skip_spans : 0;
+ ras.render_span( ras.num_gray_spans - skip,
+ ras.gray_spans + skip,
+ ras.render_span_data );
+ }
+
+ ras.skip_spans -= ras.num_gray_spans;
+
+ /* ras.render_span( span->y, ras.gray_spans, count ); */
+
+#ifdef DEBUG_GRAYS
+
+ if ( 1 )
+ {
+ int n;
+
+
+ fprintf( stderr, "y=%3d ", y );
+ span = ras.gray_spans;
+ for ( n = 0; n < count; n++, span++ )
+ fprintf( stderr, "[%d..%d]:%02x ",
+ span->x, span->x + span->len - 1, span->coverage );
+ fprintf( stderr, "\n" );
+ }
+
+#endif /* DEBUG_GRAYS */
+
+ ras.num_gray_spans = 0;
+
+ span = ras.gray_spans;
+ }
+ else
+ span++;
+
+ /* add a gray span to the current list */
+ span->x = (short)x;
+ span->len = (unsigned short)acount;
+ span->y = (short)y;
+ span->coverage = (unsigned char)coverage;
+
+ ras.num_gray_spans++;
+ }
+ }
+
+
+#ifdef DEBUG_GRAYS
+
+ /* to be called while in the debugger */
+ gray_dump_cells( RAS_ARG )
+ {
+ int yindex;
+
+
+ for ( yindex = 0; yindex < ras.ycount; yindex++ )
+ {
+ PCell cell;
+
+
+ printf( "%3d:", yindex );
+
+ for ( cell = ras.ycells[yindex]; cell != NULL; cell = cell->next )
+ printf( " (%3d, c:%4d, a:%6d)", cell->x, cell->cover, cell->area );
+ printf( "\n" );
+ }
+ }
+
+#endif /* DEBUG_GRAYS */
+
+
+ static void
+ gray_sweep( RAS_ARG_ const QT_FT_Bitmap* target )
+ {
+ int yindex;
+
+ QT_FT_UNUSED( target );
+
+
+ if ( ras.num_cells == 0 )
+ return;
+
+ for ( yindex = 0; yindex < ras.ycount; yindex++ )
+ {
+ PCell cell = ras.ycells[yindex];
+ TCoord cover = 0;
+ TCoord x = 0;
+
+
+ for ( ; cell != NULL; cell = cell->next )
+ {
+ TArea area;
+
+
+ if ( cell->x > x && cover != 0 )
+ gray_hline( RAS_VAR_ x, yindex, cover * ( ONE_PIXEL * 2 ),
+ cell->x - x );
+
+ cover += cell->cover;
+ area = cover * ( ONE_PIXEL * 2 ) - cell->area;
+
+ if ( area != 0 && cell->x >= 0 )
+ gray_hline( RAS_VAR_ cell->x, yindex, area, 1 );
+
+ x = cell->x + 1;
+ }
+
+ if ( ras.count_ex > x && cover != 0 )
+ gray_hline( RAS_VAR_ x, yindex, cover * ( ONE_PIXEL * 2 ),
+ ras.count_ex - x );
+ }
+ }
+
+ /*************************************************************************/
+ /* */
+ /* The following function should only compile in stand_alone mode, */
+ /* i.e., when building this component without the rest of FreeType. */
+ /* */
+ /*************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* QT_FT_Outline_Decompose */
+ /* */
+ /* <Description> */
+ /* Walks over an outline's structure to decompose it into individual */
+ /* segments and Bezier arcs. This function is also able to emit */
+ /* `move to' and `close to' operations to indicate the start and end */
+ /* of new contours in the outline. */
+ /* */
+ /* <Input> */
+ /* outline :: A pointer to the source target. */
+ /* */
+ /* user :: A typeless pointer which is passed to each */
+ /* emitter during the decomposition. It can be */
+ /* used to store the state during the */
+ /* decomposition. */
+ /* */
+ /* <Return> */
+ /* Error code. 0 means success. */
+ /* */
+ static
+ int QT_FT_Outline_Decompose( const QT_FT_Outline* outline,
+ void* user )
+ {
+#undef SCALED
+#define SCALED( x ) (x)
+
+ QT_FT_Vector v_last;
+ QT_FT_Vector v_control;
+ QT_FT_Vector v_start;
+
+ QT_FT_Vector* point;
+ QT_FT_Vector* limit;
+ char* tags;
+
+ int n; /* index of contour in outline */
+ int first; /* index of first point in contour */
+ int error;
+ char tag; /* current point's state */
+
+ first = 0;
+
+ for ( n = 0; n < outline->n_contours; n++ )
+ {
+ int last; /* index of last point in contour */
+
+
+ last = outline->contours[n];
+ limit = outline->points + last;
+
+ v_start = outline->points[first];
+ v_last = outline->points[last];
+
+ v_start.x = SCALED( v_start.x );
+ v_start.y = SCALED( v_start.y );
+
+ v_last.x = SCALED( v_last.x );
+ v_last.y = SCALED( v_last.y );
+
+ v_control = v_start;
+
+ point = outline->points + first;
+ tags = outline->tags + first;
+ tag = QT_FT_CURVE_TAG( tags[0] );
+
+ /* A contour cannot start with a cubic control point! */
+ if ( tag == QT_FT_CURVE_TAG_CUBIC )
+ goto Invalid_Outline;
+
+ /* check first point to determine origin */
+ if ( tag == QT_FT_CURVE_TAG_CONIC )
+ {
+ /* first point is conic control. Yes, this happens. */
+ if ( QT_FT_CURVE_TAG( outline->tags[last] ) == QT_FT_CURVE_TAG_ON )
+ {
+ /* start at last point if it is on the curve */
+ v_start = v_last;
+ limit--;
+ }
+ else
+ {
+ /* if both first and last points are conic, */
+ /* start at their middle and record its position */
+ /* for closure */
+ v_start.x = ( v_start.x + v_last.x ) / 2;
+ v_start.y = ( v_start.y + v_last.y ) / 2;
+
+ v_last = v_start;
+ }
+ point--;
+ tags--;
+ }
+
+ error = gray_move_to( &v_start, user );
+ if ( error )
+ goto Exit;
+
+ while ( point < limit )
+ {
+ point++;
+ tags++;
+
+ tag = QT_FT_CURVE_TAG( tags[0] );
+ switch ( tag )
+ {
+ case QT_FT_CURVE_TAG_ON: /* emit a single line_to */
+ {
+ QT_FT_Vector vec;
+
+
+ vec.x = SCALED( point->x );
+ vec.y = SCALED( point->y );
+
+ error = gray_line_to( &vec, user );
+ if ( error )
+ goto Exit;
+ continue;
+ }
+
+ case QT_FT_CURVE_TAG_CONIC: /* consume conic arcs */
+ {
+ v_control.x = SCALED( point->x );
+ v_control.y = SCALED( point->y );
+
+ Do_Conic:
+ if ( point < limit )
+ {
+ QT_FT_Vector vec;
+ QT_FT_Vector v_middle;
+
+
+ point++;
+ tags++;
+ tag = QT_FT_CURVE_TAG( tags[0] );
+
+ vec.x = SCALED( point->x );
+ vec.y = SCALED( point->y );
+
+ if ( tag == QT_FT_CURVE_TAG_ON )
+ {
+ error = gray_conic_to( &v_control, &vec,
+ user );
+ if ( error )
+ goto Exit;
+ continue;
+ }
+
+ if ( tag != QT_FT_CURVE_TAG_CONIC )
+ goto Invalid_Outline;
+
+ v_middle.x = ( v_control.x + vec.x ) / 2;
+ v_middle.y = ( v_control.y + vec.y ) / 2;
+
+ error = gray_conic_to( &v_control, &v_middle,
+ user );
+ if ( error )
+ goto Exit;
+
+ v_control = vec;
+ goto Do_Conic;
+ }
+
+ error = gray_conic_to( &v_control, &v_start,
+ user );
+ goto Close;
+ }
+
+ default: /* QT_FT_CURVE_TAG_CUBIC */
+ {
+ QT_FT_Vector vec1, vec2;
+
+
+ if ( point + 1 > limit ||
+ QT_FT_CURVE_TAG( tags[1] ) != QT_FT_CURVE_TAG_CUBIC )
+ goto Invalid_Outline;
+
+ point += 2;
+ tags += 2;
+
+ vec1.x = SCALED( point[-2].x );
+ vec1.y = SCALED( point[-2].y );
+
+ vec2.x = SCALED( point[-1].x );
+ vec2.y = SCALED( point[-1].y );
+
+ if ( point <= limit )
+ {
+ QT_FT_Vector vec;
+
+
+ vec.x = SCALED( point->x );
+ vec.y = SCALED( point->y );
+
+ error = gray_cubic_to( &vec1, &vec2, &vec, user );
+ if ( error )
+ goto Exit;
+ continue;
+ }
+
+ error = gray_cubic_to( &vec1, &vec2, &v_start, user );
+ goto Close;
+ }
+ }
+ }
+
+ /* close the contour with a line segment */
+ error = gray_line_to( &v_start, user );
+
+ Close:
+ if ( error )
+ goto Exit;
+
+ first = last + 1;
+ }
+
+ return 0;
+
+ Exit:
+ return error;
+
+ Invalid_Outline:
+ return ErrRaster_Invalid_Outline;
+ }
+
+ typedef struct TBand_
+ {
+ TPos min, max;
+
+ } TBand;
+
+
+ static int
+ gray_convert_glyph_inner( RAS_ARG )
+ {
+ volatile int error = 0;
+
+ if ( qt_ft_setjmp( ras.jump_buffer ) == 0 )
+ {
+ error = QT_FT_Outline_Decompose( &ras.outline, &ras );
+ gray_record_cell( RAS_VAR );
+ }
+ else
+ {
+ error = ErrRaster_Memory_Overflow;
+ }
+
+ return error;
+ }
+
+
+ static int
+ gray_convert_glyph( RAS_ARG )
+ {
+ TBand bands[40];
+ TBand* volatile band;
+ int volatile n, num_bands;
+ TPos volatile min, max, max_y;
+ QT_FT_BBox* clip;
+ int skip;
+
+ ras.num_gray_spans = 0;
+
+ /* Set up state in the raster object */
+ gray_compute_cbox( RAS_VAR );
+
+ /* clip to target bitmap, exit if nothing to do */
+ clip = &ras.clip_box;
+
+ if ( ras.max_ex <= clip->xMin || ras.min_ex >= clip->xMax ||
+ ras.max_ey <= clip->yMin || ras.min_ey >= clip->yMax )
+ return 0;
+
+ if ( ras.min_ex < clip->xMin ) ras.min_ex = clip->xMin;
+ if ( ras.min_ey < clip->yMin ) ras.min_ey = clip->yMin;
+
+ if ( ras.max_ex > clip->xMax ) ras.max_ex = clip->xMax;
+ if ( ras.max_ey > clip->yMax ) ras.max_ey = clip->yMax;
+
+ ras.count_ex = ras.max_ex - ras.min_ex;
+ ras.count_ey = ras.max_ey - ras.min_ey;
+
+ /* simple heuristic used to speed-up the bezier decomposition -- see */
+ /* the code in gray_render_conic() and gray_render_cubic() for more */
+ /* details */
+ ras.conic_level = 32;
+ ras.cubic_level = 16;
+
+ {
+ int level = 0;
+
+
+ if ( ras.count_ex > 24 || ras.count_ey > 24 )
+ level++;
+ if ( ras.count_ex > 120 || ras.count_ey > 120 )
+ level++;
+
+ ras.conic_level <<= level;
+ ras.cubic_level <<= level;
+ }
+
+ /* setup vertical bands */
+ num_bands = (int)( ( ras.max_ey - ras.min_ey ) / ras.band_size );
+ if ( num_bands == 0 ) num_bands = 1;
+ if ( num_bands >= 39 ) num_bands = 39;
+
+ ras.band_shoot = 0;
+
+ min = ras.min_ey;
+ max_y = ras.max_ey;
+
+ for ( n = 0; n < num_bands; n++, min = max )
+ {
+ max = min + ras.band_size;
+ if ( n == num_bands - 1 || max > max_y )
+ max = max_y;
+
+ bands[0].min = min;
+ bands[0].max = max;
+ band = bands;
+
+ while ( band >= bands )
+ {
+ TPos bottom, top, middle;
+ int error;
+
+ {
+ PCell cells_max;
+ int yindex;
+ int cell_start, cell_end, cell_mod;
+
+
+ ras.ycells = (PCell*)ras.buffer;
+ ras.ycount = band->max - band->min;
+
+ cell_start = sizeof ( PCell ) * ras.ycount;
+ cell_mod = cell_start % sizeof ( TCell );
+ if ( cell_mod > 0 )
+ cell_start += sizeof ( TCell ) - cell_mod;
+
+ cell_end = ras.buffer_size;
+ cell_end -= cell_end % sizeof( TCell );
+
+ cells_max = (PCell)( (char*)ras.buffer + cell_end );
+ ras.cells = (PCell)( (char*)ras.buffer + cell_start );
+ if ( ras.cells >= cells_max )
+ goto ReduceBands;
+
+ ras.max_cells = (int)(cells_max - ras.cells);
+ if ( ras.max_cells < 2 )
+ goto ReduceBands;
+
+ for ( yindex = 0; yindex < ras.ycount; yindex++ )
+ ras.ycells[yindex] = NULL;
+ }
+
+ ras.num_cells = 0;
+ ras.invalid = 1;
+ ras.min_ey = band->min;
+ ras.max_ey = band->max;
+ ras.count_ey = band->max - band->min;
+
+ error = gray_convert_glyph_inner( RAS_VAR );
+
+ if ( !error )
+ {
+ gray_sweep( RAS_VAR_ &ras.target );
+ band--;
+ continue;
+ }
+ else if ( error != ErrRaster_Memory_Overflow )
+ return 1;
+
+ ReduceBands:
+ /* render pool overflow; we will reduce the render band by half */
+ bottom = band->min;
+ top = band->max;
+ middle = bottom + ( ( top - bottom ) >> 1 );
+
+ /* This is too complex for a single scanline; there must */
+ /* be some problems. */
+ if ( middle == bottom )
+ {
+#ifdef DEBUG_GRAYS
+ fprintf( stderr, "Rotten glyph!\n" );
+#endif
+ return ErrRaster_OutOfMemory;
+ }
+
+ if ( bottom-top >= ras.band_size )
+ ras.band_shoot++;
+
+ band[1].min = bottom;
+ band[1].max = middle;
+ band[0].min = middle;
+ band[0].max = top;
+ band++;
+ }
+ }
+
+ if ( ras.render_span && ras.num_gray_spans > ras.skip_spans )
+ {
+ skip = ras.skip_spans > 0 ? ras.skip_spans : 0;
+ ras.render_span( ras.num_gray_spans - skip,
+ ras.gray_spans + skip,
+ ras.render_span_data );
+ }
+
+ ras.skip_spans -= ras.num_gray_spans;
+
+ if ( ras.band_shoot > 8 && ras.band_size > 16 )
+ ras.band_size = ras.band_size / 2;
+
+ return 0;
+ }
+
+
+ static int
+ gray_raster_render( QT_FT_Raster raster,
+ const QT_FT_Raster_Params* params )
+ {
+ const QT_FT_Outline* outline = (const QT_FT_Outline*)params->source;
+ const QT_FT_Bitmap* target_map = params->target;
+ PWorker worker;
+
+
+ if ( !raster || !raster->buffer || !raster->buffer_size )
+ return ErrRaster_Invalid_Argument;
+
+ if ( raster->worker )
+ raster->worker->skip_spans = params->skip_spans;
+
+ // If raster object and raster buffer are allocated, but
+ // raster size isn't of the minimum size, indicate out of
+ // memory.
+ if (raster->buffer_allocated_size < MINIMUM_POOL_SIZE )
+ return ErrRaster_OutOfMemory;
+
+ /* return immediately if the outline is empty */
+ if ( outline->n_points == 0 || outline->n_contours <= 0 )
+ return 0;
+
+ if ( !outline || !outline->contours || !outline->points )
+ return ErrRaster_Invalid_Outline;
+
+ if ( outline->n_points !=
+ outline->contours[outline->n_contours - 1] + 1 )
+ return ErrRaster_Invalid_Outline;
+
+ worker = raster->worker;
+
+ /* if direct mode is not set, we must have a target bitmap */
+ if ( ( params->flags & QT_FT_RASTER_FLAG_DIRECT ) == 0 )
+ {
+ if ( !target_map )
+ return ErrRaster_Invalid_Argument;
+
+ /* nothing to do */
+ if ( !target_map->width || !target_map->rows )
+ return 0;
+
+ if ( !target_map->buffer )
+ return ErrRaster_Invalid_Argument;
+ }
+
+ /* this version does not support monochrome rendering */
+ if ( !( params->flags & QT_FT_RASTER_FLAG_AA ) )
+ return ErrRaster_Invalid_Mode;
+
+ /* compute clipping box */
+ if ( ( params->flags & QT_FT_RASTER_FLAG_DIRECT ) == 0 )
+ {
+ /* compute clip box from target pixmap */
+ ras.clip_box.xMin = 0;
+ ras.clip_box.yMin = 0;
+ ras.clip_box.xMax = target_map->width;
+ ras.clip_box.yMax = target_map->rows;
+ }
+ else if ( params->flags & QT_FT_RASTER_FLAG_CLIP )
+ {
+ ras.clip_box = params->clip_box;
+ }
+ else
+ {
+ ras.clip_box.xMin = -32768L;
+ ras.clip_box.yMin = -32768L;
+ ras.clip_box.xMax = 32767L;
+ ras.clip_box.yMax = 32767L;
+ }
+
+ gray_init_cells( worker, raster->buffer, raster->buffer_size );
+
+ ras.outline = *outline;
+ ras.num_cells = 0;
+ ras.invalid = 1;
+ ras.band_size = raster->band_size;
+
+ if ( target_map )
+ ras.target = *target_map;
+
+ ras.render_span = (QT_FT_Raster_Span_Func)gray_render_span;
+ ras.render_span_data = &ras;
+
+ if ( params->flags & QT_FT_RASTER_FLAG_DIRECT )
+ {
+ ras.render_span = (QT_FT_Raster_Span_Func)params->gray_spans;
+ ras.render_span_data = params->user;
+ }
+
+ return gray_convert_glyph( worker );
+ }
+
+
+ /**** RASTER OBJECT CREATION: In standalone mode, we simply use *****/
+ /**** a static object. *****/
+
+ static int
+ gray_raster_new( QT_FT_Raster* araster )
+ {
+ *araster = malloc(sizeof(TRaster));
+ if (!*araster) {
+ *araster = 0;
+ return ErrRaster_Memory_Overflow;
+ }
+ QT_FT_MEM_ZERO(*araster, sizeof(TRaster));
+
+ return 0;
+ }
+
+
+ static void
+ gray_raster_done( QT_FT_Raster raster )
+ {
+ free(raster);
+ }
+
+
+ static void
+ gray_raster_reset( QT_FT_Raster raster,
+ char* pool_base,
+ long pool_size )
+ {
+ PRaster rast = (PRaster)raster;
+
+ if ( raster )
+ {
+ if ( pool_base && ( pool_size >= MINIMUM_POOL_SIZE ) )
+ {
+ PWorker worker = (PWorker)pool_base;
+
+
+ rast->worker = worker;
+ rast->buffer = pool_base +
+ ( ( sizeof ( TWorker ) + sizeof ( TCell ) - 1 ) &
+ ~( sizeof ( TCell ) - 1 ) );
+ rast->buffer_size = (long)( ( pool_base + pool_size ) -
+ (char*)rast->buffer ) &
+ ~( sizeof ( TCell ) - 1 );
+ rast->band_size = (int)( rast->buffer_size /
+ ( sizeof ( TCell ) * 8 ) );
+ }
+ else if ( pool_base)
+ { // Case when there is a raster pool allocated, but it
+ // doesn't have the minimum size (and so memory will be reallocated)
+ rast->buffer = pool_base;
+ rast->worker = NULL;
+ rast->buffer_size = pool_size;
+ }
+ else
+ {
+ rast->buffer = NULL;
+ rast->buffer_size = 0;
+ rast->worker = NULL;
+ }
+ rast->buffer_allocated_size = pool_size;
+ }
+ }
+
+ const QT_FT_Raster_Funcs qt_ft_grays_raster =
+ {
+ QT_FT_GLYPH_FORMAT_OUTLINE,
+
+ (QT_FT_Raster_New_Func) gray_raster_new,
+ (QT_FT_Raster_Reset_Func) gray_raster_reset,
+ (QT_FT_Raster_Set_Mode_Func)0,
+ (QT_FT_Raster_Render_Func) gray_raster_render,
+ (QT_FT_Raster_Done_Func) gray_raster_done
+ };
+
+/* END */
diff --git a/src/gui/painting/qgrayraster_p.h b/src/gui/painting/qgrayraster_p.h
new file mode 100644
index 0000000000..b9a4aed66d
--- /dev/null
+++ b/src/gui/painting/qgrayraster_p.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/***************************************************************************/
+/* */
+/* qgrayraster_p.h, derived from ftgrays.h */
+/* */
+/* FreeType smooth renderer declaration */
+/* */
+/* Copyright 1996-2001 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, ../../3rdparty/freetype/docs/FTL.TXT. By continuing to use, */
+/* modify, or distribute this file you indicate that you have read */
+/* the license and understand and accept it fully. */
+/***************************************************************************/
+
+
+#ifndef __FTGRAYS_H__
+#define __FTGRAYS_H__
+
+/*
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+*/
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+
+#include <private/qrasterdefs_p.h>
+
+ /*************************************************************************/
+ /* */
+ /* To make ftgrays.h independent from configuration files we check */
+ /* whether QT_FT_EXPORT_VAR has been defined already. */
+ /* */
+ /* On some systems and compilers (Win32 mostly), an extra keyword is */
+ /* necessary to compile the library as a DLL. */
+ /* */
+#ifndef QT_FT_EXPORT_VAR
+#define QT_FT_EXPORT_VAR( x ) extern x
+#endif
+
+/* Minimum buffer size for raster object, that accounts
+ for TWorker and TCell sizes.*/
+#define MINIMUM_POOL_SIZE 8192
+
+ QT_FT_EXPORT_VAR( const QT_FT_Raster_Funcs ) qt_ft_grays_raster;
+
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* __FTGRAYS_H__ */
+
+/* END */
diff --git a/src/gui/painting/qimagescale.cpp b/src/gui/painting/qimagescale.cpp
new file mode 100644
index 0000000000..87df7976bf
--- /dev/null
+++ b/src/gui/painting/qimagescale.cpp
@@ -0,0 +1,1031 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <private/qimagescale_p.h>
+#include <private/qdrawhelper_p.h>
+
+#include "qimage.h"
+#include "qcolor.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QImageScale {
+ struct QImageScaleInfo;
+}
+
+typedef void (*qt_qimageScaleFunc)(QImageScale::QImageScaleInfo *isi, unsigned int *dest,
+ int dxx, int dyy, int dx, int dy, int dw,
+ int dh, int dow, int sow);
+
+static void qt_qimageScaleAARGB(QImageScale::QImageScaleInfo *isi, unsigned int *dest,
+ int dxx, int dyy, int dx, int dy, int dw,
+ int dh, int dow, int sow);
+
+static void qt_qimageScaleAARGBA(QImageScale::QImageScaleInfo *isi, unsigned int *dest,
+ int dxx, int dyy, int dx, int dy, int dw,
+ int dh, int dow, int sow);
+
+qt_qimageScaleFunc qt_qimageScaleArgb = qt_qimageScaleAARGBA;
+qt_qimageScaleFunc qt_qimageScaleRgb = qt_qimageScaleAARGB;
+
+
+/*
+ * Copyright (C) 2004, 2005 Daniel M. Duley
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/* OTHER CREDITS:
+ *
+ * This is the normal smoothscale method, based on Imlib2's smoothscale.
+ *
+ * Originally I took the algorithm used in NetPBM and Qt and added MMX/3dnow
+ * optimizations. It ran in about 1/2 the time as Qt. Then I ported Imlib's
+ * C algorithm and it ran at about the same speed as my MMX optimized one...
+ * Finally I ported Imlib's MMX version and it ran in less than half the
+ * time as my MMX algorithm, (taking only a quarter of the time Qt does).
+ * After further optimization it seems to run at around 1/6th.
+ *
+ * Changes include formatting, namespaces and other C++'ings, removal of old
+ * #ifdef'ed code, and removal of unneeded border calculation code.
+ *
+ * Imlib2 is (C) Carsten Haitzler and various contributors. The MMX code
+ * is by Willem Monsuwe <willem@stack.nl>. All other modifications are
+ * (C) Daniel M. Duley.
+ */
+
+
+namespace QImageScale {
+ struct QImageScaleInfo {
+ int *xpoints;
+ unsigned int **ypoints;
+ int *xapoints, *yapoints;
+ int xup_yup;
+ };
+
+ unsigned int** qimageCalcYPoints(unsigned int *src, int sw, int sh,
+ int dh);
+ int* qimageCalcXPoints(int sw, int dw);
+ int* qimageCalcApoints(int s, int d, int up);
+ QImageScaleInfo* qimageFreeScaleInfo(QImageScaleInfo *isi);
+ QImageScaleInfo *qimageCalcScaleInfo(const QImage &img, int sw, int sh,
+ int dw, int dh, char aa);
+}
+
+using namespace QImageScale;
+
+//
+// Code ported from Imlib...
+//
+
+// FIXME: replace with qRed, etc... These work on pointers to pixels, not
+// pixel values
+#define A_VAL(p) (qAlpha(*p))
+#define R_VAL(p) (qRed(*p))
+#define G_VAL(p) (qGreen(*p))
+#define B_VAL(p) (qBlue(*p))
+
+#define INV_XAP (256 - xapoints[x])
+#define XAP (xapoints[x])
+#define INV_YAP (256 - yapoints[dyy + y])
+#define YAP (yapoints[dyy + y])
+
+unsigned int** QImageScale::qimageCalcYPoints(unsigned int *src,
+ int sw, int sh, int dh)
+{
+ unsigned int **p;
+ int i, j = 0;
+ int val, inc, rv = 0;
+
+ if(dh < 0){
+ dh = -dh;
+ rv = 1;
+ }
+ p = new unsigned int* [dh+1];
+
+ int up = qAbs(dh) >= sh;
+ val = up ? 0x8000 * sh / dh - 0x8000 : 0;
+ inc = (sh << 16) / dh;
+ for(i = 0; i < dh; i++){
+ p[j++] = src + qMax(0, val >> 16) * sw;
+ val += inc;
+ }
+ if(rv){
+ for(i = dh / 2; --i >= 0; ){
+ unsigned int *tmp = p[i];
+ p[i] = p[dh - i - 1];
+ p[dh - i - 1] = tmp;
+ }
+ }
+ return(p);
+}
+
+int* QImageScale::qimageCalcXPoints(int sw, int dw)
+{
+ int *p, i, j = 0;
+ int val, inc, rv = 0;
+
+ if(dw < 0){
+ dw = -dw;
+ rv = 1;
+ }
+ p = new int[dw+1];
+
+ int up = qAbs(dw) >= sw;
+ val = up ? 0x8000 * sw / dw - 0x8000 : 0;
+ inc = (sw << 16) / dw;
+ for(i = 0; i < dw; i++){
+ p[j++] = qMax(0, val >> 16);
+ val += inc;
+ }
+
+ if(rv){
+ for(i = dw / 2; --i >= 0; ){
+ int tmp = p[i];
+ p[i] = p[dw - i - 1];
+ p[dw - i - 1] = tmp;
+ }
+ }
+ return(p);
+}
+
+int* QImageScale::qimageCalcApoints(int s, int d, int up)
+{
+ int *p, i, j = 0, rv = 0;
+
+ if(d < 0){
+ rv = 1;
+ d = -d;
+ }
+ p = new int[d];
+
+ /* scaling up */
+ if(up){
+ int val, inc;
+
+ val = 0x8000 * s / d - 0x8000;
+ inc = (s << 16) / d;
+ for(i = 0; i < d; i++){
+ int pos = val >> 16;
+ if (pos < 0)
+ p[j++] = 0;
+ else if (pos >= (s - 1))
+ p[j++] = 0;
+ else
+ p[j++] = (val >> 8) - ((val >> 8) & 0xffffff00);
+ val += inc;
+ }
+ }
+ /* scaling down */
+ else{
+ int val, inc, ap, Cp;
+ val = 0;
+ inc = (s << 16) / d;
+ Cp = ((d << 14) / s) + 1;
+ for(i = 0; i < d; i++){
+ ap = ((0x100 - ((val >> 8) & 0xff)) * Cp) >> 8;
+ p[j] = ap | (Cp << 16);
+ j++;
+ val += inc;
+ }
+ }
+ if(rv){
+ int tmp;
+ for(i = d / 2; --i >= 0; ){
+ tmp = p[i];
+ p[i] = p[d - i - 1];
+ p[d - i - 1] = tmp;
+ }
+ }
+ return(p);
+}
+
+QImageScaleInfo* QImageScale::qimageFreeScaleInfo(QImageScaleInfo *isi)
+{
+ if(isi){
+ delete[] isi->xpoints;
+ delete[] isi->ypoints;
+ delete[] isi->xapoints;
+ delete[] isi->yapoints;
+ delete isi;
+ }
+ return 0;
+}
+
+QImageScaleInfo* QImageScale::qimageCalcScaleInfo(const QImage &img,
+ int sw, int sh,
+ int dw, int dh, char aa)
+{
+ QImageScaleInfo *isi;
+ int scw, sch;
+
+ scw = dw * qlonglong(img.width()) / sw;
+ sch = dh * qlonglong(img.height()) / sh;
+
+ isi = new QImageScaleInfo;
+ if(!isi)
+ return 0;
+ memset(isi, 0, sizeof(QImageScaleInfo));
+
+ isi->xup_yup = (qAbs(dw) >= sw) + ((qAbs(dh) >= sh) << 1);
+
+ isi->xpoints = qimageCalcXPoints(img.width(), scw);
+ if(!isi->xpoints)
+ return(qimageFreeScaleInfo(isi));
+ isi->ypoints = qimageCalcYPoints((unsigned int *)img.scanLine(0),
+ img.bytesPerLine() / 4, img.height(), sch);
+ if (!isi->ypoints)
+ return(qimageFreeScaleInfo(isi));
+ if(aa) {
+ isi->xapoints = qimageCalcApoints(img.width(), scw, isi->xup_yup & 1);
+ if(!isi->xapoints)
+ return(qimageFreeScaleInfo(isi));
+ isi->yapoints = qimageCalcApoints(img.height(), sch, isi->xup_yup & 2);
+ if(!isi->yapoints)
+ return(qimageFreeScaleInfo(isi));
+ }
+ return(isi);
+}
+
+/* FIXME: NEED to optimize ScaleAARGBA - currently its "ok" but needs work*/
+
+/* scale by area sampling */
+static void qt_qimageScaleAARGBA(QImageScaleInfo *isi, unsigned int *dest,
+ int dxx, int dyy, int dx, int dy, int dw,
+ int dh, int dow, int sow)
+{
+ unsigned int *sptr, *dptr;
+ int x, y, end;
+ unsigned int **ypoints = isi->ypoints;
+ int *xpoints = isi->xpoints;
+ int *xapoints = isi->xapoints;
+ int *yapoints = isi->yapoints;
+
+ end = dxx + dw;
+ /* scaling up both ways */
+ if(isi->xup_yup == 3){
+ /* go through every scanline in the output buffer */
+ for(y = 0; y < dh; y++){
+ /* calculate the source line we'll scan from */
+ dptr = dest + dx + ((y + dy) * dow);
+ sptr = ypoints[dyy + y];
+ if(YAP > 0){
+ for(x = dxx; x < end; x++){
+ int r, g, b, a;
+ int rr, gg, bb, aa;
+ unsigned int *pix;
+
+ if(XAP > 0){
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL(pix) * INV_XAP;
+ g = G_VAL(pix) * INV_XAP;
+ b = B_VAL(pix) * INV_XAP;
+ a = A_VAL(pix) * INV_XAP;
+ pix++;
+ r += R_VAL(pix) * XAP;
+ g += G_VAL(pix) * XAP;
+ b += B_VAL(pix) * XAP;
+ a += A_VAL(pix) * XAP;
+ pix += sow;
+ rr = R_VAL(pix) * XAP;
+ gg = G_VAL(pix) * XAP;
+ bb = B_VAL(pix) * XAP;
+ aa = A_VAL(pix) * XAP;
+ pix--;
+ rr += R_VAL(pix) * INV_XAP;
+ gg += G_VAL(pix) * INV_XAP;
+ bb += B_VAL(pix) * INV_XAP;
+ aa += A_VAL(pix) * INV_XAP;
+ r = ((rr * YAP) + (r * INV_YAP)) >> 16;
+ g = ((gg * YAP) + (g * INV_YAP)) >> 16;
+ b = ((bb * YAP) + (b * INV_YAP)) >> 16;
+ a = ((aa * YAP) + (a * INV_YAP)) >> 16;
+ *dptr++ = qRgba(r, g, b, a);
+ }
+ else{
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL(pix) * INV_YAP;
+ g = G_VAL(pix) * INV_YAP;
+ b = B_VAL(pix) * INV_YAP;
+ a = A_VAL(pix) * INV_YAP;
+ pix += sow;
+ r += R_VAL(pix) * YAP;
+ g += G_VAL(pix) * YAP;
+ b += B_VAL(pix) * YAP;
+ a += A_VAL(pix) * YAP;
+ r >>= 8;
+ g >>= 8;
+ b >>= 8;
+ a >>= 8;
+ *dptr++ = qRgba(r, g, b, a);
+ }
+ }
+ }
+ else{
+ for(x = dxx; x < end; x++){
+ int r, g, b, a;
+ unsigned int *pix;
+
+ if(XAP > 0){
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL(pix) * INV_XAP;
+ g = G_VAL(pix) * INV_XAP;
+ b = B_VAL(pix) * INV_XAP;
+ a = A_VAL(pix) * INV_XAP;
+ pix++;
+ r += R_VAL(pix) * XAP;
+ g += G_VAL(pix) * XAP;
+ b += B_VAL(pix) * XAP;
+ a += A_VAL(pix) * XAP;
+ r >>= 8;
+ g >>= 8;
+ b >>= 8;
+ a >>= 8;
+ *dptr++ = qRgba(r, g, b, a);
+ }
+ else
+ *dptr++ = sptr[xpoints[x] ];
+ }
+ }
+ }
+ }
+ /* if we're scaling down vertically */
+ else if(isi->xup_yup == 1){
+ /*\ 'Correct' version, with math units prepared for MMXification \*/
+ int Cy, j;
+ unsigned int *pix;
+ int r, g, b, a, rr, gg, bb, aa;
+ int yap;
+
+ /* go through every scanline in the output buffer */
+ for(y = 0; y < dh; y++){
+ Cy = YAP >> 16;
+ yap = YAP & 0xffff;
+
+ dptr = dest + dx + ((y + dy) * dow);
+ for(x = dxx; x < end; x++){
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL(pix) * yap;
+ g = G_VAL(pix) * yap;
+ b = B_VAL(pix) * yap;
+ a = A_VAL(pix) * yap;
+ for(j = (1 << 14) - yap; j > Cy; j -= Cy){
+ pix += sow;
+ r += R_VAL(pix) * Cy;
+ g += G_VAL(pix) * Cy;
+ b += B_VAL(pix) * Cy;
+ a += A_VAL(pix) * Cy;
+ }
+ if(j > 0){
+ pix += sow;
+ r += R_VAL(pix) * j;
+ g += G_VAL(pix) * j;
+ b += B_VAL(pix) * j;
+ a += A_VAL(pix) * j;
+ }
+ if(XAP > 0){
+ pix = ypoints[dyy + y] + xpoints[x] + 1;
+ rr = R_VAL(pix) * yap;
+ gg = G_VAL(pix) * yap;
+ bb = B_VAL(pix) * yap;
+ aa = A_VAL(pix) * yap;
+ for(j = (1 << 14) - yap; j > Cy; j -= Cy){
+ pix += sow;
+ rr += R_VAL(pix) * Cy;
+ gg += G_VAL(pix) * Cy;
+ bb += B_VAL(pix) * Cy;
+ aa += A_VAL(pix) * Cy;
+ }
+ if(j > 0){
+ pix += sow;
+ rr += R_VAL(pix) * j;
+ gg += G_VAL(pix) * j;
+ bb += B_VAL(pix) * j;
+ aa += A_VAL(pix) * j;
+ }
+ r = r * INV_XAP;
+ g = g * INV_XAP;
+ b = b * INV_XAP;
+ a = a * INV_XAP;
+ r = (r + ((rr * XAP))) >> 12;
+ g = (g + ((gg * XAP))) >> 12;
+ b = (b + ((bb * XAP))) >> 12;
+ a = (a + ((aa * XAP))) >> 12;
+ }
+ else{
+ r >>= 4;
+ g >>= 4;
+ b >>= 4;
+ a >>= 4;
+ }
+ *dptr = qRgba(r >> 10, g >> 10, b >> 10, a >> 10);
+ dptr++;
+ }
+ }
+ }
+ /* if we're scaling down horizontally */
+ else if(isi->xup_yup == 2){
+ /*\ 'Correct' version, with math units prepared for MMXification \*/
+ int Cx, j;
+ unsigned int *pix;
+ int r, g, b, a, rr, gg, bb, aa;
+ int xap;
+
+ /* go through every scanline in the output buffer */
+ for(y = 0; y < dh; y++){
+ dptr = dest + dx + ((y + dy) * dow);
+ for(x = dxx; x < end; x++){
+ Cx = XAP >> 16;
+ xap = XAP & 0xffff;
+
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL(pix) * xap;
+ g = G_VAL(pix) * xap;
+ b = B_VAL(pix) * xap;
+ a = A_VAL(pix) * xap;
+ for(j = (1 << 14) - xap; j > Cx; j -= Cx){
+ pix++;
+ r += R_VAL(pix) * Cx;
+ g += G_VAL(pix) * Cx;
+ b += B_VAL(pix) * Cx;
+ a += A_VAL(pix) * Cx;
+ }
+ if(j > 0){
+ pix++;
+ r += R_VAL(pix) * j;
+ g += G_VAL(pix) * j;
+ b += B_VAL(pix) * j;
+ a += A_VAL(pix) * j;
+ }
+ if(YAP > 0){
+ pix = ypoints[dyy + y] + xpoints[x] + sow;
+ rr = R_VAL(pix) * xap;
+ gg = G_VAL(pix) * xap;
+ bb = B_VAL(pix) * xap;
+ aa = A_VAL(pix) * xap;
+ for(j = (1 << 14) - xap; j > Cx; j -= Cx){
+ pix++;
+ rr += R_VAL(pix) * Cx;
+ gg += G_VAL(pix) * Cx;
+ bb += B_VAL(pix) * Cx;
+ aa += A_VAL(pix) * Cx;
+ }
+ if(j > 0){
+ pix++;
+ rr += R_VAL(pix) * j;
+ gg += G_VAL(pix) * j;
+ bb += B_VAL(pix) * j;
+ aa += A_VAL(pix) * j;
+ }
+ r = r * INV_YAP;
+ g = g * INV_YAP;
+ b = b * INV_YAP;
+ a = a * INV_YAP;
+ r = (r + ((rr * YAP))) >> 12;
+ g = (g + ((gg * YAP))) >> 12;
+ b = (b + ((bb * YAP))) >> 12;
+ a = (a + ((aa * YAP))) >> 12;
+ }
+ else{
+ r >>= 4;
+ g >>= 4;
+ b >>= 4;
+ a >>= 4;
+ }
+ *dptr = qRgba(r >> 10, g >> 10, b >> 10, a >> 10);
+ dptr++;
+ }
+ }
+ }
+ /* if we're scaling down horizontally & vertically */
+ else{
+ /*\ 'Correct' version, with math units prepared for MMXification:
+ |*| The operation 'b = (b * c) >> 16' translates to pmulhw,
+ |*| so the operation 'b = (b * c) >> d' would translate to
+ |*| psllw (16 - d), %mmb; pmulh %mmc, %mmb
+ \*/
+ int Cx, Cy, i, j;
+ unsigned int *pix;
+ int a, r, g, b, ax, rx, gx, bx;
+ int xap, yap;
+
+ for(y = 0; y < dh; y++){
+ Cy = YAP >> 16;
+ yap = YAP & 0xffff;
+
+ dptr = dest + dx + ((y + dy) * dow);
+ for(x = dxx; x < end; x++){
+ Cx = XAP >> 16;
+ xap = XAP & 0xffff;
+
+ sptr = ypoints[dyy + y] + xpoints[x];
+ pix = sptr;
+ sptr += sow;
+ rx = R_VAL(pix) * xap;
+ gx = G_VAL(pix) * xap;
+ bx = B_VAL(pix) * xap;
+ ax = A_VAL(pix) * xap;
+
+ pix++;
+ for(i = (1 << 14) - xap; i > Cx; i -= Cx){
+ rx += R_VAL(pix) * Cx;
+ gx += G_VAL(pix) * Cx;
+ bx += B_VAL(pix) * Cx;
+ ax += A_VAL(pix) * Cx;
+ pix++;
+ }
+ if(i > 0){
+ rx += R_VAL(pix) * i;
+ gx += G_VAL(pix) * i;
+ bx += B_VAL(pix) * i;
+ ax += A_VAL(pix) * i;
+ }
+
+ r = (rx >> 5) * yap;
+ g = (gx >> 5) * yap;
+ b = (bx >> 5) * yap;
+ a = (ax >> 5) * yap;
+
+ for(j = (1 << 14) - yap; j > Cy; j -= Cy){
+ pix = sptr;
+ sptr += sow;
+ rx = R_VAL(pix) * xap;
+ gx = G_VAL(pix) * xap;
+ bx = B_VAL(pix) * xap;
+ ax = A_VAL(pix) * xap;
+ pix++;
+ for(i = (1 << 14) - xap; i > Cx; i -= Cx){
+ rx += R_VAL(pix) * Cx;
+ gx += G_VAL(pix) * Cx;
+ bx += B_VAL(pix) * Cx;
+ ax += A_VAL(pix) * Cx;
+ pix++;
+ }
+ if(i > 0){
+ rx += R_VAL(pix) * i;
+ gx += G_VAL(pix) * i;
+ bx += B_VAL(pix) * i;
+ ax += A_VAL(pix) * i;
+ }
+
+ r += (rx >> 5) * Cy;
+ g += (gx >> 5) * Cy;
+ b += (bx >> 5) * Cy;
+ a += (ax >> 5) * Cy;
+ }
+ if(j > 0){
+ pix = sptr;
+ sptr += sow;
+ rx = R_VAL(pix) * xap;
+ gx = G_VAL(pix) * xap;
+ bx = B_VAL(pix) * xap;
+ ax = A_VAL(pix) * xap;
+ pix++;
+ for(i = (1 << 14) - xap; i > Cx; i -= Cx){
+ rx += R_VAL(pix) * Cx;
+ gx += G_VAL(pix) * Cx;
+ bx += B_VAL(pix) * Cx;
+ ax += A_VAL(pix) * Cx;
+ pix++;
+ }
+ if(i > 0){
+ rx += R_VAL(pix) * i;
+ gx += G_VAL(pix) * i;
+ bx += B_VAL(pix) * i;
+ ax += A_VAL(pix) * i;
+ }
+
+ r += (rx >> 5) * j;
+ g += (gx >> 5) * j;
+ b += (bx >> 5) * j;
+ a += (ax >> 5) * j;
+ }
+
+ *dptr = qRgba(r >> 23, g >> 23, b >> 23, a >> 23);
+ dptr++;
+ }
+ }
+ }
+}
+
+/* scale by area sampling - IGNORE the ALPHA byte*/
+static void qt_qimageScaleAARGB(QImageScaleInfo *isi, unsigned int *dest,
+ int dxx, int dyy, int dx, int dy, int dw,
+ int dh, int dow, int sow)
+{
+ unsigned int *sptr, *dptr;
+ int x, y, end;
+ unsigned int **ypoints = isi->ypoints;
+ int *xpoints = isi->xpoints;
+ int *xapoints = isi->xapoints;
+ int *yapoints = isi->yapoints;
+
+ end = dxx + dw;
+ /* scaling up both ways */
+ if(isi->xup_yup == 3){
+ /* go through every scanline in the output buffer */
+ for(y = 0; y < dh; y++){
+ /* calculate the source line we'll scan from */
+ dptr = dest + dx + ((y + dy) * dow);
+ sptr = ypoints[dyy + y];
+ if(YAP > 0){
+ for(x = dxx; x < end; x++){
+ int r = 0, g = 0, b = 0;
+ int rr = 0, gg = 0, bb = 0;
+ unsigned int *pix;
+
+ if(XAP > 0){
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL(pix) * INV_XAP;
+ g = G_VAL(pix) * INV_XAP;
+ b = B_VAL(pix) * INV_XAP;
+ pix++;
+ r += R_VAL(pix) * XAP;
+ g += G_VAL(pix) * XAP;
+ b += B_VAL(pix) * XAP;
+ pix += sow;
+ rr = R_VAL(pix) * XAP;
+ gg = G_VAL(pix) * XAP;
+ bb = B_VAL(pix) * XAP;
+ pix --;
+ rr += R_VAL(pix) * INV_XAP;
+ gg += G_VAL(pix) * INV_XAP;
+ bb += B_VAL(pix) * INV_XAP;
+ r = ((rr * YAP) + (r * INV_YAP)) >> 16;
+ g = ((gg * YAP) + (g * INV_YAP)) >> 16;
+ b = ((bb * YAP) + (b * INV_YAP)) >> 16;
+ *dptr++ = qRgba(r, g, b, 0xff);
+ }
+ else{
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL(pix) * INV_YAP;
+ g = G_VAL(pix) * INV_YAP;
+ b = B_VAL(pix) * INV_YAP;
+ pix += sow;
+ r += R_VAL(pix) * YAP;
+ g += G_VAL(pix) * YAP;
+ b += B_VAL(pix) * YAP;
+ r >>= 8;
+ g >>= 8;
+ b >>= 8;
+ *dptr++ = qRgba(r, g, b, 0xff);
+ }
+ }
+ }
+ else{
+ for(x = dxx; x < end; x++){
+ int r = 0, g = 0, b = 0;
+ unsigned int *pix;
+
+ if(XAP > 0){
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL(pix) * INV_XAP;
+ g = G_VAL(pix) * INV_XAP;
+ b = B_VAL(pix) * INV_XAP;
+ pix++;
+ r += R_VAL(pix) * XAP;
+ g += G_VAL(pix) * XAP;
+ b += B_VAL(pix) * XAP;
+ r >>= 8;
+ g >>= 8;
+ b >>= 8;
+ *dptr++ = qRgba(r, g, b, 0xff);
+ }
+ else
+ *dptr++ = sptr[xpoints[x] ];
+ }
+ }
+ }
+ }
+ /* if we're scaling down vertically */
+ else if(isi->xup_yup == 1){
+ /*\ 'Correct' version, with math units prepared for MMXification \*/
+ int Cy, j;
+ unsigned int *pix;
+ int r, g, b, rr, gg, bb;
+ int yap;
+
+ /* go through every scanline in the output buffer */
+ for(y = 0; y < dh; y++){
+ Cy = YAP >> 16;
+ yap = YAP & 0xffff;
+
+ dptr = dest + dx + ((y + dy) * dow);
+ for(x = dxx; x < end; x++){
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL(pix) * yap;
+ g = G_VAL(pix) * yap;
+ b = B_VAL(pix) * yap;
+ pix += sow;
+ for(j = (1 << 14) - yap; j > Cy; j -= Cy){
+ r += R_VAL(pix) * Cy;
+ g += G_VAL(pix) * Cy;
+ b += B_VAL(pix) * Cy;
+ pix += sow;
+ }
+ if(j > 0){
+ r += R_VAL(pix) * j;
+ g += G_VAL(pix) * j;
+ b += B_VAL(pix) * j;
+ }
+ if(XAP > 0){
+ pix = ypoints[dyy + y] + xpoints[x] + 1;
+ rr = R_VAL(pix) * yap;
+ gg = G_VAL(pix) * yap;
+ bb = B_VAL(pix) * yap;
+ pix += sow;
+ for(j = (1 << 14) - yap; j > Cy; j -= Cy){
+ rr += R_VAL(pix) * Cy;
+ gg += G_VAL(pix) * Cy;
+ bb += B_VAL(pix) * Cy;
+ pix += sow;
+ }
+ if(j > 0){
+ rr += R_VAL(pix) * j;
+ gg += G_VAL(pix) * j;
+ bb += B_VAL(pix) * j;
+ }
+ r = r * INV_XAP;
+ g = g * INV_XAP;
+ b = b * INV_XAP;
+ r = (r + ((rr * XAP))) >> 12;
+ g = (g + ((gg * XAP))) >> 12;
+ b = (b + ((bb * XAP))) >> 12;
+ }
+ else{
+ r >>= 4;
+ g >>= 4;
+ b >>= 4;
+ }
+ *dptr = qRgba(r >> 10, g >> 10, b >> 10, 0xff);
+ dptr++;
+ }
+ }
+ }
+ /* if we're scaling down horizontally */
+ else if(isi->xup_yup == 2){
+ /*\ 'Correct' version, with math units prepared for MMXification \*/
+ int Cx, j;
+ unsigned int *pix;
+ int r, g, b, rr, gg, bb;
+ int xap;
+
+ /* go through every scanline in the output buffer */
+ for(y = 0; y < dh; y++){
+ dptr = dest + dx + ((y + dy) * dow);
+ for(x = dxx; x < end; x++){
+ Cx = XAP >> 16;
+ xap = XAP & 0xffff;
+
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL(pix) * xap;
+ g = G_VAL(pix) * xap;
+ b = B_VAL(pix) * xap;
+ pix++;
+ for(j = (1 << 14) - xap; j > Cx; j -= Cx){
+ r += R_VAL(pix) * Cx;
+ g += G_VAL(pix) * Cx;
+ b += B_VAL(pix) * Cx;
+ pix++;
+ }
+ if(j > 0){
+ r += R_VAL(pix) * j;
+ g += G_VAL(pix) * j;
+ b += B_VAL(pix) * j;
+ }
+ if(YAP > 0){
+ pix = ypoints[dyy + y] + xpoints[x] + sow;
+ rr = R_VAL(pix) * xap;
+ gg = G_VAL(pix) * xap;
+ bb = B_VAL(pix) * xap;
+ pix++;
+ for(j = (1 << 14) - xap; j > Cx; j -= Cx){
+ rr += R_VAL(pix) * Cx;
+ gg += G_VAL(pix) * Cx;
+ bb += B_VAL(pix) * Cx;
+ pix++;
+ }
+ if(j > 0){
+ rr += R_VAL(pix) * j;
+ gg += G_VAL(pix) * j;
+ bb += B_VAL(pix) * j;
+ }
+ r = r * INV_YAP;
+ g = g * INV_YAP;
+ b = b * INV_YAP;
+ r = (r + ((rr * YAP))) >> 12;
+ g = (g + ((gg * YAP))) >> 12;
+ b = (b + ((bb * YAP))) >> 12;
+ }
+ else{
+ r >>= 4;
+ g >>= 4;
+ b >>= 4;
+ }
+ *dptr = qRgba(r >> 10, g >> 10, b >> 10, 0xff);
+ dptr++;
+ }
+ }
+ }
+ /* fully optimized (i think) - onyl change of algorithm can help */
+ /* if we're scaling down horizontally & vertically */
+ else{
+ /*\ 'Correct' version, with math units prepared for MMXification \*/
+ int Cx, Cy, i, j;
+ unsigned int *pix;
+ int r, g, b, rx, gx, bx;
+ int xap, yap;
+
+ for(y = 0; y < dh; y++){
+ Cy = YAP >> 16;
+ yap = YAP & 0xffff;
+
+ dptr = dest + dx + ((y + dy) * dow);
+ for(x = dxx; x < end; x++){
+ Cx = XAP >> 16;
+ xap = XAP & 0xffff;
+
+ sptr = ypoints[dyy + y] + xpoints[x];
+ pix = sptr;
+ sptr += sow;
+ rx = R_VAL(pix) * xap;
+ gx = G_VAL(pix) * xap;
+ bx = B_VAL(pix) * xap;
+ pix++;
+ for(i = (1 << 14) - xap; i > Cx; i -= Cx){
+ rx += R_VAL(pix) * Cx;
+ gx += G_VAL(pix) * Cx;
+ bx += B_VAL(pix) * Cx;
+ pix++;
+ }
+ if(i > 0){
+ rx += R_VAL(pix) * i;
+ gx += G_VAL(pix) * i;
+ bx += B_VAL(pix) * i;
+ }
+
+ r = (rx >> 5) * yap;
+ g = (gx >> 5) * yap;
+ b = (bx >> 5) * yap;
+
+ for(j = (1 << 14) - yap; j > Cy; j -= Cy){
+ pix = sptr;
+ sptr += sow;
+ rx = R_VAL(pix) * xap;
+ gx = G_VAL(pix) * xap;
+ bx = B_VAL(pix) * xap;
+ pix++;
+ for(i = (1 << 14) - xap; i > Cx; i -= Cx){
+ rx += R_VAL(pix) * Cx;
+ gx += G_VAL(pix) * Cx;
+ bx += B_VAL(pix) * Cx;
+ pix++;
+ }
+ if(i > 0){
+ rx += R_VAL(pix) * i;
+ gx += G_VAL(pix) * i;
+ bx += B_VAL(pix) * i;
+ }
+
+ r += (rx >> 5) * Cy;
+ g += (gx >> 5) * Cy;
+ b += (bx >> 5) * Cy;
+ }
+ if(j > 0){
+ pix = sptr;
+ sptr += sow;
+ rx = R_VAL(pix) * xap;
+ gx = G_VAL(pix) * xap;
+ bx = B_VAL(pix) * xap;
+ pix++;
+ for(i = (1 << 14) - xap; i > Cx; i -= Cx){
+ rx += R_VAL(pix) * Cx;
+ gx += G_VAL(pix) * Cx;
+ bx += B_VAL(pix) * Cx;
+ pix++;
+ }
+ if(i > 0){
+ rx += R_VAL(pix) * i;
+ gx += G_VAL(pix) * i;
+ bx += B_VAL(pix) * i;
+ }
+
+ r += (rx >> 5) * j;
+ g += (gx >> 5) * j;
+ b += (bx >> 5) * j;
+ }
+
+ *dptr = qRgb(r >> 23, g >> 23, b >> 23);
+ dptr++;
+ }
+ }
+ }
+}
+
+#if 0
+static void qt_qimageScaleAARGBASetup(QImageScaleInfo *isi, unsigned int *dest,
+ int dxx, int dyy, int dx, int dy, int dw,
+ int dh, int dow, int sow)
+{
+ qInitDrawhelperAsm();
+ qt_qimageScaleAARGBA(isi, dest, dxx, dyy, dx, dy, dw, dh, dow, sow);
+}
+
+static void qt_qimageScaleAARGBSetup(QImageScaleInfo *isi, unsigned int *dest,
+ int dxx, int dyy, int dx, int dy, int dw,
+ int dh, int dow, int sow)
+{
+ qInitDrawhelperAsm();
+ qt_qimageScaleAARGB(isi, dest, dxx, dyy, dx, dy, dw, dh, dow, sow);
+}
+#endif
+
+QImage qSmoothScaleImage(const QImage &src, int dw, int dh)
+{
+ QImage buffer;
+ if (src.isNull() || dw <= 0 || dh <= 0)
+ return buffer;
+
+ int w = src.width();
+ int h = src.height();
+ QImageScaleInfo *scaleinfo =
+ qimageCalcScaleInfo(src, w, h, dw, dh, true);
+ if (!scaleinfo)
+ return buffer;
+
+ buffer = QImage(dw, dh, src.format());
+ if (buffer.isNull()) {
+ qWarning("QImage: out of memory, returning null");
+ qimageFreeScaleInfo(scaleinfo);
+ return QImage();
+ }
+
+ if (src.format() == QImage::Format_ARGB32_Premultiplied)
+ qt_qimageScaleArgb(scaleinfo, (unsigned int *)buffer.scanLine(0),
+ 0, 0, 0, 0, dw, dh, dw, src.bytesPerLine() / 4);
+ else
+ qt_qimageScaleRgb(scaleinfo, (unsigned int *)buffer.scanLine(0),
+ 0, 0, 0, 0, dw, dh, dw, src.bytesPerLine() / 4);
+
+ qimageFreeScaleInfo(scaleinfo);
+ return buffer;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qimagescale_p.h b/src/gui/painting/qimagescale_p.h
new file mode 100644
index 0000000000..fbf162addf
--- /dev/null
+++ b/src/gui/painting/qimagescale_p.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QIMAGESCALE_P_H
+#define QIMAGESCALE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qimage.h>
+
+QT_BEGIN_NAMESPACE
+
+/*
+ This version accepts only supported formats.
+*/
+QImage qSmoothScaleImage(const QImage &img, int w, int h);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/painting/qmath_p.h b/src/gui/painting/qmath_p.h
new file mode 100644
index 0000000000..63d23e0dcb
--- /dev/null
+++ b/src/gui/painting/qmath_p.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMATH_P_H
+#define QMATH_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <math.h>
+#include <qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+static const qreal Q_PI = qreal(3.14159265358979323846); // pi
+static const qreal Q_2PI = qreal(6.28318530717958647693); // 2*pi
+static const qreal Q_PI2 = qreal(1.57079632679489661923); // pi/2
+
+inline int qIntSqrtInt(int v)
+{
+ return static_cast<int>(qSqrt(static_cast<qreal>(v)));
+}
+
+QT_END_NAMESPACE
+
+#endif // QMATH_P_H
diff --git a/src/gui/painting/qmatrix.cpp b/src/gui/painting/qmatrix.cpp
new file mode 100644
index 0000000000..38f78e1a46
--- /dev/null
+++ b/src/gui/painting/qmatrix.cpp
@@ -0,0 +1,1229 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdatastream.h"
+#include "qdebug.h"
+#include "qmatrix.h"
+#include "qregion.h"
+#include "qpainterpath.h"
+#include "qvariant.h"
+#include <qmath.h>
+
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QMatrix
+ \brief The QMatrix class specifies 2D transformations of a
+ coordinate system.
+ \obsolete
+
+ \ingroup painting
+
+ A matrix specifies how to translate, scale, shear or rotate the
+ coordinate system, and is typically used when rendering graphics.
+ QMatrix, in contrast to QTransform, does not allow perspective
+ transformations. QTransform is the recommended transformation
+ class in Qt.
+
+ A QMatrix object can be built using the setMatrix(), scale(),
+ rotate(), translate() and shear() functions. Alternatively, it
+ can be built by applying \l {QMatrix#Basic Matrix
+ Operations}{basic matrix operations}. The matrix can also be
+ defined when constructed, and it can be reset to the identity
+ matrix (the default) using the reset() function.
+
+ The QMatrix class supports mapping of graphic primitives: A given
+ point, line, polygon, region, or painter path can be mapped to the
+ coordinate system defined by \e this matrix using the map()
+ function. In case of a rectangle, its coordinates can be
+ transformed using the mapRect() function. A rectangle can also be
+ transformed into a \e polygon (mapped to the coordinate system
+ defined by \e this matrix), using the mapToPolygon() function.
+
+ QMatrix provides the isIdentity() function which returns true if
+ the matrix is the identity matrix, and the isInvertible() function
+ which returns true if the matrix is non-singular (i.e. AB = BA =
+ I). The inverted() function returns an inverted copy of \e this
+ matrix if it is invertible (otherwise it returns the identity
+ matrix). In addition, QMatrix provides the determinant() function
+ returning the matrix's determinant.
+
+ Finally, the QMatrix class supports matrix multiplication, and
+ objects of the class can be streamed as well as compared.
+
+ \tableofcontents
+
+ \section1 Rendering Graphics
+
+ When rendering graphics, the matrix defines the transformations
+ but the actual transformation is performed by the drawing routines
+ in QPainter.
+
+ By default, QPainter operates on the associated device's own
+ coordinate system. The standard coordinate system of a
+ QPaintDevice has its origin located at the top-left position. The
+ \e x values increase to the right; \e y values increase
+ downward. For a complete description, see the \l {Coordinate
+ System}{coordinate system} documentation.
+
+ QPainter has functions to translate, scale, shear and rotate the
+ coordinate system without using a QMatrix. For example:
+
+ \table 100%
+ \row
+ \o \inlineimage qmatrix-simpletransformation.png
+ \o
+ \snippet doc/src/snippets/matrix/matrix.cpp 0
+ \endtable
+
+ Although these functions are very convenient, it can be more
+ efficient to build a QMatrix and call QPainter::setMatrix() if you
+ want to perform more than a single transform operation. For
+ example:
+
+ \table 100%
+ \row
+ \o \inlineimage qmatrix-combinedtransformation.png
+ \o
+ \snippet doc/src/snippets/matrix/matrix.cpp 1
+ \endtable
+
+ \section1 Basic Matrix Operations
+
+ \image qmatrix-representation.png
+
+ A QMatrix object contains a 3 x 3 matrix. The \c dx and \c dy
+ elements specify horizontal and vertical translation. The \c m11
+ and \c m22 elements specify horizontal and vertical scaling. And
+ finally, the \c m21 and \c m12 elements specify horizontal and
+ vertical \e shearing.
+
+ QMatrix transforms a point in the plane to another point using the
+ following formulas:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qmatrix.cpp 0
+
+ The point \e (x, y) is the original point, and \e (x', y') is the
+ transformed point. \e (x', y') can be transformed back to \e (x,
+ y) by performing the same operation on the inverted() matrix.
+
+ The various matrix elements can be set when constructing the
+ matrix, or by using the setMatrix() function later on. They can also
+ be manipulated using the translate(), rotate(), scale() and
+ shear() convenience functions, The currently set values can be
+ retrieved using the m11(), m12(), m21(), m22(), dx() and dy()
+ functions.
+
+ Translation is the simplest transformation. Setting \c dx and \c
+ dy will move the coordinate system \c dx units along the X axis
+ and \c dy units along the Y axis. Scaling can be done by setting
+ \c m11 and \c m22. For example, setting \c m11 to 2 and \c m22 to
+ 1.5 will double the height and increase the width by 50%. The
+ identity matrix has \c m11 and \c m22 set to 1 (all others are set
+ to 0) mapping a point to itself. Shearing is controlled by \c m12
+ and \c m21. Setting these elements to values different from zero
+ will twist the coordinate system. Rotation is achieved by
+ carefully setting both the shearing factors and the scaling
+ factors.
+
+ Here's the combined transformations example using basic matrix
+ operations:
+
+ \table 100%
+ \row
+ \o \inlineimage qmatrix-combinedtransformation.png
+ \o
+ \snippet doc/src/snippets/matrix/matrix.cpp 2
+ \endtable
+
+ \sa QPainter, QTransform, {Coordinate System},
+ {demos/affine}{Affine Transformations Demo}, {Transformations Example}
+*/
+
+
+// some defines to inline some code
+#define MAPDOUBLE(x, y, nx, ny) \
+{ \
+ qreal fx = x; \
+ qreal fy = y; \
+ nx = _m11*fx + _m21*fy + _dx; \
+ ny = _m12*fx + _m22*fy + _dy; \
+}
+
+#define MAPINT(x, y, nx, ny) \
+{ \
+ qreal fx = x; \
+ qreal fy = y; \
+ nx = qRound(_m11*fx + _m21*fy + _dx); \
+ ny = qRound(_m12*fx + _m22*fy + _dy); \
+}
+
+/*****************************************************************************
+ QMatrix member functions
+ *****************************************************************************/
+/*!
+ \fn QMatrix::QMatrix(Qt::Initialization)
+ \internal
+*/
+
+/*!
+ Constructs an identity matrix.
+
+ All elements are set to zero except \c m11 and \c m22 (specifying
+ the scale), which are set to 1.
+
+ \sa reset()
+*/
+
+QMatrix::QMatrix()
+ : _m11(1.)
+ , _m12(0.)
+ , _m21(0.)
+ , _m22(1.)
+ , _dx(0.)
+ , _dy(0.)
+{
+}
+
+/*!
+ Constructs a matrix with the elements, \a m11, \a m12, \a m21, \a
+ m22, \a dx and \a dy.
+
+ \sa setMatrix()
+*/
+
+QMatrix::QMatrix(qreal m11, qreal m12, qreal m21, qreal m22, qreal dx, qreal dy)
+ : _m11(m11)
+ , _m12(m12)
+ , _m21(m21)
+ , _m22(m22)
+ , _dx(dx)
+ , _dy(dy)
+{
+}
+
+
+/*!
+ Constructs a matrix that is a copy of the given \a matrix.
+ */
+QMatrix::QMatrix(const QMatrix &matrix)
+ : _m11(matrix._m11)
+ , _m12(matrix._m12)
+ , _m21(matrix._m21)
+ , _m22(matrix._m22)
+ , _dx(matrix._dx)
+ , _dy(matrix._dy)
+{
+}
+
+/*!
+ Sets the matrix elements to the specified values, \a m11, \a m12,
+ \a m21, \a m22, \a dx and \a dy.
+
+ Note that this function replaces the previous values. QMatrix
+ provide the translate(), rotate(), scale() and shear() convenience
+ functions to manipulate the various matrix elements based on the
+ currently defined coordinate system.
+
+ \sa QMatrix()
+*/
+
+void QMatrix::setMatrix(qreal m11, qreal m12, qreal m21, qreal m22, qreal dx, qreal dy)
+{
+ _m11 = m11;
+ _m12 = m12;
+ _m21 = m21;
+ _m22 = m22;
+ _dx = dx;
+ _dy = dy;
+}
+
+
+/*!
+ \fn qreal QMatrix::m11() const
+
+ Returns the horizontal scaling factor.
+
+ \sa scale(), {QMatrix#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+
+/*!
+ \fn qreal QMatrix::m12() const
+
+ Returns the vertical shearing factor.
+
+ \sa shear(), {QMatrix#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+
+/*!
+ \fn qreal QMatrix::m21() const
+
+ Returns the horizontal shearing factor.
+
+ \sa shear(), {QMatrix#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+
+/*!
+ \fn qreal QMatrix::m22() const
+
+ Returns the vertical scaling factor.
+
+ \sa scale(), {QMatrix#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+
+/*!
+ \fn qreal QMatrix::dx() const
+
+ Returns the horizontal translation factor.
+
+ \sa translate(), {QMatrix#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+
+/*!
+ \fn qreal QMatrix::dy() const
+
+ Returns the vertical translation factor.
+
+ \sa translate(), {QMatrix#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+
+
+/*!
+ Maps the given coordinates \a x and \a y into the coordinate
+ system defined by this matrix. The resulting values are put in *\a
+ tx and *\a ty, respectively.
+
+ The coordinates are transformed using the following formulas:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qmatrix.cpp 1
+
+ The point (x, y) is the original point, and (x', y') is the
+ transformed point.
+
+ \sa {QMatrix#Basic Matrix Operations}{Basic Matrix Operations}
+*/
+
+void QMatrix::map(qreal x, qreal y, qreal *tx, qreal *ty) const
+{
+ MAPDOUBLE(x, y, *tx, *ty);
+}
+
+
+
+/*!
+ \overload
+
+ Maps the given coordinates \a x and \a y into the coordinate
+ system defined by this matrix. The resulting values are put in *\a
+ tx and *\a ty, respectively. Note that the transformed coordinates
+ are rounded to the nearest integer.
+*/
+
+void QMatrix::map(int x, int y, int *tx, int *ty) const
+{
+ MAPINT(x, y, *tx, *ty);
+}
+
+QRect QMatrix::mapRect(const QRect &rect) const
+{
+ QRect result;
+ if (_m12 == 0.0F && _m21 == 0.0F) {
+ int x = qRound(_m11*rect.x() + _dx);
+ int y = qRound(_m22*rect.y() + _dy);
+ int w = qRound(_m11*rect.width());
+ int h = qRound(_m22*rect.height());
+ if (w < 0) {
+ w = -w;
+ x -= w;
+ }
+ if (h < 0) {
+ h = -h;
+ y -= h;
+ }
+ result = QRect(x, y, w, h);
+ } else {
+ // see mapToPolygon for explanations of the algorithm.
+ qreal x0, y0;
+ qreal x, y;
+ MAPDOUBLE(rect.left(), rect.top(), x0, y0);
+ qreal xmin = x0;
+ qreal ymin = y0;
+ qreal xmax = x0;
+ qreal ymax = y0;
+ MAPDOUBLE(rect.right() + 1, rect.top(), x, y);
+ xmin = qMin(xmin, x);
+ ymin = qMin(ymin, y);
+ xmax = qMax(xmax, x);
+ ymax = qMax(ymax, y);
+ MAPDOUBLE(rect.right() + 1, rect.bottom() + 1, x, y);
+ xmin = qMin(xmin, x);
+ ymin = qMin(ymin, y);
+ xmax = qMax(xmax, x);
+ ymax = qMax(ymax, y);
+ MAPDOUBLE(rect.left(), rect.bottom() + 1, x, y);
+ xmin = qMin(xmin, x);
+ ymin = qMin(ymin, y);
+ xmax = qMax(xmax, x);
+ ymax = qMax(ymax, y);
+ result = QRect(qRound(xmin), qRound(ymin), qRound(xmax)-qRound(xmin), qRound(ymax)-qRound(ymin));
+ }
+ return result;
+}
+
+/*!
+ \fn QRectF QMatrix::mapRect(const QRectF &rectangle) const
+
+ Creates and returns a QRectF object that is a copy of the given \a
+ rectangle, mapped into the coordinate system defined by this
+ matrix.
+
+ The rectangle's coordinates are transformed using the following
+ formulas:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qmatrix.cpp 2
+
+ If rotation or shearing has been specified, this function returns
+ the \e bounding rectangle. To retrieve the exact region the given
+ \a rectangle maps to, use the mapToPolygon() function instead.
+
+ \sa mapToPolygon(), {QMatrix#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+QRectF QMatrix::mapRect(const QRectF &rect) const
+{
+ QRectF result;
+ if (_m12 == 0.0F && _m21 == 0.0F) {
+ qreal x = _m11*rect.x() + _dx;
+ qreal y = _m22*rect.y() + _dy;
+ qreal w = _m11*rect.width();
+ qreal h = _m22*rect.height();
+ if (w < 0) {
+ w = -w;
+ x -= w;
+ }
+ if (h < 0) {
+ h = -h;
+ y -= h;
+ }
+ result = QRectF(x, y, w, h);
+ } else {
+ qreal x0, y0;
+ qreal x, y;
+ MAPDOUBLE(rect.x(), rect.y(), x0, y0);
+ qreal xmin = x0;
+ qreal ymin = y0;
+ qreal xmax = x0;
+ qreal ymax = y0;
+ MAPDOUBLE(rect.x() + rect.width(), rect.y(), x, y);
+ xmin = qMin(xmin, x);
+ ymin = qMin(ymin, y);
+ xmax = qMax(xmax, x);
+ ymax = qMax(ymax, y);
+ MAPDOUBLE(rect.x() + rect.width(), rect.y() + rect.height(), x, y);
+ xmin = qMin(xmin, x);
+ ymin = qMin(ymin, y);
+ xmax = qMax(xmax, x);
+ ymax = qMax(ymax, y);
+ MAPDOUBLE(rect.x(), rect.y() + rect.height(), x, y);
+ xmin = qMin(xmin, x);
+ ymin = qMin(ymin, y);
+ xmax = qMax(xmax, x);
+ ymax = qMax(ymax, y);
+ result = QRectF(xmin, ymin, xmax-xmin, ymax - ymin);
+ }
+ return result;
+}
+
+/*!
+ \fn QRect QMatrix::mapRect(const QRect &rectangle) const
+ \overload
+
+ Creates and returns a QRect object that is a copy of the given \a
+ rectangle, mapped into the coordinate system defined by this
+ matrix. Note that the transformed coordinates are rounded to the
+ nearest integer.
+*/
+
+
+/*!
+ \fn QPoint operator*(const QPoint &point, const QMatrix &matrix)
+ \relates QMatrix
+
+ This is the same as \a{matrix}.map(\a{point}).
+
+ \sa QMatrix::map()
+*/
+
+QPoint QMatrix::map(const QPoint &p) const
+{
+ qreal fx = p.x();
+ qreal fy = p.y();
+ return QPoint(qRound(_m11*fx + _m21*fy + _dx),
+ qRound(_m12*fx + _m22*fy + _dy));
+}
+
+/*!
+ \fn QPointF operator*(const QPointF &point, const QMatrix &matrix)
+ \relates QMatrix
+
+ Same as \a{matrix}.map(\a{point}).
+
+ \sa QMatrix::map()
+*/
+
+/*!
+ \overload
+
+ Creates and returns a QPointF object that is a copy of the given
+ \a point, mapped into the coordinate system defined by this
+ matrix.
+*/
+QPointF QMatrix::map(const QPointF &point) const
+{
+ qreal fx = point.x();
+ qreal fy = point.y();
+ return QPointF(_m11*fx + _m21*fy + _dx, _m12*fx + _m22*fy + _dy);
+}
+
+/*!
+ \fn QPoint QMatrix::map(const QPoint &point) const
+ \overload
+
+ Creates and returns a QPoint object that is a copy of the given \a
+ point, mapped into the coordinate system defined by this
+ matrix. Note that the transformed coordinates are rounded to the
+ nearest integer.
+*/
+
+/*!
+ \fn QLineF operator*(const QLineF &line, const QMatrix &matrix)
+ \relates QMatrix
+
+ This is the same as \a{matrix}.map(\a{line}).
+
+ \sa QMatrix::map()
+*/
+
+/*!
+ \fn QLine operator*(const QLine &line, const QMatrix &matrix)
+ \relates QMatrix
+
+ This is the same as \a{matrix}.map(\a{line}).
+
+ \sa QMatrix::map()
+*/
+
+/*!
+ \overload
+
+ Creates and returns a QLineF object that is a copy of the given \a
+ line, mapped into the coordinate system defined by this matrix.
+*/
+QLineF QMatrix::map(const QLineF &line) const
+{
+ return QLineF(map(line.p1()), map(line.p2()));
+}
+
+/*!
+ \overload
+
+ Creates and returns a QLine object that is a copy of the given \a
+ line, mapped into the coordinate system defined by this matrix.
+ Note that the transformed coordinates are rounded to the nearest
+ integer.
+*/
+QLine QMatrix::map(const QLine &line) const
+{
+ return QLine(map(line.p1()), map(line.p2()));
+}
+
+/*!
+ \fn QPolygonF operator *(const QPolygonF &polygon, const QMatrix &matrix)
+ \relates QMatrix
+
+ This is the same as \a{matrix}.map(\a{polygon}).
+
+ \sa QMatrix::map()
+*/
+
+/*!
+ \fn QPolygon operator*(const QPolygon &polygon, const QMatrix &matrix)
+ \relates QMatrix
+
+ This is the same as \a{matrix}.map(\a{polygon}).
+
+ \sa QMatrix::map()
+*/
+
+QPolygon QMatrix::map(const QPolygon &a) const
+{
+ int size = a.size();
+ int i;
+ QPolygon p(size);
+ const QPoint *da = a.constData();
+ QPoint *dp = p.data();
+ for(i = 0; i < size; i++) {
+ MAPINT(da[i].x(), da[i].y(), dp[i].rx(), dp[i].ry());
+ }
+ return p;
+}
+
+/*!
+ \fn QPolygonF QMatrix::map(const QPolygonF &polygon) const
+ \overload
+
+ Creates and returns a QPolygonF object that is a copy of the given
+ \a polygon, mapped into the coordinate system defined by this
+ matrix.
+*/
+QPolygonF QMatrix::map(const QPolygonF &a) const
+{
+ int size = a.size();
+ int i;
+ QPolygonF p(size);
+ const QPointF *da = a.constData();
+ QPointF *dp = p.data();
+ for(i = 0; i < size; i++) {
+ MAPDOUBLE(da[i].xp, da[i].yp, dp[i].xp, dp[i].yp);
+ }
+ return p;
+}
+
+/*!
+ \fn QPolygon QMatrix::map(const QPolygon &polygon) const
+ \overload
+
+ Creates and returns a QPolygon object that is a copy of the given
+ \a polygon, mapped into the coordinate system defined by this
+ matrix. Note that the transformed coordinates are rounded to the
+ nearest integer.
+*/
+
+/*!
+ \fn QRegion operator*(const QRegion &region, const QMatrix &matrix)
+ \relates QMatrix
+
+ This is the same as \a{matrix}.map(\a{region}).
+
+ \sa QMatrix::map()
+*/
+
+extern QPainterPath qt_regionToPath(const QRegion &region);
+
+/*!
+ \fn QRegion QMatrix::map(const QRegion &region) const
+ \overload
+
+ Creates and returns a QRegion object that is a copy of the given
+ \a region, mapped into the coordinate system defined by this matrix.
+
+ Calling this method can be rather expensive if rotations or
+ shearing are used.
+*/
+QRegion QMatrix::map(const QRegion &r) const
+{
+ if (_m11 == 1.0 && _m22 == 1.0 && _m12 == 0.0 && _m21 == 0.0) { // translate or identity
+ if (_dx == 0.0 && _dy == 0.0) // Identity
+ return r;
+ QRegion copy(r);
+ copy.translate(qRound(_dx), qRound(_dy));
+ return copy;
+ }
+
+ QPainterPath p = map(qt_regionToPath(r));
+ return p.toFillPolygon().toPolygon();
+}
+
+/*!
+ \fn QPainterPath operator *(const QPainterPath &path, const QMatrix &matrix)
+ \relates QMatrix
+
+ This is the same as \a{matrix}.map(\a{path}).
+
+ \sa QMatrix::map()
+*/
+
+/*!
+ \overload
+
+ Creates and returns a QPainterPath object that is a copy of the
+ given \a path, mapped into the coordinate system defined by this
+ matrix.
+*/
+QPainterPath QMatrix::map(const QPainterPath &path) const
+{
+ if (path.isEmpty())
+ return QPainterPath();
+
+ QPainterPath copy = path;
+
+ // Translate or identity
+ if (_m11 == 1.0 && _m22 == 1.0 && _m12 == 0.0 && _m21 == 0.0) {
+
+ // Translate
+ if (_dx != 0.0 || _dy != 0.0) {
+ copy.detach();
+ for (int i=0; i<path.elementCount(); ++i) {
+ QPainterPath::Element &e = copy.d_ptr->elements[i];
+ e.x += _dx;
+ e.y += _dy;
+ }
+ }
+
+ // Full xform
+ } else {
+ copy.detach();
+ for (int i=0; i<path.elementCount(); ++i) {
+ QPainterPath::Element &e = copy.d_ptr->elements[i];
+ qreal fx = e.x, fy = e.y;
+ e.x = _m11*fx + _m21*fy + _dx;
+ e.y = _m12*fx + _m22*fy + _dy;
+ }
+ }
+
+ return copy;
+}
+
+/*!
+ \fn QRegion QMatrix::mapToRegion(const QRect &rectangle) const
+
+ Returns the transformed rectangle \a rectangle as a QRegion
+ object. A rectangle which has been rotated or sheared may result
+ in a non-rectangular region being returned.
+
+ Use the mapToPolygon() or map() function instead.
+*/
+#ifdef QT3_SUPPORT
+QRegion QMatrix::mapToRegion(const QRect &rect) const
+{
+ QRegion result;
+ if (isIdentity()) {
+ result = rect;
+ } else if (m12() == 0.0F && m21() == 0.0F) {
+ int x = qRound(m11()*rect.x() + dx());
+ int y = qRound(m22()*rect.y() + dy());
+ int w = qRound(m11()*rect.width());
+ int h = qRound(m22()*rect.height());
+ if (w < 0) {
+ w = -w;
+ x -= w - 1;
+ }
+ if (h < 0) {
+ h = -h;
+ y -= h - 1;
+ }
+ result = QRect(x, y, w, h);
+ } else {
+ result = QRegion(mapToPolygon(rect));
+ }
+ return result;
+
+}
+#endif
+/*!
+ \fn QPolygon QMatrix::mapToPolygon(const QRect &rectangle) const
+
+ Creates and returns a QPolygon representation of the given \a
+ rectangle, mapped into the coordinate system defined by this
+ matrix.
+
+ The rectangle's coordinates are transformed using the following
+ formulas:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qmatrix.cpp 3
+
+ Polygons and rectangles behave slightly differently when
+ transformed (due to integer rounding), so
+ \c{matrix.map(QPolygon(rectangle))} is not always the same as
+ \c{matrix.mapToPolygon(rectangle)}.
+
+ \sa mapRect(), {QMatrix#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+QPolygon QMatrix::mapToPolygon(const QRect &rect) const
+{
+ QPolygon a(4);
+ qreal x[4], y[4];
+ if (_m12 == 0.0F && _m21 == 0.0F) {
+ x[0] = _m11*rect.x() + _dx;
+ y[0] = _m22*rect.y() + _dy;
+ qreal w = _m11*rect.width();
+ qreal h = _m22*rect.height();
+ if (w < 0) {
+ w = -w;
+ x[0] -= w;
+ }
+ if (h < 0) {
+ h = -h;
+ y[0] -= h;
+ }
+ x[1] = x[0]+w;
+ x[2] = x[1];
+ x[3] = x[0];
+ y[1] = y[0];
+ y[2] = y[0]+h;
+ y[3] = y[2];
+ } else {
+ qreal right = rect.x() + rect.width();
+ qreal bottom = rect.y() + rect.height();
+ MAPDOUBLE(rect.x(), rect.y(), x[0], y[0]);
+ MAPDOUBLE(right, rect.y(), x[1], y[1]);
+ MAPDOUBLE(right, bottom, x[2], y[2]);
+ MAPDOUBLE(rect.x(), bottom, x[3], y[3]);
+ }
+#if 0
+ int i;
+ for(i = 0; i< 4; i++)
+ qDebug("coords(%d) = (%f/%f) (%d/%d)", i, x[i], y[i], qRound(x[i]), qRound(y[i]));
+ qDebug("width=%f, height=%f", qSqrt((x[1]-x[0])*(x[1]-x[0]) + (y[1]-y[0])*(y[1]-y[0])),
+ qSqrt((x[0]-x[3])*(x[0]-x[3]) + (y[0]-y[3])*(y[0]-y[3])));
+#endif
+ // all coordinates are correctly, tranform to a pointarray
+ // (rounding to the next integer)
+ a.setPoints(4, qRound(x[0]), qRound(y[0]),
+ qRound(x[1]), qRound(y[1]),
+ qRound(x[2]), qRound(y[2]),
+ qRound(x[3]), qRound(y[3]));
+ return a;
+}
+
+/*!
+ Resets the matrix to an identity matrix, i.e. all elements are set
+ to zero, except \c m11 and \c m22 (specifying the scale) which are
+ set to 1.
+
+ \sa QMatrix(), isIdentity(), {QMatrix#Basic Matrix
+ Operations}{Basic Matrix Operations}
+*/
+
+void QMatrix::reset()
+{
+ _m11 = _m22 = 1.0;
+ _m12 = _m21 = _dx = _dy = 0.0;
+}
+
+/*!
+ \fn bool QMatrix::isIdentity() const
+
+ Returns true if the matrix is the identity matrix, otherwise
+ returns false.
+
+ \sa reset()
+*/
+
+/*!
+ Moves the coordinate system \a dx along the x axis and \a dy along
+ the y axis, and returns a reference to the matrix.
+
+ \sa setMatrix()
+*/
+
+QMatrix &QMatrix::translate(qreal dx, qreal dy)
+{
+ _dx += dx*_m11 + dy*_m21;
+ _dy += dy*_m22 + dx*_m12;
+ return *this;
+}
+
+/*!
+ \fn QMatrix &QMatrix::scale(qreal sx, qreal sy)
+
+ Scales the coordinate system by \a sx horizontally and \a sy
+ vertically, and returns a reference to the matrix.
+
+ \sa setMatrix()
+*/
+
+QMatrix &QMatrix::scale(qreal sx, qreal sy)
+{
+ _m11 *= sx;
+ _m12 *= sx;
+ _m21 *= sy;
+ _m22 *= sy;
+ return *this;
+}
+
+/*!
+ Shears the coordinate system by \a sh horizontally and \a sv
+ vertically, and returns a reference to the matrix.
+
+ \sa setMatrix()
+*/
+
+QMatrix &QMatrix::shear(qreal sh, qreal sv)
+{
+ qreal tm11 = sv*_m21;
+ qreal tm12 = sv*_m22;
+ qreal tm21 = sh*_m11;
+ qreal tm22 = sh*_m12;
+ _m11 += tm11;
+ _m12 += tm12;
+ _m21 += tm21;
+ _m22 += tm22;
+ return *this;
+}
+
+const qreal deg2rad = qreal(0.017453292519943295769); // pi/180
+
+/*!
+ \fn QMatrix &QMatrix::rotate(qreal degrees)
+
+ Rotates the coordinate system the given \a degrees
+ counterclockwise.
+
+ Note that if you apply a QMatrix to a point defined in widget
+ coordinates, the direction of the rotation will be clockwise
+ because the y-axis points downwards.
+
+ Returns a reference to the matrix.
+
+ \sa setMatrix()
+*/
+
+QMatrix &QMatrix::rotate(qreal a)
+{
+ qreal sina = 0;
+ qreal cosa = 0;
+ if (a == 90. || a == -270.)
+ sina = 1.;
+ else if (a == 270. || a == -90.)
+ sina = -1.;
+ else if (a == 180.)
+ cosa = -1.;
+ else{
+ qreal b = deg2rad*a; // convert to radians
+ sina = qSin(b); // fast and convenient
+ cosa = qCos(b);
+ }
+ qreal tm11 = cosa*_m11 + sina*_m21;
+ qreal tm12 = cosa*_m12 + sina*_m22;
+ qreal tm21 = -sina*_m11 + cosa*_m21;
+ qreal tm22 = -sina*_m12 + cosa*_m22;
+ _m11 = tm11; _m12 = tm12;
+ _m21 = tm21; _m22 = tm22;
+ return *this;
+}
+
+/*!
+ \fn bool QMatrix::isInvertible() const
+
+ Returns true if the matrix is invertible, otherwise returns false.
+
+ \sa inverted()
+*/
+
+/*!
+ \obsolete
+ \fn qreal QMatrix::det() const
+
+ Returns the matrix's determinant.
+
+ \sa determinant()
+*/
+
+/*!
+ \since 4.6
+ \fn qreal QMatrix::determinant() const
+
+ Returns the matrix's determinant.
+*/
+
+/*!
+ \fn QMatrix QMatrix::invert(bool *invertible) const
+
+ Returns an inverted copy of this matrix.
+
+ Use the inverted() function instead.
+*/
+
+/*!
+ Returns an inverted copy of this matrix.
+
+ If the matrix is singular (not invertible), the returned matrix is
+ the identity matrix. If \a invertible is valid (i.e. not 0), its
+ value is set to true if the matrix is invertible, otherwise it is
+ set to false.
+
+ \sa isInvertible()
+*/
+
+QMatrix QMatrix::inverted(bool *invertible) const
+{
+ qreal dtr = determinant();
+ if (dtr == 0.0) {
+ if (invertible)
+ *invertible = false; // singular matrix
+ return QMatrix(true);
+ }
+ else { // invertible matrix
+ if (invertible)
+ *invertible = true;
+ qreal dinv = 1.0/dtr;
+ return QMatrix((_m22*dinv), (-_m12*dinv),
+ (-_m21*dinv), (_m11*dinv),
+ ((_m21*_dy - _m22*_dx)*dinv),
+ ((_m12*_dx - _m11*_dy)*dinv),
+ true);
+ }
+}
+
+
+/*!
+ \fn bool QMatrix::operator==(const QMatrix &matrix) const
+
+ Returns true if this matrix is equal to the given \a matrix,
+ otherwise returns false.
+*/
+
+bool QMatrix::operator==(const QMatrix &m) const
+{
+ return _m11 == m._m11 &&
+ _m12 == m._m12 &&
+ _m21 == m._m21 &&
+ _m22 == m._m22 &&
+ _dx == m._dx &&
+ _dy == m._dy;
+}
+
+/*!
+ \fn bool QMatrix::operator!=(const QMatrix &matrix) const
+
+ Returns true if this matrix is not equal to the given \a matrix,
+ otherwise returns false.
+*/
+
+bool QMatrix::operator!=(const QMatrix &m) const
+{
+ return _m11 != m._m11 ||
+ _m12 != m._m12 ||
+ _m21 != m._m21 ||
+ _m22 != m._m22 ||
+ _dx != m._dx ||
+ _dy != m._dy;
+}
+
+/*!
+ \fn QMatrix &QMatrix::operator *=(const QMatrix &matrix)
+ \overload
+
+ Returns the result of multiplying this matrix by the given \a
+ matrix.
+*/
+
+QMatrix &QMatrix::operator *=(const QMatrix &m)
+{
+ qreal tm11 = _m11*m._m11 + _m12*m._m21;
+ qreal tm12 = _m11*m._m12 + _m12*m._m22;
+ qreal tm21 = _m21*m._m11 + _m22*m._m21;
+ qreal tm22 = _m21*m._m12 + _m22*m._m22;
+
+ qreal tdx = _dx*m._m11 + _dy*m._m21 + m._dx;
+ qreal tdy = _dx*m._m12 + _dy*m._m22 + m._dy;
+
+ _m11 = tm11; _m12 = tm12;
+ _m21 = tm21; _m22 = tm22;
+ _dx = tdx; _dy = tdy;
+ return *this;
+}
+
+/*!
+ \fn QMatrix QMatrix::operator *(const QMatrix &matrix) const
+
+ Returns the result of multiplying this matrix by the given \a
+ matrix.
+
+ Note that matrix multiplication is not commutative, i.e. a*b !=
+ b*a.
+*/
+
+QMatrix QMatrix::operator *(const QMatrix &m) const
+{
+ qreal tm11 = _m11*m._m11 + _m12*m._m21;
+ qreal tm12 = _m11*m._m12 + _m12*m._m22;
+ qreal tm21 = _m21*m._m11 + _m22*m._m21;
+ qreal tm22 = _m21*m._m12 + _m22*m._m22;
+
+ qreal tdx = _dx*m._m11 + _dy*m._m21 + m._dx;
+ qreal tdy = _dx*m._m12 + _dy*m._m22 + m._dy;
+ return QMatrix(tm11, tm12, tm21, tm22, tdx, tdy, true);
+}
+
+/*!
+ Assigns the given \a matrix's values to this matrix.
+*/
+QMatrix &QMatrix::operator=(const QMatrix &matrix)
+{
+ _m11 = matrix._m11;
+ _m12 = matrix._m12;
+ _m21 = matrix._m21;
+ _m22 = matrix._m22;
+ _dx = matrix._dx;
+ _dy = matrix._dy;
+ return *this;
+}
+
+/*!
+ \since 4.2
+
+ Returns the matrix as a QVariant.
+*/
+QMatrix::operator QVariant() const
+{
+ return QVariant(QVariant::Matrix, this);
+}
+
+Q_GUI_EXPORT QPainterPath operator *(const QPainterPath &p, const QMatrix &m)
+{
+ return m.map(p);
+}
+
+
+/*****************************************************************************
+ QMatrix stream functions
+ *****************************************************************************/
+#ifndef QT_NO_DATASTREAM
+/*!
+ \fn QDataStream &operator<<(QDataStream &stream, const QMatrix &matrix)
+ \relates QMatrix
+
+ Writes the given \a matrix to the given \a stream and returns a
+ reference to the stream.
+
+ \sa {Serializing Qt Data Types}
+*/
+
+QDataStream &operator<<(QDataStream &s, const QMatrix &m)
+{
+ if (s.version() == 1) {
+ s << (float)m.m11() << (float)m.m12() << (float)m.m21()
+ << (float)m.m22() << (float)m.dx() << (float)m.dy();
+ } else {
+ s << double(m.m11())
+ << double(m.m12())
+ << double(m.m21())
+ << double(m.m22())
+ << double(m.dx())
+ << double(m.dy());
+ }
+ return s;
+}
+
+/*!
+ \fn QDataStream &operator>>(QDataStream &stream, QMatrix &matrix)
+ \relates QMatrix
+
+ Reads the given \a matrix from the given \a stream and returns a
+ reference to the stream.
+
+ \sa {Serializing Qt Data Types}
+*/
+
+QDataStream &operator>>(QDataStream &s, QMatrix &m)
+{
+ if (s.version() == 1) {
+ float m11, m12, m21, m22, dx, dy;
+ s >> m11; s >> m12; s >> m21; s >> m22;
+ s >> dx; s >> dy;
+ m.setMatrix(m11, m12, m21, m22, dx, dy);
+ }
+ else {
+ double m11, m12, m21, m22, dx, dy;
+ s >> m11;
+ s >> m12;
+ s >> m21;
+ s >> m22;
+ s >> dx;
+ s >> dy;
+ m.setMatrix(m11, m12, m21, m22, dx, dy);
+ }
+ return s;
+}
+#endif // QT_NO_DATASTREAM
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QMatrix &m)
+{
+ dbg.nospace() << "QMatrix("
+ << "11=" << m.m11()
+ << " 12=" << m.m12()
+ << " 21=" << m.m21()
+ << " 22=" << m.m22()
+ << " dx=" << m.dx()
+ << " dy=" << m.dy()
+ << ')';
+ return dbg.space();
+}
+#endif
+
+/*!
+ \fn QRect QMatrix::map(const QRect &rect) const
+ \compat
+
+ Creates and returns a QRect object that is a copy of the given
+ rectangle, mapped into the coordinate system defined by this
+ matrix.
+
+ Use the mapRect() function instead.
+*/
+
+
+/*!
+ \fn bool qFuzzyCompare(const QMatrix& m1, const QMatrix& m2)
+
+ \relates QMatrix
+ \since 4.6
+
+ \brief The qFuzzyCompare function is for comparing two matrices
+ using a fuzziness factor.
+
+ Returns true if \a m1 and \a m2 are equal, allowing for a small
+ fuzziness factor for floating-point comparisons; false otherwise.
+*/
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qmatrix.h b/src/gui/painting/qmatrix.h
new file mode 100644
index 0000000000..898cfa4cf2
--- /dev/null
+++ b/src/gui/painting/qmatrix.h
@@ -0,0 +1,206 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMATRIX_H
+#define QMATRIX_H
+
+#include <QtGui/qpolygon.h>
+#include <QtGui/qregion.h>
+#include <QtGui/qwindowdefs.h>
+#include <QtCore/qline.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qrect.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QPainterPath;
+class QVariant;
+
+class Q_GUI_EXPORT QMatrix // 2D transform matrix
+{
+public:
+ inline explicit QMatrix(Qt::Initialization) {}
+ QMatrix();
+ QMatrix(qreal m11, qreal m12, qreal m21, qreal m22,
+ qreal dx, qreal dy);
+ QMatrix(const QMatrix &matrix);
+
+ void setMatrix(qreal m11, qreal m12, qreal m21, qreal m22,
+ qreal dx, qreal dy);
+
+ qreal m11() const { return _m11; }
+ qreal m12() const { return _m12; }
+ qreal m21() const { return _m21; }
+ qreal m22() const { return _m22; }
+ qreal dx() const { return _dx; }
+ qreal dy() const { return _dy; }
+
+ void map(int x, int y, int *tx, int *ty) const;
+ void map(qreal x, qreal y, qreal *tx, qreal *ty) const;
+ QRect mapRect(const QRect &) const;
+ QRectF mapRect(const QRectF &) const;
+
+ QPoint map(const QPoint &p) const;
+ QPointF map(const QPointF&p) const;
+ QLine map(const QLine &l) const;
+ QLineF map(const QLineF &l) const;
+ QPolygonF map(const QPolygonF &a) const;
+ QPolygon map(const QPolygon &a) const;
+ QRegion map(const QRegion &r) const;
+ QPainterPath map(const QPainterPath &p) const;
+ QPolygon mapToPolygon(const QRect &r) const;
+
+ void reset();
+ inline bool isIdentity() const;
+
+ QMatrix &translate(qreal dx, qreal dy);
+ QMatrix &scale(qreal sx, qreal sy);
+ QMatrix &shear(qreal sh, qreal sv);
+ QMatrix &rotate(qreal a);
+
+ bool isInvertible() const { return !qFuzzyIsNull(_m11*_m22 - _m12*_m21); }
+ qreal determinant() const { return _m11*_m22 - _m12*_m21; }
+#ifdef QT_DEPRECATED
+ QT_DEPRECATED qreal det() const { return _m11*_m22 - _m12*_m21; }
+#endif
+
+ QMatrix inverted(bool *invertible = 0) const;
+
+ bool operator==(const QMatrix &) const;
+ bool operator!=(const QMatrix &) const;
+
+ QMatrix &operator*=(const QMatrix &);
+ QMatrix operator*(const QMatrix &o) const;
+
+ QMatrix &operator=(const QMatrix &);
+
+ operator QVariant() const;
+
+#ifdef QT3_SUPPORT
+ inline QT3_SUPPORT QMatrix invert(bool *invertible=0) const { return inverted(invertible); }
+ inline QT3_SUPPORT QRect map(const QRect &r) const { return mapRect(r); }
+ QT3_SUPPORT QRegion mapToRegion(const QRect &r) const;
+#endif
+
+private:
+ inline QMatrix(bool)
+ : _m11(1.)
+ , _m12(0.)
+ , _m21(0.)
+ , _m22(1.)
+ , _dx(0.)
+ , _dy(0.) {}
+ inline QMatrix(qreal am11, qreal am12, qreal am21, qreal am22, qreal adx, qreal ady, bool)
+ : _m11(am11)
+ , _m12(am12)
+ , _m21(am21)
+ , _m22(am22)
+ , _dx(adx)
+ , _dy(ady) {}
+ friend class QTransform;
+ qreal _m11, _m12;
+ qreal _m21, _m22;
+ qreal _dx, _dy;
+};
+Q_DECLARE_TYPEINFO(QMatrix, Q_MOVABLE_TYPE);
+
+// mathematical semantics
+Q_GUI_EXPORT_INLINE QPoint operator*(const QPoint &p, const QMatrix &m)
+{ return m.map(p); }
+Q_GUI_EXPORT_INLINE QPointF operator*(const QPointF &p, const QMatrix &m)
+{ return m.map(p); }
+Q_GUI_EXPORT_INLINE QLineF operator*(const QLineF &l, const QMatrix &m)
+{ return m.map(l); }
+Q_GUI_EXPORT_INLINE QLine operator*(const QLine &l, const QMatrix &m)
+{ return m.map(l); }
+Q_GUI_EXPORT_INLINE QPolygon operator *(const QPolygon &a, const QMatrix &m)
+{ return m.map(a); }
+Q_GUI_EXPORT_INLINE QPolygonF operator *(const QPolygonF &a, const QMatrix &m)
+{ return m.map(a); }
+Q_GUI_EXPORT_INLINE QRegion operator *(const QRegion &r, const QMatrix &m)
+{ return m.map(r); }
+Q_GUI_EXPORT QPainterPath operator *(const QPainterPath &p, const QMatrix &m);
+
+inline bool QMatrix::isIdentity() const
+{
+ return qFuzzyIsNull(_m11 - 1) && qFuzzyIsNull(_m22 - 1) && qFuzzyIsNull(_m12)
+ && qFuzzyIsNull(_m21) && qFuzzyIsNull(_dx) && qFuzzyIsNull(_dy);
+}
+
+inline bool qFuzzyCompare(const QMatrix& m1, const QMatrix& m2)
+{
+ return qFuzzyCompare(m1.m11(), m2.m11())
+ && qFuzzyCompare(m1.m12(), m2.m12())
+ && qFuzzyCompare(m1.m21(), m2.m21())
+ && qFuzzyCompare(m1.m22(), m2.m22())
+ && qFuzzyCompare(m1.dx(), m2.dx())
+ && qFuzzyCompare(m1.dy(), m2.dy());
+}
+
+
+/*****************************************************************************
+ QMatrix stream functions
+ *****************************************************************************/
+
+#ifndef QT_NO_DATASTREAM
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QMatrix &);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QMatrix &);
+#endif
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QMatrix &);
+#endif
+
+#ifdef QT3_SUPPORT
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <QtGui/qwmatrix.h>
+QT_END_INCLUDE_NAMESPACE
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QMATRIX_H
diff --git a/src/gui/painting/qmemrotate.cpp b/src/gui/painting/qmemrotate.cpp
new file mode 100644
index 0000000000..fe524aff3b
--- /dev/null
+++ b/src/gui/painting/qmemrotate.cpp
@@ -0,0 +1,648 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "private/qmemrotate_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#if QT_ROTATION_ALGORITHM == QT_ROTATION_TILED
+static const int tileSize = 32;
+#endif
+
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+#if QT_ROTATION_ALGORITHM == QT_ROTATION_PACKED || QT_ROTATION_ALGORITHM == QT_ROTATION_TILED
+#error Big endian version not implemented for the transformed driver!
+#endif
+#endif
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_FUNCTION inline void qt_memrotate90_cachedRead(const SRC *src, int w, int h,
+ int sstride,
+ DST *dest, int dstride)
+{
+ const char *s = reinterpret_cast<const char*>(src);
+ char *d = reinterpret_cast<char*>(dest);
+ for (int y = 0; y < h; ++y) {
+ for (int x = w - 1; x >= 0; --x) {
+ DST *destline = reinterpret_cast<DST*>(d + (w - x - 1) * dstride);
+ destline[y] = qt_colorConvert<DST,SRC>(src[x], 0);
+ }
+ s += sstride;
+ src = reinterpret_cast<const SRC*>(s);
+ }
+}
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_FUNCTION inline void qt_memrotate270_cachedRead(const SRC *src, int w, int h,
+ int sstride,
+ DST *dest, int dstride)
+{
+ const char *s = reinterpret_cast<const char*>(src);
+ char *d = reinterpret_cast<char*>(dest);
+ s += (h - 1) * sstride;
+ for (int y = h - 1; y >= 0; --y) {
+ src = reinterpret_cast<const SRC*>(s);
+ for (int x = 0; x < w; ++x) {
+ DST *destline = reinterpret_cast<DST*>(d + x * dstride);
+ destline[h - y - 1] = qt_colorConvert<DST,SRC>(src[x], 0);
+ }
+ s -= sstride;
+ }
+}
+
+#if QT_ROTATION_ALGORITHM == QT_ROTATION_CACHEDWRITE
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_FUNCTION inline void qt_memrotate90_cachedWrite(const SRC *src, int w, int h,
+ int sstride,
+ DST *dest, int dstride)
+{
+ for (int x = w - 1; x >= 0; --x) {
+ DST *d = dest + (w - x - 1) * dstride;
+ for (int y = 0; y < h; ++y) {
+ *d++ = qt_colorConvert<DST,SRC>(src[y * sstride + x], 0);
+ }
+ }
+
+}
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_FUNCTION inline void qt_memrotate270_cachedWrite(const SRC *src, int w, int h,
+ int sstride,
+ DST *dest, int dstride)
+{
+ for (int x = 0; x < w; ++x) {
+ DST *d = dest + x * dstride;
+ for (int y = h - 1; y >= 0; --y) {
+ *d++ = qt_colorConvert<DST,SRC>(src[y * sstride + x], 0);
+ }
+ }
+}
+
+#endif // QT_ROTATION_CACHEDWRITE
+
+#if QT_ROTATION_ALGORITHM == QT_ROTATION_PACKING
+
+// TODO: packing algorithms should probably be modified on 64-bit architectures
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_FUNCTION inline void qt_memrotate90_packing(const SRC *src, int w, int h,
+ int sstride,
+ DST *dest, int dstride)
+{
+ sstride /= sizeof(SRC);
+ dstride /= sizeof(DST);
+
+ const int pack = sizeof(quint32) / sizeof(DST);
+ const int unaligned = int((long(dest) & (sizeof(quint32)-1))) / sizeof(DST);
+
+ for (int x = w - 1; x >= 0; --x) {
+ int y = 0;
+
+ for (int i = 0; i < unaligned; ++i) {
+ dest[(w - x - 1) * dstride + y]
+ = qt_colorConvert<DST,SRC>(src[y * sstride + x], 0);
+ ++y;
+ }
+
+ quint32 *d = reinterpret_cast<quint32*>(dest + (w - x - 1) * dstride
+ + unaligned);
+ const int rest = (h - unaligned) % pack;
+ while (y < h - rest) {
+ quint32 c = qt_colorConvert<DST,SRC>(src[y * sstride + x], 0);
+ for (int i = 1; i < pack; ++i) {
+ c |= qt_colorConvert<DST,SRC>(src[(y + i) * sstride + x], 0)
+ << (sizeof(int) * 8 / pack * i);
+ }
+ *d++ = c;
+ y += pack;
+ }
+
+ while (y < h) {
+ dest[(w - x - 1) * dstride + y]
+ = qt_colorConvert<DST,SRC>(src[y * sstride + x], 0);
+ ++y;
+ }
+ }
+}
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_FUNCTION inline void qt_memrotate270_packing(const SRC *src, int w, int h,
+ int sstride,
+ DST *dest, int dstride)
+{
+ sstride /= sizeof(SRC);
+ dstride /= sizeof(DST);
+
+ const int pack = sizeof(quint32) / sizeof(DST);
+ const int unaligned = int((long(dest) & (sizeof(quint32)-1))) / sizeof(DST);
+
+ for (int x = 0; x < w; ++x) {
+ int y = h - 1;
+
+ for (int i = 0; i < unaligned; ++i) {
+ dest[x * dstride + h - y - 1]
+ = qt_colorConvert<DST,SRC>(src[y * sstride + x], 0);
+ --y;
+ }
+
+ quint32 *d = reinterpret_cast<quint32*>(dest + x * dstride
+ + unaligned);
+ const int rest = (h - unaligned) % pack;
+ while (y > rest) {
+ quint32 c = qt_colorConvert<DST,SRC>(src[y * sstride + x], 0);
+ for (int i = 1; i < pack; ++i) {
+ c |= qt_colorConvert<DST,SRC>(src[(y - i) * sstride + x], 0)
+ << (sizeof(int) * 8 / pack * i);
+ }
+ *d++ = c;
+ y -= pack;
+ }
+ while (y >= 0) {
+ dest[x * dstride + h - y - 1]
+ = qt_colorConvert<DST,SRC>(src[y * sstride + x], 0);
+ --y;
+ }
+ }
+}
+
+#endif // QT_ROTATION_PACKING
+
+#if QT_ROTATION_ALGORITHM == QT_ROTATION_TILED
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_FUNCTION inline void qt_memrotate90_tiled(const SRC *src, int w, int h,
+ int sstride,
+ DST *dest, int dstride)
+{
+ sstride /= sizeof(SRC);
+ dstride /= sizeof(DST);
+
+ const int pack = sizeof(quint32) / sizeof(DST);
+ const int unaligned =
+ qMin(uint((quintptr(dest) & (sizeof(quint32)-1)) / sizeof(DST)), uint(h));
+ const int restX = w % tileSize;
+ const int restY = (h - unaligned) % tileSize;
+ const int unoptimizedY = restY % pack;
+ const int numTilesX = w / tileSize + (restX > 0);
+ const int numTilesY = (h - unaligned) / tileSize + (restY >= pack);
+
+ for (int tx = 0; tx < numTilesX; ++tx) {
+ const int startx = w - tx * tileSize - 1;
+ const int stopx = qMax(startx - tileSize, 0);
+
+ if (unaligned) {
+ for (int x = startx; x >= stopx; --x) {
+ DST *d = dest + (w - x - 1) * dstride;
+ for (int y = 0; y < unaligned; ++y) {
+ *d++ = qt_colorConvert<DST,SRC>(src[y * sstride + x], 0);
+ }
+ }
+ }
+
+ for (int ty = 0; ty < numTilesY; ++ty) {
+ const int starty = ty * tileSize + unaligned;
+ const int stopy = qMin(starty + tileSize, h - unoptimizedY);
+
+ for (int x = startx; x >= stopx; --x) {
+ quint32 *d = reinterpret_cast<quint32*>(dest + (w - x - 1) * dstride + starty);
+ for (int y = starty; y < stopy; y += pack) {
+ quint32 c = qt_colorConvert<DST,SRC>(src[y * sstride + x], 0);
+ for (int i = 1; i < pack; ++i) {
+ const int shift = (sizeof(int) * 8 / pack * i);
+ const DST color = qt_colorConvert<DST,SRC>(src[(y + i) * sstride + x], 0);
+ c |= color << shift;
+ }
+ *d++ = c;
+ }
+ }
+ }
+
+ if (unoptimizedY) {
+ const int starty = h - unoptimizedY;
+ for (int x = startx; x >= stopx; --x) {
+ DST *d = dest + (w - x - 1) * dstride + starty;
+ for (int y = starty; y < h; ++y) {
+ *d++ = qt_colorConvert<DST,SRC>(src[y * sstride + x], 0);
+ }
+ }
+ }
+ }
+}
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_FUNCTION inline void qt_memrotate90_tiled_unpacked(const SRC *src, int w, int h,
+ int sstride,
+ DST *dest, int dstride)
+{
+ const int numTilesX = (w + tileSize - 1) / tileSize;
+ const int numTilesY = (h + tileSize - 1) / tileSize;
+
+ for (int tx = 0; tx < numTilesX; ++tx) {
+ const int startx = w - tx * tileSize - 1;
+ const int stopx = qMax(startx - tileSize, 0);
+
+ for (int ty = 0; ty < numTilesY; ++ty) {
+ const int starty = ty * tileSize;
+ const int stopy = qMin(starty + tileSize, h);
+
+ for (int x = startx; x >= stopx; --x) {
+ DST *d = (DST*)((char*)dest + (w - x - 1) * dstride) + starty;
+ const char *s = (const char*)(src + x) + starty * sstride;
+ for (int y = starty; y < stopy; ++y) {
+ *d++ = qt_colorConvert<DST,SRC>(*(const SRC*)(s), 0);
+ s += sstride;
+ }
+ }
+ }
+ }
+}
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_FUNCTION inline void qt_memrotate270_tiled(const SRC *src, int w, int h,
+ int sstride,
+ DST *dest, int dstride)
+{
+ sstride /= sizeof(SRC);
+ dstride /= sizeof(DST);
+
+ const int pack = sizeof(quint32) / sizeof(DST);
+ const int unaligned =
+ qMin(uint((long(dest) & (sizeof(quint32)-1)) / sizeof(DST)), uint(h));
+ const int restX = w % tileSize;
+ const int restY = (h - unaligned) % tileSize;
+ const int unoptimizedY = restY % pack;
+ const int numTilesX = w / tileSize + (restX > 0);
+ const int numTilesY = (h - unaligned) / tileSize + (restY >= pack);
+
+ for (int tx = 0; tx < numTilesX; ++tx) {
+ const int startx = tx * tileSize;
+ const int stopx = qMin(startx + tileSize, w);
+
+ if (unaligned) {
+ for (int x = startx; x < stopx; ++x) {
+ DST *d = dest + x * dstride;
+ for (int y = h - 1; y >= h - unaligned; --y) {
+ *d++ = qt_colorConvert<DST,SRC>(src[y * sstride + x], 0);
+ }
+ }
+ }
+
+ for (int ty = 0; ty < numTilesY; ++ty) {
+ const int starty = h - 1 - unaligned - ty * tileSize;
+ const int stopy = qMax(starty - tileSize, unoptimizedY);
+
+ for (int x = startx; x < stopx; ++x) {
+ quint32 *d = reinterpret_cast<quint32*>(dest + x * dstride
+ + h - 1 - starty);
+ for (int y = starty; y > stopy; y -= pack) {
+ quint32 c = qt_colorConvert<DST,SRC>(src[y * sstride + x], 0);
+ for (int i = 1; i < pack; ++i) {
+ const int shift = (sizeof(int) * 8 / pack * i);
+ const DST color = qt_colorConvert<DST,SRC>(src[(y - i) * sstride + x], 0);
+ c |= color << shift;
+ }
+ *d++ = c;
+ }
+ }
+ }
+ if (unoptimizedY) {
+ const int starty = unoptimizedY - 1;
+ for (int x = startx; x < stopx; ++x) {
+ DST *d = dest + x * dstride + h - 1 - starty;
+ for (int y = starty; y >= 0; --y) {
+ *d++ = qt_colorConvert<DST,SRC>(src[y * sstride + x], 0);
+ }
+ }
+ }
+ }
+}
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_FUNCTION inline void qt_memrotate270_tiled_unpacked(const SRC *src, int w, int h,
+ int sstride,
+ DST *dest, int dstride)
+{
+ const int numTilesX = (w + tileSize - 1) / tileSize;
+ const int numTilesY = (h + tileSize - 1) / tileSize;
+
+ for (int tx = 0; tx < numTilesX; ++tx) {
+ const int startx = tx * tileSize;
+ const int stopx = qMin(startx + tileSize, w);
+
+ for (int ty = 0; ty < numTilesY; ++ty) {
+ const int starty = h - 1 - ty * tileSize;
+ const int stopy = qMax(starty - tileSize, 0);
+
+ for (int x = startx; x < stopx; ++x) {
+ DST *d = (DST*)((char*)dest + x * dstride) + h - 1 - starty;
+ const char *s = (const char*)(src + x) + starty * sstride;
+ for (int y = starty; y >= stopy; --y) {
+ *d++ = qt_colorConvert<DST,SRC>(*(const SRC*)s, 0);
+ s -= sstride;
+ }
+ }
+ }
+ }
+}
+
+#endif // QT_ROTATION_ALGORITHM
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_FUNCTION inline void qt_memrotate90_template(const SRC *src,
+ int srcWidth, int srcHeight, int srcStride,
+ DST *dest, int dstStride)
+{
+#if QT_ROTATION_ALGORITHM == QT_ROTATION_CACHEDREAD
+ qt_memrotate90_cachedRead<DST,SRC>(src, srcWidth, srcHeight, srcStride,
+ dest, dstStride);
+#elif QT_ROTATION_ALGORITHM == QT_ROTATION_CACHEDWRITE
+ qt_memrotate90_cachedWrite<DST,SRC>(src, srcWidth, srcHeight, srcStride,
+ dest, dstStride);
+#elif QT_ROTATION_ALGORITHM == QT_ROTATION_PACKING
+ qt_memrotate90_packing<DST,SRC>(src, srcWidth, srcHeight, srcStride,
+ dest, dstStride);
+#elif QT_ROTATION_ALGORITHM == QT_ROTATION_TILED
+ qt_memrotate90_tiled<DST,SRC>(src, srcWidth, srcHeight, srcStride,
+ dest, dstStride);
+#endif
+}
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_FUNCTION inline void qt_memrotate180_template(const SRC *src,
+ int w, int h, int sstride,
+ DST *dest, int dstride)
+{
+ const char *s = (const char*)(src) + (h - 1) * sstride;
+ for (int y = h - 1; y >= 0; --y) {
+ DST *d = reinterpret_cast<DST*>((char *)(dest) + (h - y - 1) * dstride);
+ src = reinterpret_cast<const SRC*>(s);
+ for (int x = w - 1; x >= 0; --x) {
+ d[w - x - 1] = qt_colorConvert<DST,SRC>(src[x], 0);
+ }
+ s -= sstride;
+ }
+}
+
+template <class DST, class SRC>
+Q_STATIC_TEMPLATE_FUNCTION inline void qt_memrotate270_template(const SRC *src,
+ int srcWidth, int srcHeight, int srcStride,
+ DST *dest, int dstStride)
+{
+#if QT_ROTATION_ALGORITHM == QT_ROTATION_CACHEDREAD
+ qt_memrotate270_cachedRead<DST,SRC>(src, srcWidth, srcHeight, srcStride,
+ dest, dstStride);
+#elif QT_ROTATION_ALGORITHM == QT_ROTATION_CACHEDWRITE
+ qt_memrotate270_cachedWrite<DST,SRC>(src, srcWidth, srcHeight, srcStride,
+ dest, dstStride);
+#elif QT_ROTATION_ALGORITHM == QT_ROTATION_PACKING
+ qt_memrotate270_packing<DST,SRC>(src, srcWidth, srcHeight, srcStride,
+ dest, dstStride);
+#elif QT_ROTATION_ALGORITHM == QT_ROTATION_TILED
+ qt_memrotate270_tiled_unpacked<DST,SRC>(src, srcWidth, srcHeight,
+ srcStride,
+ dest, dstStride);
+#endif
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline void qt_memrotate90_template<quint24, quint24>(const quint24 *src,
+ int srcWidth, int srcHeight, int srcStride,
+ quint24 *dest, int dstStride)
+{
+#if QT_ROTATION_ALGORITHM == QT_ROTATION_CACHEDREAD
+ qt_memrotate90_cachedRead<quint24,quint24>(src, srcWidth, srcHeight,
+ srcStride, dest, dstStride);
+#elif QT_ROTATION_ALGORITHM == QT_ROTATION_CACHEDWRITE
+ qt_memrotate90_cachedWrite<quint24,quint24>(src, srcWidth, srcHeight,
+ srcStride, dest, dstStride);
+#elif QT_ROTATION_ALGORITHM == QT_ROTATION_PACKING
+ // packed algorithm not implemented
+ qt_memrotate90_cachedRead<quint24,quint24>(src, srcWidth, srcHeight,
+ srcStride, dest, dstStride);
+#elif QT_ROTATION_ALGORITHM == QT_ROTATION_TILED
+ // packed algorithm not implemented
+ qt_memrotate90_tiled_unpacked<quint24,quint24>(src, srcWidth, srcHeight,
+ srcStride, dest, dstStride);
+#endif
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline void qt_memrotate90_template<quint24, quint32>(const quint32 *src,
+ int srcWidth, int srcHeight, int srcStride,
+ quint24 *dest, int dstStride)
+{
+#if QT_ROTATION_ALGORITHM == QT_ROTATION_CACHEDREAD
+ qt_memrotate90_cachedRead<quint24,quint32>(src, srcWidth, srcHeight,
+ srcStride, dest, dstStride);
+#elif QT_ROTATION_ALGORITHM == QT_ROTATION_CACHEDWRITE
+ qt_memrotate90_cachedWrite<quint24,quint32>(src, srcWidth, srcHeight,
+ srcStride, dest, dstStride);
+#elif QT_ROTATION_ALGORITHM == QT_ROTATION_PACKING
+ // packed algorithm not implemented
+ qt_memrotate90_cachedRead<quint24,quint32>(src, srcWidth, srcHeight,
+ srcStride, dest, dstStride);
+#elif QT_ROTATION_ALGORITHM == QT_ROTATION_TILED
+ // packed algorithm not implemented
+ qt_memrotate90_tiled_unpacked<quint24,quint32>(src, srcWidth, srcHeight,
+ srcStride, dest, dstStride);
+#endif
+}
+
+template <>
+Q_STATIC_TEMPLATE_SPECIALIZATION
+inline void qt_memrotate90_template<quint18, quint32>(const quint32 *src,
+ int srcWidth, int srcHeight, int srcStride,
+ quint18 *dest, int dstStride)
+{
+#if QT_ROTATION_ALGORITHM == QT_ROTATION_CACHEDREAD
+ qt_memrotate90_cachedRead<quint18,quint32>(src, srcWidth, srcHeight,
+ srcStride, dest, dstStride);
+#elif QT_ROTATION_ALGORITHM == QT_ROTATION_CACHEDWRITE
+ qt_memrotate90_cachedWrite<quint18,quint32>(src, srcWidth, srcHeight,
+ srcStride, dest, dstStride);
+#elif QT_ROTATION_ALGORITHM == QT_ROTATION_PACKING
+ // packed algorithm not implemented
+ qt_memrotate90_cachedRead<quint18,quint32>(src, srcWidth, srcHeight,
+ srcStride, dest, dstStride);
+#elif QT_ROTATION_ALGORITHM == QT_ROTATION_TILED
+ // packed algorithm not implemented
+ qt_memrotate90_tiled_unpacked<quint18,quint32>(src, srcWidth, srcHeight,
+ srcStride, dest, dstStride);
+#endif
+}
+
+#define QT_IMPL_MEMROTATE(srctype, desttype) \
+void qt_memrotate90(const srctype *src, int w, int h, int sstride, \
+ desttype *dest, int dstride) \
+{ \
+ qt_memrotate90_template(src, w, h, sstride, dest, dstride); \
+} \
+void qt_memrotate180(const srctype *src, int w, int h, int sstride, \
+ desttype *dest, int dstride) \
+{ \
+ qt_memrotate180_template(src, w, h, sstride, dest, dstride); \
+} \
+void qt_memrotate270(const srctype *src, int w, int h, int sstride, \
+ desttype *dest, int dstride) \
+{ \
+ qt_memrotate270_template(src, w, h, sstride, dest, dstride); \
+}
+
+#define QT_IMPL_SIMPLE_MEMROTATE(srctype, desttype) \
+void qt_memrotate90(const srctype *src, int w, int h, int sstride, \
+ desttype *dest, int dstride) \
+{ \
+ qt_memrotate90_tiled_unpacked<desttype,srctype>(src, w, h, sstride, dest, dstride); \
+} \
+void qt_memrotate180(const srctype *src, int w, int h, int sstride, \
+ desttype *dest, int dstride) \
+{ \
+ qt_memrotate180_template(src, w, h, sstride, dest, dstride); \
+} \
+void qt_memrotate270(const srctype *src, int w, int h, int sstride, \
+ desttype *dest, int dstride) \
+{ \
+ qt_memrotate270_tiled_unpacked<desttype,srctype>(src, w, h, sstride, dest, dstride); \
+}
+
+
+
+
+QT_IMPL_MEMROTATE(quint32, quint32)
+QT_IMPL_MEMROTATE(quint32, quint16)
+QT_IMPL_MEMROTATE(quint16, quint32)
+QT_IMPL_MEMROTATE(quint16, quint16)
+QT_IMPL_MEMROTATE(quint24, quint24)
+QT_IMPL_MEMROTATE(quint32, quint24)
+QT_IMPL_MEMROTATE(quint32, quint18)
+QT_IMPL_MEMROTATE(quint32, quint8)
+QT_IMPL_MEMROTATE(quint16, quint8)
+QT_IMPL_MEMROTATE(qrgb444, quint8)
+QT_IMPL_MEMROTATE(quint8, quint8)
+
+#if defined(QT_QWS_ROTATE_BGR)
+QT_IMPL_SIMPLE_MEMROTATE(quint16, qbgr565)
+QT_IMPL_SIMPLE_MEMROTATE(quint32, qbgr565)
+QT_IMPL_SIMPLE_MEMROTATE(qrgb555, qbgr555)
+QT_IMPL_SIMPLE_MEMROTATE(quint32, qbgr555)
+#endif
+
+#ifdef QT_QWS_DEPTH_GENERIC
+QT_IMPL_MEMROTATE(quint32, qrgb_generic16)
+QT_IMPL_MEMROTATE(quint16, qrgb_generic16)
+#endif
+
+struct qrgb_gl_rgba
+{
+public:
+ inline qrgb_gl_rgba(quint32 v) {
+ if (QSysInfo::ByteOrder == QSysInfo::LittleEndian)
+ data = ((v << 16) & 0xff0000) | ((v >> 16) & 0xff) | (v & 0xff00ff00);
+ else
+ data = (v << 8) | ((v >> 24) & 0xff);
+ }
+
+ inline operator quint32() const { return data; }
+
+private:
+ quint32 data;
+} Q_PACKED;
+
+void Q_GUI_EXPORT qt_memrotate90_gl(const quint32 *src, int srcWidth, int srcHeight, int srcStride,
+ quint32 *dest, int dstStride)
+{
+ qt_memrotate90_template(src, srcWidth, srcHeight, srcStride, reinterpret_cast<qrgb_gl_rgba *>(dest), dstStride);
+}
+
+void qt_memrotate90_16(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl)
+{
+ qt_memrotate90((const ushort *)srcPixels, w, h, sbpl, (ushort *)destPixels, dbpl);
+}
+
+void qt_memrotate180_16(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl)
+{
+ qt_memrotate180((const ushort *)srcPixels, w, h, sbpl, (ushort *)destPixels, dbpl);
+}
+
+void qt_memrotate270_16(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl)
+{
+ qt_memrotate270((const ushort *)srcPixels, w, h, sbpl, (ushort *)destPixels, dbpl);
+}
+
+void qt_memrotate90_32(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl)
+{
+ qt_memrotate90((const uint *)srcPixels, w, h, sbpl, (uint *)destPixels, dbpl);
+}
+
+void qt_memrotate180_32(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl)
+{
+ qt_memrotate180((const uint *)srcPixels, w, h, sbpl, (uint *)destPixels, dbpl);
+}
+
+void qt_memrotate270_32(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl)
+{
+ qt_memrotate270((const uint *)srcPixels, w, h, sbpl, (uint *)destPixels, dbpl);
+}
+
+MemRotateFunc qMemRotateFunctions[QImage::NImageFormats][3] =
+// 90, 180, 270
+{
+ { 0, 0, 0 }, // Format_Invalid,
+ { 0, 0, 0 }, // Format_Mono,
+ { 0, 0, 0 }, // Format_MonoLSB,
+ { 0, 0, 0 }, // Format_Indexed8,
+ { qt_memrotate90_32, qt_memrotate180_32, qt_memrotate270_32 }, // Format_RGB32,
+ { qt_memrotate90_32, qt_memrotate180_32, qt_memrotate270_32 }, // Format_ARGB32,
+ { qt_memrotate90_32, qt_memrotate180_32, qt_memrotate270_32 }, // Format_ARGB32_Premultiplied,
+ { qt_memrotate90_16, qt_memrotate180_16, qt_memrotate270_16 }, // Format_RGB16,
+ { 0, 0, 0 }, // Format_ARGB8565_Premultiplied,
+ { 0, 0, 0 }, // Format_RGB666,
+ { 0, 0, 0 }, // Format_ARGB6666_Premultiplied,
+ { 0, 0, 0 }, // Format_RGB555,
+ { 0, 0, 0 }, // Format_ARGB8555_Premultiplied,
+ { 0, 0, 0 }, // Format_RGB888,
+ { 0, 0, 0 }, // Format_RGB444,
+ { 0, 0, 0 } // Format_ARGB4444_Premultiplied,
+};
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qmemrotate_p.h b/src/gui/painting/qmemrotate_p.h
new file mode 100644
index 0000000000..ecfa39397c
--- /dev/null
+++ b/src/gui/painting/qmemrotate_p.h
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMEMROTATE_P_H
+#define QMEMROTATE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qdrawhelper_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#define QT_ROTATION_CACHEDREAD 1
+#define QT_ROTATION_CACHEDWRITE 2
+#define QT_ROTATION_PACKING 3
+#define QT_ROTATION_TILED 4
+
+#ifndef QT_ROTATION_ALGORITHM
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+#define QT_ROTATION_ALGORITHM QT_ROTATION_TILED
+#else
+#define QT_ROTATION_ALGORITHM QT_ROTATION_CACHEDREAD
+#endif
+#endif
+
+#ifdef Q_WS_QWS
+#define Q_GUI_QWS_EXPORT Q_GUI_EXPORT
+#else
+#define Q_GUI_QWS_EXPORT
+#endif
+
+#define QT_DECL_MEMROTATE(srctype, desttype) \
+ void Q_GUI_QWS_EXPORT qt_memrotate90(const srctype*, int, int, int, desttype*, int); \
+ void Q_GUI_QWS_EXPORT qt_memrotate180(const srctype*, int, int, int, desttype*, int); \
+ void Q_GUI_QWS_EXPORT qt_memrotate270(const srctype*, int, int, int, desttype*, int)
+
+void Q_GUI_EXPORT qt_memrotate90(const quint32*, int, int, int, quint32*, int);
+void Q_GUI_QWS_EXPORT qt_memrotate180(const quint32*, int, int, int, quint32*, int);
+void Q_GUI_QWS_EXPORT qt_memrotate270(const quint32*, int, int, int, quint32*, int);
+
+QT_DECL_MEMROTATE(quint32, quint16);
+QT_DECL_MEMROTATE(quint16, quint32);
+QT_DECL_MEMROTATE(quint16, quint16);
+QT_DECL_MEMROTATE(quint24, quint24);
+QT_DECL_MEMROTATE(quint32, quint24);
+QT_DECL_MEMROTATE(quint32, quint18);
+QT_DECL_MEMROTATE(quint32, quint8);
+QT_DECL_MEMROTATE(quint16, quint8);
+QT_DECL_MEMROTATE(qrgb444, quint8);
+QT_DECL_MEMROTATE(quint8, quint8);
+
+#ifdef QT_QWS_ROTATE_BGR
+QT_DECL_MEMROTATE(quint16, qbgr565);
+QT_DECL_MEMROTATE(quint32, qbgr565);
+QT_DECL_MEMROTATE(qrgb555, qbgr555);
+QT_DECL_MEMROTATE(quint32, qbgr555);
+#endif
+
+#ifdef QT_QWS_DEPTH_GENERIC
+QT_DECL_MEMROTATE(quint32, qrgb_generic16);
+QT_DECL_MEMROTATE(quint16, qrgb_generic16);
+#endif
+
+#undef QT_DECL_MEMROTATE
+
+QT_END_NAMESPACE
+
+#endif // QMEMROTATE_P_H
diff --git a/src/gui/painting/qoutlinemapper.cpp b/src/gui/painting/qoutlinemapper.cpp
new file mode 100644
index 0000000000..aac50838d7
--- /dev/null
+++ b/src/gui/painting/qoutlinemapper.cpp
@@ -0,0 +1,393 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qoutlinemapper_p.h"
+#include <private/qpainterpath_p.h>
+#include "qmath.h"
+
+#include <stdlib.h>
+
+QT_BEGIN_NAMESPACE
+
+#define qreal_to_fixed_26_6(f) (int(f * 64))
+
+
+
+
+static const QRectF boundingRect(const QPointF *points, int pointCount)
+{
+ const QPointF *e = points;
+ const QPointF *last = points + pointCount;
+ qreal minx, maxx, miny, maxy;
+ minx = maxx = e->x();
+ miny = maxy = e->y();
+ while (++e < last) {
+ if (e->x() < minx)
+ minx = e->x();
+ else if (e->x() > maxx)
+ maxx = e->x();
+ if (e->y() < miny)
+ miny = e->y();
+ else if (e->y() > maxy)
+ maxy = e->y();
+ }
+ return QRectF(QPointF(minx, miny), QPointF(maxx, maxy));
+}
+
+
+QT_FT_Outline *QOutlineMapper::convertPath(const QPainterPath &path)
+{
+ Q_ASSERT(!path.isEmpty());
+ int elmCount = path.elementCount();
+#ifdef QT_DEBUG_CONVERT
+ printf("QOutlineMapper::convertPath(), size=%d\n", elmCount);
+#endif
+ beginOutline(path.fillRule());
+
+ for (int index=0; index<elmCount; ++index) {
+ const QPainterPath::Element &elm = path.elementAt(index);
+
+ switch (elm.type) {
+
+ case QPainterPath::MoveToElement:
+ if (index == elmCount - 1)
+ continue;
+ moveTo(elm);
+ break;
+
+ case QPainterPath::LineToElement:
+ lineTo(elm);
+ break;
+
+ case QPainterPath::CurveToElement:
+ curveTo(elm, path.elementAt(index + 1), path.elementAt(index + 2));
+ index += 2;
+ break;
+
+ default:
+ break; // This will never hit..
+ }
+ }
+
+ endOutline();
+ return outline();
+}
+
+QT_FT_Outline *QOutlineMapper::convertPath(const QVectorPath &path)
+{
+ int count = path.elementCount();
+
+#ifdef QT_DEBUG_CONVERT
+ printf("QOutlineMapper::convertPath(VP), size=%d\n", count);
+#endif
+ beginOutline(path.hasWindingFill() ? Qt::WindingFill : Qt::OddEvenFill);
+
+ if (path.elements()) {
+ // TODO: if we do closing of subpaths in convertElements instead we
+ // could avoid this loop
+ const QPainterPath::ElementType *elements = path.elements();
+ const QPointF *points = reinterpret_cast<const QPointF *>(path.points());
+
+ for (int index = 0; index < count; ++index) {
+ switch (elements[index]) {
+ case QPainterPath::MoveToElement:
+ if (index == count - 1)
+ continue;
+ moveTo(points[index]);
+ break;
+
+ case QPainterPath::LineToElement:
+ lineTo(points[index]);
+ break;
+
+ case QPainterPath::CurveToElement:
+ curveTo(points[index], points[index+1], points[index+2]);
+ index += 2;
+ break;
+
+ default:
+ break; // This will never hit..
+ }
+ }
+
+ } else {
+ // ### We can kill this copying and just use the buffer straight...
+
+ m_elements.resize(count);
+ if (count)
+ memcpy(m_elements.data(), path.points(), count* sizeof(QPointF));
+
+ m_element_types.resize(0);
+ }
+
+ endOutline();
+ return outline();
+}
+
+
+void QOutlineMapper::endOutline()
+{
+ closeSubpath();
+
+ int element_count = m_elements.size();
+
+ if (element_count == 0) {
+ memset(&m_outline, 0, sizeof(m_outline));
+ return;
+ }
+
+ QPointF *elements;
+
+ // Transform the outline
+ if (m_txop == QTransform::TxNone) {
+ elements = m_elements.data();
+ } else {
+ if (m_txop == QTransform::TxTranslate) {
+ for (int i=0; i<m_elements.size(); ++i) {
+ const QPointF &e = m_elements.at(i);
+ m_elements_dev << QPointF(e.x() + m_dx, e.y() + m_dy);
+ }
+ } else if (m_txop == QTransform::TxScale) {
+ for (int i=0; i<m_elements.size(); ++i) {
+ const QPointF &e = m_elements.at(i);
+ m_elements_dev << QPointF(m_m11 * e.x() + m_dx, m_m22 * e.y() + m_dy);
+ }
+ } else if (m_txop < QTransform::TxProject) {
+ for (int i=0; i<m_elements.size(); ++i) {
+ const QPointF &e = m_elements.at(i);
+ m_elements_dev << QPointF(m_m11 * e.x() + m_m21 * e.y() + m_dx,
+ m_m22 * e.y() + m_m12 * e.x() + m_dy);
+ }
+ } else {
+ const QVectorPath vp((qreal *)m_elements.data(), m_elements.size(), m_element_types.size() ? m_element_types.data() : 0);
+ QPainterPath path = vp.convertToPainterPath();
+ path = QTransform(m_m11, m_m12, m_m13, m_m21, m_m22, m_m23, m_dx, m_dy, m_m33).map(path);
+ if (!(m_outline.flags & QT_FT_OUTLINE_EVEN_ODD_FILL))
+ path.setFillRule(Qt::WindingFill);
+ uint old_txop = m_txop;
+ m_txop = QTransform::TxNone;
+ if (path.isEmpty())
+ m_valid = false;
+ else
+ convertPath(path);
+ m_txop = old_txop;
+ return;
+ }
+ elements = m_elements_dev.data();
+ }
+
+ controlPointRect = boundingRect(elements, element_count);
+
+#ifdef QT_DEBUG_CONVERT
+ printf(" - control point rect (%.2f, %.2f) %.2f x %.2f, clip=(%d,%d, %dx%d)\n",
+ controlPointRect.x(), controlPointRect.y(),
+ controlPointRect.width(), controlPointRect.height(),
+ m_clip_rect.x(), m_clip_rect.y(), m_clip_rect.width(), m_clip_rect.height());
+#endif
+
+
+ // Check for out of dev bounds...
+ const bool do_clip = !m_in_clip_elements && ((controlPointRect.left() < -QT_RASTER_COORD_LIMIT
+ || controlPointRect.right() > QT_RASTER_COORD_LIMIT
+ || controlPointRect.top() < -QT_RASTER_COORD_LIMIT
+ || controlPointRect.bottom() > QT_RASTER_COORD_LIMIT
+ || controlPointRect.width() > QT_RASTER_COORD_LIMIT
+ || controlPointRect.height() > QT_RASTER_COORD_LIMIT));
+
+ if (do_clip) {
+ clipElements(elements, elementTypes(), element_count);
+ } else {
+ convertElements(elements, elementTypes(), element_count);
+ }
+}
+
+void QOutlineMapper::convertElements(const QPointF *elements,
+ const QPainterPath::ElementType *types,
+ int element_count)
+{
+
+ if (types) {
+ // Translate into FT coords
+ const QPointF *e = elements;
+ for (int i=0; i<element_count; ++i) {
+ switch (*types) {
+ case QPainterPath::MoveToElement:
+ {
+ QT_FT_Vector pt_fixed = { qreal_to_fixed_26_6(e->x()),
+ qreal_to_fixed_26_6(e->y()) };
+ if (i != 0)
+ m_contours << m_points.size() - 1;
+ m_points << pt_fixed;
+ m_tags << QT_FT_CURVE_TAG_ON;
+ }
+ break;
+
+ case QPainterPath::LineToElement:
+ {
+ QT_FT_Vector pt_fixed = { qreal_to_fixed_26_6(e->x()),
+ qreal_to_fixed_26_6(e->y()) };
+ m_points << pt_fixed;
+ m_tags << QT_FT_CURVE_TAG_ON;
+ }
+ break;
+
+ case QPainterPath::CurveToElement:
+ {
+ QT_FT_Vector cp1_fixed = { qreal_to_fixed_26_6(e->x()),
+ qreal_to_fixed_26_6(e->y()) };
+ ++e;
+ QT_FT_Vector cp2_fixed = { qreal_to_fixed_26_6((e)->x()),
+ qreal_to_fixed_26_6((e)->y()) };
+ ++e;
+ QT_FT_Vector ep_fixed = { qreal_to_fixed_26_6((e)->x()),
+ qreal_to_fixed_26_6((e)->y()) };
+
+ m_points << cp1_fixed << cp2_fixed << ep_fixed;
+ m_tags << QT_FT_CURVE_TAG_CUBIC
+ << QT_FT_CURVE_TAG_CUBIC
+ << QT_FT_CURVE_TAG_ON;
+
+ types += 2;
+ i += 2;
+ }
+ break;
+ default:
+ break;
+ }
+ ++types;
+ ++e;
+ }
+ } else {
+ // Plain polygon...
+ const QPointF *last = elements + element_count;
+ const QPointF *e = elements;
+ while (e < last) {
+ QT_FT_Vector pt_fixed = { qreal_to_fixed_26_6(e->x()),
+ qreal_to_fixed_26_6(e->y()) };
+ m_points << pt_fixed;
+ m_tags << QT_FT_CURVE_TAG_ON;
+ ++e;
+ }
+ }
+
+ // close the very last subpath
+ m_contours << m_points.size() - 1;
+
+ m_outline.n_contours = m_contours.size();
+ m_outline.n_points = m_points.size();
+
+ m_outline.points = m_points.data();
+ m_outline.tags = m_tags.data();
+ m_outline.contours = m_contours.data();
+
+#ifdef QT_DEBUG_CONVERT
+ printf("QOutlineMapper::endOutline\n");
+
+ printf(" - contours: %d\n", m_outline.n_contours);
+ for (int i=0; i<m_outline.n_contours; ++i) {
+ printf(" - %d\n", m_outline.contours[i]);
+ }
+
+ printf(" - points: %d\n", m_outline.n_points);
+ for (int i=0; i<m_outline.n_points; ++i) {
+ printf(" - %d -- %.2f, %.2f, (%d, %d)\n", i,
+ (double) (m_outline.points[i].x / 64.0),
+ (double) (m_outline.points[i].y / 64.0),
+ (int) m_outline.points[i].x, (int) m_outline.points[i].y);
+ }
+#endif
+}
+
+void QOutlineMapper::clipElements(const QPointF *elements,
+ const QPainterPath::ElementType *types,
+ int element_count)
+{
+ // We could save a bit of time by actually implementing them fully
+ // instead of going through convenience functionallity, but since
+ // this part of code hardly every used, it shouldn't matter.
+
+ m_in_clip_elements = true;
+
+ QPainterPath path;
+
+ if (!(m_outline.flags & QT_FT_OUTLINE_EVEN_ODD_FILL))
+ path.setFillRule(Qt::WindingFill);
+
+ if (types) {
+ for (int i=0; i<element_count; ++i) {
+ switch (types[i]) {
+ case QPainterPath::MoveToElement:
+ path.moveTo(elements[i]);
+ break;
+
+ case QPainterPath::LineToElement:
+ path.lineTo(elements[i]);
+ break;
+
+ case QPainterPath::CurveToElement:
+ path.cubicTo(elements[i], elements[i+1], elements[i+2]);
+ i += 2;
+ break;
+ default:
+ break;
+ }
+ }
+ } else {
+ path.moveTo(elements[0]);
+ for (int i=1; i<element_count; ++i)
+ path.lineTo(elements[i]);
+ }
+
+ QPainterPath clipPath;
+ clipPath.addRect(m_clip_rect);
+ QPainterPath clippedPath = path.intersected(clipPath);
+ uint old_txop = m_txop;
+ m_txop = QTransform::TxNone;
+ if (clippedPath.isEmpty())
+ m_valid = false;
+ else
+ convertPath(clippedPath);
+ m_txop = old_txop;
+
+ m_in_clip_elements = false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qoutlinemapper_p.h b/src/gui/painting/qoutlinemapper_p.h
new file mode 100644
index 0000000000..4dd28ac172
--- /dev/null
+++ b/src/gui/painting/qoutlinemapper_p.h
@@ -0,0 +1,241 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QOUTLINEMAPPER_P_H
+#define QOUTLINEMAPPER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qrect.h>
+
+#include <QtGui/qtransform.h>
+#include <QtGui/qpainterpath.h>
+
+#define QT_FT_BEGIN_HEADER
+#define QT_FT_END_HEADER
+
+#include <private/qrasterdefs_p.h>
+#include <private/qdatabuffer_p.h>
+#include "qpaintengineex_p.h"
+
+QT_BEGIN_NAMESPACE
+
+// This limitations comes from qgrayraster.c. Any higher and
+// rasterization of shapes will produce incorrect results.
+const int QT_RASTER_COORD_LIMIT = 32767;
+
+//#define QT_DEBUG_CONVERT
+
+/********************************************************************************
+ * class QOutlineMapper
+ *
+ * Used to map between QPainterPath and the QT_FT_Outline structure used by the
+ * freetype scanconvertor.
+ *
+ * The outline mapper uses a path iterator to get points from the path,
+ * so that it is possible to transform the points as they are converted. The
+ * callback can be a noop, translate or full-fledged xform. (Tests indicated
+ * that using a C callback was low cost).
+ */
+class QOutlineMapper
+{
+public:
+ QOutlineMapper() :
+ m_element_types(0),
+ m_elements(0),
+ m_elements_dev(0),
+ m_points(0),
+ m_tags(0),
+ m_contours(0),
+ m_polygon_dev(0),
+ m_in_clip_elements(false)
+ {
+ }
+
+ /*!
+ Sets up the matrix to be used for conversion. This also
+ sets up the qt_path_iterator function that is used as a callback
+ to get points.
+ */
+ void setMatrix(const QTransform &m)
+ {
+ m_m11 = m.m11();
+ m_m12 = m.m12();
+ m_m13 = m.m13();
+ m_m21 = m.m21();
+ m_m22 = m.m22();
+ m_m23 = m.m23();
+ m_m33 = m.m33();
+ m_dx = m.dx();
+ m_dy = m.dy();
+ m_txop = m.type();
+ }
+
+ void beginOutline(Qt::FillRule fillRule)
+ {
+#ifdef QT_DEBUG_CONVERT
+ printf("QOutlineMapper::beginOutline rule=%d\n", fillRule);
+#endif
+ m_valid = true;
+ m_elements.reset();
+ m_elements_dev.reset();
+ m_element_types.reset();
+ m_points.reset();
+ m_tags.reset();
+ m_contours.reset();
+ m_outline.flags = fillRule == Qt::WindingFill
+ ? QT_FT_OUTLINE_NONE
+ : QT_FT_OUTLINE_EVEN_ODD_FILL;
+ m_subpath_start = 0;
+ }
+
+ void endOutline();
+
+ void clipElements(const QPointF *points, const QPainterPath::ElementType *types, int count);
+
+ void convertElements(const QPointF *points, const QPainterPath::ElementType *types, int count);
+
+ inline void moveTo(const QPointF &pt) {
+#ifdef QT_DEBUG_CONVERT
+ printf("QOutlineMapper::moveTo() (%f, %f)\n", pt.x(), pt.y());
+#endif
+ closeSubpath();
+ m_subpath_start = m_elements.size();
+ m_elements << pt;
+ m_element_types << QPainterPath::MoveToElement;
+ }
+
+ inline void lineTo(const QPointF &pt) {
+#ifdef QT_DEBUG_CONVERT
+ printf("QOutlineMapper::lineTo() (%f, %f)\n", pt.x(), pt.y());
+#endif
+ m_elements.add(pt);
+ m_element_types << QPainterPath::LineToElement;
+ }
+
+ inline void curveTo(const QPointF &cp1, const QPointF &cp2, const QPointF &ep) {
+#ifdef QT_DEBUG_CONVERT
+ printf("QOutlineMapper::curveTo() (%f, %f)\n", ep.x(), ep.y());
+#endif
+ m_elements << cp1 << cp2 << ep;
+ m_element_types << QPainterPath::CurveToElement
+ << QPainterPath::CurveToDataElement
+ << QPainterPath::CurveToDataElement;
+ }
+
+ inline void closeSubpath() {
+ int element_count = m_elements.size();
+ if (element_count > 0) {
+ if (m_elements.at(element_count-1) != m_elements.at(m_subpath_start)) {
+#ifdef QT_DEBUG_CONVERT
+ printf(" - implicitly closing\n");
+#endif
+ // Put the object on the stack to avoid the odd case where
+ // lineTo reallocs the databuffer and the QPointF & will
+ // be invalidated.
+ QPointF pt = m_elements.at(m_subpath_start);
+
+ // only do lineTo if we have element_type array...
+ if (m_element_types.size())
+ lineTo(pt);
+ else
+ m_elements << pt;
+
+ }
+ }
+ }
+
+ QT_FT_Outline *outline() {
+ if (m_valid)
+ return &m_outline;
+ return 0;
+ }
+
+ QT_FT_Outline *convertPath(const QPainterPath &path);
+ QT_FT_Outline *convertPath(const QVectorPath &path);
+
+ inline QPainterPath::ElementType *elementTypes() const { return m_element_types.size() == 0 ? 0 : m_element_types.data(); }
+
+public:
+ QDataBuffer<QPainterPath::ElementType> m_element_types;
+ QDataBuffer<QPointF> m_elements;
+ QDataBuffer<QPointF> m_elements_dev;
+ QDataBuffer<QT_FT_Vector> m_points;
+ QDataBuffer<char> m_tags;
+ QDataBuffer<int> m_contours;
+
+ QRect m_clip_rect;
+ QDataBuffer<QPointF> m_polygon_dev;
+
+ QRectF controlPointRect; // only valid after endOutline()
+
+ QT_FT_Outline m_outline;
+ uint m_txop;
+
+ int m_subpath_start;
+
+ // Matrix
+ qreal m_m11;
+ qreal m_m12;
+ qreal m_m13;
+ qreal m_m21;
+ qreal m_m22;
+ qreal m_m23;
+ qreal m_m33;
+ qreal m_dx;
+ qreal m_dy;
+
+ bool m_valid;
+ bool m_in_clip_elements;
+};
+
+QT_END_NAMESPACE
+
+#endif // QOUTLINEMAPPER_P_H
diff --git a/src/gui/painting/qpaintbuffer.cpp b/src/gui/painting/qpaintbuffer.cpp
new file mode 100644
index 0000000000..7870defd22
--- /dev/null
+++ b/src/gui/painting/qpaintbuffer.cpp
@@ -0,0 +1,2287 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <qmath.h>
+#include <private/qpainterpath_p.h>
+#include <private/qpaintbuffer_p.h>
+//#include <private/qtextengine_p.h>
+#include <private/qfontengine_p.h>
+#include <private/qemulationpaintengine_p.h>
+#include <private/qimage_p.h>
+#include <qstatictext.h>
+#include <private/qstatictext_p.h>
+#include <private/qrawfont_p.h>
+
+#include <QDebug>
+
+// #define QPAINTBUFFER_DEBUG_DRAW
+
+QT_BEGIN_NAMESPACE
+
+extern void qt_format_text(const QFont &font,
+ const QRectF &_r, int tf, const QTextOption *option, const QString& str, QRectF *brect,
+ int tabstops, int* tabarray, int tabarraylen,
+ QPainter *painter);
+
+QTextItemIntCopy::QTextItemIntCopy(const QTextItem &item)
+ : m_item(static_cast<const QTextItemInt &>(item))
+{
+ QChar *chars = new QChar[m_item.num_chars];
+ unsigned short *logClusters = new unsigned short[m_item.num_chars];
+ memcpy(chars, m_item.chars, m_item.num_chars * sizeof(QChar));
+ memcpy(logClusters, m_item.logClusters, m_item.num_chars * sizeof(unsigned short));
+ m_item.chars = chars;
+ m_item.logClusters = logClusters;
+
+ const int size = QGlyphLayout::spaceNeededForGlyphLayout(m_item.glyphs.numGlyphs);
+ char *glyphLayoutData = new char[size];
+ QGlyphLayout glyphs(glyphLayoutData, m_item.glyphs.numGlyphs);
+ memcpy(glyphs.offsets, m_item.glyphs.offsets, m_item.glyphs.numGlyphs * sizeof(QFixedPoint));
+ memcpy(glyphs.glyphs, m_item.glyphs.glyphs, m_item.glyphs.numGlyphs * sizeof(HB_Glyph));
+ memcpy(glyphs.advances_x, m_item.glyphs.advances_x, m_item.glyphs.numGlyphs * sizeof(QFixed));
+ memcpy(glyphs.advances_y, m_item.glyphs.advances_y, m_item.glyphs.numGlyphs * sizeof(QFixed));
+ memcpy(glyphs.justifications, m_item.glyphs.justifications, m_item.glyphs.numGlyphs * sizeof(QGlyphJustification));
+ memcpy(glyphs.attributes, m_item.glyphs.attributes, m_item.glyphs.numGlyphs * sizeof(HB_GlyphAttributes));
+ m_item.glyphs = glyphs;
+
+ m_font = *m_item.f;
+ m_item.f = &m_font;
+
+ m_item.fontEngine->ref.ref(); // Increment reference count.
+}
+
+QTextItemIntCopy::~QTextItemIntCopy()
+{
+ delete m_item.chars;
+ delete m_item.logClusters;
+ delete m_item.glyphs.data();
+ if (!m_item.fontEngine->ref.deref())
+ delete m_item.fontEngine;
+}
+
+/************************************************************************
+ *
+ * QPaintBufferSignalProxy
+ *
+ ************************************************************************/
+
+Q_GLOBAL_STATIC(QPaintBufferSignalProxy, theSignalProxy)
+
+QPaintBufferSignalProxy *QPaintBufferSignalProxy::instance()
+{
+ return theSignalProxy();
+}
+
+/************************************************************************
+ *
+ * QPaintBufferPrivate
+ *
+ ************************************************************************/
+
+QPaintBufferPrivate::QPaintBufferPrivate()
+ : ref(1), engine(0), penWidthAdjustment(0)
+ , calculateBoundingRect(true)
+ , cache(0)
+{
+}
+
+QPaintBufferPrivate::~QPaintBufferPrivate()
+{
+ QPaintBufferSignalProxy::instance()->emitAboutToDestroy(this);
+
+ for (int i = 0; i < commands.size(); ++i) {
+ const QPaintBufferCommand &cmd = commands.at(i);
+ if (cmd.id == QPaintBufferPrivate::Cmd_DrawTextItem)
+ delete reinterpret_cast<QTextItemIntCopy *>(qvariant_cast<void *>(variants.at(cmd.offset)));
+ }
+}
+
+
+inline void QPaintBufferPrivate::updateBoundingRect(const QRectF &br)
+{
+ // transform to device coords and adjust for pen width
+ Q_ASSERT(engine && engine->painter());
+ QPainter *painter = engine->painter();
+ const QTransform transform = painter->transform();
+ QRectF devRect = transform.mapRect(br);
+ if (penWidthAdjustment > 0) {
+ devRect = devRect.adjusted(-penWidthAdjustment, -penWidthAdjustment,
+ penWidthAdjustment, penWidthAdjustment);
+ }
+
+ if (boundingRect.isEmpty()) {
+ boundingRect = devRect;
+ } else {
+ qreal min_x = qMin(devRect.left(), boundingRect.left());
+ qreal min_y = qMin(devRect.top(), boundingRect.top());
+ qreal max_x = qMax(devRect.right(), boundingRect.right());
+ qreal max_y = qMax(devRect.bottom(), boundingRect.bottom());
+ boundingRect = QRectF(min_x, min_y, max_x - min_x, max_y - min_y);
+ }
+ if (painter->hasClipping())
+ boundingRect &= transform.mapRect(painter->clipRegion().boundingRect());
+}
+
+
+/************************************************************************
+ *
+ * QPaintBuffer
+ *
+ ************************************************************************/
+
+
+
+QPaintBuffer::QPaintBuffer()
+ : d_ptr(new QPaintBufferPrivate)
+{
+}
+
+QPaintBuffer::~QPaintBuffer()
+{
+ if (!d_ptr->ref.deref())
+ delete d_ptr;
+}
+
+QPaintBuffer::QPaintBuffer(const QPaintBuffer &other)
+ : QPaintDevice(), d_ptr(other.d_ptr)
+{
+ d_ptr->ref.ref();
+}
+
+QPaintEngine *QPaintBuffer::paintEngine() const
+{
+ QPaintBufferPrivate *d = const_cast<QPaintBuffer *>(this)->d_ptr;
+ if (!d->engine)
+ d->engine = new QPaintBufferEngine(d);
+ return d->engine;
+}
+
+
+int QPaintBuffer::metric(PaintDeviceMetric metric) const
+{
+ int val = 0;
+ switch (metric) {
+ case PdmWidth:
+ val = qCeil(d_ptr->boundingRect.width());
+ break;
+ case PdmHeight:
+ val = qCeil(d_ptr->boundingRect.height());
+ break;
+ case PdmDpiX:
+ case PdmPhysicalDpiX:
+ val = qt_defaultDpiX();
+ break;
+ case PdmDpiY:
+ case PdmPhysicalDpiY:
+ val = qt_defaultDpiY();
+ break;
+ default:
+ val = QPaintDevice::metric(metric);
+ }
+
+ return val;
+}
+
+int QPaintBuffer::devType() const
+{
+ return QInternal::PaintBuffer;
+}
+
+QPaintBuffer &QPaintBuffer::operator=(const QPaintBuffer &other)
+{
+ if (other.d_ptr != d_ptr) {
+ QPaintBufferPrivate *data = other.d_ptr;
+ data->ref.ref();
+ if (d_ptr->ref.deref())
+ delete d_ptr;
+ d_ptr = data;
+ }
+ return *this;
+}
+
+bool QPaintBuffer::isEmpty() const
+{
+ return d_ptr->commands.isEmpty();
+}
+
+
+
+void QPaintBuffer::draw(QPainter *painter, int frame) const
+{
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBuffer::draw() --------------------------------";
+
+ Q_D(const QPaintBuffer);
+ printf("Float buffer:");
+ for (int i=0; i<d->floats.size(); i++) {
+ if ((i % 10) == 0) {
+ printf("\n%4d-%4d: ", i, i+9);
+ }
+ printf("%4.2f ", d->floats[i]);
+ }
+ printf("\n");
+
+ printf("Int Buffer:");
+ for (int i=0; i<d->ints.size(); i++) {
+ if ((i % 10) == 0) {
+ printf("\n%4d-%4d: ", i, i+10);
+ }
+ printf("%5d", d->ints[i]);
+ }
+ printf("\n");
+#endif
+
+ processCommands(painter, frameStartIndex(frame), frameEndIndex(frame));
+
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBuffer::draw() -------------------------------- DONE!";
+#endif
+}
+
+int QPaintBuffer::frameStartIndex(int frame) const
+{
+ return (frame == 0) ? 0 : d_ptr->frames.at(frame - 1);
+}
+
+int QPaintBuffer::frameEndIndex(int frame) const
+{
+ return (frame == d_ptr->frames.size()) ? d_ptr->commands.size() : d_ptr->frames.at(frame);
+}
+
+int QPaintBuffer::processCommands(QPainter *painter, int begin, int end) const
+{
+ if (!painter || !painter->isActive())
+ return 0;
+
+ QPaintEngineEx *xengine = painter->paintEngine()->isExtended()
+ ? (QPaintEngineEx *) painter->paintEngine() : 0;
+ if (xengine) {
+ QPaintEngineExReplayer player;
+ player.processCommands(*this, painter, begin, end);
+ } else {
+ QPainterReplayer player;
+ player.processCommands(*this, painter, begin, end);
+ }
+
+ int depth = 0;
+ for (int i = begin; i < end; ++i) {
+ const QPaintBufferCommand &cmd = d_ptr->commands.at(i);
+ if (cmd.id == QPaintBufferPrivate::Cmd_Save)
+ ++depth;
+ else if (cmd.id == QPaintBufferPrivate::Cmd_Restore)
+ --depth;
+ }
+ return depth;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QString QPaintBuffer::commandDescription(int command) const
+{
+ QString desc;
+ QDebug debug(&desc);
+
+ const QPaintBufferCommand &cmd = d_ptr->commands.at(command);
+
+ switch (cmd.id) {
+ case QPaintBufferPrivate::Cmd_Save: {
+ debug << "Cmd_Save";
+ break; }
+
+ case QPaintBufferPrivate::Cmd_Restore: {
+ debug << "Cmd_Restore";
+ break; }
+
+ case QPaintBufferPrivate::Cmd_SetBrush: {
+ QBrush brush = qvariant_cast<QBrush>(d_ptr->variants.at(cmd.offset));
+ debug << "Cmd_SetBrush: " << brush;
+ break; }
+
+ case QPaintBufferPrivate::Cmd_SetBrushOrigin: {
+ debug << "Cmd_SetBrushOrigin: " << d_ptr->variants.at(cmd.offset).toPointF();
+ break; }
+
+ case QPaintBufferPrivate::Cmd_SetCompositionMode: {
+ QPainter::CompositionMode mode = (QPainter::CompositionMode) cmd.extra;
+ debug << "ExCmd_SetCompositionMode, mode: " << mode;
+ break; }
+
+ case QPaintBufferPrivate::Cmd_SetOpacity: {
+ debug << "ExCmd_SetOpacity: " << d_ptr->variants.at(cmd.offset).toDouble();
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawVectorPath: {
+ debug << "ExCmd_DrawVectorPath: size: " << cmd.size
+// << ", hints:" << d->ints[cmd.offset2+cmd.size]
+ << "pts/elms:" << cmd.offset << cmd.offset2;
+ break; }
+
+ case QPaintBufferPrivate::Cmd_StrokeVectorPath: {
+ QPen pen = qvariant_cast<QPen>(d_ptr->variants.at(cmd.extra));
+ debug << "ExCmd_StrokeVectorPath: size: " << cmd.size
+// << ", hints:" << d->ints[cmd.offset2+cmd.size]
+ << "pts/elms:" << cmd.offset << cmd.offset2 << pen;
+ break; }
+
+ case QPaintBufferPrivate::Cmd_FillVectorPath: {
+ QBrush brush = qvariant_cast<QBrush>(d_ptr->variants.at(cmd.extra));
+ debug << "ExCmd_FillVectorPath: size: " << cmd.size
+// << ", hints:" << d->ints[cmd.offset2+cmd.size]
+ << "pts/elms:" << cmd.offset << cmd.offset2 << brush;
+ break; }
+
+ case QPaintBufferPrivate::Cmd_FillRectBrush: {
+ QBrush brush = qvariant_cast<QBrush>(d_ptr->variants.at(cmd.extra));
+ QRectF *rect = (QRectF *)(d_ptr->floats.constData() + cmd.offset);
+ debug << "ExCmd_FillRectBrush, offset: " << cmd.offset << " rect: " << *rect << " brush: " << brush;
+ break; }
+
+ case QPaintBufferPrivate::Cmd_FillRectColor: {
+ QColor color = qvariant_cast<QColor>(d_ptr->variants.at(cmd.extra));
+ QRectF *rect = (QRectF *)(d_ptr->floats.constData() + cmd.offset);
+ debug << "ExCmd_FillRectBrush, offset: " << cmd.offset << " rect: " << *rect << " color: " << color;
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawPolygonF: {
+ debug << "ExCmd_DrawPolygonF, offset: " << cmd.offset << " size: " << cmd.size
+ << " mode: " << cmd.extra
+ << d_ptr->floats.at(cmd.offset)
+ << d_ptr->floats.at(cmd.offset+1);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawPolygonI: {
+ debug << "ExCmd_DrawPolygonI, offset: " << cmd.offset << " size: " << cmd.size
+ << " mode: " << cmd.extra
+ << d_ptr->ints.at(cmd.offset)
+ << d_ptr->ints.at(cmd.offset+1);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawEllipseF: {
+ debug << "ExCmd_DrawEllipseF, offset: " << cmd.offset;
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawLineF: {
+ debug << "ExCmd_DrawLineF, offset: " << cmd.offset << " size: " << cmd.size;
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawLineI: {
+ debug << "ExCmd_DrawLineI, offset: " << cmd.offset << " size: " << cmd.size;
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawPointsF: {
+ debug << "ExCmd_DrawPointsF, offset: " << cmd.offset << " size: " << cmd.size;
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawPointsI: {
+ debug << "ExCmd_DrawPointsI, offset: " << cmd.offset << " size: " << cmd.size;
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawPolylineF: {
+ debug << "ExCmd_DrawPolylineF, offset: " << cmd.offset << " size: " << cmd.size;
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawPolylineI: {
+ debug << "ExCmd_DrawPolylineI, offset: " << cmd.offset << " size: " << cmd.size;
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawRectF: {
+ debug << "ExCmd_DrawRectF, offset: " << cmd.offset << " size: " << cmd.size;
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawRectI: {
+ debug << "ExCmd_DrawRectI, offset: " << cmd.offset << " size: " << cmd.size;
+ break; }
+
+ case QPaintBufferPrivate::Cmd_SetClipEnabled: {
+ bool clipEnabled = d_ptr->variants.at(cmd.offset).toBool();
+ debug << "ExCmd_SetClipEnabled:" << clipEnabled;
+ break; }
+
+ case QPaintBufferPrivate::Cmd_ClipVectorPath: {
+ QVectorPathCmd path(d_ptr, cmd);
+ debug << "ExCmd_ClipVectorPath:" << path().elementCount();
+ break; }
+
+ case QPaintBufferPrivate::Cmd_ClipRect: {
+ QRect rect(QPoint(d_ptr->ints.at(cmd.offset), d_ptr->ints.at(cmd.offset + 1)),
+ QPoint(d_ptr->ints.at(cmd.offset + 2), d_ptr->ints.at(cmd.offset + 3)));
+ debug << "ExCmd_ClipRect:" << rect << cmd.extra;
+ break; }
+
+ case QPaintBufferPrivate::Cmd_ClipRegion: {
+ QRegion region(d_ptr->variants.at(cmd.offset).value<QRegion>());
+ debug << "ExCmd_ClipRegion:" << region.boundingRect() << cmd.extra;
+ break; }
+
+ case QPaintBufferPrivate::Cmd_SetPen: {
+ QPen pen = qvariant_cast<QPen>(d_ptr->variants.at(cmd.offset));
+ debug << "Cmd_SetPen: " << pen;
+ break; }
+
+ case QPaintBufferPrivate::Cmd_SetTransform: {
+ QTransform xform = qvariant_cast<QTransform>(d_ptr->variants.at(cmd.offset));
+ debug << "Cmd_SetTransform, offset: " << cmd.offset << xform;
+ break; }
+
+ case QPaintBufferPrivate::Cmd_SetRenderHints: {
+ debug << "Cmd_SetRenderHints, hints: " << cmd.extra;
+ break; }
+
+ case QPaintBufferPrivate::Cmd_SetBackgroundMode: {
+ debug << "Cmd_SetBackgroundMode: " << cmd.extra;
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawConvexPolygonF: {
+ debug << "Cmd_DrawConvexPolygonF, offset: " << cmd.offset << " size: " << cmd.size;
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawConvexPolygonI: {
+ debug << "Cmd_DrawConvexPolygonI, offset: " << cmd.offset << " size: " << cmd.size;
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawEllipseI: {
+ debug << "Cmd_DrawEllipseI, offset: " << cmd.offset;
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawPixmapRect: {
+ QPixmap pm(d_ptr->variants.at(cmd.offset).value<QPixmap>());
+ QRectF r(d_ptr->floats.at(cmd.extra), d_ptr->floats.at(cmd.extra+1),
+ d_ptr->floats.at(cmd.extra+2), d_ptr->floats.at(cmd.extra+3));
+
+ QRectF sr(d_ptr->floats.at(cmd.extra+4), d_ptr->floats.at(cmd.extra+5),
+ d_ptr->floats.at(cmd.extra+6), d_ptr->floats.at(cmd.extra+7));
+ debug << "Cmd_DrawPixmapRect:" << r << sr << pm.size();
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawPixmapPos: {
+ QPixmap pm(d_ptr->variants.at(cmd.offset).value<QPixmap>());
+ QPointF pos(d_ptr->floats.at(cmd.extra), d_ptr->floats.at(cmd.extra+1));
+ debug << "Cmd_DrawPixmapPos:" << pos << pm.size();
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawTiledPixmap: {
+ QPixmap pm(d_ptr->variants.at(cmd.offset).value<QPixmap>());
+ QRectF r(d_ptr->floats.at(cmd.extra), d_ptr->floats.at(cmd.extra+1),
+ d_ptr->floats.at(cmd.extra+2), d_ptr->floats.at(cmd.extra+3));
+
+ QPointF offset(d_ptr->floats.at(cmd.extra+4), d_ptr->floats.at(cmd.extra+5));
+ debug << "Cmd_DrawTiledPixmap:" << r << offset << pm.size();
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawImageRect: {
+ QImage image(d_ptr->variants.at(cmd.offset).value<QImage>());
+ QRectF r(d_ptr->floats.at(cmd.extra), d_ptr->floats.at(cmd.extra+1),
+ d_ptr->floats.at(cmd.extra+2), d_ptr->floats.at(cmd.extra+3));
+ QRectF sr(d_ptr->floats.at(cmd.extra+4), d_ptr->floats.at(cmd.extra+5),
+ d_ptr->floats.at(cmd.extra+6), d_ptr->floats.at(cmd.extra+7));
+ debug << "Cmd_DrawImageRect:" << r << sr << image.size();
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawImagePos: {
+ QImage image(d_ptr->variants.at(cmd.offset).value<QImage>());
+ QPointF pos(d_ptr->floats.at(cmd.extra), d_ptr->floats.at(cmd.extra+1));
+ debug << "Cmd_DrawImagePos:" << pos << image.size();
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawText: {
+ QPointF pos(d_ptr->floats.at(cmd.extra), d_ptr->floats.at(cmd.extra+1));
+ QList<QVariant> variants(d_ptr->variants.at(cmd.offset).value<QList<QVariant> >());
+
+ QFont font(variants.at(0).value<QFont>());
+ QString text(variants.at(1).value<QString>());
+
+ debug << "Cmd_DrawText:" << pos << text << font.family();
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawTextItem: {
+ QPointF pos(d_ptr->floats.at(cmd.extra), d_ptr->floats.at(cmd.extra+1));
+ QTextItemIntCopy *tiCopy = reinterpret_cast<QTextItemIntCopy *>(qvariant_cast<void *>(d_ptr->variants.at(cmd.offset)));
+ QTextItemInt &ti = (*tiCopy)();
+ QString text(ti.text());
+
+ QFont font(ti.font());
+ font.setUnderline(false);
+ font.setStrikeOut(false);
+ font.setOverline(false);
+
+ const QTextItemInt &si = static_cast<const QTextItemInt &>(ti);
+ qreal justificationWidth = 0;
+ if (si.justified)
+ justificationWidth = si.width.toReal();
+
+ debug << "Cmd_DrawTextItem:" << pos << " " << text;
+ break; }
+ case QPaintBufferPrivate::Cmd_SystemStateChanged: {
+ QRegion systemClip(d_ptr->variants.at(cmd.offset).value<QRegion>());
+
+ debug << "Cmd_SystemStateChanged:" << systemClip;
+ break; }
+ case QPaintBufferPrivate::Cmd_Translate: {
+ QPointF delta(d_ptr->floats.at(cmd.extra), d_ptr->floats.at(cmd.extra+1));
+ debug << "Cmd_Translate:" << delta;
+ break; }
+ case QPaintBufferPrivate::Cmd_DrawStaticText: {
+ debug << "Cmd_DrawStaticText";
+ break; }
+ }
+
+ return desc;
+}
+#endif
+
+QRectF QPaintBuffer::boundingRect() const
+{
+ return d_ptr->boundingRect;
+}
+
+void QPaintBuffer::setBoundingRect(const QRectF &rect)
+{
+ d_ptr->boundingRect = rect;
+ d_ptr->calculateBoundingRect = false;
+}
+
+
+class QPaintBufferEnginePrivate : public QPaintEngineExPrivate
+{
+ Q_DECLARE_PUBLIC(QPaintBufferEngine)
+public:
+ void systemStateChanged() {
+ Q_Q(QPaintBufferEngine);
+ q->buffer->addCommand(QPaintBufferPrivate::Cmd_SystemStateChanged, QVariant(systemClip));
+ }
+
+ QTransform last;
+};
+
+
+/************************************************************************
+ *
+ * QPaintBufferEngine
+ *
+ ************************************************************************/
+
+QPaintBufferEngine::QPaintBufferEngine(QPaintBufferPrivate *b)
+ : QPaintEngineEx(*(new QPaintBufferEnginePrivate))
+ , buffer(b)
+ , m_begin_detected(false)
+ , m_save_detected(false)
+ , m_stream_raw_text_items(false)
+{
+}
+
+bool QPaintBufferEngine::begin(QPaintDevice *)
+{
+ Q_D(QPaintBufferEngine);
+ painter()->save();
+ d->systemStateChanged();
+ return true;
+}
+
+bool QPaintBufferEngine::end()
+{
+ painter()->restore();
+ m_created_state = 0;
+ return true;
+}
+
+QPainterState *QPaintBufferEngine::createState(QPainterState *orig) const
+{
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: createState, orig=" << orig << ", current=" << state();
+#endif
+
+ Q_ASSERT(!m_begin_detected);
+ Q_ASSERT(!m_save_detected);
+
+ if (orig == 0) {
+ m_begin_detected = true;
+ return new QPainterState();
+ } else {
+ m_save_detected = true;
+ return new QPainterState(orig);
+ }
+}
+
+void QPaintBufferEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
+{
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: clip vpath:" << path.elementCount() << "op:" << op;
+#endif
+ QPaintBufferCommand *cmd =
+ buffer->addCommand(QPaintBufferPrivate::Cmd_ClipVectorPath, path);
+ cmd->extra = op;
+}
+
+void QPaintBufferEngine::clip(const QRect &rect, Qt::ClipOperation op)
+{
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: clip rect:" << rect << "op:" << op;
+#endif
+ QPaintBufferCommand *cmd =
+ buffer->addCommand(QPaintBufferPrivate::Cmd_ClipRect, (int *) &rect, 4, 1);
+ cmd->extra = op;
+}
+
+void QPaintBufferEngine::clip(const QRegion &region, Qt::ClipOperation op)
+{
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: clip region br:" << region.boundingRect() << "op:" << op;
+#endif
+ QPaintBufferCommand *cmd =
+ buffer->addCommand(QPaintBufferPrivate::Cmd_ClipRegion, QVariant(region));
+ cmd->extra = op;
+}
+
+void QPaintBufferEngine::clip(const QPainterPath &path, Qt::ClipOperation op)
+{
+ // ### TODO
+// QPaintBufferCommand *cmd =
+// buffer->addCommand(QPaintBufferPrivate::Cmd_ClipPath, QVariant(path));
+// cmd->extra = op;
+ QPaintEngineEx::clip(path, op);
+}
+
+void QPaintBufferEngine::clipEnabledChanged()
+{
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: clip enable change" << state()->clipEnabled;
+#endif
+
+ buffer->addCommand(QPaintBufferPrivate::Cmd_SetClipEnabled, state()->clipEnabled);
+}
+
+void QPaintBufferEngine::penChanged()
+{
+ const QPen &pen = state()->pen;
+
+ if (!buffer->commands.isEmpty()
+ && buffer->commands.last().id == QPaintBufferPrivate::Cmd_SetPen) {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: penChanged (compressed)" << state()->pen;
+#endif
+ buffer->variants[buffer->commands.last().offset] = pen;
+ return;
+ }
+
+ if (buffer->calculateBoundingRect) {
+ if (pen.style() == Qt::NoPen) {
+ buffer->penWidthAdjustment = 0;
+ } else {
+ qreal penWidth = (pen.widthF() == 0) ? 1 : pen.widthF();
+ QPointF transformedWidth(penWidth, penWidth);
+ if (!pen.isCosmetic())
+ transformedWidth = painter()->transform().map(transformedWidth);
+ buffer->penWidthAdjustment = transformedWidth.x() / 2.0;
+ }
+ }
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: penChanged" << state()->pen;
+#endif
+ buffer->addCommand(QPaintBufferPrivate::Cmd_SetPen, pen);
+}
+
+void QPaintBufferEngine::brushChanged()
+{
+ const QBrush &brush = state()->brush;
+
+ if (!buffer->commands.isEmpty()
+ && buffer->commands.last().id == QPaintBufferPrivate::Cmd_SetBrush) {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: brushChanged (compressed)" << state()->brush;
+#endif
+ buffer->variants[buffer->commands.last().offset] = brush;
+ return;
+ }
+
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: brushChanged" << state()->brush;
+#endif
+ buffer->addCommand(QPaintBufferPrivate::Cmd_SetBrush, brush);
+}
+
+void QPaintBufferEngine::brushOriginChanged()
+{
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: brush origin changed" << state()->brushOrigin;
+#endif
+ buffer->addCommand(QPaintBufferPrivate::Cmd_SetBrushOrigin, state()->brushOrigin);
+}
+
+void QPaintBufferEngine::opacityChanged()
+{
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: opacity changed" << state()->opacity;
+#endif
+ buffer->addCommand(QPaintBufferPrivate::Cmd_SetOpacity, state()->opacity);
+}
+
+void QPaintBufferEngine::compositionModeChanged()
+{
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: composition mode" << state()->composition_mode;
+#endif
+ QPaintBufferCommand *cmd =
+ buffer->addCommand(QPaintBufferPrivate::Cmd_SetCompositionMode);
+ cmd->extra = state()->composition_mode;
+}
+
+void QPaintBufferEngine::renderHintsChanged()
+{
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: render hints changed" << state()->renderHints;
+#endif
+ QPaintBufferCommand *cmd =
+ buffer->addCommand(QPaintBufferPrivate::Cmd_SetRenderHints);
+ cmd->extra = state()->renderHints;
+}
+
+void QPaintBufferEngine::transformChanged()
+{
+ Q_D(QPaintBufferEngine);
+ const QTransform &transform = state()->matrix;
+
+ QTransform delta;
+
+ bool invertible = false;
+ if (transform.type() <= QTransform::TxScale && transform.type() == d->last.type())
+ delta = transform * d->last.inverted(&invertible);
+
+ d->last = transform;
+
+ if (invertible && delta.type() == QTransform::TxNone)
+ return;
+
+ if (invertible && delta.type() == QTransform::TxTranslate) {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: transformChanged (translate only) " << state()->matrix;
+#endif
+ QPaintBufferCommand *cmd =
+ buffer->addCommand(QPaintBufferPrivate::Cmd_Translate);
+
+ qreal data[] = { delta.dx(), delta.dy() };
+ cmd->extra = buffer->addData((qreal *) data, 2);
+ return;
+ }
+
+ // ### accumulate, like in QBrush case...
+ if (!buffer->commands.isEmpty()
+ && buffer->commands.last().id == QPaintBufferPrivate::Cmd_SetTransform) {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: transformChanged (compressing) " << state()->matrix;
+#endif
+ buffer->variants[buffer->commands.last().offset] = state()->matrix;
+ return;
+ }
+
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: transformChanged:" << state()->matrix;
+#endif
+ buffer->addCommand(QPaintBufferPrivate::Cmd_SetTransform, state()->matrix);
+}
+
+void QPaintBufferEngine::backgroundModeChanged()
+{
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintEngineBuffer: background mode changed" << state()->bgMode;
+#endif
+ QPaintBufferCommand *cmd = buffer->addCommand(QPaintBufferPrivate::Cmd_SetBackgroundMode);
+ cmd->extra = state()->bgMode;
+}
+
+void QPaintBufferEngine::draw(const QVectorPath &path)
+{
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: draw vpath:" << path.elementCount();
+#endif
+
+ bool hasBrush = qbrush_style(state()->brush) != Qt::NoBrush;
+ bool hasPen = qpen_style(state()->pen) != Qt::NoPen
+ && qbrush_style(qpen_brush(state()->pen)) != Qt::NoBrush;
+
+ if (hasPen || hasBrush)
+ buffer->addCommand(QPaintBufferPrivate::Cmd_DrawVectorPath, path);
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ else
+ qDebug() << " - no pen or brush active, discarded...\n";
+#endif
+
+// if (buffer->calculateBoundingRect) {
+// QRealRect r = path.controlPointRect();
+// buffer->updateBoundingRect(QRectF(r.x1, r.y1, r.x2 - r.x1, r.y2 - r.y1));
+// }
+}
+
+void QPaintBufferEngine::fill(const QVectorPath &path, const QBrush &brush)
+{
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: fill vpath:" << path.elementCount() << brush;
+#endif
+ QPaintBufferCommand *cmd =
+ buffer->addCommand(QPaintBufferPrivate::Cmd_FillVectorPath, path);
+ cmd->extra = buffer->addData(QVariant(brush));
+// if (buffer->calculateBoundingRect) {
+// QRealRect r = path.controlPointRect();
+// buffer->updateBoundingRect(QRectF(r.x1, r.y1, r.x2 - r.x1, r.y2 - r.y1));
+// }
+}
+
+void QPaintBufferEngine::stroke(const QVectorPath &path, const QPen &pen)
+{
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: stroke vpath:" << path.elementCount() << pen;
+#endif
+ QPaintBufferCommand *cmd =
+ buffer->addCommand(QPaintBufferPrivate::Cmd_StrokeVectorPath, path);
+ cmd->extra = buffer->addData(QVariant(pen));
+// if (buffer->calculateBoundingRect) {
+// QRealRect r = path.controlPointRect();
+// buffer->updateBoundingRect(QRectF(r.x1, r.y1, r.x2 - r.x1, r.y2 - r.y1));
+// }
+}
+
+void QPaintBufferEngine::fillRect(const QRectF &rect, const QBrush &brush)
+{
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: fillRect brush:" << rect << brush;
+#endif
+ QPaintBufferCommand *cmd =
+ buffer->addCommand(QPaintBufferPrivate::Cmd_FillRectBrush, (qreal *) &rect, 4, 1);
+ cmd->extra = buffer->addData(brush);
+ if (buffer->calculateBoundingRect)
+ buffer->updateBoundingRect(rect);
+}
+
+void QPaintBufferEngine::fillRect(const QRectF &rect, const QColor &color)
+{
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: fillRect color:" << rect << color;
+#endif
+ QPaintBufferCommand *cmd =
+ buffer->addCommand(QPaintBufferPrivate::Cmd_FillRectColor, (qreal *) &rect, 4, 1);
+ cmd->extra = buffer->addData(color);
+ if (buffer->calculateBoundingRect)
+ buffer->updateBoundingRect(rect);
+}
+
+void QPaintBufferEngine::drawRects(const QRect *rects, int rectCount)
+{
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: drawRectsI:" << rectCount;
+#endif
+ QPaintBufferCommand *cmd =
+ buffer->addCommand(QPaintBufferPrivate::Cmd_DrawRectI, (int *) rects, 4 * rectCount, rectCount);
+ cmd->extra = rectCount;
+
+ if (buffer->calculateBoundingRect) {
+ if (rectCount == 1) {
+ buffer->updateBoundingRect(rects[0]);
+ } else {
+ int min_x = rects[0].left();
+ int min_y = rects[0].top();
+ int max_x = rects[0].left() + rects[0].width();
+ int max_y = rects[0].top() + rects[0].height();
+ for (int i=1; i< rectCount; ++i) {
+ if (rects[i].left() < min_x)
+ min_x = rects[i].left();
+ if (rects[i].top() < min_y)
+ min_y = rects[i].top();
+ if (rects[i].right() > max_x)
+ max_x = rects[i].left() + rects[i].width();
+ if (rects[i].bottom() > max_y)
+ max_y = rects[i].top() + rects[i].height();
+
+ }
+ buffer->updateBoundingRect(QRectF(min_x, min_y, max_x - min_x, max_y - min_y));
+ }
+ }
+}
+
+void QPaintBufferEngine::drawRects(const QRectF *rects, int rectCount)
+{
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: drawRectsF:" << rectCount;
+#endif
+ QPaintBufferCommand *cmd =
+ buffer->addCommand(QPaintBufferPrivate::Cmd_DrawRectF, (qreal *) rects, 4 * rectCount, rectCount);
+ cmd->extra = rectCount;
+
+ if (buffer->calculateBoundingRect) {
+ if (rectCount == 1) {
+ buffer->updateBoundingRect(rects[0]);
+ } else {
+ qreal min_x = rects[0].left();
+ qreal min_y = rects[0].top();
+ qreal max_x = rects[0].right();
+ qreal max_y = rects[0].bottom();
+ for (int i=1; i< rectCount; ++i) {
+ if (rects[i].left() < min_x)
+ min_x = rects[i].left();
+ if (rects[i].top() < min_y)
+ min_y = rects[i].top();
+ if (rects[i].right() > max_x)
+ max_x = rects[i].right();
+ if (rects[i].bottom() > max_y)
+ max_y = rects[i].bottom();
+
+ }
+ buffer->updateBoundingRect(QRectF(min_x, min_y, max_x - min_x, max_y - min_y));
+ }
+ }
+}
+
+void QPaintBufferEngine::drawLines(const QLine *lines, int lineCount)
+{
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: drawLinesI:" << lineCount;
+#endif
+ QPaintBufferCommand *cmd =
+ buffer->addCommand(QPaintBufferPrivate::Cmd_DrawLineI, (int *) lines, 4 * lineCount, lineCount);
+ cmd->extra = lineCount;
+
+ if (buffer->calculateBoundingRect) {
+ int min_x = lines[0].p1().x();
+ int min_y = lines[0].p1().y();
+ int max_x = lines[0].p2().x();
+ int max_y = lines[0].p2().y();
+ if (min_x > max_x)
+ qSwap(min_x, max_x);
+ if (min_y > max_y)
+ qSwap(min_y, max_y);
+ for (int i=1; i < lineCount; ++i) {
+ int p1_x = lines[i].p1().x();
+ int p1_y = lines[i].p1().y();
+ int p2_x = lines[i].p2().x();
+ int p2_y = lines[i].p2().y();
+ if (p1_x > p2_x) {
+ min_x = qMin(p2_x, min_x);
+ max_x = qMax(p1_x, max_x);
+ } else {
+ min_x = qMin(p1_x, min_x);
+ max_x = qMax(p2_x, max_x);
+ }
+ if (p1_y > p2_y) {
+ min_y = qMin(p2_y, min_y);
+ max_y = qMax(p1_y, max_y);
+ } else {
+ min_y = qMin(p1_y, min_y);
+ max_y = qMax(p2_y, max_y);
+ }
+ }
+ buffer->updateBoundingRect(QRectF(min_x, min_y, max_x - min_x, max_y - min_y));
+ }
+}
+
+void QPaintBufferEngine::drawLines(const QLineF *lines, int lineCount)
+{
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: drawLinesF:" << lineCount;
+#endif
+ QPaintBufferCommand *cmd =
+ buffer->addCommand(QPaintBufferPrivate::Cmd_DrawLineF, (qreal *) lines, 4 * lineCount, lineCount);
+ cmd->extra = lineCount;
+
+ if (buffer->calculateBoundingRect) {
+ qreal min_x = lines[0].p1().x();
+ qreal min_y = lines[0].p1().y();
+ qreal max_x = lines[0].p2().x();
+ qreal max_y = lines[0].p2().y();
+ if (min_x > max_x)
+ qSwap(min_x, max_x);
+ if (min_y > max_y)
+ qSwap(min_y, max_y);
+ for (int i=1; i < lineCount; ++i) {
+ qreal p1_x = lines[i].p1().x();
+ qreal p1_y = lines[i].p1().y();
+ qreal p2_x = lines[i].p2().x();
+ qreal p2_y = lines[i].p2().y();
+ if (p1_x > p2_x) {
+ min_x = qMin(p2_x, min_x);
+ max_x = qMax(p1_x, max_x);
+ } else {
+ min_x = qMin(p1_x, min_x);
+ max_x = qMax(p2_x, max_x);
+ }
+ if (p1_y > p2_y) {
+ min_y = qMin(p2_y, min_y);
+ max_y = qMax(p1_y, max_y);
+ } else {
+ min_y = qMin(p1_y, min_y);
+ max_y = qMax(p2_y, max_y);
+ }
+ }
+ buffer->updateBoundingRect(QRectF(min_x, min_y, max_x - min_x, max_y - min_y));
+ }
+}
+
+void QPaintBufferEngine::drawEllipse(const QRectF &r)
+{
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: drawEllipseF:" << r;
+#endif
+ buffer->addCommand(QPaintBufferPrivate::Cmd_DrawEllipseF, (qreal *) &r, 4, 1);
+ if (buffer->calculateBoundingRect)
+ buffer->updateBoundingRect(r);
+}
+
+void QPaintBufferEngine::drawEllipse(const QRect &r)
+{
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: drawEllipseI:" << r;
+#endif
+ buffer->addCommand(QPaintBufferPrivate::Cmd_DrawEllipseI, (int *) &r, 4, 1);
+ if (buffer->calculateBoundingRect)
+ buffer->updateBoundingRect(r);
+}
+
+void QPaintBufferEngine::drawPath(const QPainterPath &path)
+{
+// #ifdef QPAINTBUFFER_DEBUG_DRAW
+// qDebug() << "QPaintBufferEngine: drawPath: element count:" << path.elementCount();
+// #endif
+// // ### Path -> QVariant
+// // buffer->addCommand(QPaintBufferPrivate::Cmd_DrawPath, QVariant(path));
+ QPaintEngineEx::drawPath(path);
+
+// if (buffer->calculateBoundingRect)
+// buffer->updateBoundingRect(path.boundingRect());
+}
+
+void QPaintBufferEngine::drawPoints(const QPoint *points, int pointCount)
+{
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: drawPointsI: " << pointCount;
+#endif
+ buffer->addCommand(QPaintBufferPrivate::Cmd_DrawPointsI, (int *) points, 2 * pointCount, pointCount);
+
+ if (buffer->calculateBoundingRect) {
+ int min_x = points[0].x();
+ int min_y = points[0].y();
+ int max_x = points[0].x()+1;
+ int max_y = points[0].y()+1;
+ for (int i=1; i<pointCount; ++i) {
+ int x = points[i].x();
+ int y = points[i].y();
+ min_x = qMin(min_x, x);
+ min_y = qMin(min_y, y);
+ max_x = qMax(max_x, x+1);
+ max_y = qMax(max_y, y+1);
+ }
+ buffer->updateBoundingRect(QRectF(min_x, min_y, max_x - min_x, max_y - min_y));
+ }
+}
+
+void QPaintBufferEngine::drawPoints(const QPointF *points, int pointCount)
+{
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: drawPointsF: " << pointCount;
+#endif
+ buffer->addCommand(QPaintBufferPrivate::Cmd_DrawPointsF, (qreal *) points, 2 * pointCount, pointCount);
+
+ if (buffer->calculateBoundingRect) {
+ qreal min_x = points[0].x();
+ qreal min_y = points[0].y();
+ qreal max_x = points[0].x()+1;
+ qreal max_y = points[0].y()+1;
+ for (int i=1; i<pointCount; ++i) {
+ qreal x = points[i].x();
+ qreal y = points[i].y();
+ min_x = qMin(min_x, x);
+ min_y = qMin(min_y, y);
+ max_x = qMax(max_x, x+1);
+ max_y = qMax(max_y, y+1);
+ }
+ buffer->updateBoundingRect(QRectF(min_x, min_y, max_x - min_x, max_y - min_y));
+ }
+}
+
+void QPaintBufferEngine::drawPolygon(const QPoint *pts, int count, PolygonDrawMode mode)
+{
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: drawPolygonI: size:" << count << ", mode:" << mode;
+#endif
+ if (mode == QPaintEngine::OddEvenMode || mode == QPaintEngine::WindingMode) {
+ QPaintBufferCommand *cmd = buffer->addCommand(QPaintBufferPrivate::Cmd_DrawPolygonI,
+ (int *) pts, 2 * count, count);
+ cmd->extra = mode;
+ } else if (mode == QPaintEngine::PolylineMode) {
+ buffer->addCommand(QPaintBufferPrivate::Cmd_DrawPolylineI, (int *) pts, 2 * count, count);
+ } else {
+ buffer->addCommand(QPaintBufferPrivate::Cmd_DrawConvexPolygonI, (int *) pts, 2 * count, count);
+ }
+
+ if (buffer->calculateBoundingRect) {
+ int min_x = pts[0].x();
+ int min_y = pts[0].y();
+ int max_x = pts[0].x();
+ int max_y = pts[0].y();
+ for (int i=1; i<count; ++i) {
+ int x = pts[i].x();
+ int y = pts[i].y();
+ min_x = qMin(min_x, x);
+ min_y = qMin(min_y, y);
+ max_x = qMax(max_x, x);
+ max_y = qMax(max_y, y);
+ }
+ buffer->updateBoundingRect(QRectF(min_x, min_y, max_x - min_x, max_y - min_y));
+ }
+}
+
+void QPaintBufferEngine::drawPolygon(const QPointF *pts, int count, PolygonDrawMode mode)
+{
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: drawPolygonF: size:" << count << ", mode:" << mode;
+#endif
+ if (mode == QPaintEngine::OddEvenMode || mode == QPaintEngine::WindingMode) {
+ QPaintBufferCommand *cmd = buffer->addCommand(QPaintBufferPrivate::Cmd_DrawPolygonF,
+ (qreal *) pts, 2 * count, count);
+ cmd->extra = mode;
+ } else if (mode == QPaintEngine::PolylineMode) {
+ buffer->addCommand(QPaintBufferPrivate::Cmd_DrawPolylineF, (qreal *) pts, 2 * count, count);
+ } else {
+ buffer->addCommand(QPaintBufferPrivate::Cmd_DrawConvexPolygonF, (qreal *) pts, 2 * count, count);
+ }
+
+ if (buffer->calculateBoundingRect) {
+ qreal min_x = pts[0].x();
+ qreal min_y = pts[0].y();
+ qreal max_x = pts[0].x();
+ qreal max_y = pts[0].y();
+ for (int i=1; i<count; ++i) {
+ qreal x = pts[i].x();
+ qreal y = pts[i].y();
+ min_x = qMin(min_x, x);
+ min_y = qMin(min_y, y);
+ max_x = qMax(max_x, x);
+ max_y = qMax(max_y, y);
+ }
+ buffer->updateBoundingRect(QRectF(min_x, min_y, max_x - min_x, max_y - min_y));
+ }
+}
+
+void QPaintBufferEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
+{
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: drawPixmap: src/dest rects " << r << sr;
+#endif
+ QPaintBufferCommand *cmd =
+ buffer->addCommand(QPaintBufferPrivate::Cmd_DrawPixmapRect, QVariant(pm));
+ cmd->extra = buffer->addData((qreal *) &r, 4);
+ buffer->addData((qreal *) &sr, 4);
+ if (buffer->calculateBoundingRect)
+ buffer->updateBoundingRect(r);
+}
+
+void QPaintBufferEngine::drawPixmap(const QPointF &pos, const QPixmap &pm)
+{
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: drawPixmap: pos:" << pos;
+#endif
+ QPaintBufferCommand *cmd =
+ buffer->addCommand(QPaintBufferPrivate::Cmd_DrawPixmapPos, QVariant(pm));
+ cmd->extra = buffer->addData((qreal *) &pos, 2);
+ if (buffer->calculateBoundingRect)
+ buffer->updateBoundingRect(QRectF(pos, pm.size()));
+}
+
+static inline QImage qpaintbuffer_storable_image(const QImage &src)
+{
+ QImageData *d = const_cast<QImage &>(src).data_ptr();
+ return d->own_data ? src : src.copy();
+}
+
+void QPaintBufferEngine::drawImage(const QRectF &r, const QImage &image, const QRectF &sr,
+ Qt::ImageConversionFlags /*flags */)
+{
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: drawImage: src/dest rects " << r << sr;
+#endif
+ QPaintBufferCommand *cmd =
+ buffer->addCommand(QPaintBufferPrivate::Cmd_DrawImageRect,
+ QVariant(qpaintbuffer_storable_image(image)));
+ cmd->extra = buffer->addData((qreal *) &r, 4);
+ buffer->addData((qreal *) &sr, 4);
+ // ### flags...
+ if (buffer->calculateBoundingRect)
+ buffer->updateBoundingRect(r);
+}
+
+void QPaintBufferEngine::drawImage(const QPointF &pos, const QImage &image)
+{
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: drawImage: pos:" << pos;
+#endif
+ QPaintBufferCommand *cmd =
+ buffer->addCommand(QPaintBufferPrivate::Cmd_DrawImagePos,
+ QVariant(qpaintbuffer_storable_image(image)));
+ cmd->extra = buffer->addData((qreal *) &pos, 2);
+ if (buffer->calculateBoundingRect)
+ buffer->updateBoundingRect(QRectF(pos, image.size()));
+}
+
+void QPaintBufferEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &s)
+{
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: drawTiledPixmap: src rect/offset:" << r << s;
+#endif
+ QPaintBufferCommand *cmd =
+ buffer->addCommand(QPaintBufferPrivate::Cmd_DrawTiledPixmap, QVariant(pm));
+ cmd->extra = buffer->addData((qreal *) &r, 4);
+ buffer->addData((qreal *) &s, 2);
+ if (buffer->calculateBoundingRect)
+ buffer->updateBoundingRect(r);
+}
+
+void QPaintBufferEngine::drawStaticTextItem(QStaticTextItem *staticTextItem)
+{
+ QVariantList variants;
+
+ variants << QVariant(staticTextItem->font);
+ for (int i=0; i<staticTextItem->numGlyphs; ++i) {
+ variants.append(staticTextItem->glyphs[i]);
+ variants.append(staticTextItem->glyphPositions[i].toPointF());
+ }
+
+ buffer->addCommand(QPaintBufferPrivate::Cmd_DrawStaticText, QVariant(variants));
+}
+
+void QPaintBufferEngine::drawTextItem(const QPointF &pos, const QTextItem &ti)
+{
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: drawTextItem: pos:" << pos << ti.text();
+#endif
+ if (m_stream_raw_text_items) {
+ QPaintBufferCommand *cmd = buffer->addCommand(QPaintBufferPrivate::Cmd_DrawTextItem, QVariant::fromValue<void *>(new QTextItemIntCopy(ti)));
+
+ QFont font(ti.font());
+ font.setUnderline(false);
+ font.setStrikeOut(false);
+ font.setOverline(false);
+
+ const QTextItemInt &si = static_cast<const QTextItemInt &>(ti);
+ qreal justificationWidth = 0;
+ if (si.justified)
+ justificationWidth = si.width.toReal();
+ int renderFlags = ti.renderFlags();
+ qreal scaleFactor = font.d->dpi/qreal(qt_defaultDpiY());
+
+ buffer->addData(QVariant(font));
+ cmd->extra = buffer->addData((qreal *) &pos, 2);
+ buffer->addData((qreal *) &justificationWidth, 1);
+ buffer->addData((qreal *) &scaleFactor, 1);
+ cmd->offset2 = buffer->addData((int *) &renderFlags, 1);
+ } else {
+ QList<QVariant> variants;
+ variants << QVariant(ti.font()) << QVariant(ti.text());
+ QPaintBufferCommand *cmd = buffer->addCommand(QPaintBufferPrivate::Cmd_DrawText, QVariant(variants));
+ cmd->extra = buffer->addData((qreal *) &pos, 2);
+ }
+
+ if (buffer->calculateBoundingRect)
+ buffer->updateBoundingRect(QRectF(pos, QSize(ti.width(), ti.ascent() + ti.descent() + 1)));
+}
+
+
+void QPaintBufferEngine::setState(QPainterState *s)
+{
+ Q_D(QPaintBufferEngine);
+ if (m_begin_detected) {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: setState: begin, ignoring.";
+#endif
+ m_begin_detected = false;
+ } else if (m_save_detected) {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: setState: save.";
+#endif
+ m_save_detected = false;
+ buffer->addCommand(QPaintBufferPrivate::Cmd_Save);
+ } else {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << "QPaintBufferEngine: setState: restore.";
+#endif
+ buffer->addCommand(QPaintBufferPrivate::Cmd_Restore);
+ }
+
+ d->last = s->matrix;
+
+ QPaintEngineEx::setState(s);
+}
+
+
+/***********************************************************************
+ *
+ * class QPaintBufferPlayback_Painter
+ *
+ */
+
+// QFakeDevice is used to create fonts with a custom DPI
+//
+class QFakeDevice : public QPaintDevice
+{
+public:
+ QFakeDevice() { dpi_x = qt_defaultDpiX(); dpi_y = qt_defaultDpiY(); }
+ void setDpiX(int dpi) { dpi_x = dpi; }
+ void setDpiY(int dpi) { dpi_y = dpi; }
+ QPaintEngine *paintEngine() const { return 0; }
+ int metric(PaintDeviceMetric m) const
+ {
+ switch(m) {
+ case PdmPhysicalDpiX:
+ case PdmDpiX:
+ return dpi_x;
+ case PdmPhysicalDpiY:
+ case PdmDpiY:
+ return dpi_y;
+ default:
+ return QPaintDevice::metric(m);
+ }
+ }
+
+private:
+ int dpi_x;
+ int dpi_y;
+};
+
+
+void QPainterReplayer::setupTransform(QPainter *_painter)
+{
+ painter = _painter;
+ m_world_matrix = painter->transform();
+ m_world_matrix.scale(qreal(painter->device()->logicalDpiX()) / qreal(qt_defaultDpiX()),
+ qreal(painter->device()->logicalDpiY()) / qreal(qt_defaultDpiY()));
+ painter->setTransform(m_world_matrix);
+}
+
+void QPainterReplayer::processCommands(const QPaintBuffer &buffer, QPainter *p, int begin, int end)
+{
+ d = buffer.d_ptr;
+ painter = p;
+
+ for (int cmdIndex = begin; cmdIndex < end; ++cmdIndex) {
+ const QPaintBufferCommand &cmd = d->commands.at(cmdIndex);
+ process(cmd);
+ }
+}
+
+void QPaintBuffer::beginNewFrame()
+{
+ if (!d_ptr->commands.isEmpty())
+ d_ptr->frames << d_ptr->commands.size();
+}
+
+int QPaintBuffer::numFrames() const
+{
+ return d_ptr->frames.size() + 1;
+}
+
+void QPainterReplayer::process(const QPaintBufferCommand &cmd)
+{
+ switch (cmd.id) {
+ case QPaintBufferPrivate::Cmd_Save: {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_Save";
+#endif
+ painter->save();
+ break; }
+
+ case QPaintBufferPrivate::Cmd_Restore: {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_Restore";
+#endif
+ painter->restore();
+ break; }
+
+ case QPaintBufferPrivate::Cmd_SetPen: {
+ QPen pen = qvariant_cast<QPen>(d->variants.at(cmd.offset));
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_SetPen: " << pen;
+#endif
+ painter->setPen(pen);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_SetBrush: {
+ QBrush brush = qvariant_cast<QBrush>(d->variants.at(cmd.offset));
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_SetBrush: " << brush;
+#endif
+ painter->setBrush(brush);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_SetBrushOrigin: {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_SetBrushOrigin: " << d->variants.at(cmd.offset).toPointF();
+#endif
+ painter->setBrushOrigin(d->variants.at(cmd.offset).toPointF());
+ break; }
+
+ case QPaintBufferPrivate::Cmd_SetTransform: {
+ QTransform xform = qvariant_cast<QTransform>(d->variants.at(cmd.offset));
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_SetTransform, offset: " << cmd.offset << xform;
+#endif
+ painter->setTransform(xform * m_world_matrix);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_Translate: {
+ QPointF delta(d->floats.at(cmd.extra), d->floats.at(cmd.extra+1));
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_Translate, offset: " << cmd.offset << delta;
+#endif
+ painter->translate(delta.x(), delta.y());
+ return;
+ }
+
+ case QPaintBufferPrivate::Cmd_SetCompositionMode: {
+ QPainter::CompositionMode mode = (QPainter::CompositionMode) cmd.extra;
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_SetCompositionMode, mode: " << mode;
+#endif
+ painter->setCompositionMode(mode);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_SetRenderHints: {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_SetRenderHints, hints: " << cmd.extra;
+#endif
+ QPainter::RenderHints ph = painter->renderHints();
+ QPainter::RenderHints nh = (QPainter::RenderHints) cmd.extra;
+ QPainter::RenderHints xored = ph ^ nh;
+ if (xored & QPainter::Antialiasing)
+ painter->setRenderHint(QPainter::Antialiasing, nh & QPainter::Antialiasing);
+ if (xored & QPainter::HighQualityAntialiasing)
+ painter->setRenderHint(QPainter::HighQualityAntialiasing, nh & QPainter::HighQualityAntialiasing);
+ if (xored & QPainter::TextAntialiasing)
+ painter->setRenderHint(QPainter::TextAntialiasing, nh & QPainter::TextAntialiasing);
+ if (xored & QPainter::SmoothPixmapTransform)
+ painter->setRenderHint(QPainter::SmoothPixmapTransform, nh & QPainter::SmoothPixmapTransform);
+ if (xored & QPainter::NonCosmeticDefaultPen)
+ painter->setRenderHint(QPainter::NonCosmeticDefaultPen, nh & QPainter::NonCosmeticDefaultPen);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_SetOpacity: {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_SetOpacity: " << d->variants.at(cmd.offset).toDouble();
+#endif
+ painter->setOpacity(d->variants.at(cmd.offset).toDouble());
+ break; }
+
+ case QPaintBufferPrivate::Cmd_SetBackgroundMode: {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_SetBackgroundMode: " << cmd.extra;
+#endif
+ painter->setBackgroundMode((Qt::BGMode)cmd.extra);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawVectorPath: {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_DrawVectorPath: size: " << cmd.size
+// << ", hints:" << d->ints[cmd.offset2+cmd.size]
+ << "pts/elms:" << cmd.offset << cmd.offset2;
+#endif
+ QVectorPathCmd path(d, cmd);
+ painter->drawPath(path().convertToPainterPath());
+ break; }
+
+ case QPaintBufferPrivate::Cmd_StrokeVectorPath: {
+ QPen pen = qvariant_cast<QPen>(d->variants.at(cmd.extra));
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_StrokeVectorPath: size: " << cmd.size
+// << ", hints:" << d->ints[cmd.offset2+cmd.size]
+ << "pts/elms:" << cmd.offset << cmd.offset2;
+#endif
+ QVectorPathCmd path(d, cmd);
+ painter->strokePath(path().convertToPainterPath(), pen);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_FillVectorPath: {
+ QBrush brush = qvariant_cast<QBrush>(d->variants.at(cmd.extra));
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_FillVectorPath: size: " << cmd.size
+// << ", hints:" << d->ints[cmd.offset2+cmd.size]
+ << "pts/elms:" << cmd.offset << cmd.offset2 << brush;
+#endif
+ QVectorPathCmd path(d, cmd);
+ painter->fillPath(path().convertToPainterPath(), brush);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawPolygonF: {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_DrawPolygonF, offset: " << cmd.offset << " size: " << cmd.size
+ << " mode: " << cmd.extra
+ << d->floats.at(cmd.offset)
+ << d->floats.at(cmd.offset+1);
+#endif
+ Qt::FillRule fill = (QPaintEngine::PolygonDrawMode) cmd.extra == QPaintEngine::OddEvenMode
+ ? Qt::OddEvenFill : Qt::WindingFill;
+ painter->drawPolygon((QPointF *) (d->floats.constData() + cmd.offset), cmd.size, fill);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawPolygonI: {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_DrawPolygonI, offset: " << cmd.offset << " size: " << cmd.size
+ << " mode: " << cmd.extra
+ << d->ints.at(cmd.offset)
+ << d->ints.at(cmd.offset+1);
+#endif
+ Qt::FillRule fill = (QPaintEngine::PolygonDrawMode) cmd.extra == QPaintEngine::OddEvenMode
+ ? Qt::OddEvenFill : Qt::WindingFill;
+ painter->drawPolygon((QPoint *) (d->ints.constData() + cmd.offset), cmd.size, fill);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawPolylineF: {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_DrawPolylineF, offset: " << cmd.offset << " size: " << cmd.size;
+#endif
+ painter->drawPolyline((QPointF *) (d->floats.constData() + cmd.offset), cmd.size);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawPolylineI: {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_DrawPolylineI, offset: " << cmd.offset << " size: " << cmd.size;
+#endif
+ painter->drawPolyline((QPoint *) (d->ints.constData() + cmd.offset), cmd.size);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawConvexPolygonF: {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_DrawConvexPolygonF, offset: " << cmd.offset << " size: " << cmd.size;
+#endif
+ painter->drawConvexPolygon((QPointF *) (d->floats.constData() + cmd.offset), cmd.size);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawConvexPolygonI: {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_DrawConvexPolygonI, offset: " << cmd.offset << " size: " << cmd.size;
+#endif
+ painter->drawConvexPolygon((QPoint *) (d->ints.constData() + cmd.offset), cmd.size);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawEllipseF: {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_DrawEllipseF, offset: " << cmd.offset;
+#endif
+ painter->drawEllipse(*(QRectF *)(d->floats.constData() + cmd.offset));
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawEllipseI: {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_DrawEllipseI, offset: " << cmd.offset;
+#endif
+ painter->drawEllipse(*(QRect *)(d->ints.constData() + cmd.offset));
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawLineF: {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_DrawLineF, offset: " << cmd.offset << " size: " << cmd.size;
+#endif
+ painter->drawLines((QLineF *)(d->floats.constData() + cmd.offset), cmd.size);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawLineI: {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_DrawLineI, offset: " << cmd.offset << " size: " << cmd.size;
+#endif
+ painter->drawLines((QLine *)(d->ints.constData() + cmd.offset), cmd.size);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawPointsF: {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_DrawPointsF, offset: " << cmd.offset << " size: " << cmd.size;
+#endif
+ painter->drawPoints((QPointF *)(d->floats.constData() + cmd.offset), cmd.size);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawPointsI: {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_DrawPointsI, offset: " << cmd.offset << " size: " << cmd.size;
+#endif
+ painter->drawPoints((QPoint *)(d->ints.constData() + cmd.offset), cmd.size);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawPixmapRect: {
+ QPixmap pm(d->variants.at(cmd.offset).value<QPixmap>());
+ QRectF r(d->floats.at(cmd.extra), d->floats.at(cmd.extra+1),
+ d->floats.at(cmd.extra+2), d->floats.at(cmd.extra+3));
+
+ QRectF sr(d->floats.at(cmd.extra+4), d->floats.at(cmd.extra+5),
+ d->floats.at(cmd.extra+6), d->floats.at(cmd.extra+7));
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_DrawPixmapRect:" << r << sr;
+#endif
+ painter->drawPixmap(r, pm, sr);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawPixmapPos: {
+ QPixmap pm(d->variants.at(cmd.offset).value<QPixmap>());
+ QPointF pos(d->floats.at(cmd.extra), d->floats.at(cmd.extra+1));
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_DrawPixmapPos:" << pos;
+#endif
+ painter->drawPixmap(pos, pm);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawTiledPixmap: {
+ QPixmap pm(d->variants.at(cmd.offset).value<QPixmap>());
+ QRectF r(d->floats.at(cmd.extra), d->floats.at(cmd.extra+1),
+ d->floats.at(cmd.extra+2), d->floats.at(cmd.extra+3));
+
+ QPointF offset(d->floats.at(cmd.extra+4), d->floats.at(cmd.extra+5));
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_DrawTiledPixmap:" << r << offset;
+#endif
+ painter->drawTiledPixmap(r, pm, offset);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawImageRect: {
+ QImage image(d->variants.at(cmd.offset).value<QImage>());
+ QRectF r(d->floats.at(cmd.extra), d->floats.at(cmd.extra+1),
+ d->floats.at(cmd.extra+2), d->floats.at(cmd.extra+3));
+ QRectF sr(d->floats.at(cmd.extra+4), d->floats.at(cmd.extra+5),
+ d->floats.at(cmd.extra+6), d->floats.at(cmd.extra+7));
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_DrawImageRect:" << r << sr;
+#endif
+ painter->drawImage(r, image, sr);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawImagePos: {
+ QImage image(d->variants.at(cmd.offset).value<QImage>());
+ QPointF pos(d->floats.at(cmd.extra), d->floats.at(cmd.extra+1));
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_DrawImagePos:" << pos;
+#endif
+ painter->drawImage(pos, image);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawRectF: {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_DrawRectF, offset: " << cmd.offset;
+#endif
+ painter->drawRects((QRectF *)(d->floats.constData() + cmd.offset), cmd.size);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawRectI: {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_DrawRectI, offset: " << cmd.offset;
+#endif
+ painter->drawRects((QRect *)(d->ints.constData() + cmd.offset), cmd.size);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_FillRectBrush: {
+ QBrush brush = qvariant_cast<QBrush>(d->variants.at(cmd.extra));
+ QRectF *rect = (QRectF *)(d->floats.constData() + cmd.offset);
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_FillRectBrush, offset: " << cmd.offset << " rect: " << *rect << " brush: " << brush;
+#endif
+ painter->fillRect(*rect, brush);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_FillRectColor: {
+ QColor color = qvariant_cast<QColor>(d->variants.at(cmd.extra));
+ QRectF *rect = (QRectF *)(d->floats.constData() + cmd.offset);
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_FillRectBrush, offset: " << cmd.offset << " rect: " << *rect << " color: " << color;
+#endif
+ painter->fillRect(*rect, color);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_SetClipEnabled: {
+ bool clipEnabled = d->variants.at(cmd.offset).toBool();
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_SetClipEnabled:" << clipEnabled;
+#endif
+ painter->setClipping(clipEnabled);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_ClipVectorPath: {
+ QVectorPathCmd path(d, cmd);
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_ClipVectorPath:" << path().elementCount();
+#endif
+ painter->setClipPath(path().convertToPainterPath(), Qt::ClipOperation(cmd.extra));
+ break; }
+
+
+ case QPaintBufferPrivate::Cmd_ClipRect: {
+ QRect rect(QPoint(d->ints.at(cmd.offset), d->ints.at(cmd.offset + 1)),
+ QPoint(d->ints.at(cmd.offset + 2), d->ints.at(cmd.offset + 3)));
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_ClipRect:" << rect << cmd.extra;
+#endif
+ painter->setClipRect(rect, Qt::ClipOperation(cmd.extra));
+ break; }
+
+ case QPaintBufferPrivate::Cmd_ClipRegion: {
+ QRegion region(d->variants.at(cmd.offset).value<QRegion>());
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_ClipRegion:" << region.boundingRect() << cmd.extra;
+#endif
+ painter->setClipRegion(region, Qt::ClipOperation(cmd.extra));
+ break; }
+
+#if !defined(QT_NO_RAWFONT)
+ case QPaintBufferPrivate::Cmd_DrawStaticText: {
+
+ QVariantList variants(d->variants.at(cmd.offset).value<QVariantList>());
+
+ QFont font = variants.at(0).value<QFont>();
+
+ QVector<quint32> glyphIndexes;
+ QVector<QPointF> positions;
+
+ for (int i=0; i<(variants.size() - 1) / 2; ++i) {
+ glyphIndexes.append(variants.at(i*2 + 1).toUInt());
+ positions.append(variants.at(i*2 + 2).toPointF());
+ }
+
+ painter->setFont(font);
+
+ QRawFont rawFont;
+ QRawFontPrivate *rawFontD = QRawFontPrivate::get(rawFont);
+ QFontPrivate *fontD = QFontPrivate::get(font);
+ rawFontD->fontEngine = fontD->engineForScript(QUnicodeTables::Common);
+ rawFontD->fontEngine->ref.ref();
+
+ QGlyphs glyphs;
+ glyphs.setFont(rawFont);
+ glyphs.setGlyphIndexes(glyphIndexes);
+ glyphs.setPositions(positions);
+
+ painter->drawGlyphs(QPointF(), glyphs);
+ break;
+ }
+#endif
+
+ case QPaintBufferPrivate::Cmd_DrawText: {
+ QPointF pos(d->floats.at(cmd.extra), d->floats.at(cmd.extra+1));
+ QList<QVariant> variants(d->variants.at(cmd.offset).value<QList<QVariant> >());
+
+ QFont font(variants.at(0).value<QFont>());
+ QString text(variants.at(1).value<QString>());
+
+ painter->setFont(font);
+ painter->drawText(pos, text);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawTextItem: {
+ QPointF pos(d->floats.at(cmd.extra), d->floats.at(cmd.extra+1));
+ QTextItemIntCopy *tiCopy = reinterpret_cast<QTextItemIntCopy *>(qvariant_cast<void *>(d->variants.at(cmd.offset)));
+ QTextItemInt &ti = (*tiCopy)();
+ QString text(ti.text());
+
+ QFont font(ti.font());
+ font.setUnderline(false);
+ font.setStrikeOut(false);
+ font.setOverline(false);
+
+ const QTextItemInt &si = static_cast<const QTextItemInt &>(ti);
+ qreal justificationWidth = 0;
+ if (si.justified)
+ justificationWidth = si.width.toReal();
+ qreal scaleFactor = font.d->dpi/qreal(qt_defaultDpiY());
+
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_DrawTextItem:" << pos << " " << text << " " << scaleFactor;
+#endif
+
+ if (scaleFactor != 1.0) {
+ QFont fnt(font);
+ QFakeDevice fake;
+ fake.setDpiX(qRound(scaleFactor*qt_defaultDpiX()));
+ fake.setDpiY(qRound(scaleFactor*qt_defaultDpiY()));
+ font = QFont(fnt, &fake);
+ }
+
+ int flags = Qt::TextSingleLine | Qt::TextDontClip | Qt::TextForceLeftToRight;
+ QSizeF size(1, 1);
+ if (justificationWidth > 0) {
+ size.setWidth(justificationWidth);
+ flags |= Qt::TextJustificationForced;
+ flags |= Qt::AlignJustify;
+ }
+
+ QFontMetrics fm(font);
+ QPointF pt(pos.x(), pos.y() - fm.ascent());
+ qt_format_text(font, QRectF(pt, size), flags, /*opt*/0,
+ text, /*brect=*/0, /*tabstops=*/0, /*...*/0, /*tabarraylen=*/0, painter);
+ break; }
+ case QPaintBufferPrivate::Cmd_SystemStateChanged: {
+ QRegion systemClip(d->variants.at(cmd.offset).value<QRegion>());
+
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> Cmd_SystemStateChanged:" << systemClip;
+#endif
+
+ painter->paintEngine()->setSystemClip(systemClip);
+ painter->paintEngine()->d_ptr->systemStateChanged();
+ break; }
+ }
+}
+
+void QPaintEngineExReplayer::process(const QPaintBufferCommand &cmd)
+{
+ Q_ASSERT(painter->paintEngine()->isExtended());
+ QPaintEngineEx *xengine = static_cast<QPaintEngineEx *>(painter->paintEngine());
+
+ switch (cmd.id) {
+ case QPaintBufferPrivate::Cmd_SetBrushOrigin: {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> ExCmd_SetBrushOrigin: " << d->variants.at(cmd.offset).toPointF();
+#endif
+ xengine->state()->brushOrigin = d->variants.at(cmd.offset).toPointF();
+ xengine->brushOriginChanged();
+ break; }
+
+ case QPaintBufferPrivate::Cmd_SetCompositionMode: {
+ QPainter::CompositionMode mode = (QPainter::CompositionMode) cmd.extra;
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> ExCmd_SetCompositionMode, mode: " << mode;
+#endif
+ xengine->state()->composition_mode = mode;
+ xengine->compositionModeChanged();
+ break; }
+
+ case QPaintBufferPrivate::Cmd_SetOpacity: {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> ExCmd_SetOpacity: " << d->variants.at(cmd.offset).toDouble();
+#endif
+ xengine->state()->opacity = d->variants.at(cmd.offset).toDouble();
+ xengine->opacityChanged();
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawVectorPath: {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> ExCmd_DrawVectorPath: size: " << cmd.size
+// << ", hints:" << d->ints[cmd.offset2+cmd.size]
+ << "pts/elms:" << cmd.offset << cmd.offset2;
+#endif
+ QVectorPathCmd path(d, cmd);
+ xengine->draw(path());
+ break; }
+
+ case QPaintBufferPrivate::Cmd_StrokeVectorPath: {
+ QPen pen = qvariant_cast<QPen>(d->variants.at(cmd.extra));
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> ExCmd_StrokeVectorPath: size: " << cmd.size
+// << ", hints:" << d->ints[cmd.offset2+cmd.size]
+ << "pts/elms:" << cmd.offset << cmd.offset2;
+#endif
+ QVectorPathCmd path(d, cmd);
+ xengine->stroke(path(), pen);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_FillVectorPath: {
+ QBrush brush = qvariant_cast<QBrush>(d->variants.at(cmd.extra));
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> ExCmd_FillVectorPath: size: " << cmd.size
+// << ", hints:" << d->ints[cmd.offset2+cmd.size]
+ << "pts/elms:" << cmd.offset << cmd.offset2 << brush;
+#endif
+ QVectorPathCmd path(d, cmd);
+ xengine->fill(path(), brush);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_FillRectBrush: {
+ QBrush brush = qvariant_cast<QBrush>(d->variants.at(cmd.extra));
+ QRectF *rect = (QRectF *)(d->floats.constData() + cmd.offset);
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> ExCmd_FillRectBrush, offset: " << cmd.offset << " rect: " << *rect << " brush: " << brush;
+#endif
+ xengine->fillRect(*rect, brush);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_FillRectColor: {
+ QColor color = qvariant_cast<QColor>(d->variants.at(cmd.extra));
+ QRectF *rect = (QRectF *)(d->floats.constData() + cmd.offset);
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> ExCmd_FillRectBrush, offset: " << cmd.offset << " rect: " << *rect << " color: " << color;
+#endif
+ xengine->fillRect(*rect, color);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawPolygonF: {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> ExCmd_DrawPolygonF, offset: " << cmd.offset << " size: " << cmd.size
+ << " mode: " << cmd.extra
+ << d->floats.at(cmd.offset)
+ << d->floats.at(cmd.offset+1);
+#endif
+ xengine->drawPolygon((QPointF *) (d->floats.constData() + cmd.offset), cmd.size,
+ (QPaintEngine::PolygonDrawMode) cmd.extra);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawPolygonI: {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> ExCmd_DrawPolygonI, offset: " << cmd.offset << " size: " << cmd.size
+ << " mode: " << cmd.extra
+ << d->ints.at(cmd.offset)
+ << d->ints.at(cmd.offset+1);
+#endif
+ xengine->drawPolygon((QPoint *) (d->ints.constData() + cmd.offset), cmd.size,
+ (QPaintEngine::PolygonDrawMode) cmd.extra);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawEllipseF: {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> ExCmd_DrawEllipseF, offset: " << cmd.offset;
+#endif
+ xengine->drawEllipse(*(QRectF *)(d->floats.constData() + cmd.offset));
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawEllipseI: {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> ExCmd_DrawEllipseI, offset: " << cmd.offset;
+#endif
+ xengine->drawEllipse(*(QRect *)(d->ints.constData() + cmd.offset));
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawLineF: {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> ExCmd_DrawLineF, offset: " << cmd.offset << " size: " << cmd.size;
+#endif
+ xengine->drawLines((QLineF *)(d->floats.constData() + cmd.offset), cmd.size);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawLineI: {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> ExCmd_DrawLineI, offset: " << cmd.offset << " size: " << cmd.size;
+#endif
+ xengine->drawLines((QLine *)(d->ints.constData() + cmd.offset), cmd.size);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawPointsF: {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> ExCmd_DrawPointsF, offset: " << cmd.offset << " size: " << cmd.size;
+#endif
+ xengine->drawPoints((QPointF *)(d->floats.constData() + cmd.offset), cmd.size);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawPointsI: {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> ExCmd_DrawPointsI, offset: " << cmd.offset << " size: " << cmd.size;
+#endif
+ xengine->drawPoints((QPoint *)(d->ints.constData() + cmd.offset), cmd.size);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawPolylineF: {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> ExCmd_DrawPolylineF, offset: " << cmd.offset << " size: " << cmd.size;
+#endif
+ xengine->drawPolygon((QPointF *) (d->floats.constData() + cmd.offset), cmd.size, QPaintEngine::PolylineMode);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawPolylineI: {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> ExCmd_DrawPolylineI, offset: " << cmd.offset << " size: " << cmd.size;
+#endif
+ xengine->drawPolygon((QPoint *) (d->ints.constData() + cmd.offset), cmd.size, QPaintEngine::PolylineMode);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawRectF: {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> ExCmd_DrawRectF, offset: " << cmd.offset << " size: " << cmd.size;
+#endif
+ xengine->drawRects((QRectF *) (d->floats.constData() + cmd.offset), cmd.size);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_DrawRectI: {
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> ExCmd_DrawRectI, offset: " << cmd.offset << " size: " << cmd.size;
+#endif
+ xengine->drawRects((QRect *) (d->ints.constData() + cmd.offset), cmd.size);
+ break; }
+
+ case QPaintBufferPrivate::Cmd_SetClipEnabled: {
+ bool clipEnabled = d->variants.at(cmd.offset).toBool();
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> ExCmd_SetClipEnabled:" << clipEnabled;
+#endif
+ xengine->state()->clipEnabled = clipEnabled;
+ xengine->clipEnabledChanged();
+ break; }
+
+ case QPaintBufferPrivate::Cmd_ClipVectorPath: {
+ QVectorPathCmd path(d, cmd);
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> ExCmd_ClipVectorPath:" << path().elementCount();
+#endif
+ xengine->clip(path(), Qt::ClipOperation(cmd.extra));
+ break; }
+
+
+ case QPaintBufferPrivate::Cmd_ClipRect: {
+ QRect rect(QPoint(d->ints.at(cmd.offset), d->ints.at(cmd.offset + 1)),
+ QPoint(d->ints.at(cmd.offset + 2), d->ints.at(cmd.offset + 3)));
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> ExCmd_ClipRect:" << rect << cmd.extra;
+#endif
+ xengine->clip(rect, Qt::ClipOperation(cmd.extra));
+ break; }
+
+ case QPaintBufferPrivate::Cmd_ClipRegion: {
+ QRegion region(d->variants.at(cmd.offset).value<QRegion>());
+#ifdef QPAINTBUFFER_DEBUG_DRAW
+ qDebug() << " -> ExCmd_ClipRegion:" << region.boundingRect() << cmd.extra;
+#endif
+ xengine->clip(region, Qt::ClipOperation(cmd.extra));
+ break; }
+
+ default:
+ QPainterReplayer::process(cmd);
+ break;
+ }
+}
+
+QPaintBufferResource::QPaintBufferResource(FreeFunc f, QObject *parent) : QObject(parent), free(f)
+{
+ connect(QPaintBufferSignalProxy::instance(), SIGNAL(aboutToDestroy(const QPaintBufferPrivate*)), this, SLOT(remove(const QPaintBufferPrivate*)));
+}
+
+QPaintBufferResource::~QPaintBufferResource()
+{
+ for (Cache::iterator it = m_cache.begin(); it != m_cache.end(); ++it)
+ free(it.value());
+}
+
+void QPaintBufferResource::insert(const QPaintBufferPrivate *key, void *value)
+{
+ Cache::iterator it = m_cache.find(key);
+ if (it != m_cache.end()) {
+ free(it.value());
+ it.value() = value;
+ } else {
+ m_cache.insert(key, value);
+ }
+}
+
+void *QPaintBufferResource::value(const QPaintBufferPrivate *key)
+{
+ Cache::iterator it = m_cache.find(key);
+ if (it != m_cache.end())
+ return it.value();
+ return 0;
+}
+
+void QPaintBufferResource::remove(const QPaintBufferPrivate *key)
+{
+ Cache::iterator it = m_cache.find(key);
+ if (it != m_cache.end()) {
+ free(it.value());
+ m_cache.erase(it);
+ }
+}
+
+QDataStream &operator<<(QDataStream &stream, const QPaintBufferCommand &command)
+{
+ quint32 id = command.id;
+ quint32 size = command.size;
+ stream << id << size;
+ stream << command.offset << command.offset2 << command.extra;
+ return stream;
+}
+
+QDataStream &operator>>(QDataStream &stream, QPaintBufferCommand &command)
+{
+ quint32 id;
+ quint32 size;
+ stream >> id >> size;
+ stream >> command.offset >> command.offset2 >> command.extra;
+ command.id = id;
+ command.size = size;
+ return stream;
+}
+
+struct QPaintBufferCacheEntry
+{
+ QVariant::Type type;
+ quint64 cacheKey;
+};
+
+struct QPaintBufferCacheEntryV2
+{
+ enum Type {
+ ImageKey,
+ PixmapKey
+ };
+
+ struct Flags {
+ uint type : 8;
+ uint key : 24;
+ };
+
+ union {
+ Flags flags;
+ uint bits;
+ };
+};
+
+QT_END_NAMESPACE
+Q_DECLARE_METATYPE(QPaintBufferCacheEntry)
+Q_DECLARE_METATYPE(QPaintBufferCacheEntryV2)
+QT_BEGIN_NAMESPACE
+
+QDataStream &operator<<(QDataStream &stream, const QPaintBufferCacheEntry &entry)
+{
+ return stream << entry.type << entry.cacheKey;
+}
+
+QDataStream &operator>>(QDataStream &stream, QPaintBufferCacheEntry &entry)
+{
+ return stream >> entry.type >> entry.cacheKey;
+}
+
+QDataStream &operator<<(QDataStream &stream, const QPaintBufferCacheEntryV2 &entry)
+{
+ return stream << entry.bits;
+}
+
+QDataStream &operator>>(QDataStream &stream, QPaintBufferCacheEntryV2 &entry)
+{
+ return stream >> entry.bits;
+}
+
+static int qRegisterPaintBufferMetaTypes()
+{
+ qRegisterMetaType<QPaintBufferCacheEntry>();
+ qRegisterMetaTypeStreamOperators<QPaintBufferCacheEntry>("QPaintBufferCacheEntry");
+ qRegisterMetaType<QPaintBufferCacheEntryV2>();
+ qRegisterMetaTypeStreamOperators<QPaintBufferCacheEntryV2>("QPaintBufferCacheEntryV2");
+
+ return 0; // something
+}
+
+Q_CONSTRUCTOR_FUNCTION(qRegisterPaintBufferMetaTypes)
+
+QDataStream &operator<<(QDataStream &stream, const QPaintBuffer &buffer)
+{
+ QHash<qint64, uint> pixmapKeys;
+ QHash<qint64, uint> imageKeys;
+
+ QHash<qint64, QPixmap> pixmaps;
+ QHash<qint64, QImage> images;
+
+ QVector<QVariant> variants = buffer.d_ptr->variants;
+ for (int i = 0; i < variants.size(); ++i) {
+ const QVariant &v = variants.at(i);
+ if (v.type() == QVariant::Image) {
+ const QImage image(v.value<QImage>());
+
+ QPaintBufferCacheEntryV2 entry;
+ entry.flags.type = QPaintBufferCacheEntryV2::ImageKey;
+
+ QHash<qint64, uint>::iterator it = imageKeys.find(image.cacheKey());
+ if (it != imageKeys.end()) {
+ entry.flags.key = *it;
+ } else {
+ imageKeys[image.cacheKey()] = entry.flags.key = images.size();
+ images[images.size()] = image;
+ }
+
+ variants[i] = QVariant::fromValue(entry);
+ } else if (v.type() == QVariant::Pixmap) {
+ const QPixmap pixmap(v.value<QPixmap>());
+
+ QPaintBufferCacheEntryV2 entry;
+ entry.flags.type = QPaintBufferCacheEntryV2::PixmapKey;
+
+ QHash<qint64, uint>::iterator it = pixmapKeys.find(pixmap.cacheKey());
+ if (it != pixmapKeys.end()) {
+ entry.flags.key = *it;
+ } else {
+ pixmapKeys[pixmap.cacheKey()] = entry.flags.key = pixmaps.size();
+ pixmaps[pixmaps.size()] = pixmap;
+ }
+
+ variants[i] = QVariant::fromValue(entry);
+ }
+ }
+
+ stream << pixmaps;
+ stream << images;
+
+ stream << buffer.d_ptr->ints;
+ stream << buffer.d_ptr->floats;
+ stream << variants;
+ stream << buffer.d_ptr->commands;
+ stream << buffer.d_ptr->boundingRect;
+ stream << buffer.d_ptr->frames;
+
+ return stream;
+}
+
+QDataStream &operator>>(QDataStream &stream, QPaintBuffer &buffer)
+{
+ QHash<qint64, QPixmap> pixmaps;
+ QHash<qint64, QImage> images;
+
+ stream >> pixmaps;
+ stream >> images;
+
+ stream >> buffer.d_ptr->ints;
+ stream >> buffer.d_ptr->floats;
+ stream >> buffer.d_ptr->variants;
+ stream >> buffer.d_ptr->commands;
+ stream >> buffer.d_ptr->boundingRect;
+ stream >> buffer.d_ptr->frames;
+
+ QVector<QVariant> &variants = buffer.d_ptr->variants;
+ for (int i = 0; i < variants.size(); ++i) {
+ const QVariant &v = variants.at(i);
+ if (v.canConvert<QPaintBufferCacheEntry>()) {
+ QPaintBufferCacheEntry entry = v.value<QPaintBufferCacheEntry>();
+ if (entry.type == QVariant::Image)
+ variants[i] = QVariant(images.value(entry.cacheKey));
+ else
+ variants[i] = QVariant(pixmaps.value(entry.cacheKey));
+ } else if (v.canConvert<QPaintBufferCacheEntryV2>()) {
+ QPaintBufferCacheEntryV2 entry = v.value<QPaintBufferCacheEntryV2>();
+
+ if (entry.flags.type == QPaintBufferCacheEntryV2::ImageKey)
+ variants[i] = QVariant(images.value(entry.flags.key));
+ else if (entry.flags.type == QPaintBufferCacheEntryV2::PixmapKey)
+ variants[i] = QVariant(pixmaps.value(entry.flags.key));
+ else
+ qWarning() << "operator<<(QDataStream &stream, QPaintBuffer &buffer): unrecognized cache entry type:" << entry.flags.type;
+ }
+ }
+
+ return stream;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qpaintbuffer_p.h b/src/gui/painting/qpaintbuffer_p.h
new file mode 100644
index 0000000000..6272dd912d
--- /dev/null
+++ b/src/gui/painting/qpaintbuffer_p.h
@@ -0,0 +1,461 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTBUFFER_P_H
+#define QPAINTBUFFER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qpaintdevice.h>
+
+#include <private/qpaintengineex_p.h>
+#include <private/qtextengine_p.h>
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+class QPaintBufferPrivate;
+class QPaintBufferPlayback;
+
+class Q_GUI_EXPORT QPaintBuffer : public QPaintDevice
+{
+ Q_DECLARE_PRIVATE(QPaintBuffer)
+public:
+ QPaintBuffer();
+ QPaintBuffer(const QPaintBuffer &other);
+ ~QPaintBuffer();
+
+ bool isEmpty() const;
+
+ void beginNewFrame();
+ int numFrames() const;
+
+ void draw(QPainter *painter, int frame = 0) const;
+
+ int frameStartIndex(int frame) const;
+ int frameEndIndex(int frame) const;
+ int processCommands(QPainter *painter, int begin, int end) const;
+#ifndef QT_NO_DEBUG_STREAM
+ QString commandDescription(int command) const;
+#endif
+
+ void setBoundingRect(const QRectF &rect);
+ QRectF boundingRect() const;
+
+ virtual QPaintEngine *paintEngine() const;
+ virtual int metric(PaintDeviceMetric m) const;
+ virtual int devType() const;
+
+ QPaintBuffer &operator=(const QPaintBuffer &other);
+
+private:
+ friend class QPainterReplayer;
+ friend class QOpenGLReplayer;
+
+ friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &stream, const QPaintBuffer &buffer);
+ friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QPaintBuffer &buffer);
+
+ QPaintBufferPrivate *d_ptr;
+};
+
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &stream, const QPaintBuffer &buffer);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QPaintBuffer &buffer);
+
+class QPaintBufferEngine;
+
+class QTextItemIntCopy
+{
+public:
+ QTextItemIntCopy(const QTextItem &item);
+ ~QTextItemIntCopy();
+ QTextItemInt &operator () () {return m_item;}
+private:
+ QTextItemInt m_item;
+ QFont m_font;
+};
+
+struct QPaintBufferCommand
+{
+ uint id : 8;
+ uint size : 24;
+
+ int offset;
+ int offset2;
+ int extra;
+};
+
+QDataStream &operator<<(QDataStream &stream, const QPaintBufferCommand &command);
+QDataStream &operator>>(QDataStream &stream, QPaintBufferCommand &command);
+
+Q_DECLARE_TYPEINFO(QPaintBufferCommand, Q_MOVABLE_TYPE);
+
+class QPaintBufferPrivate
+{
+public:
+ enum Command {
+ Cmd_Save,
+ Cmd_Restore,
+
+ Cmd_SetBrush,
+ Cmd_SetBrushOrigin,
+ Cmd_SetClipEnabled,
+ Cmd_SetCompositionMode,
+ Cmd_SetOpacity,
+ Cmd_SetPen,
+ Cmd_SetRenderHints,
+ Cmd_SetTransform,
+ Cmd_SetBackgroundMode,
+
+ Cmd_ClipPath,
+ Cmd_ClipRect,
+ Cmd_ClipRegion,
+ Cmd_ClipVectorPath,
+
+ Cmd_DrawVectorPath,
+ Cmd_FillVectorPath,
+ Cmd_StrokeVectorPath,
+
+ Cmd_DrawConvexPolygonF,
+ Cmd_DrawConvexPolygonI,
+ Cmd_DrawEllipseF,
+ Cmd_DrawEllipseI,
+ Cmd_DrawLineF,
+ Cmd_DrawLineI,
+ Cmd_DrawPath,
+ Cmd_DrawPointsF,
+ Cmd_DrawPointsI,
+ Cmd_DrawPolygonF,
+ Cmd_DrawPolygonI,
+ Cmd_DrawPolylineF,
+ Cmd_DrawPolylineI,
+ Cmd_DrawRectF,
+ Cmd_DrawRectI,
+
+ Cmd_FillRectBrush,
+ Cmd_FillRectColor,
+
+ Cmd_DrawText,
+ Cmd_DrawTextItem,
+
+ Cmd_DrawImagePos,
+ Cmd_DrawImageRect,
+ Cmd_DrawPixmapPos,
+ Cmd_DrawPixmapRect,
+ Cmd_DrawTiledPixmap,
+
+ Cmd_SystemStateChanged,
+ Cmd_Translate,
+ Cmd_DrawStaticText,
+
+ // new commands must be added above this line
+
+ Cmd_LastCommand
+ };
+
+ QPaintBufferPrivate();
+ ~QPaintBufferPrivate();
+
+ int addData(const int *data, int count) {
+ if (count <= 0)
+ return 0;
+ int pos = ints.size();
+ ints.resize(pos + count);
+ memcpy(ints.data() + pos, data, count * sizeof(int));
+ return pos;
+ }
+
+ int addData(const qreal *data, int count) {
+ if (count <= 0)
+ return 0;
+ int pos = floats.size();
+ floats.resize(pos + count);
+ memcpy(floats.data() + pos, data, count * sizeof(qreal));
+ return pos;
+ }
+
+ int addData(const QVariant &var) {
+ variants << var;
+ return variants.size() - 1;
+ }
+
+ QPaintBufferCommand *addCommand(Command command) {
+ QPaintBufferCommand cmd;
+ cmd.id = command;
+ cmd.size = cmd.offset = cmd.offset2 = cmd.extra = 0;
+ commands << cmd;
+ return &commands.last();
+ }
+
+ QPaintBufferCommand *addCommand(Command command, const QVariant &var) {
+ QPaintBufferCommand cmd;
+ cmd.id = command;
+ cmd.offset = addData(var);
+ cmd.size = cmd.offset2 = cmd.extra = 0;
+ commands << cmd;
+ return &commands.last();
+ }
+
+ QPaintBufferCommand *addCommand(Command command, const QVectorPath &path) {
+ QPaintBufferCommand cmd;
+ cmd.id = command;
+ cmd.offset = addData(path.points(), path.elementCount() * 2);
+ cmd.offset2 = ints.size();
+ ints << path.hints();
+ // The absence of path elements is indicated by setting the highest bit in 'cmd.offset2'.
+ if (path.elements())
+ addData((const int *) path.elements(), path.elementCount());
+ else
+ cmd.offset2 |= 0x80000000;
+ cmd.size = path.elementCount();
+ cmd.extra = 0;
+ commands << cmd;
+ return &commands.last();
+ }
+
+ QPaintBufferCommand *addCommand(Command command , const qreal *pts, int arrayLength, int elementCount) {
+ QPaintBufferCommand cmd;
+ cmd.id = command;
+ cmd.offset = addData(pts, arrayLength);
+ cmd.size = elementCount;
+ cmd.offset2 = cmd.extra = 0;
+ commands << cmd;
+ return &commands.last();
+ }
+
+ QPaintBufferCommand *addCommand(Command command , const int *pts, int arrayLength, int elementCount) {
+ QPaintBufferCommand cmd;
+ cmd.id = command;
+ cmd.offset = addData(pts, arrayLength);
+ cmd.size = elementCount;
+ cmd.offset2 = cmd.extra = 0;
+ commands << cmd;
+ return &commands.last();
+ }
+
+ inline void updateBoundingRect(const QRectF &rect);
+
+ QAtomicInt ref;
+
+ QVector<int> ints;
+ QVector<qreal> floats;
+ QVector<QVariant> variants;
+
+ QVector<QPaintBufferCommand> commands;
+ QList<int> frames;
+
+ QPaintBufferEngine *engine;
+ QRectF boundingRect;
+ qreal penWidthAdjustment;
+ uint calculateBoundingRect : 1;
+
+ void *cache;
+};
+
+
+struct QVectorPathCmd
+{
+ // The absence of path elements is indicated by setting the highest bit in 'cmd.offset2'.
+ QVectorPathCmd(QPaintBufferPrivate *d, const QPaintBufferCommand &cmd)
+ : vectorPath(d->floats.constData() + cmd.offset,
+ cmd.size,
+ cmd.offset2 & 0x80000000
+ ? 0
+ : (const QPainterPath::ElementType *) (d->ints.constData() + cmd.offset2 + 1),
+ *(d->ints.constData() + (cmd.offset2 & 0x7fffffff))) {}
+
+ inline const QVectorPath &operator()() const { return vectorPath; }
+
+ QVectorPath vectorPath;
+};
+
+
+class Q_GUI_EXPORT QPainterReplayer
+{
+public:
+ QPainterReplayer() { }
+
+ virtual ~QPainterReplayer() { }
+
+ void setupTransform(QPainter *painter);
+ virtual void process(const QPaintBufferCommand &cmd);
+ void processCommands(const QPaintBuffer &buffer, QPainter *painter, int begin, int end);
+
+protected:
+ QPaintBufferPrivate *d;
+ QTransform m_world_matrix;
+
+ QPainter *painter;
+};
+
+class Q_GUI_EXPORT QPaintEngineExReplayer : public QPainterReplayer
+{
+public:
+ QPaintEngineExReplayer() { }
+
+ virtual void process(const QPaintBufferCommand &cmd);
+};
+
+class QPaintBufferEnginePrivate;
+
+class QPaintBufferEngine : public QPaintEngineEx
+{
+ Q_DECLARE_PRIVATE(QPaintBufferEngine)
+public:
+ QPaintBufferEngine(QPaintBufferPrivate *buffer);
+
+ virtual bool begin(QPaintDevice *device);
+ virtual bool end();
+
+ virtual Type type() const { return QPaintEngine::PaintBuffer; }
+
+ virtual QPainterState *createState(QPainterState *orig) const;
+
+ virtual void draw(const QVectorPath &path);
+ virtual void fill(const QVectorPath &path, const QBrush &brush);
+ virtual void stroke(const QVectorPath &path, const QPen &pen);
+
+ virtual void clip(const QVectorPath &path, Qt::ClipOperation op);
+ virtual void clip(const QRect &rect, Qt::ClipOperation op);
+ virtual void clip(const QRegion &region, Qt::ClipOperation op);
+ virtual void clip(const QPainterPath &path, Qt::ClipOperation op);
+
+ virtual void clipEnabledChanged();
+ virtual void penChanged();
+ virtual void brushChanged();
+ virtual void brushOriginChanged();
+ virtual void opacityChanged();
+ virtual void compositionModeChanged();
+ virtual void renderHintsChanged();
+ virtual void transformChanged();
+ virtual void backgroundModeChanged();
+
+ virtual void fillRect(const QRectF &rect, const QBrush &brush);
+ virtual void fillRect(const QRectF &rect, const QColor &color);
+
+ virtual void drawRects(const QRect *rects, int rectCount);
+ virtual void drawRects(const QRectF *rects, int rectCount);
+
+ virtual void drawLines(const QLine *lines, int lineCount);
+ virtual void drawLines(const QLineF *lines, int lineCount);
+
+ virtual void drawEllipse(const QRectF &r);
+ virtual void drawEllipse(const QRect &r);
+
+ virtual void drawPath(const QPainterPath &path);
+
+ virtual void drawPoints(const QPointF *points, int pointCount);
+ virtual void drawPoints(const QPoint *points, int pointCount);
+
+ virtual void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode);
+ virtual void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode);
+
+ virtual void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
+ virtual void drawPixmap(const QPointF &pos, const QPixmap &pm);
+
+ virtual void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr,
+ Qt::ImageConversionFlags flags = Qt::AutoColor);
+ virtual void drawImage(const QPointF &pos, const QImage &image);
+
+ virtual void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s);
+
+ virtual void drawTextItem(const QPointF &pos, const QTextItem &ti);
+ virtual void drawStaticTextItem(QStaticTextItem *staticTextItem);
+
+ virtual void setState(QPainterState *s);
+ virtual uint flags() const {return QPaintEngineEx::DoNotEmulate;}
+
+ QPaintBufferPrivate *buffer;
+
+ mutable int m_begin_detected : 1;
+ mutable int m_save_detected : 1;
+ mutable int m_stream_raw_text_items : 1;
+ mutable int m_unused : 29;
+
+ mutable QPainterState *m_created_state;
+};
+
+class Q_GUI_EXPORT QPaintBufferSignalProxy : public QObject
+{
+ Q_OBJECT
+public:
+ QPaintBufferSignalProxy() : QObject() {}
+ void emitAboutToDestroy(const QPaintBufferPrivate *buffer) {
+ emit aboutToDestroy(buffer);
+ }
+ static QPaintBufferSignalProxy *instance();
+Q_SIGNALS:
+ void aboutToDestroy(const QPaintBufferPrivate *buffer);
+};
+
+// One resource per paint buffer and vice versa.
+class Q_GUI_EXPORT QPaintBufferResource : public QObject
+{
+ Q_OBJECT
+public:
+ typedef void (*FreeFunc)(void *);
+
+ QPaintBufferResource(FreeFunc f, QObject *parent = 0);
+ ~QPaintBufferResource();
+ // Set resource 'value' for 'key'.
+ void insert(const QPaintBufferPrivate *key, void *value);
+ // Return resource for 'key'.
+ void *value(const QPaintBufferPrivate *key);
+public slots:
+ // Remove entry 'key' from cache and delete resource.
+ void remove(const QPaintBufferPrivate *key);
+private:
+ typedef QHash<const QPaintBufferPrivate *, void *> Cache;
+ Cache m_cache;
+ FreeFunc free;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPAINTBUFFER_P_H
diff --git a/src/gui/painting/qpaintdevice.cpp b/src/gui/painting/qpaintdevice.cpp
new file mode 100644
index 0000000000..f2ba7f0744
--- /dev/null
+++ b/src/gui/painting/qpaintdevice.cpp
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpaintdevice.h"
+
+QT_BEGIN_NAMESPACE
+
+extern void qt_painter_removePaintDevice(QPaintDevice *); //qpainter.cpp
+
+QPaintDevice::QPaintDevice()
+{
+ painters = 0;
+}
+
+QPaintDevice::~QPaintDevice()
+{
+ if (paintingActive())
+ qWarning("QPaintDevice: Cannot destroy paint device that is being "
+ "painted");
+ qt_painter_removePaintDevice(this);
+}
+
+
+#ifndef Q_WS_QPA
+int QPaintDevice::metric(PaintDeviceMetric) const
+{
+ qWarning("QPaintDevice::metrics: Device has no metric information");
+ return 0;
+}
+#endif
+
+Q_GUI_EXPORT int qt_paint_device_metric(const QPaintDevice *device, QPaintDevice::PaintDeviceMetric metric)
+{
+ return device->metric(metric);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qpaintdevice.h b/src/gui/painting/qpaintdevice.h
new file mode 100644
index 0000000000..67db3f653f
--- /dev/null
+++ b/src/gui/painting/qpaintdevice.h
@@ -0,0 +1,177 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTDEVICE_H
+#define QPAINTDEVICE_H
+
+#include <QtGui/qwindowdefs.h>
+#include <QtCore/qrect.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if defined(Q_WS_QWS)
+class QWSDisplay;
+#endif
+
+class QPaintEngine;
+
+class Q_GUI_EXPORT QPaintDevice // device for QPainter
+{
+public:
+ enum PaintDeviceMetric {
+ PdmWidth = 1,
+ PdmHeight,
+ PdmWidthMM,
+ PdmHeightMM,
+ PdmNumColors,
+ PdmDepth,
+ PdmDpiX,
+ PdmDpiY,
+ PdmPhysicalDpiX,
+ PdmPhysicalDpiY
+ };
+
+ virtual ~QPaintDevice();
+
+ virtual int devType() const;
+ bool paintingActive() const;
+ virtual QPaintEngine *paintEngine() const = 0;
+
+#if defined(Q_WS_QWS)
+ static QWSDisplay *qwsDisplay();
+#endif
+
+#ifdef Q_WS_WIN
+ virtual HDC getDC() const;
+ virtual void releaseDC(HDC hdc) const;
+#endif
+
+ int width() const { return metric(PdmWidth); }
+ int height() const { return metric(PdmHeight); }
+ int widthMM() const { return metric(PdmWidthMM); }
+ int heightMM() const { return metric(PdmHeightMM); }
+ int logicalDpiX() const { return metric(PdmDpiX); }
+ int logicalDpiY() const { return metric(PdmDpiY); }
+ int physicalDpiX() const { return metric(PdmPhysicalDpiX); }
+ int physicalDpiY() const { return metric(PdmPhysicalDpiY); }
+#ifdef QT_DEPRECATED
+ QT_DEPRECATED int numColors() const { return metric(PdmNumColors); }
+#endif
+ int colorCount() const { return metric(PdmNumColors); }
+ int depth() const { return metric(PdmDepth); }
+
+protected:
+ QPaintDevice();
+ virtual int metric(PaintDeviceMetric metric) const;
+
+ ushort painters; // refcount
+
+private:
+ Q_DISABLE_COPY(QPaintDevice)
+
+#if defined(Q_WS_X11) && defined(QT3_SUPPORT)
+public:
+ QT3_SUPPORT Display *x11Display() const;
+ QT3_SUPPORT int x11Screen() const;
+ QT3_SUPPORT int x11Depth() const;
+ QT3_SUPPORT int x11Cells() const;
+ QT3_SUPPORT Qt::HANDLE x11Colormap() const;
+ QT3_SUPPORT bool x11DefaultColormap() const;
+ QT3_SUPPORT void *x11Visual() const;
+ QT3_SUPPORT bool x11DefaultVisual() const;
+
+ static QT3_SUPPORT Display *x11AppDisplay();
+ static QT3_SUPPORT int x11AppScreen();
+ static QT3_SUPPORT int x11AppDepth(int screen = -1);
+ static QT3_SUPPORT int x11AppCells(int screen = -1);
+ static QT3_SUPPORT Qt::HANDLE x11AppRootWindow(int screen = -1);
+ static QT3_SUPPORT Qt::HANDLE x11AppColormap(int screen = -1);
+ static QT3_SUPPORT void *x11AppVisual(int screen = -1);
+ static QT3_SUPPORT bool x11AppDefaultColormap(int screen =-1);
+ static QT3_SUPPORT bool x11AppDefaultVisual(int screen =-1);
+ static QT3_SUPPORT int x11AppDpiX(int screen = -1);
+ static QT3_SUPPORT int x11AppDpiY(int screen = -1);
+ static QT3_SUPPORT void x11SetAppDpiX(int, int);
+ static QT3_SUPPORT void x11SetAppDpiY(int, int);
+#endif
+
+ friend class QPainter;
+ friend class QFontEngineMac;
+ friend class QX11PaintEngine;
+ friend Q_GUI_EXPORT int qt_paint_device_metric(const QPaintDevice *device, PaintDeviceMetric metric);
+};
+
+#ifdef QT3_SUPPORT
+QT3_SUPPORT Q_GUI_EXPORT
+void bitBlt(QPaintDevice *dst, int dx, int dy,
+ const QPaintDevice *src, int sx=0, int sy=0, int sw=-1, int sh=-1,
+ bool ignoreMask=false);
+
+QT3_SUPPORT Q_GUI_EXPORT
+void bitBlt(QPaintDevice *dst, int dx, int dy,
+ const QImage *src, int sx=0, int sy=0, int sw=-1, int sh=-1,
+ int conversion_flags=0);
+
+QT3_SUPPORT Q_GUI_EXPORT
+void bitBlt(QPaintDevice *dst, const QPoint &dp,
+ const QPaintDevice *src, const QRect &sr=QRect(0,0,-1,-1),
+ bool ignoreMask=false);
+#endif
+
+/*****************************************************************************
+ Inline functions
+ *****************************************************************************/
+
+inline int QPaintDevice::devType() const
+{ return QInternal::UnknownDevice; }
+
+inline bool QPaintDevice::paintingActive() const
+{ return painters != 0; }
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPAINTDEVICE_H
diff --git a/src/gui/painting/qpaintdevice.qdoc b/src/gui/painting/qpaintdevice.qdoc
new file mode 100644
index 0000000000..dc5c3583e7
--- /dev/null
+++ b/src/gui/painting/qpaintdevice.qdoc
@@ -0,0 +1,286 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Free Documentation License
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of this
+** file.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \class QPaintDevice
+ \brief The QPaintDevice class is the base class of objects that
+ can be painted.
+
+ \ingroup painting
+
+ A paint device is an abstraction of a two-dimensional space that
+ can be drawn using a QPainter. Its default coordinate system has
+ its origin located at the top-left position. X increases to the
+ right and Y increases downwards. The unit is one pixel.
+
+ The drawing capabilities of QPaintDevice are currently implemented
+ by the QWidget, QImage, QPixmap, QGLPixelBuffer, QPicture, and
+ QPrinter subclasses.
+
+ To implement support for a new backend, you must derive from
+ QPaintDevice and reimplement the virtual paintEngine() function to
+ tell QPainter which paint engine should be used to draw on this
+ particular device. Note that you also must create a corresponding
+ paint engine to be able to draw on the device, i.e derive from
+ QPaintEngine and reimplement its virtual functions.
+
+ \warning Qt requires that a QApplication object exists before
+ any paint devices can be created. Paint devices access window
+ system resources, and these resources are not initialized before
+ an application object is created.
+
+ The QPaintDevice class provides several functions returning the
+ various device metrics: The depth() function returns its bit depth
+ (number of bit planes). The height() function returns its height
+ in default coordinate system units (e.g. pixels for QPixmap and
+ QWidget) while heightMM() returns the height of the device in
+ millimeters. Similiarily, the width() and widthMM() functions
+ return the width of the device in default coordinate system units
+ and in millimeters, respectively. Alternatively, the protected
+ metric() function can be used to retrieve the metric information
+ by specifying the desired PaintDeviceMetric as argument.
+
+ The logicalDpiX() and logicalDpiY() functions return the
+ horizontal and vertical resolution of the device in dots per
+ inch. The physicalDpiX() and physicalDpiY() functions also return
+ the resolution of the device in dots per inch, but note that if
+ the logical and physical resolution differ, the corresponding
+ QPaintEngine must handle the mapping. Finally, the colorCount()
+ function returns the number of different colors available for the
+ paint device.
+
+ \sa QPaintEngine, QPainter, {Coordinate System}, {Paint System}
+*/
+
+/*!
+ \enum QPaintDevice::PaintDeviceMetric
+
+ Describes the various metrics of a paint device.
+
+ \value PdmWidth The width of the paint device in default
+ coordinate system units (e.g. pixels for QPixmap and QWidget). See
+ also width().
+
+ \value PdmHeight The height of the paint device in default
+ coordinate system units (e.g. pixels for QPixmap and QWidget). See
+ also height().
+
+ \value PdmWidthMM The width of the paint device in millimeters. See
+ also widthMM().
+
+ \value PdmHeightMM The height of the paint device in millimeters. See
+ also heightMM().
+
+ \value PdmNumColors The number of different colors available for
+ the paint device. See also colorCount().
+
+ \value PdmDepth The bit depth (number of bit planes) of the paint
+ device. See also depth().
+
+ \value PdmDpiX The horizontal resolution of the device in dots per
+ inch. See also logicalDpiX().
+
+ \value PdmDpiY The vertical resolution of the device in dots per inch. See
+ also logicalDpiY().
+
+ \value PdmPhysicalDpiX The horizontal resolution of the device in
+ dots per inch. See also physicalDpiX().
+
+ \value PdmPhysicalDpiY The vertical resolution of the device in
+ dots per inch. See also physicalDpiY().
+
+ \sa metric()
+*/
+
+/*!
+ \fn QPaintDevice::QPaintDevice()
+
+ Constructs a paint device. This constructor can be invoked only from
+ subclasses of QPaintDevice.
+*/
+
+/*!
+ \fn QPaintDevice::~QPaintDevice()
+
+ Destroys the paint device and frees window system resources.
+*/
+
+/*!
+ \fn int QPaintDevice::devType() const
+
+ \internal
+
+ Returns the device type identifier, which is QInternal::Widget
+ if the device is a QWidget, QInternal::Pixmap if it's a
+ QPixmap, QInternal::Printer if it's a QPrinter,
+ QInternal::Picture if it's a QPicture, or
+ QInternal::UnknownDevice in other cases.
+*/
+
+/*!
+ \fn bool QPaintDevice::paintingActive() const
+
+ Returns true if the device is currently being painted on, i.e. someone has
+ called QPainter::begin() but not yet called QPainter::end() for
+ this device; otherwise returns false.
+
+ \sa QPainter::isActive()
+*/
+
+/*!
+ \fn QPaintEngine *QPaintDevice::paintEngine() const
+
+ Returns a pointer to the paint engine used for drawing on the
+ device.
+*/
+
+/*!
+ \fn int QPaintDevice::metric(PaintDeviceMetric metric) const
+
+ Returns the metric information for the given paint device \a metric.
+
+ \sa PaintDeviceMetric
+*/
+
+/*!
+ \fn int QPaintDevice::width() const
+
+ Returns the width of the paint device in default coordinate system
+ units (e.g. pixels for QPixmap and QWidget).
+
+ \sa widthMM()
+*/
+
+/*!
+ \fn int QPaintDevice::height() const
+
+ Returns the height of the paint device in default coordinate
+ system units (e.g. pixels for QPixmap and QWidget).
+
+ \sa heightMM()
+*/
+
+/*!
+ \fn int QPaintDevice::widthMM() const
+
+ Returns the width of the paint device in millimeters. Due to platform
+ limitations it may not be possible to use this function to determine
+ the actual physical size of a widget on the screen.
+
+ \sa width()
+*/
+
+/*!
+ \fn int QPaintDevice::heightMM() const
+
+ Returns the height of the paint device in millimeters. Due to platform
+ limitations it may not be possible to use this function to determine
+ the actual physical size of a widget on the screen.
+
+ \sa height()
+*/
+
+/*!
+ \fn int QPaintDevice::numColors() const
+ \deprecated
+
+ Use colorCount() instead.
+
+ Returns the number of different colors available for the paint
+ device. Since this value is an int, it will not be sufficient to
+ represent the number of colors on 32 bit displays, in this case
+ INT_MAX is returned instead.
+ */
+
+/*!
+ \fn int QPaintDevice::colorCount() const
+
+ Returns the number of different colors available for the paint
+ device. Since this value is an int, it will not be sufficient to
+ represent the number of colors on 32 bit displays, in this case
+ INT_MAX is returned instead.
+*/
+
+/*!
+ \fn int QPaintDevice::depth() const
+
+ Returns the bit depth (number of bit planes) of the paint device.
+*/
+
+/*!
+ \fn int QPaintDevice::logicalDpiX() const
+
+ Returns the horizontal resolution of the device in dots per inch,
+ which is used when computing font sizes. For X11, this is usually
+ the same as could be computed from widthMM().
+
+ Note that if the logicalDpiX() doesn't equal the physicalDpiX(),
+ the corresponding QPaintEngine must handle the resolution mapping.
+
+ \sa logicalDpiY(), physicalDpiX()
+*/
+
+/*!
+ \fn int QPaintDevice::logicalDpiY() const
+
+ Returns the vertical resolution of the device in dots per inch,
+ which is used when computing font sizes. For X11, this is usually
+ the same as could be computed from heightMM().
+
+ Note that if the logicalDpiY() doesn't equal the physicalDpiY(),
+ the corresponding QPaintEngine must handle the resolution mapping.
+
+ \sa logicalDpiX(), physicalDpiY()
+*/
+
+/*!
+ \fn int QPaintDevice::physicalDpiX() const
+
+ Returns the horizontal resolution of the device in dots per inch.
+ For example, when printing, this resolution refers to the physical
+ printer's resolution. The logical DPI on the other hand, refers to
+ the resolution used by the actual paint engine.
+
+ Note that if the physicalDpiX() doesn't equal the logicalDpiX(),
+ the corresponding QPaintEngine must handle the resolution mapping.
+
+ \sa physicalDpiY(), logicalDpiX()
+*/
+
+/*!
+ \fn int QPaintDevice::physicalDpiY() const
+
+ Returns the horizontal resolution of the device in dots per inch.
+ For example, when printing, this resolution refers to the physical
+ printer's resolution. The logical DPI on the other hand, refers to
+ the resolution used by the actual paint engine.
+
+ Note that if the physicalDpiY() doesn't equal the logicalDpiY(),
+ the corresponding QPaintEngine must handle the resolution mapping.
+
+ \sa physicalDpiX(), logicalDpiY()
+*/
diff --git a/src/gui/painting/qpaintdevice_mac.cpp b/src/gui/painting/qpaintdevice_mac.cpp
new file mode 100644
index 0000000000..245408a0b0
--- /dev/null
+++ b/src/gui/painting/qpaintdevice_mac.cpp
@@ -0,0 +1,152 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpaintdevice.h"
+#include "qpainter.h"
+#include "qwidget.h"
+#include "qbitmap.h"
+#include "qapplication.h"
+#include "qprinter.h"
+#include <qdebug.h>
+#include <private/qt_mac_p.h>
+#include <private/qprintengine_mac_p.h>
+#include <private/qpixmap_mac_p.h>
+#include <private/qpixmap_raster_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*****************************************************************************
+ Internal variables and functions
+ *****************************************************************************/
+
+/*! \internal */
+float qt_mac_defaultDpi_x()
+{
+ // Mac OS X currently assumes things to be 72 dpi.
+ // (see http://developer.apple.com/releasenotes/GraphicsImaging/RN-ResolutionIndependentUI/)
+ // This may need to be re-worked as we go further in the resolution-independence stuff.
+ return 72;
+}
+
+/*! \internal */
+float qt_mac_defaultDpi_y()
+{
+ // Mac OS X currently assumes things to be 72 dpi.
+ // (see http://developer.apple.com/releasenotes/GraphicsImaging/RN-ResolutionIndependentUI/)
+ // This may need to be re-worked as we go further in the resolution-independence stuff.
+ return 72;
+}
+
+
+/*! \internal
+
+ Returns the QuickDraw CGrafPtr of the paint device. 0 is returned
+ if it can't be obtained. Do not hold the pointer around for long
+ as it can be relocated.
+
+ \warning This function is only available on Mac OS X.
+*/
+
+Q_GUI_EXPORT GrafPtr qt_mac_qd_context(const QPaintDevice *device)
+{
+ if (device->devType() == QInternal::Pixmap) {
+ return static_cast<GrafPtr>(static_cast<const QPixmap *>(device)->macQDHandle());
+ } else if(device->devType() == QInternal::Widget) {
+ return static_cast<GrafPtr>(static_cast<const QWidget *>(device)->macQDHandle());
+ } else if(device->devType() == QInternal::Printer) {
+ QPaintEngine *engine = static_cast<const QPrinter *>(device)->paintEngine();
+ return static_cast<GrafPtr>(static_cast<const QMacPrintEngine *>(engine)->handle());
+ }
+ return 0;
+}
+
+extern CGColorSpaceRef qt_mac_colorSpaceForDeviceType(const QPaintDevice *pdev);
+
+/*! \internal
+
+ Returns the CoreGraphics CGContextRef of the paint device. 0 is
+ returned if it can't be obtained. It is the caller's responsiblity to
+ CGContextRelease the context when finished using it.
+
+ \warning This function is only available on Mac OS X.
+*/
+
+Q_GUI_EXPORT CGContextRef qt_mac_cg_context(const QPaintDevice *pdev)
+{
+ if (pdev->devType() == QInternal::Pixmap) {
+ const QPixmap *pm = static_cast<const QPixmap*>(pdev);
+ CGColorSpaceRef colorspace = qt_mac_colorSpaceForDeviceType(pdev);
+ uint flags = kCGImageAlphaPremultipliedFirst;
+#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version
+ flags |= kCGBitmapByteOrder32Host;
+#endif
+ CGContextRef ret = 0;
+
+ // It would make sense to put this into a mac #ifdef'ed
+ // virtual function in the QPixmapData at some point
+ if (pm->data->classId() == QPixmapData::MacClass) {
+ const QMacPixmapData *pmData = static_cast<const QMacPixmapData*>(pm->data.data());
+ ret = CGBitmapContextCreate(pmData->pixels, pmData->w, pmData->h,
+ 8, pmData->bytesPerRow, colorspace,
+ flags);
+ if(!ret)
+ qWarning("QPaintDevice: Unable to create context for pixmap (%d/%d/%d)",
+ pmData->w, pmData->h, (pmData->bytesPerRow * pmData->h));
+ } else if (pm->data->classId() == QPixmapData::RasterClass) {
+ QImage *image = pm->data->buffer();
+ ret = CGBitmapContextCreate(image->bits(), image->width(), image->height(),
+ 8, image->bytesPerLine(), colorspace, flags);
+ }
+
+ CGContextTranslateCTM(ret, 0, pm->height());
+ CGContextScaleCTM(ret, 1, -1);
+ return ret;
+ } else if (pdev->devType() == QInternal::Widget) {
+ CGContextRef ret = static_cast<CGContextRef>(static_cast<const QWidget *>(pdev)->macCGHandle());
+ CGContextRetain(ret);
+ return ret;
+ } else if (pdev->devType() == QInternal::MacQuartz) {
+ return static_cast<const QMacQuartzPaintDevice *>(pdev)->cgContext();
+ }
+ return 0;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qpaintdevice_qpa.cpp b/src/gui/painting/qpaintdevice_qpa.cpp
new file mode 100644
index 0000000000..0d1ca92d45
--- /dev/null
+++ b/src/gui/painting/qpaintdevice_qpa.cpp
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpaintdevice.h"
+#include "qpainter.h"
+#include "qwidget.h"
+#include "qbitmap.h"
+#include "qapplication.h"
+
+QT_BEGIN_NAMESPACE
+
+extern void qt_painter_removePaintDevice(QPaintDevice *); //qpainter.cpp
+
+int QPaintDevice::metric(PaintDeviceMetric m) const
+{
+ qWarning("QPaintDevice::metrics: Device has no metric information");
+ if (m == PdmDpiX) {
+ return 72;
+ } else if (m == PdmDpiY) {
+ return 72;
+ } else if (m == PdmNumColors) {
+ // FIXME: does this need to be a real value?
+ return 256;
+ } else {
+ qDebug("Unrecognised metric %d!",m);
+ return 0;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qpaintdevice_qws.cpp b/src/gui/painting/qpaintdevice_qws.cpp
new file mode 100644
index 0000000000..6f9433ab62
--- /dev/null
+++ b/src/gui/painting/qpaintdevice_qws.cpp
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpaintdevice.h"
+#include "qpainter.h"
+#include "qwidget.h"
+#include "qbitmap.h"
+#include "qapplication.h"
+#include "qwsdisplay_qws.h"
+
+QT_BEGIN_NAMESPACE
+
+QWSDisplay *QPaintDevice::qwsDisplay()
+{
+ return qt_fbdpy;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qpaintdevice_win.cpp b/src/gui/painting/qpaintdevice_win.cpp
new file mode 100644
index 0000000000..3dbe97492a
--- /dev/null
+++ b/src/gui/painting/qpaintdevice_win.cpp
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpaintdevice.h"
+#include "qpainter.h"
+#include "qwidget.h"
+#include "qbitmap.h"
+#include "qapplication.h"
+#include <private/qapplication_p.h>
+#include "qt_windows.h"
+#include "qprinter.h"
+
+QT_BEGIN_NAMESPACE
+
+HDC QPaintDevice::getDC() const
+{
+ return 0;
+}
+
+void QPaintDevice::releaseDC(HDC) const
+{
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qpaintdevice_x11.cpp b/src/gui/painting/qpaintdevice_x11.cpp
new file mode 100644
index 0000000000..b6be0761c5
--- /dev/null
+++ b/src/gui/painting/qpaintdevice_x11.cpp
@@ -0,0 +1,198 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpaintdevice.h"
+#include "qpainter.h"
+#include "qwidget.h"
+#include "qbitmap.h"
+#include "qapplication.h"
+#include <private/qt_x11_p.h>
+#include "qx11info_x11.h"
+
+QT_BEGIN_NAMESPACE
+
+/*! \internal
+
+ Returns the X11 Drawable of the paint device. 0 is returned if it
+ can't be obtained.
+*/
+
+Drawable Q_GUI_EXPORT qt_x11Handle(const QPaintDevice *pd)
+{
+ if (!pd) return 0;
+ if (pd->devType() == QInternal::Widget)
+ return static_cast<const QWidget *>(pd)->handle();
+ else if (pd->devType() == QInternal::Pixmap)
+ return static_cast<const QPixmap *>(pd)->handle();
+ return 0;
+}
+
+/*!
+ \relates QPaintDevice
+
+ Returns the QX11Info structure for the \a pd paint device. 0 is
+ returned if it can't be obtained.
+*/
+const Q_GUI_EXPORT QX11Info *qt_x11Info(const QPaintDevice *pd)
+{
+ if (!pd) return 0;
+ if (pd->devType() == QInternal::Widget)
+ return &static_cast<const QWidget *>(pd)->x11Info();
+ else if (pd->devType() == QInternal::Pixmap)
+ return &static_cast<const QPixmap *>(pd)->x11Info();
+ return 0;
+}
+
+
+
+#ifdef QT3_SUPPORT
+
+Display *QPaintDevice::x11Display() const
+{
+ return X11->display;
+}
+
+int QPaintDevice::x11Screen() const
+{
+ const QX11Info *info = qt_x11Info(this);
+ if (info)
+ return info->screen();
+ return QX11Info::appScreen();
+}
+
+void *QPaintDevice::x11Visual() const
+{
+ const QX11Info *info = qt_x11Info(this);
+ if (info)
+ return info->visual();
+ return QX11Info::appVisual();
+}
+
+int QPaintDevice::x11Depth() const
+{
+ const QX11Info *info = qt_x11Info(this);
+ if (info)
+ return info->depth();
+ return QX11Info::appDepth();
+}
+
+int QPaintDevice::x11Cells() const
+{
+ const QX11Info *info = qt_x11Info(this);
+ if (info)
+ return info->cells();
+ return QX11Info::appCells();
+}
+
+Qt::HANDLE QPaintDevice::x11Colormap() const
+{
+ const QX11Info *info = qt_x11Info(this);
+ if (info)
+ return info->colormap();
+ return QX11Info::appColormap();
+}
+
+bool QPaintDevice::x11DefaultColormap() const
+{
+ const QX11Info *info = qt_x11Info(this);
+ if (info)
+ return info->defaultColormap();
+ return QX11Info::appDefaultColormap();
+}
+
+bool QPaintDevice::x11DefaultVisual() const
+{
+ const QX11Info *info = qt_x11Info(this);
+ if (info)
+ return info->defaultVisual();
+ return QX11Info::appDefaultVisual();
+}
+
+void *QPaintDevice::x11AppVisual(int screen)
+{ return QX11Info::appVisual(screen); }
+
+Qt::HANDLE QPaintDevice::x11AppColormap(int screen)
+{ return QX11Info::appColormap(screen); }
+
+Display *QPaintDevice::x11AppDisplay()
+{ return QX11Info::display(); }
+
+int QPaintDevice::x11AppScreen()
+{ return QX11Info::appScreen(); }
+
+int QPaintDevice::x11AppDepth(int screen)
+{ return QX11Info::appDepth(screen); }
+
+int QPaintDevice::x11AppCells(int screen)
+{ return QX11Info::appCells(screen); }
+
+Qt::HANDLE QPaintDevice::x11AppRootWindow(int screen)
+{ return QX11Info::appRootWindow(screen); }
+
+bool QPaintDevice::x11AppDefaultColormap(int screen)
+{ return QX11Info::appDefaultColormap(screen); }
+
+bool QPaintDevice::x11AppDefaultVisual(int screen)
+{ return QX11Info::appDefaultVisual(screen); }
+
+void QPaintDevice::x11SetAppDpiX(int dpi, int screen)
+{
+ QX11Info::setAppDpiX(dpi, screen);
+}
+
+void QPaintDevice::x11SetAppDpiY(int dpi, int screen)
+{
+ QX11Info::setAppDpiY(dpi, screen);
+}
+
+int QPaintDevice::x11AppDpiX(int screen)
+{
+ return QX11Info::appDpiX(screen);
+}
+
+int QPaintDevice::x11AppDpiY(int screen)
+{
+ return QX11Info::appDpiY(screen);
+}
+#endif
+
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qpaintengine.cpp b/src/gui/painting/qpaintengine.cpp
new file mode 100644
index 0000000000..6eb09e5966
--- /dev/null
+++ b/src/gui/painting/qpaintengine.cpp
@@ -0,0 +1,1030 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qpaintengine.h"
+#include "qpaintengine_p.h"
+#include "qpainter_p.h"
+#include "qpolygon.h"
+#include "qbitmap.h"
+#include "qapplication.h"
+#include <qdebug.h>
+#include <qmath.h>
+#include <private/qtextengine_p.h>
+#include <qvarlengtharray.h>
+#include <private/qfontengine_p.h>
+#include <private/qpaintengineex_p.h>
+
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QTextItem
+
+ \brief The QTextItem class provides all the information required to draw
+ text in a custom paint engine.
+
+ When you reimplement your own paint engine, you must reimplement
+ QPaintEngine::drawTextItem(), a function that takes a QTextItem as
+ one of its arguments.
+*/
+
+/*!
+ \enum QTextItem::RenderFlag
+
+ \value RightToLeft Render the text from right to left.
+ \value Overline Paint a line above the text.
+ \value Underline Paint a line under the text.
+ \value StrikeOut Paint a line through the text.
+ \omitvalue Dummy
+*/
+
+
+/*!
+ \fn qreal QTextItem::descent() const
+
+ Corresponds to the \l{QFontMetrics::descent()}{descent} of the piece of text that is drawn.
+*/
+qreal QTextItem::descent() const
+{
+ const QTextItemInt *ti = static_cast<const QTextItemInt *>(this);
+ return ti->descent.toReal();
+}
+
+/*!
+ \fn qreal QTextItem::ascent() const
+
+ Corresponds to the \l{QFontMetrics::ascent()}{ascent} of the piece of text that is drawn.
+*/
+qreal QTextItem::ascent() const
+{
+ const QTextItemInt *ti = static_cast<const QTextItemInt *>(this);
+ return ti->ascent.toReal();
+}
+
+/*!
+ \fn qreal QTextItem::width() const
+
+ Specifies the total width of the text to be drawn.
+*/
+qreal QTextItem::width() const
+{
+ const QTextItemInt *ti = static_cast<const QTextItemInt *>(this);
+ return ti->width.toReal();
+}
+
+/*!
+ \fn QTextItem::RenderFlags QTextItem::renderFlags() const
+
+ Returns the render flags used.
+*/
+QTextItem::RenderFlags QTextItem::renderFlags() const
+{
+ const QTextItemInt *ti = static_cast<const QTextItemInt *>(this);
+ return ti->flags;
+}
+
+/*!
+ \fn QString QTextItem::text() const
+
+ Returns the text that should be drawn.
+*/
+QString QTextItem::text() const
+{
+ const QTextItemInt *ti = static_cast<const QTextItemInt *>(this);
+ return QString(ti->chars, ti->num_chars);
+}
+
+/*!
+ \fn QFont QTextItem::font() const
+
+ Returns the font that should be used to draw the text.
+*/
+QFont QTextItem::font() const
+{
+ const QTextItemInt *ti = static_cast<const QTextItemInt *>(this);
+ return ti->f ? *ti->f : QApplication::font();
+}
+
+
+/*!
+ \class QPaintEngine
+ \ingroup painting
+
+ \brief The QPaintEngine class provides an abstract definition of how
+ QPainter draws to a given device on a given platform.
+
+ Qt 4.0 provides several premade implementations of QPaintEngine for the
+ different painter backends we support. We provide one paint engine for each
+ window system and painting framework we support. This includes X11 on
+ Unix/Linux and CoreGraphics on Mac OS X. In addition we provide QPaintEngine
+ implementations for OpenGL (accessible through QGLWidget) and PostScript
+ (accessible through QPSPrinter on X11). Additionally there is a raster-based
+ paint engine that is a fallback for when an engine does not support a certain
+ capability.
+
+ If one wants to use QPainter to draw to a different backend,
+ one must subclass QPaintEngine and reimplement all its virtual
+ functions. The QPaintEngine implementation is then made available by
+ subclassing QPaintDevice and reimplementing the virtual function
+ QPaintDevice::paintEngine().
+
+ QPaintEngine is created and owned by the QPaintDevice that created it.
+
+ The big advantage of the QPaintEngine approach opposed to
+ Qt 3's QPainter/QPaintDevice::cmd() approach is that it is now
+ possible to adapt to multiple technologies on each platform and take
+ advantage of each to the fullest.
+
+ \sa QPainter, QPaintDevice::paintEngine(), {Paint System}
+*/
+
+/*!
+ \enum QPaintEngine::PaintEngineFeature
+
+ This enum is used to describe the features or capabilities that the
+ paint engine has. If a feature is not supported by the engine,
+ QPainter will do a best effort to emulate that feature through other
+ means and pass on an alpha blended QImage to the engine with the
+ emulated results. Some features cannot be emulated: AlphaBlend and PorterDuff.
+
+ \value AlphaBlend The engine can alpha blend primitives.
+ \value Antialiasing The engine can use antialising to improve the appearance
+ of rendered primitives.
+ \value BlendModes The engine supports blending modes.
+ \value BrushStroke The engine supports drawing strokes that
+ contain brushes as fills, not just solid
+ colors (e.g. a dashed gradient line of
+ width 2).
+ \value ConicalGradientFill The engine supports conical gradient fills.
+ \value ConstantOpacity The engine supports the feature provided by
+ QPainter::setOpacity().
+ \value LinearGradientFill The engine supports linear gradient fills.
+ \value MaskedBrush The engine is capable of rendering brushes that has a
+ texture with an alpha channel or a mask.
+ \value ObjectBoundingModeGradients The engine has native support for gradients
+ with coordinate mode QGradient::ObjectBoundingMode.
+ Otherwise, if QPaintEngine::PatternTransform is
+ supported, object bounding mode gradients are
+ converted to gradients with coordinate mode
+ QGradient::LogicalMode and a brush transform for
+ the coordinate mapping.
+ \value PainterPaths The engine has path support.
+ \value PaintOutsidePaintEvent The engine is capable of painting outside of
+ paint events.
+ \value PatternBrush The engine is capable of rendering brushes with
+ the brush patterns specified in Qt::BrushStyle.
+ \value PatternTransform The engine has support for transforming brush
+ patterns.
+ \value PerspectiveTransform The engine has support for performing perspective
+ transformations on primitives.
+ \value PixmapTransform The engine can transform pixmaps, including
+ rotation and shearing.
+ \value PorterDuff The engine supports Porter-Duff operations
+ \value PrimitiveTransform The engine has support for transforming
+ drawing primitives.
+ \value RadialGradientFill The engine supports radial gradient fills.
+ \value RasterOpModes The engine supports bitwise raster operations.
+ \value AllFeatures All of the above features. This enum value is usually
+ used as a bit mask.
+*/
+
+/*!
+ \enum QPaintEngine::PolygonDrawMode
+
+ \value OddEvenMode The polygon should be drawn using OddEven fill
+ rule.
+
+ \value WindingMode The polygon should be drawn using Winding fill rule.
+
+ \value ConvexMode The polygon is a convex polygon and can be drawn
+ using specialized algorithms where available.
+
+ \value PolylineMode Only the outline of the polygon should be
+ drawn.
+
+*/
+
+/*!
+ \enum QPaintEngine::DirtyFlag
+
+ \value DirtyPen The pen is dirty and needs to be updated.
+
+ \value DirtyBrush The brush is dirty and needs to be updated.
+
+ \value DirtyBrushOrigin The brush origin is dirty and needs to
+ updated.
+
+ \value DirtyFont The font is dirty and needs to be updated.
+
+ \value DirtyBackground The background is dirty and needs to be
+ updated.
+
+ \value DirtyBackgroundMode The background mode is dirty and needs
+ to be updated.
+
+ \value DirtyTransform The transform is dirty and needs to be
+ updated.
+
+ \value DirtyClipRegion The clip region is dirty and needs to be
+ updated.
+
+ \value DirtyClipPath The clip path is dirty and needs to be
+ updated.
+
+ \value DirtyHints The render hints is dirty and needs to be
+ updated.
+
+ \value DirtyCompositionMode The composition mode is dirty and
+ needs to be updated.
+
+ \value DirtyClipEnabled Whether clipping is enabled or not is
+ dirty and needs to be updated.
+
+ \value DirtyOpacity The constant opacity has changed and needs to
+ be updated as part of the state change in
+ QPaintEngine::updateState().
+
+ \value AllDirty Convenience enum used internally.
+
+ These types are used by QPainter to trigger lazy updates of the
+ various states in the QPaintEngine using
+ QPaintEngine::updateState().
+
+ A paint engine must update every dirty state.
+*/
+
+/*!
+ \fn void QPaintEngine::syncState()
+
+ \internal
+
+ Updates all dirty states in this engine. This function should ONLY
+ be used when drawing with native handles directly and immediate sync
+ from QPainters state to the native state is required.
+*/
+void QPaintEngine::syncState()
+{
+ Q_ASSERT(state);
+ updateState(*state);
+
+ if (isExtended())
+ static_cast<QPaintEngineEx *>(this)->sync();
+}
+
+static QPaintEngine *qt_polygon_recursion = 0;
+struct QT_Point {
+ int x;
+ int y;
+};
+
+/*!
+ \fn void QPaintEngine::drawPolygon(const QPointF *points, int pointCount,
+ PolygonDrawMode mode)
+
+ Reimplement this virtual function to draw the polygon defined
+ by the \a pointCount first points in \a points, using mode \a
+ mode.
+
+ \note At least one of the drawPolygon() functions must be reimplemented.
+*/
+void QPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
+{
+ Q_ASSERT_X(qt_polygon_recursion != this, "QPaintEngine::drawPolygon",
+ "At least one drawPolygon function must be implemented");
+ qt_polygon_recursion = this;
+ Q_ASSERT(sizeof(QT_Point) == sizeof(QPoint));
+ QVarLengthArray<QT_Point> p(pointCount);
+ for (int i = 0; i < pointCount; ++i) {
+ p[i].x = qRound(points[i].x());
+ p[i].y = qRound(points[i].y());
+ }
+ drawPolygon((QPoint *)p.data(), pointCount, mode);
+ qt_polygon_recursion = 0;
+}
+
+struct QT_PointF {
+ qreal x;
+ qreal y;
+};
+/*!
+ \overload
+
+ Reimplement this virtual function to draw the polygon defined by the
+ \a pointCount first points in \a points, using mode \a mode.
+
+ \note At least one of the drawPolygon() functions must be reimplemented.
+*/
+void QPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
+{
+ Q_ASSERT_X(qt_polygon_recursion != this, "QPaintEngine::drawPolygon",
+ "At least one drawPolygon function must be implemented");
+ qt_polygon_recursion = this;
+ Q_ASSERT(sizeof(QT_PointF) == sizeof(QPointF));
+ QVarLengthArray<QT_PointF> p(pointCount);
+ for (int i=0; i<pointCount; ++i) {
+ p[i].x = points[i].x();
+ p[i].y = points[i].y();
+ }
+ drawPolygon((QPointF *)p.data(), pointCount, mode);
+ qt_polygon_recursion = 0;
+}
+
+/*!
+ \enum QPaintEngine::Type
+
+ \value X11
+ \value Windows
+ \value MacPrinter
+ \value CoreGraphics Mac OS X's Quartz2D (CoreGraphics)
+ \value QuickDraw Mac OS X's QuickDraw
+ \value QWindowSystem Qt for Embedded Linux
+ \value PostScript
+ \value OpenGL
+ \value Picture QPicture format
+ \value SVG Scalable Vector Graphics XML format
+ \value Raster
+ \value Direct3D Windows only, Direct3D based engine
+ \value Pdf Portable Document Format
+ \value OpenVG
+ \value User First user type ID
+ \value MaxUser Last user type ID
+ \value OpenGL2
+ \value PaintBuffer
+*/
+
+/*!
+ \fn bool QPaintEngine::isActive() const
+
+ Returns true if the paint engine is actively drawing; otherwise
+ returns false.
+
+ \sa setActive()
+*/
+
+/*!
+ \fn void QPaintEngine::setActive(bool state)
+
+ Sets the active state of the paint engine to \a state.
+
+ \sa isActive()
+*/
+
+/*!
+ \fn bool QPaintEngine::begin(QPaintDevice *pdev)
+
+ Reimplement this function to initialise your paint engine when
+ painting is to start on the paint device \a pdev. Return true if
+ the initialization was successful; otherwise return false.
+
+ \sa end() isActive()
+*/
+
+/*!
+ \fn bool QPaintEngine::end()
+
+ Reimplement this function to finish painting on the current paint
+ device. Return true if painting was finished successfully;
+ otherwise return false.
+
+ \sa begin() isActive()
+*/
+
+
+/*!
+ Draws the first \a pointCount points in the buffer \a points
+*/
+void QPaintEngine::drawPoints(const QPointF *points, int pointCount)
+{
+ QPainter *p = painter();
+ if (!p)
+ return;
+
+ qreal penWidth = p->pen().widthF();
+ if (penWidth == 0)
+ penWidth = 1;
+
+ bool ellipses = p->pen().capStyle() == Qt::RoundCap;
+
+ p->save();
+
+ QTransform transform;
+ if (p->pen().isCosmetic()) {
+ transform = p->transform();
+ p->setTransform(QTransform());
+ }
+
+ p->setBrush(p->pen().brush());
+ p->setPen(Qt::NoPen);
+
+ for (int i=0; i<pointCount; ++i) {
+ QPointF pos = transform.map(points[i]);
+ QRectF rect(pos.x() - penWidth / 2, pos.y() - penWidth / 2, penWidth, penWidth);
+
+ if (ellipses)
+ p->drawEllipse(rect);
+ else
+ p->drawRect(rect);
+ }
+
+ p->restore();
+}
+
+
+/*!
+ Draws the first \a pointCount points in the buffer \a points
+
+ The default implementation converts the first \a pointCount QPoints in \a points
+ to QPointFs and calls the floating point version of drawPoints.
+
+*/
+void QPaintEngine::drawPoints(const QPoint *points, int pointCount)
+{
+ Q_ASSERT(sizeof(QT_PointF) == sizeof(QPointF));
+ QT_PointF fp[256];
+ while (pointCount) {
+ int i = 0;
+ while (i < pointCount && i < 256) {
+ fp[i].x = points[i].x();
+ fp[i].y = points[i].y();
+ ++i;
+ }
+ drawPoints((QPointF *)(void *)fp, i);
+ points += i;
+ pointCount -= i;
+ }
+}
+
+/*!
+ \fn void QPaintEngine::drawEllipse(const QRectF &rect)
+
+ Reimplement this function to draw the largest ellipse that can be
+ contained within rectangle \a rect.
+
+ The default implementation calls drawPolygon().
+*/
+void QPaintEngine::drawEllipse(const QRectF &rect)
+{
+ QPainterPath path;
+ path.addEllipse(rect);
+ if (hasFeature(PainterPaths)) {
+ drawPath(path);
+ } else {
+ QPolygonF polygon = path.toFillPolygon();
+ drawPolygon(polygon.data(), polygon.size(), ConvexMode);
+ }
+}
+
+/*!
+ The default implementation of this function calls the floating
+ point version of this function
+*/
+void QPaintEngine::drawEllipse(const QRect &rect)
+{
+ drawEllipse(QRectF(rect));
+}
+
+/*!
+ \fn void QPaintEngine::drawPixmap(const QRectF &r, const QPixmap
+ &pm, const QRectF &sr)
+
+ Reimplement this function to draw the part of the \a pm
+ specified by the \a sr rectangle in the given \a r.
+*/
+
+
+void qt_fill_tile(QPixmap *tile, const QPixmap &pixmap)
+{
+ QPainter p(tile);
+ p.drawPixmap(0, 0, pixmap);
+ int x = pixmap.width();
+ while (x < tile->width()) {
+ p.drawPixmap(x, 0, *tile, 0, 0, x, pixmap.height());
+ x *= 2;
+ }
+ int y = pixmap.height();
+ while (y < tile->height()) {
+ p.drawPixmap(0, y, *tile, 0, 0, tile->width(), y);
+ y *= 2;
+ }
+}
+
+void qt_draw_tile(QPaintEngine *gc, qreal x, qreal y, qreal w, qreal h,
+ const QPixmap &pixmap, qreal xOffset, qreal yOffset)
+{
+ qreal yPos, xPos, drawH, drawW, yOff, xOff;
+ yPos = y;
+ yOff = yOffset;
+ while(yPos < y + h) {
+ drawH = pixmap.height() - yOff; // Cropping first row
+ if (yPos + drawH > y + h) // Cropping last row
+ drawH = y + h - yPos;
+ xPos = x;
+ xOff = xOffset;
+ while(xPos < x + w) {
+ drawW = pixmap.width() - xOff; // Cropping first column
+ if (xPos + drawW > x + w) // Cropping last column
+ drawW = x + w - xPos;
+ if (drawW > 0 && drawH > 0)
+ gc->drawPixmap(QRectF(xPos, yPos, drawW, drawH), pixmap, QRectF(xOff, yOff, drawW, drawH));
+ xPos += drawW;
+ xOff = 0;
+ }
+ yPos += drawH;
+ yOff = 0;
+ }
+}
+
+
+/*!
+ Reimplement this function to draw the \a pixmap in the given \a
+ rect, starting at the given \a p. The pixmap will be
+ drawn repeatedly until the \a rect is filled.
+*/
+void QPaintEngine::drawTiledPixmap(const QRectF &rect, const QPixmap &pixmap, const QPointF &p)
+{
+ int sw = pixmap.width();
+ int sh = pixmap.height();
+
+ if (sw*sh < 8192 && sw*sh < 16*rect.width()*rect.height()) {
+ int tw = sw, th = sh;
+ while (tw*th < 32678 && tw < rect.width()/2)
+ tw *= 2;
+ while (tw*th < 32678 && th < rect.height()/2)
+ th *= 2;
+ QPixmap tile;
+ if (pixmap.depth() == 1) {
+ tile = QBitmap(tw, th);
+ } else {
+ tile = QPixmap(tw, th);
+ if (pixmap.hasAlphaChannel())
+ tile.fill(Qt::transparent);
+ }
+ qt_fill_tile(&tile, pixmap);
+ qt_draw_tile(this, rect.x(), rect.y(), rect.width(), rect.height(), tile, p.x(), p.y());
+ } else {
+ qt_draw_tile(this, rect.x(), rect.y(), rect.width(), rect.height(), pixmap, p.x(), p.y());
+ }
+}
+
+/*!
+ \fn void QPaintEngine::drawImage(const QRectF &rectangle, const QImage
+ &image, const QRectF &sr, Qt::ImageConversionFlags flags)
+
+ Reimplement this function to draw the part of the \a image
+ specified by the \a sr rectangle in the given \a rectangle using
+ the given conversion flags \a flags, to convert it to a pixmap.
+*/
+
+void QPaintEngine::drawImage(const QRectF &r, const QImage &image, const QRectF &sr,
+ Qt::ImageConversionFlags flags)
+{
+ QRectF baseSize(0, 0, image.width(), image.height());
+ QImage im = image;
+ if (baseSize != sr)
+ im = im.copy(qFloor(sr.x()), qFloor(sr.y()),
+ qCeil(sr.width()), qCeil(sr.height()));
+ QPixmap pm = QPixmap::fromImage(im, flags);
+ drawPixmap(r, pm, QRectF(QPointF(0, 0), pm.size()));
+}
+
+/*!
+ \fn Type QPaintEngine::type() const
+
+ Reimplement this function to return the paint engine \l{Type}.
+*/
+
+/*!
+ \fn void QPaintEngine::fix_neg_rect(int *x, int *y, int *w, int *h);
+
+ \internal
+*/
+
+/*!
+ \fn bool QPaintEngine::testDirty(DirtyFlags df)
+
+ \internal
+*/
+
+/*!
+ \fn void QPaintEngine::clearDirty(DirtyFlags df)
+
+ \internal
+*/
+
+/*!
+ \fn void QPaintEngine::setDirty(DirtyFlags df)
+
+ \internal
+*/
+
+/*!
+ \fn bool QPaintEngine::hasFeature(PaintEngineFeatures feature) const
+
+ Returns true if the paint engine supports the specified \a
+ feature; otherwise returns false.
+*/
+
+/*!
+ \fn bool QPaintEngine::isExtended() const
+
+ \internal
+
+ Returns true if the paint engine is a QPaintEngineEx derivative.
+*/
+
+/*!
+ \fn void QPaintEngine::updateState(const QPaintEngineState &state)
+
+ Reimplement this function to update the state of a paint engine.
+
+ When implemented, this function is responsible for checking the
+ paint engine's current \a state and update the properties that are
+ changed. Use the QPaintEngineState::state() function to find out
+ which properties that must be updated, then use the corresponding
+ \l {GetFunction}{get function} to retrieve the current values for
+ the given properties.
+
+ \sa QPaintEngineState
+*/
+
+/*!
+ Creates a paint engine with the featureset specified by \a caps.
+*/
+
+QPaintEngine::QPaintEngine(PaintEngineFeatures caps)
+ : state(0),
+ gccaps(caps),
+ active(0),
+ selfDestruct(false),
+ extended(false),
+ d_ptr(new QPaintEnginePrivate)
+{
+ d_ptr->q_ptr = this;
+}
+
+/*!
+ \internal
+*/
+
+QPaintEngine::QPaintEngine(QPaintEnginePrivate &dptr, PaintEngineFeatures caps)
+ : state(0),
+ gccaps(caps),
+ active(0),
+ selfDestruct(false),
+ extended(false),
+ d_ptr(&dptr)
+{
+ d_ptr->q_ptr = this;
+}
+
+/*!
+ Destroys the paint engine.
+*/
+QPaintEngine::~QPaintEngine()
+{
+}
+
+/*!
+ Returns the paint engine's painter.
+*/
+QPainter *QPaintEngine::painter() const
+{
+ return state ? state->painter() : 0;
+}
+
+/*!
+ The default implementation ignores the \a path and does nothing.
+*/
+
+void QPaintEngine::drawPath(const QPainterPath &)
+{
+ if (hasFeature(PainterPaths)) {
+ qWarning("QPaintEngine::drawPath: Must be implemented when feature PainterPaths is set");
+ }
+}
+
+/*!
+ This function draws the text item \a textItem at position \a p. The
+ default implementation of this function converts the text to a
+ QPainterPath and paints the resulting path.
+*/
+
+void QPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
+{
+ const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
+
+ QPainterPath path;
+#ifndef Q_WS_MAC
+ path.setFillRule(Qt::WindingFill);
+#endif
+ if (ti.glyphs.numGlyphs)
+ ti.fontEngine->addOutlineToPath(p.x(), p.y(), ti.glyphs, &path, ti.flags);
+ if (!path.isEmpty()) {
+ bool oldAA = painter()->renderHints() & QPainter::Antialiasing;
+ painter()->setRenderHint(QPainter::Antialiasing,
+ bool((painter()->renderHints() & QPainter::TextAntialiasing)
+ && !(painter()->font().styleStrategy() & QFont::NoAntialias)));
+ painter()->fillPath(path, state->pen().brush());
+ painter()->setRenderHint(QPainter::Antialiasing, oldAA);
+ }
+}
+
+/*!
+ The default implementation splits the list of lines in \a lines
+ into \a lineCount separate calls to drawPath() or drawPolygon()
+ depending on the feature set of the paint engine.
+*/
+void QPaintEngine::drawLines(const QLineF *lines, int lineCount)
+{
+ for (int i=0; i<lineCount; ++i) {
+ QPointF pts[2] = { lines[i].p1(), lines[i].p2() };
+
+ if (pts[0] == pts[1]) {
+ if (state->pen().capStyle() != Qt::FlatCap)
+ drawPoints(pts, 1);
+ continue;
+ }
+
+ drawPolygon(pts, 2, PolylineMode);
+ }
+}
+
+/*!
+ \overload
+
+ The default implementation converts the first \a lineCount lines
+ in \a lines to a QLineF and calls the floating point version of
+ this function.
+*/
+void QPaintEngine::drawLines(const QLine *lines, int lineCount)
+{
+ struct PointF {
+ qreal x;
+ qreal y;
+ };
+ struct LineF {
+ PointF p1;
+ PointF p2;
+ };
+ Q_ASSERT(sizeof(PointF) == sizeof(QPointF));
+ Q_ASSERT(sizeof(LineF) == sizeof(QLineF));
+ LineF fl[256];
+ while (lineCount) {
+ int i = 0;
+ while (i < lineCount && i < 256) {
+ fl[i].p1.x = lines[i].x1();
+ fl[i].p1.y = lines[i].y1();
+ fl[i].p2.x = lines[i].x2();
+ fl[i].p2.y = lines[i].y2();
+ ++i;
+ }
+ drawLines((QLineF *)(void *)fl, i);
+ lines += i;
+ lineCount -= i;
+ }
+}
+
+
+/*!
+ \overload
+
+ The default implementation converts the first \a rectCount
+ rectangles in the buffer \a rects to a QRectF and calls the
+ floating point version of this function.
+*/
+void QPaintEngine::drawRects(const QRect *rects, int rectCount)
+{
+ struct RectF {
+ qreal x;
+ qreal y;
+ qreal w;
+ qreal h;
+ };
+ Q_ASSERT(sizeof(RectF) == sizeof(QRectF));
+ RectF fr[256];
+ while (rectCount) {
+ int i = 0;
+ while (i < rectCount && i < 256) {
+ fr[i].x = rects[i].x();
+ fr[i].y = rects[i].y();
+ fr[i].w = rects[i].width();
+ fr[i].h = rects[i].height();
+ ++i;
+ }
+ drawRects((QRectF *)(void *)fr, i);
+ rects += i;
+ rectCount -= i;
+ }
+}
+
+/*!
+ Draws the first \a rectCount rectangles in the buffer \a
+ rects. The default implementation of this function calls drawPath()
+ or drawPolygon() depending on the feature set of the paint engine.
+*/
+void QPaintEngine::drawRects(const QRectF *rects, int rectCount)
+{
+ if (hasFeature(PainterPaths) &&
+ !state->penNeedsResolving() &&
+ !state->brushNeedsResolving()) {
+ for (int i=0; i<rectCount; ++i) {
+ QPainterPath path;
+ path.addRect(rects[i]);
+ if (path.isEmpty())
+ continue;
+ drawPath(path);
+ }
+ } else {
+ for (int i=0; i<rectCount; ++i) {
+ QRectF rf = rects[i];
+ QPointF pts[4] = { QPointF(rf.x(), rf.y()),
+ QPointF(rf.x() + rf.width(), rf.y()),
+ QPointF(rf.x() + rf.width(), rf.y() + rf.height()),
+ QPointF(rf.x(), rf.y() + rf.height()) };
+ drawPolygon(pts, 4, ConvexMode);
+ }
+ }
+}
+
+/*!
+ \internal
+ Sets the paintdevice that this engine operates on to \a device
+*/
+void QPaintEngine::setPaintDevice(QPaintDevice *device)
+{
+ d_func()->pdev = device;
+}
+
+/*!
+ Returns the device that this engine is painting on, if painting is
+ active; otherwise returns 0.
+*/
+QPaintDevice *QPaintEngine::paintDevice() const
+{
+ return d_func()->pdev;
+}
+
+#ifdef Q_WS_WIN
+/*!
+ \internal
+
+ Empty default implementation.
+*/
+
+HDC QPaintEngine::getDC() const
+{
+ return 0;
+}
+
+
+/*!
+ \internal
+
+ Empty default implementation.
+*/
+
+void QPaintEngine::releaseDC(HDC) const
+{
+}
+
+#endif
+
+/*!
+ \internal
+
+ Returns the offset from the painters origo to the engines
+ origo. This value is used by QPainter for engines who have
+ internal double buffering.
+
+ This function only makes sense when the engine is active.
+*/
+QPoint QPaintEngine::coordinateOffset() const
+{
+ return QPoint();
+}
+
+/*!
+ \internal
+
+ Sets the system clip for this engine. The system clip defines the
+ basis area that the engine has to draw in. All clips that are
+ set will be be an intersection with the system clip.
+
+ Reset the systemclip to no clip by setting an empty region.
+*/
+void QPaintEngine::setSystemClip(const QRegion &region)
+{
+ Q_D(QPaintEngine);
+ d->systemClip = region;
+ // Be backward compatible and only call d->systemStateChanged()
+ // if we currently have a system transform/viewport set.
+ if (d->hasSystemTransform || d->hasSystemViewport) {
+ d->transformSystemClip();
+ d->systemStateChanged();
+ }
+}
+
+/*!
+ \internal
+
+ Returns the system clip. The system clip is read only while the
+ painter is active. An empty region indicates that system clip
+ is not in use.
+*/
+
+QRegion QPaintEngine::systemClip() const
+{
+ return d_func()->systemClip;
+}
+
+/*!
+ \internal
+
+ Sets the target rect for drawing within the backing store. This
+ function should ONLY be used by the backing store.
+*/
+void QPaintEngine::setSystemRect(const QRect &rect)
+{
+ if (isActive()) {
+ qWarning("QPaintEngine::setSystemRect: Should not be changed while engine is active");
+ return;
+ }
+ d_func()->systemRect = rect;
+}
+
+/*!
+ \internal
+
+ Retrieves the rect for drawing within the backing store. This
+ function should ONLY be used by the backing store.
+ */
+QRect QPaintEngine::systemRect() const
+{
+ return d_func()->systemRect;
+}
+
+void QPaintEnginePrivate::drawBoxTextItem(const QPointF &p, const QTextItemInt &ti)
+{
+ if (!ti.glyphs.numGlyphs)
+ return;
+
+ // any fixes here should probably also be done in QFontEngineBox::draw
+ const int size = qRound(ti.fontEngine->ascent());
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> glyphs;
+ QTransform matrix = QTransform::fromTranslate(p.x(), p.y() - size);
+ ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
+ if (glyphs.size() == 0)
+ return;
+
+ QSize s(size - 3, size - 3);
+
+ QPainter *painter = q_func()->state->painter();
+ painter->save();
+ painter->setBrush(Qt::NoBrush);
+ QPen pen = painter->pen();
+ pen.setWidthF(ti.fontEngine->lineThickness().toReal());
+ painter->setPen(pen);
+ for (int k = 0; k < positions.size(); k++)
+ painter->drawRect(QRectF(positions[k].toPointF(), s));
+ painter->restore();
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qpaintengine.h b/src/gui/painting/qpaintengine.h
new file mode 100644
index 0000000000..93aa63bea9
--- /dev/null
+++ b/src/gui/painting/qpaintengine.h
@@ -0,0 +1,368 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTENGINE_H
+#define QPAINTENGINE_H
+
+#include <QtCore/qnamespace.h>
+#include <QtCore/qobjectdefs.h>
+#include <QtCore/qscopedpointer.h>
+#include <QtGui/qpainter.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QFontEngine;
+class QLineF;
+class QPaintDevice;
+class QPaintEnginePrivate;
+class QPainterPath;
+class QPointF;
+class QPolygonF;
+class QRectF;
+struct QGlyphLayout;
+class QTextItemInt;
+class QPaintEngineState;
+
+class Q_GUI_EXPORT QTextItem {
+public:
+ enum RenderFlag {
+ RightToLeft = 0x1,
+ Overline = 0x10,
+ Underline = 0x20,
+ StrikeOut = 0x40,
+
+ Dummy = 0xffffffff
+ };
+ Q_DECLARE_FLAGS(RenderFlags, RenderFlag)
+ qreal descent() const;
+ qreal ascent() const;
+ qreal width() const;
+
+ RenderFlags renderFlags() const;
+ QString text() const;
+ QFont font() const;
+};
+Q_DECLARE_TYPEINFO(QTextItem, Q_PRIMITIVE_TYPE);
+
+
+class Q_GUI_EXPORT QPaintEngine
+{
+ Q_DECLARE_PRIVATE(QPaintEngine)
+public:
+ enum PaintEngineFeature {
+ PrimitiveTransform = 0x00000001, // Can transform primitives brushes
+ PatternTransform = 0x00000002, // Can transform pattern brushes
+ PixmapTransform = 0x00000004, // Can transform pixmaps
+ PatternBrush = 0x00000008, // Can fill with pixmaps and standard patterns
+ LinearGradientFill = 0x00000010, // Can fill gradient areas
+ RadialGradientFill = 0x00000020, // Can render radial gradients
+ ConicalGradientFill = 0x00000040, // Can render conical gradients
+ AlphaBlend = 0x00000080, // Can do source over alpha blend
+ PorterDuff = 0x00000100, // Can do general porter duff compositions
+ PainterPaths = 0x00000200, // Can fill, outline and clip paths
+ Antialiasing = 0x00000400, // Can antialias lines
+ BrushStroke = 0x00000800, // Can render brush based pens
+ ConstantOpacity = 0x00001000, // Can render at constant opacity
+ MaskedBrush = 0x00002000, // Can fill with textures that has an alpha channel or mask
+ PerspectiveTransform = 0x00004000, // Can do perspective transformations
+ BlendModes = 0x00008000, // Can do extended Porter&Duff composition
+ ObjectBoundingModeGradients = 0x00010000, // Can do object bounding mode gradients
+ RasterOpModes = 0x00020000, // Can do logical raster operations
+ PaintOutsidePaintEvent = 0x20000000, // Engine is capable of painting outside paint events
+ /* 0x10000000, // Used for emulating
+ QGradient::StretchToDevice,
+ defined in qpainter.cpp
+
+ 0x40000000, // Used internally for emulating opaque backgrounds
+ */
+
+ AllFeatures = 0xffffffff // For convenience
+ };
+ Q_DECLARE_FLAGS(PaintEngineFeatures, PaintEngineFeature)
+
+ enum DirtyFlag {
+ DirtyPen = 0x0001,
+ DirtyBrush = 0x0002,
+ DirtyBrushOrigin = 0x0004,
+ DirtyFont = 0x0008,
+ DirtyBackground = 0x0010,
+ DirtyBackgroundMode = 0x0020,
+ DirtyTransform = 0x0040,
+ DirtyClipRegion = 0x0080,
+ DirtyClipPath = 0x0100,
+ DirtyHints = 0x0200,
+ DirtyCompositionMode = 0x0400,
+ DirtyClipEnabled = 0x0800,
+ DirtyOpacity = 0x1000,
+
+ AllDirty = 0xffff
+ };
+ Q_DECLARE_FLAGS(DirtyFlags, DirtyFlag)
+
+ enum PolygonDrawMode {
+ OddEvenMode,
+ WindingMode,
+ ConvexMode,
+ PolylineMode
+ };
+
+ explicit QPaintEngine(PaintEngineFeatures features=0);
+ virtual ~QPaintEngine();
+
+ bool isActive() const { return active; }
+ void setActive(bool newState) { active = newState; }
+
+ virtual bool begin(QPaintDevice *pdev) = 0;
+ virtual bool end() = 0;
+
+ virtual void updateState(const QPaintEngineState &state) = 0;
+
+ virtual void drawRects(const QRect *rects, int rectCount);
+ virtual void drawRects(const QRectF *rects, int rectCount);
+
+ virtual void drawLines(const QLine *lines, int lineCount);
+ virtual void drawLines(const QLineF *lines, int lineCount);
+
+ virtual void drawEllipse(const QRectF &r);
+ virtual void drawEllipse(const QRect &r);
+
+ virtual void drawPath(const QPainterPath &path);
+
+ virtual void drawPoints(const QPointF *points, int pointCount);
+ virtual void drawPoints(const QPoint *points, int pointCount);
+
+ virtual void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode);
+ virtual void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode);
+
+ virtual void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) = 0;
+ virtual void drawTextItem(const QPointF &p, const QTextItem &textItem);
+ virtual void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s);
+ virtual void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr,
+ Qt::ImageConversionFlags flags = Qt::AutoColor);
+
+ void setPaintDevice(QPaintDevice *device);
+ QPaintDevice *paintDevice() const;
+
+ void setSystemClip(const QRegion &baseClip);
+ QRegion systemClip() const;
+
+ void setSystemRect(const QRect &rect);
+ QRect systemRect() const;
+
+#ifdef Q_WS_WIN
+ virtual HDC getDC() const;
+ virtual void releaseDC(HDC hdc) const;
+#endif
+
+ virtual QPoint coordinateOffset() const;
+
+ enum Type {
+ X11,
+ Windows,
+ QuickDraw, CoreGraphics, MacPrinter,
+ QWindowSystem,
+ PostScript,
+ OpenGL,
+ Picture,
+ SVG,
+ Raster,
+ Direct3D,
+ Pdf,
+ OpenVG,
+ OpenGL2,
+ PaintBuffer,
+ Blitter,
+
+ User = 50, // first user type id
+ MaxUser = 100 // last user type id
+ };
+ virtual Type type() const = 0;
+
+ inline void fix_neg_rect(int *x, int *y, int *w, int *h);
+
+ inline bool testDirty(DirtyFlags df);
+ inline void setDirty(DirtyFlags df);
+ inline void clearDirty(DirtyFlags df);
+
+ bool hasFeature(PaintEngineFeatures feature) const { return (gccaps & feature) != 0; }
+
+ QPainter *painter() const;
+
+ void syncState();
+ inline bool isExtended() const { return extended; }
+
+protected:
+ QPaintEngine(QPaintEnginePrivate &data, PaintEngineFeatures devcaps=0);
+
+ QPaintEngineState *state;
+ PaintEngineFeatures gccaps;
+
+ uint active : 1;
+ uint selfDestruct : 1;
+ uint extended : 1;
+
+ QScopedPointer<QPaintEnginePrivate> d_ptr;
+
+private:
+ void setAutoDestruct(bool autoDestr) { selfDestruct = autoDestr; }
+ bool autoDestruct() const { return selfDestruct; }
+ Q_DISABLE_COPY(QPaintEngine)
+
+ friend class QPainterReplayer;
+ friend class QFontEngineBox;
+ friend class QFontEngineMac;
+ friend class QFontEngineWin;
+#ifndef QT_NO_FREETYPE
+ friend class QFontEngineFT;
+#endif
+#ifndef QT_NO_QWS_QPF
+ friend class QFontEngineQPF1;
+#endif
+#ifndef QT_NO_QWS_QPF2
+ friend class QFontEngineQPF;
+#endif
+ friend class QPSPrintEngine;
+ friend class QMacPrintEngine;
+ friend class QMacPrintEnginePrivate;
+#ifdef Q_WS_QWS
+ friend class QtopiaPrintEngine;
+ friend class QtopiaPrintEnginePrivate;
+ friend class QProxyFontEngine;
+#endif
+#ifdef Q_WS_QPA
+ friend class QFontEngineQPA;
+#endif
+ friend class QPainter;
+ friend class QPainterPrivate;
+ friend class QWidget;
+ friend class QWidgetPrivate;
+ friend class QWin32PaintEngine;
+ friend class QWin32PaintEnginePrivate;
+ friend class QMacCGContext;
+ friend class QPreviewPaintEngine;
+ friend class QX11GLPixmapData;
+};
+
+
+class Q_GUI_EXPORT QPaintEngineState
+{
+public:
+ QPaintEngine::DirtyFlags state() const { return dirtyFlags; }
+
+ QPen pen() const;
+ QBrush brush() const;
+ QPointF brushOrigin() const;
+ QBrush backgroundBrush() const;
+ Qt::BGMode backgroundMode() const;
+ QFont font() const;
+ QMatrix matrix() const;
+ QTransform transform() const;
+
+ Qt::ClipOperation clipOperation() const;
+ QRegion clipRegion() const;
+ QPainterPath clipPath() const;
+ bool isClipEnabled() const;
+
+ QPainter::RenderHints renderHints() const;
+ QPainter::CompositionMode compositionMode() const;
+ qreal opacity() const;
+
+ QPainter *painter() const;
+
+ bool brushNeedsResolving() const;
+ bool penNeedsResolving() const;
+
+protected:
+ friend class QPaintEngine;
+ friend class QRasterPaintEngine;
+ friend class QWidget;
+ friend class QPainter;
+ friend class QPainterPrivate;
+ friend class QMacPrintEnginePrivate;
+
+ QPaintEngine::DirtyFlags dirtyFlags;
+};
+
+//
+// inline functions
+//
+
+inline void QPaintEngine::fix_neg_rect(int *x, int *y, int *w, int *h)
+{
+ if (*w < 0) {
+ *w = -*w;
+ *x -= *w - 1;
+ }
+ if (*h < 0) {
+ *h = -*h;
+ *y -= *h - 1;
+ }
+}
+
+inline bool QPaintEngine::testDirty(DirtyFlags df) {
+ Q_ASSERT(state);
+ return ((state->dirtyFlags & df) != 0);
+}
+
+inline void QPaintEngine::setDirty(DirtyFlags df) {
+ Q_ASSERT(state);
+ state->dirtyFlags |= df;
+}
+
+inline void QPaintEngine::clearDirty(DirtyFlags df)
+{
+ Q_ASSERT(state);
+ state->dirtyFlags &= ~static_cast<uint>(df);
+}
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QTextItem::RenderFlags)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QPaintEngine::PaintEngineFeatures)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QPaintEngine::DirtyFlags)
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPAINTENGINE_H
diff --git a/src/gui/painting/qpaintengine_alpha.cpp b/src/gui/painting/qpaintengine_alpha.cpp
new file mode 100644
index 0000000000..171850ded6
--- /dev/null
+++ b/src/gui/painting/qpaintengine_alpha.cpp
@@ -0,0 +1,516 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qglobal.h>
+
+#ifndef QT_NO_PRINTER
+#include <qdebug.h>
+#include "private/qpaintengine_alpha_p.h"
+
+#include "private/qpicture_p.h"
+#include "private/qfont_p.h"
+#include "QtGui/qpicture.h"
+
+QT_BEGIN_NAMESPACE
+
+QAlphaPaintEngine::QAlphaPaintEngine(QAlphaPaintEnginePrivate &data, PaintEngineFeatures devcaps)
+ : QPaintEngine(data, devcaps)
+{
+
+}
+
+QAlphaPaintEngine::~QAlphaPaintEngine()
+{
+
+}
+
+bool QAlphaPaintEngine::begin(QPaintDevice *pdev)
+{
+ Q_D(QAlphaPaintEngine);
+
+ d->m_continueCall = true;
+ if (d->m_pass != 0) {
+ return true;
+ }
+
+ d->m_savedcaps = gccaps;
+ d->m_pdev = pdev;
+
+ d->m_alphaPen = false;
+ d->m_alphaBrush = false;
+ d->m_alphaOpacity = false;
+ d->m_hasalpha = false;
+ d->m_advancedPen = false;
+ d->m_advancedBrush = false;
+ d->m_complexTransform = false;
+ d->m_emulateProjectiveTransforms = false;
+
+ // clear alpha region
+ d->m_alphargn = QRegion();
+ d->m_cliprgn = QRegion();
+ d->m_pen = QPen();
+ d->m_transform = QTransform();
+
+ flushAndInit();
+
+ return true;
+}
+
+bool QAlphaPaintEngine::end()
+{
+ Q_D(QAlphaPaintEngine);
+
+ d->m_continueCall = true;
+ if (d->m_pass != 0) {
+ return true;
+ }
+
+ flushAndInit(false);
+ return true;
+}
+
+void QAlphaPaintEngine::updateState(const QPaintEngineState &state)
+{
+ Q_D(QAlphaPaintEngine);
+
+ DirtyFlags flags = state.state();
+ if (flags & QPaintEngine::DirtyTransform) {
+ d->m_transform = state.transform();
+ d->m_complexTransform = (d->m_transform.type() > QTransform::TxScale);
+ d->m_emulateProjectiveTransforms = !(d->m_savedcaps & QPaintEngine::PerspectiveTransform)
+ && !(d->m_savedcaps & QPaintEngine::AlphaBlend)
+ && (d->m_transform.type() >= QTransform::TxProject);
+ }
+ if (flags & QPaintEngine::DirtyPen) {
+ d->m_pen = state.pen();
+ if (d->m_pen.style() == Qt::NoPen) {
+ d->m_advancedPen = false;
+ d->m_alphaPen = false;
+ } else {
+ d->m_advancedPen = (d->m_pen.brush().style() != Qt::SolidPattern);
+ d->m_alphaPen = !d->m_pen.brush().isOpaque();
+ }
+ }
+
+ if (d->m_pass != 0) {
+ d->m_continueCall = true;
+ return;
+ }
+ d->m_continueCall = false;
+
+ if (flags & QPaintEngine::DirtyOpacity) {
+ d->m_alphaOpacity = (state.opacity() != 1.0f);
+ }
+
+ if (flags & QPaintEngine::DirtyBrush) {
+ if (state.brush().style() == Qt::NoBrush) {
+ d->m_advancedBrush = false;
+ d->m_alphaBrush = false;
+ } else {
+ d->m_advancedBrush = (state.brush().style() != Qt::SolidPattern);
+ d->m_alphaBrush = !state.brush().isOpaque();
+ }
+ }
+
+
+ d->m_hasalpha = d->m_alphaOpacity || d->m_alphaBrush || d->m_alphaPen;
+
+ if (d->m_picengine)
+ d->m_picengine->updateState(state);
+}
+
+void QAlphaPaintEngine::drawPath(const QPainterPath &path)
+{
+ Q_D(QAlphaPaintEngine);
+
+ QRectF tr = d->addPenWidth(path);
+
+ if (d->m_pass == 0) {
+ d->m_continueCall = false;
+ if (d->m_hasalpha || d->m_advancedPen || d->m_advancedBrush
+ || d->m_emulateProjectiveTransforms)
+ {
+ d->addAlphaRect(tr);
+ }
+ if (d->m_picengine)
+ d->m_picengine->drawPath(path);
+ } else {
+ d->m_continueCall = !d->fullyContained(tr);
+ }
+}
+
+void QAlphaPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
+{
+ Q_D(QAlphaPaintEngine);
+
+ QPolygonF poly;
+ for (int i=0; i<pointCount; ++i)
+ poly.append(points[i]);
+
+ QPainterPath path;
+ path.addPolygon(poly);
+ QRectF tr = d->addPenWidth(path);
+
+ if (d->m_pass == 0) {
+ d->m_continueCall = false;
+ if (d->m_hasalpha || d->m_advancedPen || d->m_advancedBrush
+ || d->m_emulateProjectiveTransforms)
+ {
+ d->addAlphaRect(tr);
+ }
+
+ if (d->m_picengine)
+ d->m_picengine->drawPolygon(points, pointCount, mode);
+ } else {
+ d->m_continueCall = !d->fullyContained(tr);
+ }
+}
+
+void QAlphaPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
+{
+ Q_D(QAlphaPaintEngine);
+
+ QRectF tr = d->m_transform.mapRect(r);
+ if (d->m_pass == 0) {
+ d->m_continueCall = false;
+ if (pm.hasAlpha() || d->m_alphaOpacity || d->m_complexTransform || pm.isQBitmap()) {
+ d->addAlphaRect(tr);
+ }
+
+ if (d->m_picengine)
+ d->m_picengine->drawPixmap(r, pm, sr);
+
+ } else {
+ d->m_continueCall = !d->fullyContained(tr);
+ }
+}
+
+void QAlphaPaintEngine::drawImage(const QRectF &r, const QImage &image, const QRectF &sr)
+{
+ Q_D(QAlphaPaintEngine);
+
+ QRectF tr = d->m_transform.mapRect(r);
+ if (d->m_pass == 0) {
+ d->m_continueCall = false;
+ if (image.hasAlphaChannel() || d->m_alphaOpacity || d->m_complexTransform) {
+ d->addAlphaRect(tr);
+ }
+
+ if (d->m_picengine)
+ d->m_picengine->drawImage(r, image, sr);
+
+ } else {
+ d->m_continueCall = !d->fullyContained(tr);
+ }
+}
+
+void QAlphaPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
+{
+ Q_D(QAlphaPaintEngine);
+
+ QRectF tr(p.x(), p.y() - textItem.ascent(), textItem.width() + 5, textItem.ascent() + textItem.descent() + 5);
+ tr = d->m_transform.mapRect(tr);
+
+ if (d->m_pass == 0) {
+ d->m_continueCall = false;
+ if (d->m_alphaPen || d->m_alphaOpacity || d->m_advancedPen) {
+ d->addAlphaRect(tr);
+ }
+ if (d->m_picengine) {
+ d->m_picengine->drawTextItem(p, textItem);
+ }
+ } else {
+ d->m_continueCall = !d->fullyContained(tr);
+ }
+}
+
+void QAlphaPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s)
+{
+ Q_D(QAlphaPaintEngine);
+
+ QRectF brect = d->m_transform.mapRect(r);
+
+ if (d->m_pass == 0) {
+ d->m_continueCall = false;
+ if (pixmap.hasAlpha() || d->m_alphaOpacity || d->m_complexTransform || pixmap.isQBitmap()) {
+ d->addAlphaRect(brect);
+ }
+ if (d->m_picengine)
+ d->m_picengine->drawTiledPixmap(r, pixmap, s);
+ } else {
+ d->m_continueCall = !d->fullyContained(brect);
+ }
+}
+
+QRegion QAlphaPaintEngine::alphaClipping() const
+{
+ Q_D(const QAlphaPaintEngine);
+ return d->m_cliprgn;
+}
+
+bool QAlphaPaintEngine::continueCall() const
+{
+ Q_D(const QAlphaPaintEngine);
+ return d->m_continueCall;
+}
+
+void QAlphaPaintEngine::flushAndInit(bool init)
+{
+ Q_D(QAlphaPaintEngine);
+ Q_ASSERT(d->m_pass == 0);
+
+ if (d->m_pic) {
+ d->m_picpainter->end();
+
+ // set clip region
+ d->m_alphargn = d->m_alphargn.intersected(QRect(0, 0, d->m_pdev->width(), d->m_pdev->height()));
+
+ // just use the bounding rect if it's a complex region..
+ QVector<QRect> rects = d->m_alphargn.rects();
+ if (rects.size() > 10) {
+ QRect br = d->m_alphargn.boundingRect();
+ d->m_alphargn = QRegion(br);
+ rects.clear();
+ rects.append(br);
+ }
+
+ d->m_cliprgn = d->m_alphargn;
+
+ // now replay the QPicture
+ ++d->m_pass; // we are now doing pass #2
+
+ // reset states
+ gccaps = d->m_savedcaps;
+
+ painter()->save();
+ d->resetState(painter());
+
+ // make sure the output from QPicture is unscaled
+ QTransform mtx;
+ mtx.scale(1.0f / (qreal(d->m_pdev->logicalDpiX()) / qreal(qt_defaultDpiX())),
+ 1.0f / (qreal(d->m_pdev->logicalDpiY()) / qreal(qt_defaultDpiY())));
+ painter()->setTransform(mtx);
+ painter()->drawPicture(0, 0, *d->m_pic);
+
+ d->m_cliprgn = QRegion();
+ d->resetState(painter());
+
+ // fill in the alpha images
+ for (int i=0; i<rects.size(); ++i)
+ d->drawAlphaImage(rects.at(i));
+
+ d->m_alphargn = QRegion();
+
+ painter()->restore();
+
+ --d->m_pass; // pass #2 finished
+
+ cleanUp();
+ }
+
+ if (init) {
+ gccaps = PaintEngineFeatures(AllFeatures & ~QPaintEngine::ObjectBoundingModeGradients);
+
+ d->m_pic = new QPicture();
+ d->m_pic->d_ptr->in_memory_only = true;
+ d->m_picpainter = new QPainter(d->m_pic);
+ d->m_picengine = d->m_picpainter->paintEngine();
+
+ // When newPage() is called and the m_picpainter is recreated
+ // we have to copy the current state of the original printer
+ // painter back to the m_picpainter
+ d->m_picpainter->setPen(painter()->pen());
+ d->m_picpainter->setBrush(painter()->brush());
+ d->m_picpainter->setBrushOrigin(painter()->brushOrigin());
+ d->m_picpainter->setFont(painter()->font());
+ d->m_picpainter->setOpacity(painter()->opacity());
+ d->m_picpainter->setTransform(painter()->combinedTransform());
+ d->m_picengine->syncState();
+ }
+}
+
+void QAlphaPaintEngine::cleanUp()
+{
+ Q_D(QAlphaPaintEngine);
+
+ delete d->m_picpainter;
+ delete d->m_pic;
+
+ d->m_picpainter = 0;
+ d->m_pic = 0;
+ d->m_picengine = 0;
+}
+
+QAlphaPaintEnginePrivate::QAlphaPaintEnginePrivate()
+ : m_pass(0),
+ m_pic(0),
+ m_picengine(0),
+ m_picpainter(0),
+ m_hasalpha(false),
+ m_alphaPen(false),
+ m_alphaBrush(false),
+ m_alphaOpacity(false),
+ m_advancedPen(false),
+ m_advancedBrush(false),
+ m_complexTransform(false)
+{
+
+}
+
+QAlphaPaintEnginePrivate::~QAlphaPaintEnginePrivate()
+{
+ delete m_picpainter;
+ delete m_pic;
+}
+
+QRectF QAlphaPaintEnginePrivate::addPenWidth(const QPainterPath &path)
+{
+ QPainterPath tmp = path;
+
+ if (m_pen.style() == Qt::NoPen)
+ return (path.controlPointRect() * m_transform).boundingRect();
+ if (m_pen.isCosmetic())
+ tmp = path * m_transform;
+
+ QPainterPathStroker stroker;
+ if (m_pen.widthF() == 0.0f)
+ stroker.setWidth(1.0);
+ else
+ stroker.setWidth(m_pen.widthF());
+ stroker.setJoinStyle(m_pen.joinStyle());
+ stroker.setCapStyle(m_pen.capStyle());
+ tmp = stroker.createStroke(tmp);
+ if (m_pen.isCosmetic())
+ return tmp.controlPointRect();
+
+ return (tmp.controlPointRect() * m_transform).boundingRect();
+}
+
+QRect QAlphaPaintEnginePrivate::toRect(const QRectF &rect) const
+{
+ QRect r;
+ r.setLeft(int(rect.left()));
+ r.setTop(int(rect.top()));
+ r.setRight(int(rect.right() + 1));
+ r.setBottom(int(rect.bottom() + 1));
+ return r;
+}
+
+void QAlphaPaintEnginePrivate::addAlphaRect(const QRectF &rect)
+{
+ m_alphargn |= toRect(rect);
+}
+
+void QAlphaPaintEnginePrivate::drawAlphaImage(const QRectF &rect)
+{
+ Q_Q(QAlphaPaintEngine);
+
+ qreal dpiX = qMax(m_pdev->logicalDpiX(), 300);
+ qreal dpiY = qMax(m_pdev->logicalDpiY(), 300);
+ qreal xscale = (dpiX / m_pdev->logicalDpiX());
+ qreal yscale = (dpiY / m_pdev->logicalDpiY());
+
+ QTransform picscale;
+ picscale.scale(xscale, yscale);
+
+ const int tileSize = 2048;
+ QSize size((int(rect.width() * xscale)), int(rect.height() * yscale));
+ int divw = (size.width() / tileSize);
+ int divh = (size.height() / tileSize);
+ divw += 1;
+ divh += 1;
+
+ int incx = int(rect.width() / divw);
+ int incy = int(rect.height() / divh);
+
+ for (int y=0; y<divh; ++y) {
+ int ypos = int((incy * y) + rect.y());
+ int height = int((y == (divh - 1)) ? (rect.height() - (incy * y)) : incy) + 1;
+
+ for (int x=0; x<divw; ++x) {
+ int xpos = int((incx * x) + rect.x());
+ int width = int((x == (divw - 1)) ? (rect.width() - (incx * x)) : incx) + 1;
+
+ QSize imgsize((int)(width * xscale), (int)(height * yscale));
+ QImage img(imgsize, QImage::Format_RGB32);
+ img.fill(0xffffffff);
+
+ QPainter imgpainter(&img);
+ imgpainter.setTransform(picscale);
+ QPointF picpos(qreal(-xpos), qreal(-ypos));
+ imgpainter.drawPicture(picpos, *m_pic);
+ imgpainter.end();
+
+ q->painter()->setTransform(QTransform());
+ QRect r(xpos, ypos, width, height);
+ q->painter()->drawImage(r, img);
+ }
+ }
+}
+
+bool QAlphaPaintEnginePrivate::fullyContained(const QRectF &rect) const
+{
+ QRegion r(toRect(rect));
+ return (m_cliprgn.intersected(r) == r);
+}
+
+void QAlphaPaintEnginePrivate::resetState(QPainter *p)
+{
+ p->setPen(QPen());
+ p->setBrush(QBrush());
+ p->setBrushOrigin(0,0);
+ p->setBackground(QBrush());
+ p->setFont(QFont());
+ p->setTransform(QTransform());
+ // The view transform is already recorded and included in the
+ // picture we're about to replay. If we don't turn if off,
+ // the view matrix will be applied twice.
+ p->setViewTransformEnabled(false);
+ p->setClipRegion(QRegion(), Qt::NoClip);
+ p->setClipPath(QPainterPath(), Qt::NoClip);
+ p->setClipping(false);
+ p->setOpacity(1.0f);
+}
+
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
diff --git a/src/gui/painting/qpaintengine_alpha_p.h b/src/gui/painting/qpaintengine_alpha_p.h
new file mode 100644
index 0000000000..2b8b4dda07
--- /dev/null
+++ b/src/gui/painting/qpaintengine_alpha_p.h
@@ -0,0 +1,135 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTENGINE_ALPHA_P_H
+#define QPAINTENGINE_ALPHA_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef QT_NO_PRINTER
+#include "private/qpaintengine_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QAlphaPaintEnginePrivate;
+
+class QAlphaPaintEngine : public QPaintEngine
+{
+ Q_DECLARE_PRIVATE(QAlphaPaintEngine)
+public:
+ ~QAlphaPaintEngine();
+
+ virtual bool begin(QPaintDevice *pdev);
+ virtual bool end();
+
+ virtual void updateState(const QPaintEngineState &state);
+
+ virtual void drawPath(const QPainterPath &path);
+
+ virtual void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode);
+
+ virtual void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
+ virtual void drawImage(const QRectF &r, const QImage &image, const QRectF &sr);
+ virtual void drawTextItem(const QPointF &p, const QTextItem &textItem);
+ virtual void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s);
+
+protected:
+ QAlphaPaintEngine(QAlphaPaintEnginePrivate &data, PaintEngineFeatures devcaps = 0);
+ QRegion alphaClipping() const;
+ bool continueCall() const;
+ void flushAndInit(bool init = true);
+ void cleanUp();
+};
+
+class QAlphaPaintEnginePrivate : public QPaintEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QAlphaPaintEngine)
+public:
+ QAlphaPaintEnginePrivate();
+ ~QAlphaPaintEnginePrivate();
+
+ int m_pass;
+ QPicture *m_pic;
+ QPaintEngine *m_picengine;
+ QPainter *m_picpainter;
+
+ QPaintEngine::PaintEngineFeatures m_savedcaps;
+ QPaintDevice *m_pdev;
+
+ QRegion m_alphargn;
+ QRegion m_cliprgn;
+
+ bool m_hasalpha;
+ bool m_alphaPen;
+ bool m_alphaBrush;
+ bool m_alphaOpacity;
+ bool m_advancedPen;
+ bool m_advancedBrush;
+ bool m_complexTransform;
+ bool m_emulateProjectiveTransforms;
+ bool m_continueCall;
+
+ QTransform m_transform;
+ QPen m_pen;
+
+ void addAlphaRect(const QRectF &rect);
+ QRectF addPenWidth(const QPainterPath &path);
+ void drawAlphaImage(const QRectF &rect);
+ QRect toRect(const QRectF &rect) const;
+ bool fullyContained(const QRectF &rect) const;
+
+ void resetState(QPainter *p);
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
+
+#endif // QPAINTENGINE_ALPHA_P_H
diff --git a/src/gui/painting/qpaintengine_blitter.cpp b/src/gui/painting/qpaintengine_blitter.cpp
new file mode 100644
index 0000000000..500748e159
--- /dev/null
+++ b/src/gui/painting/qpaintengine_blitter.cpp
@@ -0,0 +1,663 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "private/qpaintengine_blitter_p.h"
+
+#include "private/qblittable_p.h"
+#include "private/qpaintengine_raster_p.h"
+#include "private/qpainter_p.h"
+#include "private/qapplication_p.h"
+#include "private/qpixmap_blitter_p.h"
+
+#ifndef QT_NO_BLITTABLE
+QT_BEGIN_NAMESPACE
+
+#define STATE_XFORM_SCALE 0x00000001
+#define STATE_XFORM_COMPLEX 0x00000002
+
+#define STATE_BRUSH_PATTERN 0x00000010
+#define STATE_BRUSH_ALPHA 0x00000020
+
+#define STATE_PEN_ENABLED 0x00000100
+
+#define STATE_ANTIALIASING 0x00001000
+#define STATE_ALPHA 0x00002000
+#define STATE_BLENDING_COMPLEX 0x00004000
+
+#define STATE_CLIPSYS_COMPLEX 0x00010000
+#define STATE_CLIP_COMPLEX 0x00020000
+
+
+static inline void updateStateBits(uint *state, uint mask, bool on)
+{
+ *state = on ? (*state | mask) : (*state & ~mask);
+}
+
+static inline bool checkStateAgainstMask(uint state, uint mask)
+{
+ return !state || (state & mask && !(state & ~mask));
+}
+
+class CapabilitiesToStateMask
+{
+public:
+ CapabilitiesToStateMask(QBlittable::Capabilities capabilities)
+ : m_capabilities(capabilities),
+ fillRectMask(0),
+ drawRectMask(0),
+ drawPixmapMask(0),
+ capabillitiesState(0)
+ {
+ if (capabilities & QBlittable::SolidRectCapability) {
+ setFillRectMask();
+ }
+ if (capabilities & QBlittable::SourcePixmapCapability) {
+ setSourcePixmapMask();
+ }
+ if (capabilities & QBlittable::SourceOverPixmapCapability) {
+ setSourceOverPixmapMask();
+ }
+ if (capabilities & QBlittable::SourceOverScaledPixmapCapability) {
+ setSourceOverScaledPixmapMask();
+ }
+ }
+
+ inline bool canBlitterFillRect() const
+ {
+ return checkStateAgainstMask(capabillitiesState,fillRectMask);
+ }
+
+ inline bool canBlitterDrawRectMask() const
+ {
+ return checkStateAgainstMask(capabillitiesState,drawRectMask);
+ }
+
+ bool canBlitterDrawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) const
+ {
+ if (pm.pixmapData()->classId() != QPixmapData::BlitterClass)
+ return false;
+ if (checkStateAgainstMask(capabillitiesState,drawPixmapMask)) {
+ if (m_capabilities & (QBlittable::SourceOverPixmapCapability
+ | QBlittable::SourceOverScaledPixmapCapability)) {
+ if (r.size() != sr.size()) {
+ return m_capabilities & QBlittable::SourceOverScaledPixmapCapability;
+ } else {
+ return m_capabilities & QBlittable::SourceOverPixmapCapability;
+ }
+ }
+ if ((m_capabilities & QBlittable::SourcePixmapCapability) && r.size() == sr.size() && !pm.hasAlphaChannel()) {
+ return m_capabilities & QBlittable::SourcePixmapCapability;
+ }
+ }
+ return false;
+ }
+
+ inline void updateState(uint mask, bool on) {
+ updateStateBits(&capabillitiesState,mask,on);
+ }
+
+public:
+
+ void setFillRectMask() {
+ updateStateBits(&fillRectMask, STATE_XFORM_SCALE, false);
+ updateStateBits(&fillRectMask, STATE_XFORM_COMPLEX, false);
+
+ updateStateBits(&fillRectMask, STATE_BRUSH_PATTERN, false);
+ updateStateBits(&fillRectMask, STATE_BRUSH_ALPHA, false);
+
+ updateStateBits(&fillRectMask, STATE_PEN_ENABLED, true);
+
+ //Sub-pixel aliasing should not be sent to the blitter
+ updateStateBits(&fillRectMask, STATE_ANTIALIASING, true);
+ updateStateBits(&fillRectMask, STATE_ALPHA, false);
+ updateStateBits(&fillRectMask, STATE_BLENDING_COMPLEX, false);
+
+ updateStateBits(&fillRectMask, STATE_CLIPSYS_COMPLEX, false);
+ updateStateBits(&fillRectMask, STATE_CLIP_COMPLEX, false);
+ }
+
+ void setSourcePixmapMask() {
+ updateStateBits(&drawPixmapMask, STATE_XFORM_SCALE, true);
+ updateStateBits(&drawPixmapMask, STATE_XFORM_COMPLEX, false);
+
+ updateStateBits(&drawPixmapMask, STATE_BRUSH_PATTERN, true);
+ updateStateBits(&drawPixmapMask, STATE_BRUSH_ALPHA, false);
+
+ updateStateBits(&drawPixmapMask, STATE_PEN_ENABLED, true);
+
+ updateStateBits(&drawPixmapMask, STATE_ANTIALIASING, true);
+ updateStateBits(&drawPixmapMask, STATE_ALPHA, false);
+ updateStateBits(&drawPixmapMask, STATE_BLENDING_COMPLEX, false);
+
+ updateStateBits(&drawPixmapMask, STATE_CLIPSYS_COMPLEX, false);
+ updateStateBits(&drawPixmapMask, STATE_CLIP_COMPLEX, false);
+ }
+
+ void setSourceOverPixmapMask() {
+ setSourcePixmapMask();
+ }
+
+ void setSourceOverScaledPixmapMask() {
+ setSourceOverPixmapMask();
+ updateStateBits(&drawRectMask, STATE_XFORM_SCALE, true);
+ }
+
+ QBlittable::Capabilities m_capabilities;
+ uint fillRectMask;
+ uint drawRectMask;
+ uint drawPixmapMask;
+ uint capabillitiesState;
+};
+
+class QBlitterPaintEnginePrivate : public QPaintEngineExPrivate
+{
+ Q_DECLARE_PUBLIC(QBlitterPaintEngine);
+public:
+ QBlitterPaintEnginePrivate(QBlittablePixmapData *p)
+ : QPaintEngineExPrivate(),
+ pmData(p),
+ isBlitterLocked(false),
+ hasXForm(false)
+
+ {
+ raster = new QRasterPaintEngine(p->buffer());
+ capabillities = new CapabilitiesToStateMask(pmData->blittable()->capabilities());
+ }
+
+ inline void lock() {
+ if (!isBlitterLocked) {
+ raster->d_func()->rasterBuffer->prepare(pmData->blittable()->lock());
+ isBlitterLocked = true;
+ }
+ }
+
+ inline void unlock() {
+ if (isBlitterLocked) {
+ pmData->blittable()->unlock();
+ isBlitterLocked = false;
+ }
+ }
+
+ void fillRect(const QRectF &rect, const QColor &color) {
+ Q_Q(QBlitterPaintEngine);
+ pmData->unmarkRasterOverlay(rect);
+ QRectF targetRect = rect;
+ if (hasXForm) {
+ targetRect = q->state()->matrix.mapRect(rect);
+ }
+ const QClipData *clipData = q->clip();
+ if (clipData) {
+ if (clipData->hasRectClip) {
+ unlock();
+ pmData->blittable()->fillRect(targetRect & clipData->clipRect, color);
+ } else if (clipData->hasRegionClip) {
+ QVector<QRect> rects = clipData->clipRegion.rects();
+ for ( int i = 0; i < rects.size(); i++ ) {
+ QRect intersectRect = rects.at(i).intersected(targetRect.toRect());
+ if (!intersectRect.isEmpty()) {
+ unlock();
+ pmData->blittable()->fillRect(intersectRect,color);
+ }
+ }
+ }
+ } else {
+ if (targetRect.x() >= 0 && targetRect.y() >= 0
+ && targetRect.width() <= raster->paintDevice()->width()
+ && targetRect.height() <= raster->paintDevice()->height()) {
+ unlock();
+ pmData->blittable()->fillRect(targetRect,color);
+ } else {
+ QRectF deviceRect(0,0,raster->paintDevice()->width(), raster->paintDevice()->height());
+ unlock();
+ pmData->blittable()->fillRect(deviceRect&targetRect,color);
+ }
+ }
+ }
+
+ void clipAndDrawPixmap(const QRectF &clip, const QRectF &target, const QPixmap &pm, const QRectF &sr) {
+ QRectF intersectedRect = clip.intersected(target);
+ if (intersectedRect.isEmpty())
+ return;
+ QRectF source = sr;
+ if(intersectedRect.size() != target.size()) {
+ qreal deltaTop = target.top() - intersectedRect.top();
+ qreal deltaLeft = target.left() - intersectedRect.left();
+ qreal deltaBottom = target.bottom() - intersectedRect.bottom();
+ qreal deltaRight = target.right() - intersectedRect.right();
+ source.adjust(-deltaLeft,-deltaTop,-deltaRight,-deltaBottom);
+ }
+ pmData->unmarkRasterOverlay(intersectedRect);
+ pmData->blittable()->drawPixmap(intersectedRect, pm, source);
+ }
+
+ void updateClip() {
+ Q_Q(QBlitterPaintEngine);
+ const QClipData *clip = q->clip();
+ bool complex = clip && !(clip->hasRectClip || clip->hasRegionClip);
+ capabillities->updateState(STATE_CLIP_COMPLEX, complex);
+ }
+
+ void systemStateChanged() {
+ raster->d_func()->systemStateChanged();
+ }
+
+ QRasterPaintEngine *raster;
+
+ QBlittablePixmapData *pmData;
+ bool isBlitterLocked;
+
+ CapabilitiesToStateMask *capabillities;
+
+ uint hasXForm;
+};
+
+QBlitterPaintEngine::QBlitterPaintEngine(QBlittablePixmapData *p)
+ : QPaintEngineEx(*(new QBlitterPaintEnginePrivate(p)))
+{
+}
+
+QBlitterPaintEngine::~QBlitterPaintEngine()
+{
+}
+
+QPainterState *QBlitterPaintEngine::createState(QPainterState *orig) const
+{
+ Q_D(const QBlitterPaintEngine);
+ return d->raster->createState(orig);
+}
+
+bool QBlitterPaintEngine::begin(QPaintDevice *pdev)
+{
+ Q_D(QBlitterPaintEngine);
+
+ setActive(true);
+ bool ok = d->raster->begin(pdev);
+#ifdef QT_BLITTER_RASTEROVERLAY
+ d->pmData->unmergeOverlay();
+#endif
+ return ok;
+}
+
+
+bool QBlitterPaintEngine::end()
+{
+ Q_D(QBlitterPaintEngine);
+
+ setActive(false);
+#ifdef QT_BLITTER_RASTEROVERLAY
+ d->pmData->mergeOverlay();
+#endif
+ return d->raster->end();
+}
+
+
+void QBlitterPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
+{
+ Q_D(QBlitterPaintEngine);
+ if (path.shape() == QVectorPath::RectangleHint) {
+ QRectF rect(((QPointF *) path.points())[0], ((QPointF *) path.points())[2]);
+ fillRect(rect, brush);
+ } else {
+ d->lock();
+ d->pmData->markRasterOverlay(path);
+ d->raster->fill(path, brush);
+ }
+}
+
+void QBlitterPaintEngine::fillRect(const QRectF &rect, const QColor &color)
+{
+ Q_D(QBlitterPaintEngine);
+ if (d->capabillities->canBlitterFillRect() && color.alpha() == 0xff) {
+ d->fillRect(rect, color);
+ } else {
+ d->lock();
+ d->pmData->markRasterOverlay(rect);
+ d->raster->fillRect(rect, color);
+ }
+}
+
+void QBlitterPaintEngine::fillRect(const QRectF &rect, const QBrush &brush)
+{
+ if(rect.size().isEmpty())
+ return;
+
+ Q_D(QBlitterPaintEngine);
+
+ if (qbrush_style(brush) == Qt::SolidPattern
+ && qbrush_color(brush).alpha() == 0xff
+ && d->capabillities->canBlitterFillRect())
+ {
+ d->fillRect(rect, qbrush_color(brush));
+ }else if (brush.style() == Qt::TexturePattern
+ && d->capabillities->canBlitterDrawPixmap(rect,brush.texture(),rect))
+ {
+ bool rectIsFilled = false;
+ QRectF transformedRect = state()->matrix.mapRect(rect);
+ qreal x = transformedRect.x();
+ qreal y = transformedRect.y();
+ QPixmap pm = brush.texture();
+ d->unlock();
+ int srcX = int(rect.x() - state()->brushOrigin.x()) % pm.width();
+ if (srcX < 0)
+ srcX = pm.width() + srcX;
+ const int startX = srcX;
+ int srcY = int(rect.y() - state()->brushOrigin.y()) % pm.height();
+ if (srcY < 0)
+ srcY = pm.height() + srcY;
+ while (!rectIsFilled) {
+ qreal blitWidth = (pm.width() ) - srcX;
+ qreal blitHeight = (pm.height() ) - srcY;
+ if (x + blitWidth > transformedRect.right())
+ blitWidth = transformedRect.right() -x;
+ if (y + blitHeight > transformedRect.bottom())
+ blitHeight = transformedRect.bottom() - y;
+ const QClipData *clipData = clip();
+ if (clipData->hasRectClip) {
+ QRect targetRect = QRect(x,y,blitWidth,blitHeight).intersected(clipData->clipRect);
+ if (targetRect.isValid()) {
+ int tmpSrcX = srcX + (targetRect.x() - x);
+ int tmpSrcY = srcY + (targetRect.y() - y);
+ QRect srcRect(tmpSrcX,tmpSrcY,targetRect.width(),targetRect.height());
+ d->pmData->blittable()->drawPixmap(targetRect,pm,srcRect);
+ }
+ } else if (clipData->hasRegionClip) {
+ QVector<QRect> clipRects = clipData->clipRegion.rects();
+ QRect unclippedTargetRect(x,y,blitWidth,blitHeight);
+ QRegion intersectedRects = clipData->clipRegion.intersected(unclippedTargetRect);
+
+ for ( int i = 0; i < intersectedRects.rects().size(); i++ ) {
+ QRect targetRect = intersectedRects.rects().at(i);
+ if (!targetRect.isValid() || targetRect.isEmpty())
+ continue;
+ int tmpSrcX = srcX + (targetRect.x() - x);
+ int tmpSrcY = srcY + (targetRect.y() - y);
+ QRect srcRect(tmpSrcX,tmpSrcY,targetRect.width(),targetRect.height());
+ d->pmData->blittable()->drawPixmap(targetRect,pm,srcRect);
+ }
+ }
+ x+=blitWidth;
+ if (x>=transformedRect.right()) {
+ x = transformedRect.x();
+ srcX = startX;
+ srcY = 0;
+ y+=blitHeight;
+ if (y>=transformedRect.bottom())
+ rectIsFilled = true;
+ } else
+ srcX = 0;
+ }
+ } else {
+ d->lock();
+ d->pmData->markRasterOverlay(rect);
+ d->raster->fillRect(rect, brush);
+ }
+
+}
+
+void QBlitterPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
+{
+ Q_D(QBlitterPaintEngine);
+ d->lock();
+ d->pmData->markRasterOverlay(path);
+ d->raster->stroke(path, pen);
+}
+
+void QBlitterPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
+{
+ Q_D(QBlitterPaintEngine);
+ d->lock();
+ d->raster->clip(path, op);
+ d->updateClip();
+}
+void QBlitterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op){
+ Q_D(QBlitterPaintEngine);
+ d->lock();
+ d->raster->clip(rect,op);
+ d->updateClip();
+}
+void QBlitterPaintEngine::clip(const QRegion &region, Qt::ClipOperation op)
+{
+ Q_D(QBlitterPaintEngine);
+ d->lock();
+ d->raster->clip(region,op);
+ d->updateClip();
+}
+
+void QBlitterPaintEngine::clipEnabledChanged()
+{
+ Q_D(QBlitterPaintEngine);
+ d->lock();
+ d->raster->clipEnabledChanged();
+}
+
+void QBlitterPaintEngine::penChanged()
+{
+ Q_D(QBlitterPaintEngine);
+ d->lock();
+ d->raster->penChanged();
+ d->capabillities->updateState(STATE_PEN_ENABLED,qpen_style(state()->pen) != Qt::NoPen);
+}
+
+void QBlitterPaintEngine::brushChanged()
+{
+ Q_D(QBlitterPaintEngine);
+ d->raster->brushChanged();
+
+ bool solid = qbrush_style(state()->brush) == Qt::SolidPattern;
+
+ d->capabillities->updateState(STATE_BRUSH_PATTERN, !solid);
+ d->capabillities->updateState(STATE_BRUSH_ALPHA,
+ qbrush_color(state()->brush).alpha() < 255);
+}
+
+void QBlitterPaintEngine::brushOriginChanged()
+{
+ Q_D(QBlitterPaintEngine);
+ d->raster->brushOriginChanged();
+}
+
+void QBlitterPaintEngine::opacityChanged()
+{
+ Q_D(QBlitterPaintEngine);
+ d->raster->opacityChanged();
+
+ bool translucent = state()->opacity < 1;
+ d->capabillities->updateState(STATE_ALPHA,translucent);
+}
+
+void QBlitterPaintEngine::compositionModeChanged()
+{
+ Q_D(QBlitterPaintEngine);
+ d->raster->compositionModeChanged();
+
+ bool nonTrivial = state()->composition_mode != QPainter::CompositionMode_SourceOver
+ && state()->composition_mode != QPainter::CompositionMode_Source;
+
+ d->capabillities->updateState(STATE_BLENDING_COMPLEX,nonTrivial);
+}
+
+void QBlitterPaintEngine::renderHintsChanged()
+{
+ Q_D(QBlitterPaintEngine);
+ d->raster->renderHintsChanged();
+
+ bool aa = state()->renderHints & QPainter::Antialiasing;
+ d->capabillities->updateState(STATE_ANTIALIASING, aa);
+
+}
+
+void QBlitterPaintEngine::transformChanged()
+{
+ Q_D(QBlitterPaintEngine);
+ d->raster->transformChanged();
+
+ QTransform::TransformationType type = state()->matrix.type();
+
+ d->capabillities->updateState(STATE_XFORM_COMPLEX, type > QTransform::TxScale);
+ d->capabillities->updateState(STATE_XFORM_SCALE, type > QTransform::TxTranslate);
+
+ d->hasXForm = type >= QTransform::TxTranslate;
+
+}
+
+void QBlitterPaintEngine::drawRects(const QRect *rects, int rectCount)
+{
+ Q_D(QBlitterPaintEngine);
+ if (d->capabillities->canBlitterDrawRectMask()) {
+ for (int i=0; i<rectCount; ++i) {
+ d->fillRect(rects[i], qbrush_color(state()->brush));
+ }
+ } else {
+ d->pmData->markRasterOverlay(rects,rectCount);
+ QPaintEngineEx::drawRects(rects, rectCount);
+ }
+}
+
+void QBlitterPaintEngine::drawRects(const QRectF *rects, int rectCount)
+{
+ Q_D(QBlitterPaintEngine);
+ if (d->capabillities->canBlitterDrawRectMask()) {
+ for (int i=0; i<rectCount; ++i) {
+ d->fillRect(rects[i], qbrush_color(state()->brush));
+ }
+ } else {
+ d->pmData->markRasterOverlay(rects,rectCount);
+ QPaintEngineEx::drawRects(rects, rectCount);
+ }
+}
+
+void QBlitterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
+{
+ Q_D(QBlitterPaintEngine);
+ if (d->capabillities->canBlitterDrawPixmap(r,pm,sr)) {
+
+ d->unlock();
+ QRectF targetRect = r;
+ if (d->hasXForm) {
+ targetRect = state()->matrix.mapRect(r);
+ }
+ const QClipData *clipData = clip();
+ if (clipData) {
+ if (clipData->hasRectClip) {
+ d->clipAndDrawPixmap(clipData->clipRect,targetRect,pm,sr);
+ }else if (clipData->hasRegionClip) {
+ QVector<QRect>rects = clipData->clipRegion.rects();
+ for (int i = 0; i<rects.size(); i++) {
+ d->clipAndDrawPixmap(rects.at(i),targetRect,pm,sr);
+ }
+ }
+ } else {
+ QRectF deviceRect(0,0,d->raster->paintDevice()->width(), d->raster->paintDevice()->height());
+ d->clipAndDrawPixmap(deviceRect,targetRect,pm,sr);
+ }
+ }else {
+ d->lock();
+ d->pmData->markRasterOverlay(r);
+ d->raster->drawPixmap(r, pm, sr);
+ }
+}
+
+void QBlitterPaintEngine::drawImage(const QRectF &r, const QImage &pm, const QRectF &sr,
+ Qt::ImageConversionFlags flags)
+{
+ Q_D(QBlitterPaintEngine);
+ d->lock();
+ d->pmData->markRasterOverlay(r);
+ d->raster->drawImage(r, pm, sr, flags);
+}
+
+
+void QBlitterPaintEngine::drawTextItem(const QPointF &pos, const QTextItem &ti)
+{
+ Q_D(QBlitterPaintEngine);
+ d->lock();
+ d->raster->drawTextItem(pos, ti);
+ d->pmData->markRasterOverlay(pos,ti);
+}
+
+void QBlitterPaintEngine::drawStaticTextItem(QStaticTextItem *sti)
+{
+ Q_D(QBlitterPaintEngine);
+ d->lock();
+ d->raster->drawStaticTextItem(sti);
+
+//#### d->pmData->markRasterOverlay(sti);
+ qWarning("not implemented: markRasterOverlay for QStaticTextItem");
+
+}
+
+
+void QBlitterPaintEngine::drawEllipse(const QRectF &r)
+{
+ Q_D(QBlitterPaintEngine);
+ d->lock();
+ d->pmData->markRasterOverlay(r);
+ d->raster->drawEllipse(r);
+}
+
+void QBlitterPaintEngine::setState(QPainterState *s)
+{
+ Q_D(QBlitterPaintEngine);
+ d->lock();
+ QPaintEngineEx::setState(s);
+ d->raster->setState(s);
+
+ clipEnabledChanged();
+ penChanged();
+ brushChanged();
+ brushOriginChanged();
+ opacityChanged();
+ compositionModeChanged();
+ renderHintsChanged();
+ transformChanged();
+
+ d->updateClip();
+}
+
+inline QRasterPaintEngine *QBlitterPaintEngine::raster() const
+{
+ Q_D(const QBlitterPaintEngine);
+ return d->raster;
+}
+
+QT_END_NAMESPACE
+#endif //QT_NO_BLITTABLE
+
diff --git a/src/gui/painting/qpaintengine_blitter_p.h b/src/gui/painting/qpaintengine_blitter_p.h
new file mode 100644
index 0000000000..be8b2bc5ea
--- /dev/null
+++ b/src/gui/painting/qpaintengine_blitter_p.h
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTENGINE_BLITTER_P_H
+#define QPAINTENGINE_BLITTER_P_H
+
+#include "private/qpaintengineex_p.h"
+#include "private/qpaintengine_raster_p.h"
+
+#ifndef QT_NO_BLITTABLE
+QT_BEGIN_NAMESPACE
+
+class QBlitterPaintEnginePrivate;
+class QBlittablePixmapData;
+class QBlittable;
+
+class Q_GUI_EXPORT QBlitterPaintEngine : public QPaintEngineEx
+{
+ Q_DECLARE_PRIVATE(QBlitterPaintEngine);
+public:
+ QBlitterPaintEngine(QBlittablePixmapData *p);
+ ~QBlitterPaintEngine();
+
+ virtual QPainterState *createState(QPainterState *orig) const;
+
+ virtual QPaintEngine::Type type() const { return Blitter; }
+
+ virtual bool begin(QPaintDevice *pdev);
+ virtual bool end();
+
+ virtual void fill(const QVectorPath &path, const QBrush &brush);
+ virtual void stroke(const QVectorPath &path, const QPen &pen);
+
+ virtual void clip(const QVectorPath &path, Qt::ClipOperation op);
+ virtual void clip(const QRect &rect, Qt::ClipOperation op);
+ virtual void clip(const QRegion &region, Qt::ClipOperation op);
+
+ virtual void clipEnabledChanged();
+ virtual void penChanged();
+ virtual void brushChanged();
+ virtual void brushOriginChanged();
+ virtual void opacityChanged();
+ virtual void compositionModeChanged();
+ virtual void renderHintsChanged();
+ virtual void transformChanged();
+
+ virtual void fillRect(const QRectF &rect, const QBrush &brush);
+ virtual void fillRect(const QRectF &rect, const QColor &color);
+
+ virtual void drawRects(const QRect *rects, int rectCount);
+ virtual void drawRects(const QRectF *rects, int rectCount);
+
+ virtual void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
+
+ virtual void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, Qt::ImageConversionFlags flags);
+
+ virtual void drawTextItem(const QPointF &pos, const QTextItem &ti);
+ virtual void drawStaticTextItem(QStaticTextItem *);
+
+ virtual void drawEllipse(const QRectF &r);
+
+ virtual void setState(QPainterState *s);
+
+ inline QPainterState *state() { return raster()->state(); }
+ inline const QPainterState *state() const { const QPainterState *state = raster()->state(); return state;}
+ inline const QClipData *clip(){return raster()->d_func()->clip();}
+
+private:
+ QRasterPaintEngine *raster() const;
+};
+
+QT_END_NAMESPACE
+#endif //QT_NO_BLITTABLE
+#endif // QPAINTENGINE_BLITTER_P_H
+
diff --git a/src/gui/painting/qpaintengine_mac.cpp b/src/gui/painting/qpaintengine_mac.cpp
new file mode 100644
index 0000000000..8aab7c7a4b
--- /dev/null
+++ b/src/gui/painting/qpaintengine_mac.cpp
@@ -0,0 +1,1749 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qbitmap.h>
+#include <qpaintdevice.h>
+#include <private/qpaintengine_mac_p.h>
+#include <qpainterpath.h>
+#include <qpixmapcache.h>
+#include <private/qpaintengine_raster_p.h>
+#include <private/qprintengine_mac_p.h>
+#include <qprinter.h>
+#include <qstack.h>
+#include <qtextcodec.h>
+#include <qwidget.h>
+#include <qvarlengtharray.h>
+#include <qdebug.h>
+#include <qcoreapplication.h>
+#include <qmath.h>
+
+#include <private/qfont_p.h>
+#include <private/qfontengine_p.h>
+#include <private/qfontengine_coretext_p.h>
+#include <private/qfontengine_mac_p.h>
+#include <private/qnumeric_p.h>
+#include <private/qpainter_p.h>
+#include <private/qpainterpath_p.h>
+#include <private/qpixmap_mac_p.h>
+#include <private/qt_mac_p.h>
+#include <private/qtextengine_p.h>
+#include <private/qwidget_p.h>
+#include <private/qt_cocoa_helpers_mac_p.h>
+
+#include <string.h>
+
+QT_BEGIN_NAMESPACE
+
+extern int qt_antialiasing_threshold; // QApplication.cpp
+
+/*****************************************************************************
+ External functions
+ *****************************************************************************/
+extern CGImageRef qt_mac_create_imagemask(const QPixmap &px, const QRectF &sr); //qpixmap_mac.cpp
+extern QPoint qt_mac_posInWindow(const QWidget *w); //qwidget_mac.cpp
+extern OSWindowRef qt_mac_window_for(const QWidget *); //qwidget_mac.cpp
+extern CGContextRef qt_mac_cg_context(const QPaintDevice *); //qpaintdevice_mac.cpp
+extern void qt_mac_dispose_rgn(RgnHandle r); //qregion_mac.cpp
+extern QPixmap qt_pixmapForBrush(int, bool); //qbrush.cpp
+
+void qt_mac_clip_cg(CGContextRef hd, const QRegion &rgn, CGAffineTransform *orig_xform);
+
+
+//Implemented for qt_mac_p.h
+QMacCGContext::QMacCGContext(QPainter *p)
+{
+ QPaintEngine *pe = p->paintEngine();
+ if (pe->type() == QPaintEngine::MacPrinter)
+ pe = static_cast<QMacPrintEngine*>(pe)->paintEngine();
+ pe->syncState();
+ context = 0;
+ if(pe->type() == QPaintEngine::CoreGraphics)
+ context = static_cast<QCoreGraphicsPaintEngine*>(pe)->handle();
+
+ int devType = p->device()->devType();
+ if (pe->type() == QPaintEngine::Raster
+ && (devType == QInternal::Widget || devType == QInternal::Pixmap)) {
+
+ extern CGColorSpaceRef qt_mac_colorSpaceForDeviceType(const QPaintDevice *paintDevice);
+ CGColorSpaceRef colorspace = qt_mac_colorSpaceForDeviceType(pe->paintDevice());
+ uint flags = kCGImageAlphaPremultipliedFirst;
+#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version
+ flags |= kCGBitmapByteOrder32Host;
+#endif
+ const QImage *image = (const QImage *) pe->paintDevice();
+
+ context = CGBitmapContextCreate((void *) image->bits(), image->width(), image->height(),
+ 8, image->bytesPerLine(), colorspace, flags);
+
+ CGContextTranslateCTM(context, 0, image->height());
+ CGContextScaleCTM(context, 1, -1);
+
+ if (devType == QInternal::Widget) {
+ QRegion clip = p->paintEngine()->systemClip();
+ QTransform native = p->deviceTransform();
+ QTransform logical = p->combinedTransform();
+
+ if (p->hasClipping()) {
+ QRegion r = p->clipRegion();
+ r.translate(native.dx(), native.dy());
+ if (clip.isEmpty())
+ clip = r;
+ else
+ clip &= r;
+ }
+ qt_mac_clip_cg(context, clip, 0);
+
+ CGContextTranslateCTM(context, native.dx(), native.dy());
+ }
+ } else {
+ CGContextRetain(context);
+ }
+}
+
+
+/*****************************************************************************
+ QCoreGraphicsPaintEngine utility functions
+ *****************************************************************************/
+
+//conversion
+inline static float qt_mac_convert_color_to_cg(int c) { return ((float)c * 1000 / 255) / 1000; }
+inline static int qt_mac_convert_color_from_cg(float c) { return qRound(c * 255); }
+CGAffineTransform qt_mac_convert_transform_to_cg(const QTransform &t) {
+ return CGAffineTransformMake(t.m11(), t.m12(), t.m21(), t.m22(), t.dx(), t.dy());
+}
+
+CGColorSpaceRef qt_mac_colorSpaceForDeviceType(const QPaintDevice *paintDevice)
+{
+ bool isWidget = (paintDevice->devType() == QInternal::Widget);
+ return QCoreGraphicsPaintEngine::macDisplayColorSpace(isWidget ? static_cast<const QWidget *>(paintDevice)
+ : 0);
+}
+
+inline static QCFType<CGColorRef> cgColorForQColor(const QColor &col, QPaintDevice *pdev)
+{
+ CGFloat components[] = {
+ qt_mac_convert_color_to_cg(col.red()),
+ qt_mac_convert_color_to_cg(col.green()),
+ qt_mac_convert_color_to_cg(col.blue()),
+ qt_mac_convert_color_to_cg(col.alpha())
+ };
+ return CGColorCreate(qt_mac_colorSpaceForDeviceType(pdev), components);
+}
+
+// There's architectural problems with using native gradients
+// on the Mac at the moment, so disable them.
+// #define QT_MAC_USE_NATIVE_GRADIENTS
+
+#ifdef QT_MAC_USE_NATIVE_GRADIENTS
+static bool drawGradientNatively(const QGradient *gradient)
+{
+ return gradient->spread() == QGradient::PadSpread;
+}
+
+// gradiant callback
+static void qt_mac_color_gradient_function(void *info, const CGFloat *in, CGFloat *out)
+{
+ QBrush *brush = static_cast<QBrush *>(info);
+ Q_ASSERT(brush && brush->gradient());
+
+ const QGradientStops stops = brush->gradient()->stops();
+ const int n = stops.count();
+ Q_ASSERT(n >= 1);
+ const QGradientStop *begin = stops.constBegin();
+ const QGradientStop *end = begin + n;
+
+ qreal p = in[0];
+ const QGradientStop *i = begin;
+ while (i != end && i->first < p)
+ ++i;
+
+ QRgb c;
+ if (i == begin) {
+ c = begin->second.rgba();
+ } else if (i == end) {
+ c = (end - 1)->second.rgba();
+ } else {
+ const QGradientStop &s1 = *(i - 1);
+ const QGradientStop &s2 = *i;
+ qreal p1 = s1.first;
+ qreal p2 = s2.first;
+ QRgb c1 = s1.second.rgba();
+ QRgb c2 = s2.second.rgba();
+ int idist = 256 * (p - p1) / (p2 - p1);
+ int dist = 256 - idist;
+ c = qRgba(INTERPOLATE_PIXEL_256(qRed(c1), dist, qRed(c2), idist),
+ INTERPOLATE_PIXEL_256(qGreen(c1), dist, qGreen(c2), idist),
+ INTERPOLATE_PIXEL_256(qBlue(c1), dist, qBlue(c2), idist),
+ INTERPOLATE_PIXEL_256(qAlpha(c1), dist, qAlpha(c2), idist));
+ }
+
+ out[0] = qt_mac_convert_color_to_cg(qRed(c));
+ out[1] = qt_mac_convert_color_to_cg(qGreen(c));
+ out[2] = qt_mac_convert_color_to_cg(qBlue(c));
+ out[3] = qt_mac_convert_color_to_cg(qAlpha(c));
+}
+#endif
+
+//clipping handling
+void QCoreGraphicsPaintEnginePrivate::resetClip()
+{
+ static bool inReset = false;
+ if (inReset)
+ return;
+ inReset = true;
+
+ CGAffineTransform old_xform = CGContextGetCTM(hd);
+
+ //setup xforms
+ CGContextConcatCTM(hd, CGAffineTransformInvert(old_xform));
+ while (stackCount > 0) {
+ restoreGraphicsState();
+ }
+ saveGraphicsState();
+ inReset = false;
+ //reset xforms
+ CGContextConcatCTM(hd, CGAffineTransformInvert(CGContextGetCTM(hd)));
+ CGContextConcatCTM(hd, old_xform);
+}
+
+static CGRect qt_mac_compose_rect(const QRectF &r, float off=0)
+{
+ return CGRectMake(r.x()+off, r.y()+off, r.width(), r.height());
+}
+
+static CGMutablePathRef qt_mac_compose_path(const QPainterPath &p, float off=0)
+{
+ CGMutablePathRef ret = CGPathCreateMutable();
+ QPointF startPt;
+ for (int i=0; i<p.elementCount(); ++i) {
+ const QPainterPath::Element &elm = p.elementAt(i);
+ switch (elm.type) {
+ case QPainterPath::MoveToElement:
+ if(i > 0
+ && p.elementAt(i - 1).x == startPt.x()
+ && p.elementAt(i - 1).y == startPt.y())
+ CGPathCloseSubpath(ret);
+ startPt = QPointF(elm.x, elm.y);
+ CGPathMoveToPoint(ret, 0, elm.x+off, elm.y+off);
+ break;
+ case QPainterPath::LineToElement:
+ CGPathAddLineToPoint(ret, 0, elm.x+off, elm.y+off);
+ break;
+ case QPainterPath::CurveToElement:
+ Q_ASSERT(p.elementAt(i+1).type == QPainterPath::CurveToDataElement);
+ Q_ASSERT(p.elementAt(i+2).type == QPainterPath::CurveToDataElement);
+ CGPathAddCurveToPoint(ret, 0,
+ elm.x+off, elm.y+off,
+ p.elementAt(i+1).x+off, p.elementAt(i+1).y+off,
+ p.elementAt(i+2).x+off, p.elementAt(i+2).y+off);
+ i+=2;
+ break;
+ default:
+ qFatal("QCoreGraphicsPaintEngine::drawPath(), unhandled type: %d", elm.type);
+ break;
+ }
+ }
+ if(!p.isEmpty()
+ && p.elementAt(p.elementCount() - 1).x == startPt.x()
+ && p.elementAt(p.elementCount() - 1).y == startPt.y())
+ CGPathCloseSubpath(ret);
+ return ret;
+}
+
+CGColorSpaceRef QCoreGraphicsPaintEngine::m_genericColorSpace = 0;
+QHash<CGDirectDisplayID, CGColorSpaceRef> QCoreGraphicsPaintEngine::m_displayColorSpaceHash;
+bool QCoreGraphicsPaintEngine::m_postRoutineRegistered = false;
+
+CGColorSpaceRef QCoreGraphicsPaintEngine::macGenericColorSpace()
+{
+#if 0
+ if (!m_genericColorSpace) {
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
+ m_genericColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
+ } else
+#endif
+ {
+ m_genericColorSpace = CGColorSpaceCreateDeviceRGB();
+ }
+ if (!m_postRoutineRegistered) {
+ m_postRoutineRegistered = true;
+ qAddPostRoutine(QCoreGraphicsPaintEngine::cleanUpMacColorSpaces);
+ }
+ }
+ return m_genericColorSpace;
+#else
+ // Just return the main display colorspace for the moment.
+ return macDisplayColorSpace();
+#endif
+}
+
+/*
+ Ideally, we should pass the widget in here, and use CGGetDisplaysWithRect() etc.
+ to support multiple displays correctly.
+*/
+CGColorSpaceRef QCoreGraphicsPaintEngine::macDisplayColorSpace(const QWidget *widget)
+{
+ CGColorSpaceRef colorSpace;
+
+ CGDirectDisplayID displayID;
+ CMProfileRef displayProfile = 0;
+ if (widget == 0) {
+ displayID = CGMainDisplayID();
+ } else {
+ const QRect &qrect = widget->window()->geometry();
+ CGRect rect = CGRectMake(qrect.x(), qrect.y(), qrect.width(), qrect.height());
+ CGDisplayCount throwAway;
+ CGDisplayErr dErr = CGGetDisplaysWithRect(rect, 1, &displayID, &throwAway);
+ if (dErr != kCGErrorSuccess)
+ return macDisplayColorSpace(0); // fall back on main display
+ }
+ if ((colorSpace = m_displayColorSpaceHash.value(displayID)))
+ return colorSpace;
+
+ CMError err = CMGetProfileByAVID((CMDisplayIDType)displayID, &displayProfile);
+ if (err == noErr) {
+ colorSpace = CGColorSpaceCreateWithPlatformColorSpace(displayProfile);
+ } else if (widget) {
+ return macDisplayColorSpace(0); // fall back on main display
+ }
+
+ if (colorSpace == 0)
+ colorSpace = CGColorSpaceCreateDeviceRGB();
+
+ m_displayColorSpaceHash.insert(displayID, colorSpace);
+ CMCloseProfile(displayProfile);
+ if (!m_postRoutineRegistered) {
+ m_postRoutineRegistered = true;
+ qAddPostRoutine(QCoreGraphicsPaintEngine::cleanUpMacColorSpaces);
+ }
+ return colorSpace;
+}
+
+void QCoreGraphicsPaintEngine::cleanUpMacColorSpaces()
+{
+ if (m_genericColorSpace) {
+ CFRelease(m_genericColorSpace);
+ m_genericColorSpace = 0;
+ }
+ QHash<CGDirectDisplayID, CGColorSpaceRef>::const_iterator it = m_displayColorSpaceHash.constBegin();
+ while (it != m_displayColorSpaceHash.constEnd()) {
+ if (it.value())
+ CFRelease(it.value());
+ ++it;
+ }
+ m_displayColorSpaceHash.clear();
+}
+
+void qt_mac_clip_cg(CGContextRef hd, const QRegion &rgn, CGAffineTransform *orig_xform)
+{
+ CGAffineTransform old_xform = CGAffineTransformIdentity;
+ if(orig_xform) { //setup xforms
+ old_xform = CGContextGetCTM(hd);
+ CGContextConcatCTM(hd, CGAffineTransformInvert(old_xform));
+ CGContextConcatCTM(hd, *orig_xform);
+ }
+
+ //do the clipping
+ CGContextBeginPath(hd);
+ if(rgn.isEmpty()) {
+ CGContextAddRect(hd, CGRectMake(0, 0, 0, 0));
+ } else {
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
+ QCFType<HIMutableShapeRef> shape = rgn.toHIMutableShape();
+ Q_ASSERT(!HIShapeIsEmpty(shape));
+ HIShapeReplacePathInCGContext(shape, hd);
+ } else
+#endif
+ {
+ QVector<QRect> rects = rgn.rects();
+ const int count = rects.size();
+ for(int i = 0; i < count; i++) {
+ const QRect &r = rects[i];
+ CGRect mac_r = CGRectMake(r.x(), r.y(), r.width(), r.height());
+ CGContextAddRect(hd, mac_r);
+ }
+ }
+
+ }
+ CGContextClip(hd);
+
+ if(orig_xform) {//reset xforms
+ CGContextConcatCTM(hd, CGAffineTransformInvert(CGContextGetCTM(hd)));
+ CGContextConcatCTM(hd, old_xform);
+ }
+}
+
+
+//pattern handling (tiling)
+#if 1
+# define QMACPATTERN_MASK_MULTIPLIER 32
+#else
+# define QMACPATTERN_MASK_MULTIPLIER 1
+#endif
+class QMacPattern
+{
+public:
+ QMacPattern() : as_mask(false), pdev(0), image(0) { data.bytes = 0; }
+ ~QMacPattern() { CGImageRelease(image); }
+ int width() {
+ if(image)
+ return CGImageGetWidth(image);
+ if(data.bytes)
+ return 8*QMACPATTERN_MASK_MULTIPLIER;
+ return data.pixmap.width();
+ }
+ int height() {
+ if(image)
+ return CGImageGetHeight(image);
+ if(data.bytes)
+ return 8*QMACPATTERN_MASK_MULTIPLIER;
+ return data.pixmap.height();
+ }
+
+ //input
+ QColor foreground;
+ bool as_mask;
+ struct {
+ QPixmap pixmap;
+ const uchar *bytes;
+ } data;
+ QPaintDevice *pdev;
+ //output
+ CGImageRef image;
+};
+static void qt_mac_draw_pattern(void *info, CGContextRef c)
+{
+ QMacPattern *pat = (QMacPattern*)info;
+ int w = 0, h = 0;
+ bool isBitmap = (pat->data.pixmap.depth() == 1);
+ if(!pat->image) { //lazy cache
+ if(pat->as_mask) {
+ Q_ASSERT(pat->data.bytes);
+ w = h = 8;
+#if (QMACPATTERN_MASK_MULTIPLIER == 1)
+ CGDataProviderRef provider = CGDataProviderCreateWithData(0, pat->data.bytes, w*h, 0);
+ pat->image = CGImageMaskCreate(w, h, 1, 1, 1, provider, 0, false);
+ CGDataProviderRelease(provider);
+#else
+ const int numBytes = (w*h)/sizeof(uchar);
+ uchar xor_bytes[numBytes];
+ for(int i = 0; i < numBytes; ++i)
+ xor_bytes[i] = pat->data.bytes[i] ^ 0xFF;
+ CGDataProviderRef provider = CGDataProviderCreateWithData(0, xor_bytes, w*h, 0);
+ CGImageRef swatch = CGImageMaskCreate(w, h, 1, 1, 1, provider, 0, false);
+ CGDataProviderRelease(provider);
+
+ const QColor c0(0, 0, 0, 0), c1(255, 255, 255, 255);
+ QPixmap pm(w*QMACPATTERN_MASK_MULTIPLIER, h*QMACPATTERN_MASK_MULTIPLIER);
+ pm.fill(c0);
+ CGContextRef pm_ctx = qt_mac_cg_context(&pm);
+ CGContextSetFillColorWithColor(c, cgColorForQColor(c1, pat->pdev));
+ CGRect rect = CGRectMake(0, 0, w, h);
+ for(int x = 0; x < QMACPATTERN_MASK_MULTIPLIER; ++x) {
+ rect.origin.x = x * w;
+ for(int y = 0; y < QMACPATTERN_MASK_MULTIPLIER; ++y) {
+ rect.origin.y = y * h;
+ qt_mac_drawCGImage(pm_ctx, &rect, swatch);
+ }
+ }
+ pat->image = qt_mac_create_imagemask(pm, pm.rect());
+ CGImageRelease(swatch);
+ CGContextRelease(pm_ctx);
+ w *= QMACPATTERN_MASK_MULTIPLIER;
+ h *= QMACPATTERN_MASK_MULTIPLIER;
+#endif
+ } else {
+ w = pat->data.pixmap.width();
+ h = pat->data.pixmap.height();
+ if (isBitmap)
+ pat->image = qt_mac_create_imagemask(pat->data.pixmap, pat->data.pixmap.rect());
+ else
+ pat->image = (CGImageRef)pat->data.pixmap.macCGHandle();
+ }
+ } else {
+ w = CGImageGetWidth(pat->image);
+ h = CGImageGetHeight(pat->image);
+ }
+
+ //draw
+ bool needRestore = false;
+ if (CGImageIsMask(pat->image)) {
+ CGContextSaveGState(c);
+ CGContextSetFillColorWithColor(c, cgColorForQColor(pat->foreground, pat->pdev));
+ }
+ CGRect rect = CGRectMake(0, 0, w, h);
+ qt_mac_drawCGImage(c, &rect, pat->image);
+ if(needRestore)
+ CGContextRestoreGState(c);
+}
+static void qt_mac_dispose_pattern(void *info)
+{
+ QMacPattern *pat = (QMacPattern*)info;
+ delete pat;
+}
+
+/*****************************************************************************
+ QCoreGraphicsPaintEngine member functions
+ *****************************************************************************/
+
+inline static QPaintEngine::PaintEngineFeatures qt_mac_cg_features()
+{
+ return QPaintEngine::PaintEngineFeatures(QPaintEngine::AllFeatures & ~QPaintEngine::PaintOutsidePaintEvent
+ & ~QPaintEngine::PerspectiveTransform
+ & ~QPaintEngine::ConicalGradientFill
+ & ~QPaintEngine::LinearGradientFill
+ & ~QPaintEngine::RadialGradientFill
+ & ~QPaintEngine::BrushStroke);
+}
+
+QCoreGraphicsPaintEngine::QCoreGraphicsPaintEngine()
+: QPaintEngine(*(new QCoreGraphicsPaintEnginePrivate), qt_mac_cg_features())
+{
+}
+
+QCoreGraphicsPaintEngine::QCoreGraphicsPaintEngine(QPaintEnginePrivate &dptr)
+: QPaintEngine(dptr, qt_mac_cg_features())
+{
+}
+
+QCoreGraphicsPaintEngine::~QCoreGraphicsPaintEngine()
+{
+}
+
+bool
+QCoreGraphicsPaintEngine::begin(QPaintDevice *pdev)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ if(isActive()) { // already active painting
+ qWarning("QCoreGraphicsPaintEngine::begin: Painter already active");
+ return false;
+ }
+
+ //initialization
+ d->pdev = pdev;
+ d->complexXForm = false;
+ d->cosmeticPen = QCoreGraphicsPaintEnginePrivate::CosmeticSetPenWidth;
+ d->cosmeticPenSize = 1;
+ d->current.clipEnabled = false;
+ d->pixelSize = QPoint(1,1);
+ d->hd = qt_mac_cg_context(pdev);
+ if(d->hd) {
+ d->saveGraphicsState();
+ d->orig_xform = CGContextGetCTM(d->hd);
+ if (d->shading) {
+ CGShadingRelease(d->shading);
+ d->shading = 0;
+ }
+ d->setClip(0); //clear the context's clipping
+ }
+
+ setActive(true);
+
+ if(d->pdev->devType() == QInternal::Widget) { // device is a widget
+ QWidget *w = (QWidget*)d->pdev;
+ bool unclipped = w->testAttribute(Qt::WA_PaintUnclipped);
+
+ if((w->windowType() == Qt::Desktop)) {
+ if(!unclipped)
+ qWarning("QCoreGraphicsPaintEngine::begin: Does not support clipped desktop on Mac OS X");
+ // ## need to do [qt_mac_window_for(w) makeKeyAndOrderFront]; (need to rename the file)
+ } else if(unclipped) {
+ qWarning("QCoreGraphicsPaintEngine::begin: Does not support unclipped painting");
+ }
+ } else if(d->pdev->devType() == QInternal::Pixmap) { // device is a pixmap
+ QPixmap *pm = (QPixmap*)d->pdev;
+ if(pm->isNull()) {
+ qWarning("QCoreGraphicsPaintEngine::begin: Cannot paint null pixmap");
+ end();
+ return false;
+ }
+ }
+
+ setDirty(QPaintEngine::DirtyPen);
+ setDirty(QPaintEngine::DirtyBrush);
+ setDirty(QPaintEngine::DirtyBackground);
+ setDirty(QPaintEngine::DirtyHints);
+ return true;
+}
+
+bool
+QCoreGraphicsPaintEngine::end()
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ setActive(false);
+ if(d->pdev->devType() == QInternal::Widget && static_cast<QWidget*>(d->pdev)->windowType() == Qt::Desktop) {
+#ifndef QT_MAC_USE_COCOA
+ HideWindow(qt_mac_window_for(static_cast<QWidget*>(d->pdev)));
+#else
+// // ### need to do [qt_mac_window_for(static_cast<QWidget *>(d->pdev)) orderOut]; (need to rename)
+#endif
+
+ }
+ if(d->shading) {
+ CGShadingRelease(d->shading);
+ d->shading = 0;
+ }
+ d->pdev = 0;
+ if(d->hd) {
+ d->restoreGraphicsState();
+ CGContextSynchronize(d->hd);
+ CGContextRelease(d->hd);
+ d->hd = 0;
+ }
+ return true;
+}
+
+void
+QCoreGraphicsPaintEngine::updateState(const QPaintEngineState &state)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ QPaintEngine::DirtyFlags flags = state.state();
+
+ if (flags & DirtyTransform)
+ updateMatrix(state.transform());
+
+ if (flags & DirtyClipEnabled) {
+ if (state.isClipEnabled())
+ updateClipPath(painter()->clipPath(), Qt::ReplaceClip);
+ else
+ updateClipPath(QPainterPath(), Qt::NoClip);
+ }
+
+ if (flags & DirtyClipPath) {
+ updateClipPath(state.clipPath(), state.clipOperation());
+ } else if (flags & DirtyClipRegion) {
+ updateClipRegion(state.clipRegion(), state.clipOperation());
+ }
+
+ // If the clip has changed we need to update all other states
+ // too, since they are included in the system context on OSX,
+ // and changing the clip resets that context back to scratch.
+ if (flags & (DirtyClipPath | DirtyClipRegion | DirtyClipEnabled))
+ flags |= AllDirty;
+
+ if (flags & DirtyPen)
+ updatePen(state.pen());
+ if (flags & (DirtyBrush|DirtyBrushOrigin))
+ updateBrush(state.brush(), state.brushOrigin());
+ if (flags & DirtyFont)
+ updateFont(state.font());
+ if (flags & DirtyOpacity)
+ updateOpacity(state.opacity());
+ if (flags & DirtyHints)
+ updateRenderHints(state.renderHints());
+ if (flags & DirtyCompositionMode)
+ updateCompositionMode(state.compositionMode());
+
+ if (flags & (DirtyPen | DirtyTransform)) {
+ if (!d->current.pen.isCosmetic()) {
+ d->cosmeticPen = QCoreGraphicsPaintEnginePrivate::CosmeticNone;
+ } else if (d->current.transform.m11() < d->current.transform.m22()-1.0 ||
+ d->current.transform.m11() > d->current.transform.m22()+1.0) {
+ d->cosmeticPen = QCoreGraphicsPaintEnginePrivate::CosmeticTransformPath;
+ d->cosmeticPenSize = d->adjustPenWidth(d->current.pen.widthF());
+ if (!d->cosmeticPenSize)
+ d->cosmeticPenSize = 1.0;
+ } else {
+ d->cosmeticPen = QCoreGraphicsPaintEnginePrivate::CosmeticSetPenWidth;
+ static const float sqrt2 = sqrt(2);
+ qreal width = d->current.pen.widthF();
+ if (!width)
+ width = 1;
+ d->cosmeticPenSize = sqrt(pow(d->pixelSize.y(), 2) + pow(d->pixelSize.x(), 2)) / sqrt2 * width;
+ }
+ }
+}
+
+void
+QCoreGraphicsPaintEngine::updatePen(const QPen &pen)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+ d->current.pen = pen;
+ d->setStrokePen(pen);
+}
+
+void
+QCoreGraphicsPaintEngine::updateBrush(const QBrush &brush, const QPointF &brushOrigin)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+ d->current.brush = brush;
+
+#ifdef QT_MAC_USE_NATIVE_GRADIENTS
+ // Quartz supports only pad spread
+ if (const QGradient *gradient = brush.gradient()) {
+ if (drawGradientNatively(gradient)) {
+ gccaps |= QPaintEngine::LinearGradientFill | QPaintEngine::RadialGradientFill;
+ } else {
+ gccaps &= ~(QPaintEngine::LinearGradientFill | QPaintEngine::RadialGradientFill);
+ }
+ }
+#endif
+
+ if (d->shading) {
+ CGShadingRelease(d->shading);
+ d->shading = 0;
+ }
+ d->setFillBrush(brushOrigin);
+}
+
+void
+QCoreGraphicsPaintEngine::updateOpacity(qreal opacity)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ CGContextSetAlpha(d->hd, opacity);
+}
+
+void
+QCoreGraphicsPaintEngine::updateFont(const QFont &)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+ updatePen(d->current.pen);
+}
+
+void
+QCoreGraphicsPaintEngine::updateMatrix(const QTransform &transform)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+
+ if (qt_is_nan(transform.m11()) || qt_is_nan(transform.m12()) || qt_is_nan(transform.m13())
+ || qt_is_nan(transform.m21()) || qt_is_nan(transform.m22()) || qt_is_nan(transform.m23())
+ || qt_is_nan(transform.m31()) || qt_is_nan(transform.m32()) || qt_is_nan(transform.m33()))
+ return;
+
+ d->current.transform = transform;
+ d->setTransform(transform.isIdentity() ? 0 : &transform);
+ d->complexXForm = (transform.m11() != 1 || transform.m22() != 1
+ || transform.m12() != 0 || transform.m21() != 0);
+ d->pixelSize = d->devicePixelSize(d->hd);
+}
+
+void
+QCoreGraphicsPaintEngine::updateClipPath(const QPainterPath &p, Qt::ClipOperation op)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+ if(op == Qt::NoClip) {
+ if(d->current.clipEnabled) {
+ d->current.clipEnabled = false;
+ d->current.clip = QRegion();
+ d->setClip(0);
+ }
+ } else {
+ if(!d->current.clipEnabled)
+ op = Qt::ReplaceClip;
+ d->current.clipEnabled = true;
+ QRegion clipRegion(p.toFillPolygon().toPolygon(), p.fillRule());
+ if(op == Qt::ReplaceClip) {
+ d->current.clip = clipRegion;
+ d->setClip(0);
+ if(p.isEmpty()) {
+ CGRect rect = CGRectMake(0, 0, 0, 0);
+ CGContextClipToRect(d->hd, rect);
+ } else {
+ CGMutablePathRef path = qt_mac_compose_path(p);
+ CGContextBeginPath(d->hd);
+ CGContextAddPath(d->hd, path);
+ if(p.fillRule() == Qt::WindingFill)
+ CGContextClip(d->hd);
+ else
+ CGContextEOClip(d->hd);
+ CGPathRelease(path);
+ }
+ } else if(op == Qt::IntersectClip) {
+ d->current.clip = d->current.clip.intersected(clipRegion);
+ d->setClip(&d->current.clip);
+ } else if(op == Qt::UniteClip) {
+ d->current.clip = d->current.clip.united(clipRegion);
+ d->setClip(&d->current.clip);
+ }
+ }
+}
+
+void
+QCoreGraphicsPaintEngine::updateClipRegion(const QRegion &clipRegion, Qt::ClipOperation op)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+ if(op == Qt::NoClip) {
+ d->current.clipEnabled = false;
+ d->current.clip = QRegion();
+ d->setClip(0);
+ } else {
+ if(!d->current.clipEnabled)
+ op = Qt::ReplaceClip;
+ d->current.clipEnabled = true;
+ if(op == Qt::IntersectClip)
+ d->current.clip = d->current.clip.intersected(clipRegion);
+ else if(op == Qt::ReplaceClip)
+ d->current.clip = clipRegion;
+ else if(op == Qt::UniteClip)
+ d->current.clip = d->current.clip.united(clipRegion);
+ d->setClip(&d->current.clip);
+ }
+}
+
+void
+QCoreGraphicsPaintEngine::drawPath(const QPainterPath &p)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+
+ if (state->compositionMode() == QPainter::CompositionMode_Destination)
+ return;
+
+ CGMutablePathRef path = qt_mac_compose_path(p);
+ uchar ops = QCoreGraphicsPaintEnginePrivate::CGStroke;
+ if(p.fillRule() == Qt::WindingFill)
+ ops |= QCoreGraphicsPaintEnginePrivate::CGFill;
+ else
+ ops |= QCoreGraphicsPaintEnginePrivate::CGEOFill;
+ CGContextBeginPath(d->hd);
+ d->drawPath(ops, path);
+ CGPathRelease(path);
+}
+
+void
+QCoreGraphicsPaintEngine::drawRects(const QRectF *rects, int rectCount)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+
+ if (state->compositionMode() == QPainter::CompositionMode_Destination)
+ return;
+
+ for (int i=0; i<rectCount; ++i) {
+ QRectF r = rects[i];
+
+ CGMutablePathRef path = CGPathCreateMutable();
+ CGPathAddRect(path, 0, qt_mac_compose_rect(r));
+ d->drawPath(QCoreGraphicsPaintEnginePrivate::CGFill|QCoreGraphicsPaintEnginePrivate::CGStroke,
+ path);
+ CGPathRelease(path);
+ }
+}
+
+void
+QCoreGraphicsPaintEngine::drawPoints(const QPointF *points, int pointCount)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+
+ if (state->compositionMode() == QPainter::CompositionMode_Destination)
+ return;
+
+ if (d->current.pen.capStyle() == Qt::FlatCap)
+ CGContextSetLineCap(d->hd, kCGLineCapSquare);
+
+ CGMutablePathRef path = CGPathCreateMutable();
+ for(int i=0; i < pointCount; i++) {
+ float x = points[i].x(), y = points[i].y();
+ CGPathMoveToPoint(path, 0, x, y);
+ CGPathAddLineToPoint(path, 0, x+0.001, y);
+ }
+
+ bool doRestore = false;
+ if(d->cosmeticPen == QCoreGraphicsPaintEnginePrivate::CosmeticNone && !(state->renderHints() & QPainter::Antialiasing)) {
+ //we don't want adjusted pens for point rendering
+ doRestore = true;
+ d->saveGraphicsState();
+ CGContextSetLineWidth(d->hd, d->current.pen.widthF());
+ }
+ d->drawPath(QCoreGraphicsPaintEnginePrivate::CGStroke, path);
+ if (doRestore)
+ d->restoreGraphicsState();
+ CGPathRelease(path);
+ if (d->current.pen.capStyle() == Qt::FlatCap)
+ CGContextSetLineCap(d->hd, kCGLineCapButt);
+}
+
+void
+QCoreGraphicsPaintEngine::drawEllipse(const QRectF &r)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+
+ if (state->compositionMode() == QPainter::CompositionMode_Destination)
+ return;
+
+ CGMutablePathRef path = CGPathCreateMutable();
+ CGAffineTransform transform = CGAffineTransformMakeScale(r.width() / r.height(), 1);
+ CGPathAddArc(path, &transform,(r.x() + (r.width() / 2)) / (r.width() / r.height()),
+ r.y() + (r.height() / 2), r.height() / 2, 0, (2 * M_PI), false);
+ d->drawPath(QCoreGraphicsPaintEnginePrivate::CGFill | QCoreGraphicsPaintEnginePrivate::CGStroke,
+ path);
+ CGPathRelease(path);
+}
+
+void
+QCoreGraphicsPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+
+ if (state->compositionMode() == QPainter::CompositionMode_Destination)
+ return;
+
+ CGMutablePathRef path = CGPathCreateMutable();
+ CGPathMoveToPoint(path, 0, points[0].x(), points[0].y());
+ for(int x = 1; x < pointCount; ++x)
+ CGPathAddLineToPoint(path, 0, points[x].x(), points[x].y());
+ if(mode != PolylineMode && points[0] != points[pointCount-1])
+ CGPathAddLineToPoint(path, 0, points[0].x(), points[0].y());
+ uint op = QCoreGraphicsPaintEnginePrivate::CGStroke;
+ if (mode != PolylineMode)
+ op |= mode == OddEvenMode ? QCoreGraphicsPaintEnginePrivate::CGEOFill
+ : QCoreGraphicsPaintEnginePrivate::CGFill;
+ d->drawPath(op, path);
+ CGPathRelease(path);
+}
+
+void
+QCoreGraphicsPaintEngine::drawLines(const QLineF *lines, int lineCount)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+
+ if (state->compositionMode() == QPainter::CompositionMode_Destination)
+ return;
+
+ CGMutablePathRef path = CGPathCreateMutable();
+ for(int i = 0; i < lineCount; i++) {
+ const QPointF start = lines[i].p1(), end = lines[i].p2();
+ CGPathMoveToPoint(path, 0, start.x(), start.y());
+ CGPathAddLineToPoint(path, 0, end.x(), end.y());
+ }
+ d->drawPath(QCoreGraphicsPaintEnginePrivate::CGStroke, path);
+ CGPathRelease(path);
+}
+
+void QCoreGraphicsPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+
+ if (state->compositionMode() == QPainter::CompositionMode_Destination)
+ return;
+
+ if(pm.isNull())
+ return;
+
+ bool differentSize = (QRectF(0, 0, pm.width(), pm.height()) != sr), doRestore = false;
+ CGRect rect = CGRectMake(r.x(), r.y(), r.width(), r.height());
+ QCFType<CGImageRef> image;
+ bool isBitmap = (pm.depth() == 1);
+ if (isBitmap) {
+ doRestore = true;
+ d->saveGraphicsState();
+
+ const QColor &col = d->current.pen.color();
+ CGContextSetFillColorWithColor(d->hd, cgColorForQColor(col, d->pdev));
+ image = qt_mac_create_imagemask(pm, sr);
+ } else if (differentSize) {
+ QCFType<CGImageRef> img = pm.toMacCGImageRef();
+ image = CGImageCreateWithImageInRect(img, CGRectMake(qRound(sr.x()), qRound(sr.y()), qRound(sr.width()), qRound(sr.height())));
+ } else {
+ image = (CGImageRef)pm.macCGHandle();
+ }
+ qt_mac_drawCGImage(d->hd, &rect, image);
+ if (doRestore)
+ d->restoreGraphicsState();
+}
+
+static void drawImageReleaseData (void *info, const void *, size_t)
+{
+ delete static_cast<QImage *>(info);
+}
+
+CGImageRef qt_mac_createCGImageFromQImage(const QImage &img, const QImage **imagePtr = 0)
+{
+ QImage *image;
+ if (img.depth() != 32)
+ image = new QImage(img.convertToFormat(QImage::Format_ARGB32_Premultiplied));
+ else
+ image = new QImage(img);
+
+ uint cgflags = kCGImageAlphaNone;
+ switch (image->format()) {
+ case QImage::Format_ARGB32_Premultiplied:
+ cgflags = kCGImageAlphaPremultipliedFirst;
+ break;
+ case QImage::Format_ARGB32:
+ cgflags = kCGImageAlphaFirst;
+ break;
+ case QImage::Format_RGB32:
+ cgflags = kCGImageAlphaNoneSkipFirst;
+ default:
+ break;
+ }
+#if defined(kCGBitmapByteOrder32Host) //only needed because CGImage.h added symbols in the minor version
+ cgflags |= kCGBitmapByteOrder32Host;
+#endif
+ QCFType<CGDataProviderRef> dataProvider = CGDataProviderCreateWithData(image,
+ static_cast<const QImage *>(image)->bits(),
+ image->byteCount(),
+ drawImageReleaseData);
+ if (imagePtr)
+ *imagePtr = image;
+ return CGImageCreate(image->width(), image->height(), 8, 32,
+ image->bytesPerLine(),
+ QCoreGraphicsPaintEngine::macGenericColorSpace(),
+ cgflags, dataProvider, 0, false, kCGRenderingIntentDefault);
+
+}
+
+void QCoreGraphicsPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
+ Qt::ImageConversionFlags flags)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_UNUSED(flags);
+ Q_ASSERT(isActive());
+
+ if (img.isNull() || state->compositionMode() == QPainter::CompositionMode_Destination)
+ return;
+
+ const QImage *image;
+ QCFType<CGImageRef> cgimage = qt_mac_createCGImageFromQImage(img, &image);
+ CGRect rect = CGRectMake(r.x(), r.y(), r.width(), r.height());
+ if (QRectF(0, 0, img.width(), img.height()) != sr)
+ cgimage = CGImageCreateWithImageInRect(cgimage, CGRectMake(sr.x(), sr.y(),
+ sr.width(), sr.height()));
+ qt_mac_drawCGImage(d->hd, &rect, cgimage);
+}
+
+void QCoreGraphicsPaintEngine::initialize()
+{
+}
+
+void QCoreGraphicsPaintEngine::cleanup()
+{
+}
+
+CGContextRef
+QCoreGraphicsPaintEngine::handle() const
+{
+ return d_func()->hd;
+}
+
+void
+QCoreGraphicsPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap,
+ const QPointF &p)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ Q_ASSERT(isActive());
+
+ if (state->compositionMode() == QPainter::CompositionMode_Destination)
+ return;
+
+ //save the old state
+ d->saveGraphicsState();
+
+ //setup the pattern
+ QMacPattern *qpattern = new QMacPattern;
+ qpattern->data.pixmap = pixmap;
+ qpattern->foreground = d->current.pen.color();
+ qpattern->pdev = d->pdev;
+ CGPatternCallbacks callbks;
+ callbks.version = 0;
+ callbks.drawPattern = qt_mac_draw_pattern;
+ callbks.releaseInfo = qt_mac_dispose_pattern;
+ const int width = qpattern->width(), height = qpattern->height();
+ CGAffineTransform trans = CGContextGetCTM(d->hd);
+ CGPatternRef pat = CGPatternCreate(qpattern, CGRectMake(0, 0, width, height),
+ trans, width, height,
+ kCGPatternTilingNoDistortion, true, &callbks);
+ CGColorSpaceRef cs = CGColorSpaceCreatePattern(0);
+ CGContextSetFillColorSpace(d->hd, cs);
+ CGFloat component = 1.0; //just one
+ CGContextSetFillPattern(d->hd, pat, &component);
+ CGSize phase = CGSizeApplyAffineTransform(CGSizeMake(-(p.x()-r.x()), -(p.y()-r.y())), trans);
+ CGContextSetPatternPhase(d->hd, phase);
+
+ //fill the rectangle
+ CGRect mac_rect = CGRectMake(r.x(), r.y(), r.width(), r.height());
+ CGContextFillRect(d->hd, mac_rect);
+
+ //restore the state
+ d->restoreGraphicsState();
+ //cleanup
+ CGColorSpaceRelease(cs);
+ CGPatternRelease(pat);
+}
+
+void QCoreGraphicsPaintEngine::drawTextItem(const QPointF &pos, const QTextItem &item)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ if (d->current.transform.type() == QTransform::TxProject
+#ifndef QMAC_NATIVE_GRADIENTS
+ || painter()->pen().brush().gradient() //Just let the base engine "emulate" the gradient
+#endif
+ ) {
+ QPaintEngine::drawTextItem(pos, item);
+ return;
+ }
+
+ if (state->compositionMode() == QPainter::CompositionMode_Destination)
+ return;
+
+ const QTextItemInt &ti = static_cast<const QTextItemInt &>(item);
+
+ QPen oldPen = painter()->pen();
+ QBrush oldBrush = painter()->brush();
+ QPointF oldBrushOrigin = painter()->brushOrigin();
+ updatePen(Qt::NoPen);
+ updateBrush(oldPen.brush(), QPointF(0, 0));
+
+ Q_ASSERT(type() == QPaintEngine::CoreGraphics);
+
+ QFontEngine *fe = ti.fontEngine;
+
+ const bool textAA = state->renderHints() & QPainter::TextAntialiasing && fe->fontDef.pointSize > qt_antialiasing_threshold && !(fe->fontDef.styleStrategy & QFont::NoAntialias);
+ const bool lineAA = state->renderHints() & QPainter::Antialiasing;
+ if(textAA != lineAA)
+ CGContextSetShouldAntialias(d->hd, textAA);
+
+ if (ti.glyphs.numGlyphs) {
+ switch (fe->type()) {
+ case QFontEngine::Mac:
+#ifdef QT_MAC_USE_COCOA
+ static_cast<QCoreTextFontEngine *>(fe)->draw(d->hd, pos.x(), pos.y(), ti, paintDevice()->height());
+#else
+ static_cast<QFontEngineMac *>(fe)->draw(d->hd, pos.x(), pos.y(), ti, paintDevice()->height());
+#endif
+ break;
+ case QFontEngine::Box:
+ d->drawBoxTextItem(pos, ti);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if(textAA != lineAA)
+ CGContextSetShouldAntialias(d->hd, !textAA);
+
+ updatePen(oldPen);
+ updateBrush(oldBrush, oldBrushOrigin);
+}
+
+QPainter::RenderHints
+QCoreGraphicsPaintEngine::supportedRenderHints() const
+{
+ return QPainter::RenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
+}
+enum CGCompositeMode {
+ kCGCompositeModeClear = 0,
+ kCGCompositeModeCopy = 1,
+ kCGCompositeModeSourceOver = 2,
+ kCGCompositeModeSourceIn = 3,
+ kCGCompositeModeSourceOut = 4,
+ kCGCompositeModeSourceAtop = 5,
+ kCGCompositeModeDestinationOver = 6,
+ kCGCompositeModeDestinationIn = 7,
+ kCGCompositeModeDestinationOut = 8,
+ kCGCompositeModeDestinationAtop = 9,
+ kCGCompositeModeXOR = 10,
+ kCGCompositeModePlusDarker = 11, // (max (0, (1-d) + (1-s)))
+ kCGCompositeModePlusLighter = 12, // (min (1, s + d))
+ };
+extern "C" {
+ extern void CGContextSetCompositeOperation(CGContextRef, int);
+} // private function, but is in all versions of OS X.
+void
+QCoreGraphicsPaintEngine::updateCompositionMode(QPainter::CompositionMode mode)
+{
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
+ int cg_mode = kCGBlendModeNormal;
+ switch(mode) {
+ case QPainter::CompositionMode_Multiply:
+ cg_mode = kCGBlendModeMultiply;
+ break;
+ case QPainter::CompositionMode_Screen:
+ cg_mode = kCGBlendModeScreen;
+ break;
+ case QPainter::CompositionMode_Overlay:
+ cg_mode = kCGBlendModeOverlay;
+ break;
+ case QPainter::CompositionMode_Darken:
+ cg_mode = kCGBlendModeDarken;
+ break;
+ case QPainter::CompositionMode_Lighten:
+ cg_mode = kCGBlendModeLighten;
+ break;
+ case QPainter::CompositionMode_ColorDodge:
+ cg_mode = kCGBlendModeColorDodge;
+ break;
+ case QPainter::CompositionMode_ColorBurn:
+ cg_mode = kCGBlendModeColorBurn;
+ break;
+ case QPainter::CompositionMode_HardLight:
+ cg_mode = kCGBlendModeHardLight;
+ break;
+ case QPainter::CompositionMode_SoftLight:
+ cg_mode = kCGBlendModeSoftLight;
+ break;
+ case QPainter::CompositionMode_Difference:
+ cg_mode = kCGBlendModeDifference;
+ break;
+ case QPainter::CompositionMode_Exclusion:
+ cg_mode = kCGBlendModeExclusion;
+ break;
+ case QPainter::CompositionMode_Plus:
+ cg_mode = kCGBlendModePlusLighter;
+ break;
+ case QPainter::CompositionMode_SourceOver:
+ cg_mode = kCGBlendModeNormal;
+ break;
+ case QPainter::CompositionMode_DestinationOver:
+ cg_mode = kCGBlendModeDestinationOver;
+ break;
+ case QPainter::CompositionMode_Clear:
+ cg_mode = kCGBlendModeClear;
+ break;
+ case QPainter::CompositionMode_Source:
+ cg_mode = kCGBlendModeCopy;
+ break;
+ case QPainter::CompositionMode_Destination:
+ cg_mode = -1;
+ break;
+ case QPainter::CompositionMode_SourceIn:
+ cg_mode = kCGBlendModeSourceIn;
+ break;
+ case QPainter::CompositionMode_DestinationIn:
+ cg_mode = kCGCompositeModeDestinationIn;
+ break;
+ case QPainter::CompositionMode_SourceOut:
+ cg_mode = kCGBlendModeSourceOut;
+ break;
+ case QPainter::CompositionMode_DestinationOut:
+ cg_mode = kCGBlendModeDestinationOver;
+ break;
+ case QPainter::CompositionMode_SourceAtop:
+ cg_mode = kCGBlendModeSourceAtop;
+ break;
+ case QPainter::CompositionMode_DestinationAtop:
+ cg_mode = kCGBlendModeDestinationAtop;
+ break;
+ case QPainter::CompositionMode_Xor:
+ cg_mode = kCGBlendModeXOR;
+ break;
+ default:
+ break;
+ }
+ if (cg_mode > -1) {
+ CGContextSetBlendMode(d_func()->hd, CGBlendMode(cg_mode));
+ }
+ } else
+#endif
+ // The standard porter duff ops.
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_3
+ && mode <= QPainter::CompositionMode_Xor) {
+ int cg_mode = kCGCompositeModeCopy;
+ switch (mode) {
+ case QPainter::CompositionMode_SourceOver:
+ cg_mode = kCGCompositeModeSourceOver;
+ break;
+ case QPainter::CompositionMode_DestinationOver:
+ cg_mode = kCGCompositeModeDestinationOver;
+ break;
+ case QPainter::CompositionMode_Clear:
+ cg_mode = kCGCompositeModeClear;
+ break;
+ default:
+ qWarning("QCoreGraphicsPaintEngine: Unhandled composition mode %d", (int)mode);
+ break;
+ case QPainter::CompositionMode_Source:
+ cg_mode = kCGCompositeModeCopy;
+ break;
+ case QPainter::CompositionMode_Destination:
+ cg_mode = CGCompositeMode(-1);
+ break;
+ case QPainter::CompositionMode_SourceIn:
+ cg_mode = kCGCompositeModeSourceIn;
+ break;
+ case QPainter::CompositionMode_DestinationIn:
+ cg_mode = kCGCompositeModeDestinationIn;
+ break;
+ case QPainter::CompositionMode_SourceOut:
+ cg_mode = kCGCompositeModeSourceOut;
+ break;
+ case QPainter::CompositionMode_DestinationOut:
+ cg_mode = kCGCompositeModeDestinationOut;
+ break;
+ case QPainter::CompositionMode_SourceAtop:
+ cg_mode = kCGCompositeModeSourceAtop;
+ break;
+ case QPainter::CompositionMode_DestinationAtop:
+ cg_mode = kCGCompositeModeDestinationAtop;
+ break;
+ case QPainter::CompositionMode_Xor:
+ cg_mode = kCGCompositeModeXOR;
+ break;
+ }
+ if (cg_mode > -1)
+ CGContextSetCompositeOperation(d_func()->hd, CGCompositeMode(cg_mode));
+ } else {
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ bool needPrivateAPI = false;
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
+ int cg_mode = kCGBlendModeNormal;
+ switch (mode) {
+ case QPainter::CompositionMode_Multiply:
+ cg_mode = kCGBlendModeMultiply;
+ break;
+ case QPainter::CompositionMode_Screen:
+ cg_mode = kCGBlendModeScreen;
+ break;
+ case QPainter::CompositionMode_Overlay:
+ cg_mode = kCGBlendModeOverlay;
+ break;
+ case QPainter::CompositionMode_Darken:
+ cg_mode = kCGBlendModeDarken;
+ break;
+ case QPainter::CompositionMode_Lighten:
+ cg_mode = kCGBlendModeLighten;
+ break;
+ case QPainter::CompositionMode_ColorDodge:
+ cg_mode = kCGBlendModeColorDodge;
+ break;
+ case QPainter::CompositionMode_ColorBurn:
+ cg_mode = kCGBlendModeColorBurn;
+ break;
+ case QPainter::CompositionMode_HardLight:
+ cg_mode = kCGBlendModeHardLight;
+ break;
+ case QPainter::CompositionMode_SoftLight:
+ cg_mode = kCGBlendModeSoftLight;
+ break;
+ case QPainter::CompositionMode_Difference:
+ cg_mode = kCGBlendModeDifference;
+ break;
+ case QPainter::CompositionMode_Exclusion:
+ cg_mode = kCGBlendModeExclusion;
+ break;
+ case QPainter::CompositionMode_Plus:
+ needPrivateAPI = true;
+ cg_mode = kCGCompositeModePlusLighter;
+ break;
+ default:
+ break;
+ }
+ if (!needPrivateAPI)
+ CGContextSetBlendMode(d_func()->hd, CGBlendMode(cg_mode));
+ else
+ CGContextSetCompositeOperation(d_func()->hd, CGCompositeMode(cg_mode));
+ }
+#endif
+ }
+}
+
+void
+QCoreGraphicsPaintEngine::updateRenderHints(QPainter::RenderHints hints)
+{
+ Q_D(QCoreGraphicsPaintEngine);
+ CGContextSetShouldAntialias(d->hd, hints & QPainter::Antialiasing);
+ static const CGFloat ScaleFactor = qt_mac_get_scalefactor();
+ if (ScaleFactor > 1.) {
+ CGContextSetInterpolationQuality(d->hd, kCGInterpolationHigh);
+ } else {
+ CGContextSetInterpolationQuality(d->hd, (hints & QPainter::SmoothPixmapTransform) ?
+ kCGInterpolationHigh : kCGInterpolationNone);
+ }
+ bool textAntialiasing = (hints & QPainter::TextAntialiasing) == QPainter::TextAntialiasing;
+ if (!textAntialiasing || d->disabledSmoothFonts) {
+ d->disabledSmoothFonts = !textAntialiasing;
+ CGContextSetShouldSmoothFonts(d->hd, textAntialiasing);
+ }
+}
+
+/*
+ Returns the size of one device pixel in user-space coordinates.
+*/
+QPointF QCoreGraphicsPaintEnginePrivate::devicePixelSize(CGContextRef)
+{
+ QPointF p1 = current.transform.inverted().map(QPointF(0, 0));
+ QPointF p2 = current.transform.inverted().map(QPointF(1, 1));
+ return QPointF(qAbs(p2.x() - p1.x()), qAbs(p2.y() - p1.y()));
+}
+
+/*
+ Adjusts the pen width so we get correct line widths in the
+ non-transformed, aliased case.
+*/
+float QCoreGraphicsPaintEnginePrivate::adjustPenWidth(float penWidth)
+{
+ Q_Q(QCoreGraphicsPaintEngine);
+ float ret = penWidth;
+ if (!complexXForm && !(q->state->renderHints() & QPainter::Antialiasing)) {
+ if (penWidth < 2)
+ ret = 1;
+ else if (penWidth < 3)
+ ret = 1.5;
+ else
+ ret = penWidth -1;
+ }
+ return ret;
+}
+
+void
+QCoreGraphicsPaintEnginePrivate::setStrokePen(const QPen &pen)
+{
+ //pencap
+ CGLineCap cglinecap = kCGLineCapButt;
+ if(pen.capStyle() == Qt::SquareCap)
+ cglinecap = kCGLineCapSquare;
+ else if(pen.capStyle() == Qt::RoundCap)
+ cglinecap = kCGLineCapRound;
+ CGContextSetLineCap(hd, cglinecap);
+ CGContextSetLineWidth(hd, adjustPenWidth(pen.widthF()));
+
+ //join
+ CGLineJoin cglinejoin = kCGLineJoinMiter;
+ if(pen.joinStyle() == Qt::BevelJoin)
+ cglinejoin = kCGLineJoinBevel;
+ else if(pen.joinStyle() == Qt::RoundJoin)
+ cglinejoin = kCGLineJoinRound;
+ CGContextSetLineJoin(hd, cglinejoin);
+// CGContextSetMiterLimit(hd, pen.miterLimit());
+
+ //pen style
+ QVector<CGFloat> linedashes;
+ if(pen.style() == Qt::CustomDashLine) {
+ QVector<qreal> customs = pen.dashPattern();
+ for(int i = 0; i < customs.size(); ++i)
+ linedashes.append(customs.at(i));
+ } else if(pen.style() == Qt::DashLine) {
+ linedashes.append(4);
+ linedashes.append(2);
+ } else if(pen.style() == Qt::DotLine) {
+ linedashes.append(1);
+ linedashes.append(2);
+ } else if(pen.style() == Qt::DashDotLine) {
+ linedashes.append(4);
+ linedashes.append(2);
+ linedashes.append(1);
+ linedashes.append(2);
+ } else if(pen.style() == Qt::DashDotDotLine) {
+ linedashes.append(4);
+ linedashes.append(2);
+ linedashes.append(1);
+ linedashes.append(2);
+ linedashes.append(1);
+ linedashes.append(2);
+ }
+ const CGFloat cglinewidth = pen.widthF() <= 0.0f ? 1.0f : float(pen.widthF());
+ for(int i = 0; i < linedashes.size(); ++i) {
+ linedashes[i] *= cglinewidth;
+ if(cglinewidth < 3 && (cglinecap == kCGLineCapSquare || cglinecap == kCGLineCapRound)) {
+ if((i%2))
+ linedashes[i] += cglinewidth/2;
+ else
+ linedashes[i] -= cglinewidth/2;
+ }
+ }
+ CGContextSetLineDash(hd, pen.dashOffset() * cglinewidth, linedashes.data(), linedashes.size());
+
+ // color
+ CGContextSetStrokeColorWithColor(hd, cgColorForQColor(pen.color(), pdev));
+}
+
+// Add our own patterns here to deal with the fact that the coordinate system
+// is flipped vertically with Quartz2D.
+static const uchar *qt_mac_patternForBrush(int brushStyle)
+{
+ Q_ASSERT(brushStyle > Qt::SolidPattern && brushStyle < Qt::LinearGradientPattern);
+ static const uchar dense1_pat[] = { 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x44, 0x00 };
+ static const uchar dense2_pat[] = { 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00, 0x88 };
+ static const uchar dense3_pat[] = { 0x11, 0xaa, 0x44, 0xaa, 0x11, 0xaa, 0x44, 0xaa };
+ static const uchar dense4_pat[] = { 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55 };
+ static const uchar dense5_pat[] = { 0xee, 0x55, 0xbb, 0x55, 0xee, 0x55, 0xbb, 0x55 };
+ static const uchar dense6_pat[] = { 0xff, 0xdd, 0xff, 0x77, 0xff, 0xdd, 0xff, 0x77 };
+ static const uchar dense7_pat[] = { 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, 0xff };
+ static const uchar hor_pat[] = { 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff };
+ static const uchar ver_pat[] = { 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef };
+ static const uchar cross_pat[] = { 0xef, 0xef, 0xef, 0xef, 0x00, 0xef, 0xef, 0xef };
+ static const uchar fdiag_pat[] = { 0x7f, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, 0xfe };
+ static const uchar bdiag_pat[] = { 0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f };
+ static const uchar dcross_pat[] = { 0x7e, 0xbd, 0xdb, 0xe7, 0xe7, 0xdb, 0xbd, 0x7e };
+ static const uchar *const pat_tbl[] = {
+ dense1_pat, dense2_pat, dense3_pat, dense4_pat, dense5_pat,
+ dense6_pat, dense7_pat,
+ hor_pat, ver_pat, cross_pat, bdiag_pat, fdiag_pat, dcross_pat };
+ return pat_tbl[brushStyle - Qt::Dense1Pattern];
+}
+
+void QCoreGraphicsPaintEnginePrivate::setFillBrush(const QPointF &offset)
+{
+ // pattern
+ Qt::BrushStyle bs = current.brush.style();
+#ifdef QT_MAC_USE_NATIVE_GRADIENTS
+ if (bs == Qt::LinearGradientPattern || bs == Qt::RadialGradientPattern) {
+ const QGradient *grad = static_cast<const QGradient*>(current.brush.gradient());
+ if (drawGradientNatively(grad)) {
+ Q_ASSERT(grad->spread() == QGradient::PadSpread);
+
+ static const CGFloat domain[] = { 0.0f, +1.0f };
+ static const CGFunctionCallbacks callbacks = { 0, qt_mac_color_gradient_function, 0 };
+ CGFunctionRef fill_func = CGFunctionCreate(reinterpret_cast<void *>(&current.brush),
+ 1, domain, 4, 0, &callbacks);
+
+ CGColorSpaceRef colorspace = qt_mac_colorSpaceForDeviceType(pdev);
+ if (bs == Qt::LinearGradientPattern) {
+ const QLinearGradient *linearGrad = static_cast<const QLinearGradient *>(grad);
+ const QPointF start(linearGrad->start());
+ const QPointF stop(linearGrad->finalStop());
+ shading = CGShadingCreateAxial(colorspace, CGPointMake(start.x(), start.y()),
+ CGPointMake(stop.x(), stop.y()), fill_func, true, true);
+ } else {
+ Q_ASSERT(bs == Qt::RadialGradientPattern);
+ const QRadialGradient *radialGrad = static_cast<const QRadialGradient *>(grad);
+ QPointF center(radialGrad->center());
+ QPointF focal(radialGrad->focalPoint());
+ qreal radius = radialGrad->radius();
+ shading = CGShadingCreateRadial(colorspace, CGPointMake(focal.x(), focal.y()),
+ 0.0, CGPointMake(center.x(), center.y()), radius, fill_func, false, true);
+ }
+
+ CGFunctionRelease(fill_func);
+ }
+ } else
+#endif
+ if(bs != Qt::SolidPattern && bs != Qt::NoBrush
+#ifndef QT_MAC_USE_NATIVE_GRADIENTS
+ && (bs < Qt::LinearGradientPattern || bs > Qt::ConicalGradientPattern)
+#endif
+ )
+ {
+ QMacPattern *qpattern = new QMacPattern;
+ qpattern->pdev = pdev;
+ CGFloat components[4] = { 1.0, 1.0, 1.0, 1.0 };
+ CGColorSpaceRef base_colorspace = 0;
+ if(bs == Qt::TexturePattern) {
+ qpattern->data.pixmap = current.brush.texture();
+ if(qpattern->data.pixmap.isQBitmap()) {
+ const QColor &col = current.brush.color();
+ components[0] = qt_mac_convert_color_to_cg(col.red());
+ components[1] = qt_mac_convert_color_to_cg(col.green());
+ components[2] = qt_mac_convert_color_to_cg(col.blue());
+ base_colorspace = QCoreGraphicsPaintEngine::macGenericColorSpace();
+ }
+ } else {
+ qpattern->as_mask = true;
+
+ qpattern->data.bytes = qt_mac_patternForBrush(bs);
+ const QColor &col = current.brush.color();
+ components[0] = qt_mac_convert_color_to_cg(col.red());
+ components[1] = qt_mac_convert_color_to_cg(col.green());
+ components[2] = qt_mac_convert_color_to_cg(col.blue());
+ base_colorspace = QCoreGraphicsPaintEngine::macGenericColorSpace();
+ }
+ int width = qpattern->width(), height = qpattern->height();
+ qpattern->foreground = current.brush.color();
+
+ CGColorSpaceRef fill_colorspace = CGColorSpaceCreatePattern(base_colorspace);
+ CGContextSetFillColorSpace(hd, fill_colorspace);
+
+ CGAffineTransform xform = CGContextGetCTM(hd);
+ xform = CGAffineTransformConcat(qt_mac_convert_transform_to_cg(current.brush.transform()), xform);
+ xform = CGAffineTransformTranslate(xform, offset.x(), offset.y());
+
+ CGPatternCallbacks callbks;
+ callbks.version = 0;
+ callbks.drawPattern = qt_mac_draw_pattern;
+ callbks.releaseInfo = qt_mac_dispose_pattern;
+ CGPatternRef fill_pattern = CGPatternCreate(qpattern, CGRectMake(0, 0, width, height),
+ xform, width, height, kCGPatternTilingNoDistortion,
+ !base_colorspace, &callbks);
+ CGContextSetFillPattern(hd, fill_pattern, components);
+
+ CGPatternRelease(fill_pattern);
+ CGColorSpaceRelease(fill_colorspace);
+ } else if(bs != Qt::NoBrush) {
+ CGContextSetFillColorWithColor(hd, cgColorForQColor(current.brush.color(), pdev));
+ }
+}
+
+void
+QCoreGraphicsPaintEnginePrivate::setClip(const QRegion *rgn)
+{
+ Q_Q(QCoreGraphicsPaintEngine);
+ if(hd) {
+ resetClip();
+ QRegion sysClip = q->systemClip();
+ if(!sysClip.isEmpty())
+ qt_mac_clip_cg(hd, sysClip, &orig_xform);
+ if(rgn)
+ qt_mac_clip_cg(hd, *rgn, 0);
+ }
+}
+
+struct qt_mac_cg_transform_path {
+ CGMutablePathRef path;
+ CGAffineTransform transform;
+};
+
+void qt_mac_cg_transform_path_apply(void *info, const CGPathElement *element)
+{
+ Q_ASSERT(info && element);
+ qt_mac_cg_transform_path *t = (qt_mac_cg_transform_path*)info;
+ switch(element->type) {
+ case kCGPathElementMoveToPoint:
+ CGPathMoveToPoint(t->path, &t->transform, element->points[0].x, element->points[0].y);
+ break;
+ case kCGPathElementAddLineToPoint:
+ CGPathAddLineToPoint(t->path, &t->transform, element->points[0].x, element->points[0].y);
+ break;
+ case kCGPathElementAddQuadCurveToPoint:
+ CGPathAddQuadCurveToPoint(t->path, &t->transform, element->points[0].x, element->points[0].y,
+ element->points[1].x, element->points[1].y);
+ break;
+ case kCGPathElementAddCurveToPoint:
+ CGPathAddCurveToPoint(t->path, &t->transform, element->points[0].x, element->points[0].y,
+ element->points[1].x, element->points[1].y,
+ element->points[2].x, element->points[2].y);
+ break;
+ case kCGPathElementCloseSubpath:
+ CGPathCloseSubpath(t->path);
+ break;
+ default:
+ qDebug() << "Unhandled path transform type: " << element->type;
+ }
+}
+
+void QCoreGraphicsPaintEnginePrivate::drawPath(uchar ops, CGMutablePathRef path)
+{
+ Q_Q(QCoreGraphicsPaintEngine);
+ Q_ASSERT((ops & (CGFill | CGEOFill)) != (CGFill | CGEOFill)); //can't really happen
+ if((ops & (CGFill | CGEOFill))) {
+ if (shading) {
+ Q_ASSERT(path);
+ CGContextBeginPath(hd);
+ CGContextAddPath(hd, path);
+ saveGraphicsState();
+ if (ops & CGFill)
+ CGContextClip(hd);
+ else if (ops & CGEOFill)
+ CGContextEOClip(hd);
+ if (current.brush.gradient()->coordinateMode() == QGradient::ObjectBoundingMode) {
+ CGRect boundingBox = CGPathGetBoundingBox(path);
+ CGContextConcatCTM(hd,
+ CGAffineTransformMake(boundingBox.size.width, 0,
+ 0, boundingBox.size.height,
+ boundingBox.origin.x, boundingBox.origin.y));
+ }
+ CGContextDrawShading(hd, shading);
+ restoreGraphicsState();
+ ops &= ~CGFill;
+ ops &= ~CGEOFill;
+ } else if (current.brush.style() == Qt::NoBrush) {
+ ops &= ~CGFill;
+ ops &= ~CGEOFill;
+ }
+ }
+ if((ops & CGStroke) && current.pen.style() == Qt::NoPen)
+ ops &= ~CGStroke;
+
+ if(ops & (CGEOFill | CGFill)) {
+ CGContextBeginPath(hd);
+ CGContextAddPath(hd, path);
+ if (ops & CGEOFill) {
+ CGContextEOFillPath(hd);
+ } else {
+ CGContextFillPath(hd);
+ }
+ }
+
+ // Avoid saving and restoring the context if we can.
+ const bool needContextSave = (cosmeticPen != QCoreGraphicsPaintEnginePrivate::CosmeticNone ||
+ !(q->state->renderHints() & QPainter::Antialiasing));
+ if(ops & CGStroke) {
+ if (needContextSave)
+ saveGraphicsState();
+ CGContextBeginPath(hd);
+
+ // Translate a fraction of a pixel size in the y direction
+ // to make sure that primitives painted at pixel borders
+ // fills the right pixel. This is needed since the y xais
+ // in the Quartz coordinate system is inverted compared to Qt.
+ if (!(q->state->renderHints() & QPainter::Antialiasing)) {
+ if (current.pen.style() == Qt::SolidLine || current.pen.width() >= 3)
+ CGContextTranslateCTM(hd, double(pixelSize.x()) * 0.25, double(pixelSize.y()) * 0.25);
+ else if (current.pen.style() == Qt::DotLine && QSysInfo::MacintoshVersion == QSysInfo::MV_10_3)
+ ; // Do nothing.
+ else
+ CGContextTranslateCTM(hd, 0, double(pixelSize.y()) * 0.1);
+ }
+
+ if (cosmeticPen != QCoreGraphicsPaintEnginePrivate::CosmeticNone) {
+ // If antialiazing is enabled, use the cosmetic pen size directly.
+ if (q->state->renderHints() & QPainter::Antialiasing)
+ CGContextSetLineWidth(hd, cosmeticPenSize);
+ else if (current.pen.widthF() <= 1)
+ CGContextSetLineWidth(hd, cosmeticPenSize * 0.9f);
+ else
+ CGContextSetLineWidth(hd, cosmeticPenSize);
+ }
+ if(cosmeticPen == QCoreGraphicsPaintEnginePrivate::CosmeticTransformPath) {
+ qt_mac_cg_transform_path t;
+ t.transform = qt_mac_convert_transform_to_cg(current.transform);
+ t.path = CGPathCreateMutable();
+ CGPathApply(path, &t, qt_mac_cg_transform_path_apply); //transform the path
+ setTransform(0); //unset the context transform
+ CGContextSetLineWidth(hd, cosmeticPenSize);
+ CGContextAddPath(hd, t.path);
+ CGPathRelease(t.path);
+ } else {
+ CGContextAddPath(hd, path);
+ }
+
+ CGContextStrokePath(hd);
+ if (needContextSave)
+ restoreGraphicsState();
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qpaintengine_mac_p.h b/src/gui/painting/qpaintengine_mac_p.h
new file mode 100644
index 0000000000..3e71d6ca6b
--- /dev/null
+++ b/src/gui/painting/qpaintengine_mac_p.h
@@ -0,0 +1,254 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTENGINE_MAC_P_H
+#define QPAINTENGINE_MAC_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtGui/qpaintengine.h"
+#include "private/qt_mac_p.h"
+#include "private/qpaintengine_p.h"
+#include "private/qpolygonclipper_p.h"
+#include "private/qfont_p.h"
+#include "QtCore/qhash.h"
+
+typedef struct CGColorSpace *CGColorSpaceRef;
+QT_BEGIN_NAMESPACE
+
+class QCoreGraphicsPaintEnginePrivate;
+class QCoreGraphicsPaintEngine : public QPaintEngine
+{
+ Q_DECLARE_PRIVATE(QCoreGraphicsPaintEngine)
+
+public:
+ QCoreGraphicsPaintEngine();
+ ~QCoreGraphicsPaintEngine();
+
+ bool begin(QPaintDevice *pdev);
+ bool end();
+ static CGColorSpaceRef macGenericColorSpace();
+ static CGColorSpaceRef macDisplayColorSpace(const QWidget *widget = 0);
+
+ void updateState(const QPaintEngineState &state);
+
+ void updatePen(const QPen &pen);
+ void updateBrush(const QBrush &brush, const QPointF &pt);
+ void updateFont(const QFont &font);
+ void updateOpacity(qreal opacity);
+ void updateMatrix(const QTransform &matrix);
+ void updateTransform(const QTransform &matrix);
+ void updateClipRegion(const QRegion &region, Qt::ClipOperation op);
+ void updateClipPath(const QPainterPath &path, Qt::ClipOperation op);
+ void updateCompositionMode(QPainter::CompositionMode mode);
+ void updateRenderHints(QPainter::RenderHints hints);
+
+ void drawLines(const QLineF *lines, int lineCount);
+ void drawRects(const QRectF *rects, int rectCount);
+ void drawPoints(const QPointF *p, int pointCount);
+ void drawEllipse(const QRectF &r);
+ void drawPath(const QPainterPath &path);
+
+ void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode);
+ void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
+ void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s);
+
+ void drawTextItem(const QPointF &pos, const QTextItem &item);
+ void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr,
+ Qt::ImageConversionFlags flags = Qt::AutoColor);
+
+ Type type() const { return QPaintEngine::CoreGraphics; }
+
+ CGContextRef handle() const;
+
+ static void initialize();
+ static void cleanup();
+
+ QPainter::RenderHints supportedRenderHints() const;
+
+ //avoid partial shadowed overload warnings...
+ void drawLines(const QLine *lines, int lineCount) { QPaintEngine::drawLines(lines, lineCount); }
+ void drawRects(const QRect *rects, int rectCount) { QPaintEngine::drawRects(rects, rectCount); }
+ void drawPoints(const QPoint *p, int pointCount) { QPaintEngine::drawPoints(p, pointCount); }
+ void drawEllipse(const QRect &r) { QPaintEngine::drawEllipse(r); }
+ void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
+ { QPaintEngine::drawPolygon(points, pointCount, mode); }
+
+protected:
+ friend class QMacPrintEngine;
+ friend class QMacPrintEnginePrivate;
+ friend void qt_mac_display_change_callbk(CGDirectDisplayID, CGDisplayChangeSummaryFlags, void *);
+ friend void qt_color_profile_changed(CFNotificationCenterRef center, void *,
+ CFStringRef , const void *, CFDictionaryRef);
+ QCoreGraphicsPaintEngine(QPaintEnginePrivate &dptr);
+
+private:
+ static bool m_postRoutineRegistered;
+ static CGColorSpaceRef m_genericColorSpace;
+ static QHash<CGDirectDisplayID, CGColorSpaceRef> m_displayColorSpaceHash;
+ static void cleanUpMacColorSpaces();
+ Q_DISABLE_COPY(QCoreGraphicsPaintEngine)
+};
+
+/*****************************************************************************
+ Private data
+ *****************************************************************************/
+class QCoreGraphicsPaintEnginePrivate : public QPaintEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QCoreGraphicsPaintEngine)
+public:
+ QCoreGraphicsPaintEnginePrivate()
+ : hd(0), shading(0), stackCount(0), complexXForm(false), disabledSmoothFonts(false)
+ {
+ }
+
+ struct {
+ QPen pen;
+ QBrush brush;
+ uint clipEnabled : 1;
+ QRegion clip;
+ QTransform transform;
+ } current;
+
+ //state info (shared with QD)
+ CGAffineTransform orig_xform;
+
+ //cg structures
+ CGContextRef hd;
+ CGShadingRef shading;
+ int stackCount;
+ bool complexXForm;
+ bool disabledSmoothFonts;
+ enum { CosmeticNone, CosmeticTransformPath, CosmeticSetPenWidth } cosmeticPen;
+
+ // pixel and cosmetic pen size in user coordinates.
+ QPointF pixelSize;
+ float cosmeticPenSize;
+
+ //internal functions
+ enum { CGStroke=0x01, CGEOFill=0x02, CGFill=0x04 };
+ void drawPath(uchar ops, CGMutablePathRef path = 0);
+ void setClip(const QRegion *rgn=0);
+ void resetClip();
+ void setFillBrush(const QPointF &origin=QPoint());
+ void setStrokePen(const QPen &pen);
+ inline void saveGraphicsState();
+ inline void restoreGraphicsState();
+ float penOffset();
+ QPointF devicePixelSize(CGContextRef context);
+ float adjustPenWidth(float penWidth);
+ inline void setTransform(const QTransform *matrix=0)
+ {
+ CGContextConcatCTM(hd, CGAffineTransformInvert(CGContextGetCTM(hd)));
+ CGAffineTransform xform = orig_xform;
+ if(matrix) {
+ extern CGAffineTransform qt_mac_convert_transform_to_cg(const QTransform &);
+ xform = CGAffineTransformConcat(qt_mac_convert_transform_to_cg(*matrix), xform);
+ }
+ CGContextConcatCTM(hd, xform);
+ CGContextSetTextMatrix(hd, xform);
+ }
+};
+
+inline void QCoreGraphicsPaintEnginePrivate::saveGraphicsState()
+{
+ ++stackCount;
+ CGContextSaveGState(hd);
+}
+
+inline void QCoreGraphicsPaintEnginePrivate::restoreGraphicsState()
+{
+ --stackCount;
+ Q_ASSERT(stackCount >= 0);
+ CGContextRestoreGState(hd);
+}
+
+class QMacQuartzPaintDevice : public QPaintDevice
+{
+public:
+ QMacQuartzPaintDevice(CGContextRef cg, int width, int height, int bytesPerLine)
+ : mCG(cg), mWidth(width), mHeight(height), mBytesPerLine(bytesPerLine)
+ { }
+ int devType() const { return QInternal::MacQuartz; }
+ CGContextRef cgContext() const { return mCG; }
+ int metric(PaintDeviceMetric metric) const {
+ switch (metric) {
+ case PdmWidth:
+ return mWidth;
+ case PdmHeight:
+ return mHeight;
+ case PdmWidthMM:
+ return (qt_defaultDpiX() * mWidth) / 2.54;
+ case PdmHeightMM:
+ return (qt_defaultDpiY() * mHeight) / 2.54;
+ case PdmNumColors:
+ return 0;
+ case PdmDepth:
+ return 32;
+ case PdmDpiX:
+ case PdmPhysicalDpiX:
+ return qt_defaultDpiX();
+ case PdmDpiY:
+ case PdmPhysicalDpiY:
+ return qt_defaultDpiY();
+ }
+ return 0;
+ }
+ QPaintEngine *paintEngine() const { qWarning("This function should never be called."); return 0; }
+private:
+ CGContextRef mCG;
+ int mWidth;
+ int mHeight;
+ int mBytesPerLine;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPAINTENGINE_MAC_P_H
diff --git a/src/gui/painting/qpaintengine_p.h b/src/gui/painting/qpaintengine_p.h
new file mode 100644
index 0000000000..d6aaffc254
--- /dev/null
+++ b/src/gui/painting/qpaintengine_p.h
@@ -0,0 +1,125 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTENGINE_P_H
+#define QPAINTENGINE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtGui/qpainter.h"
+#include "QtGui/qpaintengine.h"
+#include "QtGui/qregion.h"
+#include "private/qobject_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QPaintDevice;
+
+class QPaintEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QPaintEngine)
+public:
+ QPaintEnginePrivate() : pdev(0), q_ptr(0), currentClipWidget(0), hasSystemTransform(0),
+ hasSystemViewport(0) {}
+ virtual ~QPaintEnginePrivate() { }
+ QPaintDevice *pdev;
+ QPaintEngine *q_ptr;
+ QRegion systemClip;
+ QRect systemRect;
+ QRegion systemViewport;
+ QTransform systemTransform;
+ QWidget *currentClipWidget;
+ uint hasSystemTransform : 1;
+ uint hasSystemViewport : 1;
+
+ inline void transformSystemClip()
+ {
+ if (systemClip.isEmpty())
+ return;
+
+ if (hasSystemTransform) {
+ if (systemTransform.type() <= QTransform::TxTranslate)
+ systemClip.translate(qRound(systemTransform.dx()), qRound(systemTransform.dy()));
+ else
+ systemClip = systemTransform.map(systemClip);
+ }
+
+ // Make sure we're inside the viewport.
+ if (hasSystemViewport) {
+ systemClip &= systemViewport;
+ if (systemClip.isEmpty()) {
+ // We don't want to paint without system clip, so set it to 1 pixel :)
+ systemClip = QRect(systemViewport.boundingRect().topLeft(), QSize(1, 1));
+ }
+ }
+ }
+
+ inline void setSystemTransform(const QTransform &xform)
+ {
+ systemTransform = xform;
+ if ((hasSystemTransform = !xform.isIdentity()) || hasSystemViewport)
+ transformSystemClip();
+ systemStateChanged();
+ }
+
+ inline void setSystemViewport(const QRegion &region)
+ {
+ systemViewport = region;
+ hasSystemViewport = !systemViewport.isEmpty();
+ }
+
+ virtual void systemStateChanged() { }
+
+ void drawBoxTextItem(const QPointF &p, const QTextItemInt &ti);
+};
+
+QT_END_NAMESPACE
+
+#endif // QPAINTENGINE_P_H
diff --git a/src/gui/painting/qpaintengine_preview.cpp b/src/gui/painting/qpaintengine_preview.cpp
new file mode 100644
index 0000000000..99fe4f7fcc
--- /dev/null
+++ b/src/gui/painting/qpaintengine_preview.cpp
@@ -0,0 +1,223 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qpaintengine_preview_p.h>
+#include <private/qpainter_p.h>
+#include <private/qpaintengine_p.h>
+#include <private/qpicture_p.h>
+
+#include <QtGui/qprintengine.h>
+#include <QtGui/qpainter.h>
+#include <QtGui/qpicture.h>
+
+#ifndef QT_NO_PRINTPREVIEWWIDGET
+QT_BEGIN_NAMESPACE
+
+class QPreviewPaintEnginePrivate : public QPaintEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QPreviewPaintEngine)
+public:
+ QPreviewPaintEnginePrivate() : state(QPrinter::Idle) {}
+ ~QPreviewPaintEnginePrivate() {}
+
+ QList<const QPicture *> pages;
+ QPaintEngine *engine;
+ QPainter *painter;
+ QPrinter::PrinterState state;
+
+ QPaintEngine *proxy_paint_engine;
+ QPrintEngine *proxy_print_engine;
+};
+
+
+QPreviewPaintEngine::QPreviewPaintEngine()
+ : QPaintEngine(*(new QPreviewPaintEnginePrivate), PaintEngineFeatures(AllFeatures & ~ObjectBoundingModeGradients))
+{
+ Q_D(QPreviewPaintEngine);
+ d->proxy_print_engine = 0;
+ d->proxy_paint_engine = 0;
+}
+
+QPreviewPaintEngine::~QPreviewPaintEngine()
+{
+ Q_D(QPreviewPaintEngine);
+
+ qDeleteAll(d->pages);
+}
+
+bool QPreviewPaintEngine::begin(QPaintDevice *)
+{
+ Q_D(QPreviewPaintEngine);
+
+ qDeleteAll(d->pages);
+ d->pages.clear();
+
+ QPicture *page = new QPicture;
+ page->d_func()->in_memory_only = true;
+ d->painter = new QPainter(page);
+ d->engine = d->painter->paintEngine();
+ d->pages.append(page);
+ d->state = QPrinter::Active;
+ return true;
+}
+
+bool QPreviewPaintEngine::end()
+{
+ Q_D(QPreviewPaintEngine);
+
+ delete d->painter;
+ d->painter = 0;
+ d->engine = 0;
+ d->state = QPrinter::Idle;
+ return true;
+}
+
+void QPreviewPaintEngine::updateState(const QPaintEngineState &state)
+{
+ Q_D(QPreviewPaintEngine);
+ d->engine->updateState(state);
+}
+
+void QPreviewPaintEngine::drawPath(const QPainterPath &path)
+{
+ Q_D(QPreviewPaintEngine);
+ d->engine->drawPath(path);
+}
+
+void QPreviewPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
+{
+ Q_D(QPreviewPaintEngine);
+ d->engine->drawPolygon(points, pointCount, mode);
+}
+
+void QPreviewPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
+{
+ Q_D(QPreviewPaintEngine);
+ d->engine->drawTextItem(p, textItem);
+}
+
+void QPreviewPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
+{
+ Q_D(QPreviewPaintEngine);
+ d->engine->drawPixmap(r, pm, sr);
+}
+
+void QPreviewPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &p)
+{
+ Q_D(QPreviewPaintEngine);
+ d->engine->drawTiledPixmap(r, pm, p);
+}
+
+bool QPreviewPaintEngine::newPage()
+{
+ Q_D(QPreviewPaintEngine);
+
+ QPicture *page = new QPicture;
+ page->d_func()->in_memory_only = true;
+ QPainter *tmp_painter = new QPainter(page);
+ QPaintEngine *tmp_engine = tmp_painter->paintEngine();
+
+ // copy the painter state from the original painter
+ Q_ASSERT(painter()->d_func()->state && tmp_painter->d_func()->state);
+ *tmp_painter->d_func()->state = *painter()->d_func()->state;
+
+ // composition modes aren't supported on a QPrinter and yields a
+ // warning, so ignore it for now
+ tmp_engine->setDirty(DirtyFlags(AllDirty & ~DirtyCompositionMode));
+ tmp_engine->syncState();
+
+ delete d->painter;
+ d->painter = tmp_painter;
+ d->pages.append(page);
+ d->engine = tmp_engine;
+ return true;
+}
+
+bool QPreviewPaintEngine::abort()
+{
+ Q_D(QPreviewPaintEngine);
+ end();
+ qDeleteAll(d->pages);
+ d->state = QPrinter::Aborted;
+
+ return true;
+}
+
+QList<const QPicture *> QPreviewPaintEngine::pages()
+{
+ Q_D(QPreviewPaintEngine);
+ return d->pages;
+}
+
+void QPreviewPaintEngine::setProxyEngines(QPrintEngine *printEngine, QPaintEngine *paintEngine)
+{
+ Q_D(QPreviewPaintEngine);
+ d->proxy_print_engine = printEngine;
+ d->proxy_paint_engine = paintEngine;
+}
+
+void QPreviewPaintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &value)
+{
+ Q_D(QPreviewPaintEngine);
+ d->proxy_print_engine->setProperty(key, value);
+}
+
+QVariant QPreviewPaintEngine::property(PrintEnginePropertyKey key) const
+{
+ Q_D(const QPreviewPaintEngine);
+ return d->proxy_print_engine->property(key);
+}
+
+int QPreviewPaintEngine::metric(QPaintDevice::PaintDeviceMetric id) const
+{
+ Q_D(const QPreviewPaintEngine);
+ return d->proxy_print_engine->metric(id);
+}
+
+QPrinter::PrinterState QPreviewPaintEngine::printerState() const
+{
+ Q_D(const QPreviewPaintEngine);
+ return d->state;
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/painting/qpaintengine_preview_p.h b/src/gui/painting/qpaintengine_preview_p.h
new file mode 100644
index 0000000000..0101aa0073
--- /dev/null
+++ b/src/gui/painting/qpaintengine_preview_p.h
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPREVIEWPAINTENGINE_P_H
+#define QPREVIEWPAINTENGINE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of QPreviewPrinter and friends. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+//
+
+#include <QtGui/qpaintengine.h>
+#include <QtGui/qprintengine.h>
+
+#ifndef QT_NO_PRINTPREVIEWWIDGET
+
+QT_BEGIN_NAMESPACE
+
+class QPreviewPaintEnginePrivate;
+
+class QPreviewPaintEngine : public QPaintEngine, public QPrintEngine
+{
+ Q_DECLARE_PRIVATE(QPreviewPaintEngine)
+public:
+ QPreviewPaintEngine();
+ ~QPreviewPaintEngine();
+
+ bool begin(QPaintDevice *dev);
+ bool end();
+
+ void updateState(const QPaintEngineState &state);
+
+ void drawPath(const QPainterPath &path);
+ void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode);
+ void drawTextItem(const QPointF &p, const QTextItem &textItem);
+
+ void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
+ void drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &p);
+
+ QList<const QPicture *> pages();
+
+ QPaintEngine::Type type() const { return Picture; }
+
+ void setProxyEngines(QPrintEngine *printEngine, QPaintEngine *paintEngine);
+
+ void setProperty(PrintEnginePropertyKey key, const QVariant &value);
+ QVariant property(PrintEnginePropertyKey key) const;
+
+ bool newPage();
+ bool abort();
+
+ int metric(QPaintDevice::PaintDeviceMetric) const;
+
+ QPrinter::PrinterState printerState() const;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTPREVIEWWIDGET
+
+#endif
diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp
new file mode 100644
index 0000000000..69025436c2
--- /dev/null
+++ b/src/gui/painting/qpaintengine_raster.cpp
@@ -0,0 +1,6325 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qmutex.h>
+
+#define QT_FT_BEGIN_HEADER
+#define QT_FT_END_HEADER
+
+#include <private/qrasterdefs_p.h>
+#include <private/qgrayraster_p.h>
+
+#include <qpainterpath.h>
+#include <qdebug.h>
+#include <qhash.h>
+#include <qlabel.h>
+#include <qbitmap.h>
+#include <qmath.h>
+
+#if defined (Q_WS_X11)
+# include <private/qfontengine_ft_p.h>
+#endif
+
+// #include <private/qdatabuffer_p.h>
+// #include <private/qpainter_p.h>
+#include <private/qmath_p.h>
+#include <private/qtextengine_p.h>
+#include <private/qfontengine_p.h>
+#include <private/qpixmap_raster_p.h>
+// #include <private/qpolygonclipper_p.h>
+// #include <private/qrasterizer_p.h>
+#include <private/qimage_p.h>
+#include <private/qstatictext_p.h>
+#include "qmemrotate_p.h"
+
+#include "qpaintengine_raster_p.h"
+// #include "qbezier_p.h"
+#include "qoutlinemapper_p.h"
+
+#if defined(Q_WS_WIN)
+# include <qt_windows.h>
+# include <qvarlengtharray.h>
+# include <private/qfontengine_p.h>
+# if defined(Q_OS_WINCE)
+# include "qguifunctions_wince.h"
+# endif
+#elif defined(Q_WS_MAC)
+# include <private/qt_mac_p.h>
+# include <private/qpixmap_mac_p.h>
+# include <private/qpaintengine_mac_p.h>
+#elif defined(Q_WS_QWS)
+# if !defined(QT_NO_FREETYPE)
+# include <private/qfontengine_ft_p.h>
+# endif
+# if !defined(QT_NO_QWS_QPF2)
+# include <private/qfontengine_qpf_p.h>
+# endif
+# include <private/qabstractfontengine_p.h>
+#elif defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE)
+# include <private/qfontengine_s60_p.h>
+#elif defined(Q_WS_QPA)
+# include <private/qfontengine_ft_p.h>
+#endif
+
+#if defined(Q_WS_WIN64)
+# include <malloc.h>
+#endif
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_GUI_EXPORT extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp
+
+#define qreal_to_fixed_26_6(f) (int(f * 64))
+#define qt_swap_int(x, y) { int tmp = (x); (x) = (y); (y) = tmp; }
+#define qt_swap_qreal(x, y) { qreal tmp = (x); (x) = (y); (y) = tmp; }
+
+// #define QT_DEBUG_DRAW
+#ifdef QT_DEBUG_DRAW
+void dumpClip(int width, int height, const QClipData *clip);
+#endif
+
+#define QT_FAST_SPANS
+
+
+// A little helper macro to get a better approximation of dimensions.
+// If we have a rect that starting at 0.5 of width 3.5 it should span
+// 4 pixels.
+#define int_dim(pos, dim) (int(pos+dim) - int(pos))
+
+#ifdef Q_WS_WIN
+extern bool qt_cleartype_enabled;
+#endif
+
+#ifdef Q_WS_MAC
+extern bool qt_applefontsmoothing_enabled;
+#endif
+
+
+/********************************************************************************
+ * Span functions
+ */
+static void qt_span_fill_clipRect(int count, const QSpan *spans, void *userData);
+static void qt_span_fill_clipped(int count, const QSpan *spans, void *userData);
+static void qt_span_clip(int count, const QSpan *spans, void *userData);
+static void qt_merge_clip(const QClipData *c1, const QClipData *c2, QClipData *result);
+
+struct ClipData
+{
+ QClipData *oldClip;
+ QClipData *newClip;
+ Qt::ClipOperation operation;
+};
+
+enum LineDrawMode {
+ LineDrawClipped,
+ LineDrawNormal,
+ LineDrawIncludeLastPixel
+};
+
+static void drawLine_midpoint_i(int x1, int y1, int x2, int y2, ProcessSpans span_func, QSpanData *data,
+ LineDrawMode style, const QIntRect &rect);
+static void drawLine_midpoint_dashed_i(int x1, int y1, int x2, int y2,
+ QPen *pen, ProcessSpans span_func, QSpanData *data,
+ LineDrawMode style, const QIntRect &devRect,
+ int *patternOffset);
+// static void drawLine_midpoint_f(qreal x1, qreal y1, qreal x2, qreal y2,
+// ProcessSpans span_func, QSpanData *data,
+// LineDrawMode style, const QRect &devRect);
+
+static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
+ ProcessSpans pen_func, ProcessSpans brush_func,
+ QSpanData *pen_data, QSpanData *brush_data);
+
+struct QRasterFloatPoint {
+ qreal x;
+ qreal y;
+};
+
+#ifdef QT_DEBUG_DRAW
+static const QRectF boundingRect(const QPointF *points, int pointCount)
+{
+ const QPointF *e = points;
+ const QPointF *last = points + pointCount;
+ qreal minx, maxx, miny, maxy;
+ minx = maxx = e->x();
+ miny = maxy = e->y();
+ while (++e < last) {
+ if (e->x() < minx)
+ minx = e->x();
+ else if (e->x() > maxx)
+ maxx = e->x();
+ if (e->y() < miny)
+ miny = e->y();
+ else if (e->y() > maxy)
+ maxy = e->y();
+ }
+ return QRectF(QPointF(minx, miny), QPointF(maxx, maxy));
+}
+#endif
+
+template <typename T> static inline bool isRect(const T *pts, int elementCount) {
+ return (elementCount == 5 // 5-point polygon, check for closed rect
+ && pts[0] == pts[8] && pts[1] == pts[9] // last point == first point
+ && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
+ && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
+ && pts[0] < pts[4] && pts[1] < pts[5]
+ ) ||
+ (elementCount == 4 // 4-point polygon, check for unclosed rect
+ && pts[0] == pts[6] && pts[2] == pts[4] // x values equal
+ && pts[1] == pts[3] && pts[5] == pts[7] // y values equal...
+ && pts[0] < pts[4] && pts[1] < pts[5]
+ );
+}
+
+
+static void qt_ft_outline_move_to(qfixed x, qfixed y, void *data)
+{
+ ((QOutlineMapper *) data)->moveTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
+}
+
+static void qt_ft_outline_line_to(qfixed x, qfixed y, void *data)
+{
+ ((QOutlineMapper *) data)->lineTo(QPointF(qt_fixed_to_real(x), qt_fixed_to_real(y)));
+}
+
+static void qt_ft_outline_cubic_to(qfixed c1x, qfixed c1y,
+ qfixed c2x, qfixed c2y,
+ qfixed ex, qfixed ey,
+ void *data)
+{
+ ((QOutlineMapper *) data)->curveTo(QPointF(qt_fixed_to_real(c1x), qt_fixed_to_real(c1y)),
+ QPointF(qt_fixed_to_real(c2x), qt_fixed_to_real(c2y)),
+ QPointF(qt_fixed_to_real(ex), qt_fixed_to_real(ey)));
+}
+
+
+#if !defined(QT_NO_DEBUG) && 0
+static void qt_debug_path(const QPainterPath &path)
+{
+ const char *names[] = {
+ "MoveTo ",
+ "LineTo ",
+ "CurveTo ",
+ "CurveToData"
+ };
+
+ fprintf(stderr,"\nQPainterPath: elementCount=%d\n", path.elementCount());
+ for (int i=0; i<path.elementCount(); ++i) {
+ const QPainterPath::Element &e = path.elementAt(i);
+ Q_ASSERT(e.type >= 0 && e.type <= QPainterPath::CurveToDataElement);
+ fprintf(stderr," - %3d:: %s, (%.2f, %.2f)\n", i, names[e.type], e.x, e.y);
+ }
+}
+#endif
+
+QRasterPaintEnginePrivate::QRasterPaintEnginePrivate() :
+ QPaintEngineExPrivate(),
+ cachedLines(0)
+{
+}
+
+
+/*!
+ \class QRasterPaintEngine
+ \preliminary
+ \ingroup qws
+ \since 4.2
+
+ \brief The QRasterPaintEngine class enables hardware acceleration
+ of painting operations in Qt for Embedded Linux.
+
+ Note that this functionality is only available in
+ \l{Qt for Embedded Linux}.
+
+ In \l{Qt for Embedded Linux}, painting is a pure software
+ implementation. But starting with Qt 4.2, it is
+ possible to add an accelerated graphics driver to take advantage
+ of available hardware resources.
+
+ Hardware acceleration is accomplished by creating a custom screen
+ driver, accelerating the copying from memory to the screen, and
+ implementing a custom paint engine accelerating the various
+ painting operations. Then a custom paint device (derived from the
+ QCustomRasterPaintDevice class) and a custom window surface
+ (derived from QWSWindowSurface) must be implemented to make
+ \l{Qt for Embedded Linux} aware of the accelerated driver.
+
+ \note The QRasterPaintEngine class does not support 8-bit images.
+ Instead, they need to be converted to a supported format, such as
+ QImage::Format_ARGB32_Premultiplied.
+
+ See the \l {Adding an Accelerated Graphics Driver to Qt for Embedded Linux}
+ documentation for details.
+
+ \sa QCustomRasterPaintDevice, QPaintEngine
+*/
+
+/*!
+ \fn Type QRasterPaintEngine::type() const
+ \reimp
+*/
+
+/*!
+ \typedef QSpan
+ \relates QRasterPaintEngine
+
+ A struct equivalent to QT_FT_Span, containing a position (x,
+ y), the span's length in pixels and its color/coverage (a value
+ ranging from 0 to 255).
+*/
+
+/*!
+ \since 4.5
+
+ Creates a raster based paint engine for operating on the given
+ \a device, with the complete set of \l
+ {QPaintEngine::PaintEngineFeature}{paint engine features and
+ capabilities}.
+*/
+QRasterPaintEngine::QRasterPaintEngine(QPaintDevice *device)
+ : QPaintEngineEx(*(new QRasterPaintEnginePrivate))
+{
+ d_func()->device = device;
+ init();
+}
+
+/*!
+ \internal
+*/
+QRasterPaintEngine::QRasterPaintEngine(QRasterPaintEnginePrivate &dd, QPaintDevice *device)
+ : QPaintEngineEx(dd)
+{
+ d_func()->device = device;
+ init();
+}
+
+void QRasterPaintEngine::init()
+{
+ Q_D(QRasterPaintEngine);
+
+
+#ifdef Q_WS_WIN
+ d->hdc = 0;
+#endif
+
+ // The antialiasing raster.
+ d->grayRaster.reset(new QT_FT_Raster);
+ Q_CHECK_PTR(d->grayRaster.data());
+ if (qt_ft_grays_raster.raster_new(d->grayRaster.data()))
+ QT_THROW(std::bad_alloc()); // an error creating the raster is caused by a bad malloc
+
+
+ d->rasterizer.reset(new QRasterizer);
+ d->rasterBuffer.reset(new QRasterBuffer());
+ d->outlineMapper.reset(new QOutlineMapper);
+ d->outlinemapper_xform_dirty = true;
+
+ d->basicStroker.setMoveToHook(qt_ft_outline_move_to);
+ d->basicStroker.setLineToHook(qt_ft_outline_line_to);
+ d->basicStroker.setCubicToHook(qt_ft_outline_cubic_to);
+
+ d->baseClip.reset(new QClipData(d->device->height()));
+ d->baseClip->setClipRect(QRect(0, 0, d->device->width(), d->device->height()));
+
+ d->image_filler.init(d->rasterBuffer.data(), this);
+ d->image_filler.type = QSpanData::Texture;
+
+ d->image_filler_xform.init(d->rasterBuffer.data(), this);
+ d->image_filler_xform.type = QSpanData::Texture;
+
+ d->solid_color_filler.init(d->rasterBuffer.data(), this);
+ d->solid_color_filler.type = QSpanData::Solid;
+
+ d->deviceDepth = d->device->depth();
+
+ d->mono_surface = false;
+ gccaps &= ~PorterDuff;
+
+ QImage::Format format = QImage::Format_Invalid;
+
+ switch (d->device->devType()) {
+ case QInternal::Pixmap:
+ qWarning("QRasterPaintEngine: unsupported for pixmaps...");
+ break;
+ case QInternal::Image:
+ format = d->rasterBuffer->prepare(static_cast<QImage *>(d->device));
+ break;
+#ifdef Q_WS_QWS
+ case QInternal::CustomRaster:
+ d->rasterBuffer->prepare(static_cast<QCustomRasterPaintDevice*>(d->device));
+ break;
+#endif
+ default:
+ qWarning("QRasterPaintEngine: unsupported target device %d\n", d->device->devType());
+ d->device = 0;
+ return;
+ }
+
+ switch (format) {
+ case QImage::Format_MonoLSB:
+ case QImage::Format_Mono:
+ d->mono_surface = true;
+ break;
+ case QImage::Format_ARGB8565_Premultiplied:
+ case QImage::Format_ARGB8555_Premultiplied:
+ case QImage::Format_ARGB6666_Premultiplied:
+ case QImage::Format_ARGB4444_Premultiplied:
+ case QImage::Format_ARGB32_Premultiplied:
+ case QImage::Format_ARGB32:
+ gccaps |= PorterDuff;
+ break;
+ case QImage::Format_RGB32:
+ case QImage::Format_RGB444:
+ case QImage::Format_RGB555:
+ case QImage::Format_RGB666:
+ case QImage::Format_RGB888:
+ case QImage::Format_RGB16:
+ break;
+ default:
+ break;
+ }
+}
+
+
+
+
+/*!
+ Destroys this paint engine.
+*/
+QRasterPaintEngine::~QRasterPaintEngine()
+{
+ Q_D(QRasterPaintEngine);
+
+ qt_ft_grays_raster.raster_done(*d->grayRaster.data());
+}
+
+/*!
+ \reimp
+*/
+bool QRasterPaintEngine::begin(QPaintDevice *device)
+{
+ Q_D(QRasterPaintEngine);
+
+ if (device->devType() == QInternal::Pixmap) {
+ QPixmap *pixmap = static_cast<QPixmap *>(device);
+ QPixmapData *pd = pixmap->pixmapData();
+ if (pd->classId() == QPixmapData::RasterClass || pd->classId() == QPixmapData::BlitterClass)
+ d->device = pd->buffer();
+ } else {
+ d->device = device;
+ }
+
+ // Make sure QPaintEngine::paintDevice() returns the proper device.
+ d->pdev = d->device;
+
+ Q_ASSERT(d->device->devType() == QInternal::Image
+ || d->device->devType() == QInternal::CustomRaster);
+
+ d->systemStateChanged();
+
+ QRasterPaintEngineState *s = state();
+ ensureOutlineMapper();
+ d->outlineMapper->m_clip_rect = d->deviceRect;
+
+ if (d->outlineMapper->m_clip_rect.width() > QT_RASTER_COORD_LIMIT)
+ d->outlineMapper->m_clip_rect.setWidth(QT_RASTER_COORD_LIMIT);
+ if (d->outlineMapper->m_clip_rect.height() > QT_RASTER_COORD_LIMIT)
+ d->outlineMapper->m_clip_rect.setHeight(QT_RASTER_COORD_LIMIT);
+
+ d->rasterizer->setClipRect(d->deviceRect);
+
+ s->penData.init(d->rasterBuffer.data(), this);
+ s->penData.setup(s->pen.brush(), s->intOpacity, s->composition_mode);
+ s->stroker = &d->basicStroker;
+ d->basicStroker.setClipRect(d->deviceRect);
+
+ s->brushData.init(d->rasterBuffer.data(), this);
+ s->brushData.setup(s->brush, s->intOpacity, s->composition_mode);
+
+ d->rasterBuffer->compositionMode = QPainter::CompositionMode_SourceOver;
+
+ setDirty(DirtyBrushOrigin);
+
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QRasterPaintEngine::begin(" << (void *) device
+ << ") devType:" << device->devType()
+ << "devRect:" << d->deviceRect;
+ if (d->baseClip) {
+ dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
+ }
+#endif
+
+#if defined(Q_WS_WIN)
+ d->isPlain45DegreeRotation = true;
+#endif
+
+ if (d->mono_surface)
+ d->glyphCacheType = QFontEngineGlyphCache::Raster_Mono;
+#if defined(Q_WS_WIN)
+ else if (qt_cleartype_enabled)
+#elif defined (Q_WS_MAC)
+ else if (qt_applefontsmoothing_enabled)
+#else
+ else if (false)
+#endif
+ {
+ QImage::Format format = static_cast<QImage *>(d->device)->format();
+ if (format == QImage::Format_ARGB32_Premultiplied || format == QImage::Format_RGB32)
+ d->glyphCacheType = QFontEngineGlyphCache::Raster_RGBMask;
+ else
+ d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
+ } else
+ d->glyphCacheType = QFontEngineGlyphCache::Raster_A8;
+
+ setActive(true);
+ return true;
+}
+
+/*!
+ \reimp
+*/
+bool QRasterPaintEngine::end()
+{
+#ifdef QT_DEBUG_DRAW
+ Q_D(QRasterPaintEngine);
+ qDebug() << "QRasterPaintEngine::end devRect:" << d->deviceRect;
+ if (d->baseClip) {
+ dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->baseClip);
+ }
+#endif
+
+ return true;
+}
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::releaseBuffer()
+{
+ Q_D(QRasterPaintEngine);
+ d->rasterBuffer.reset(new QRasterBuffer);
+}
+
+/*!
+ \internal
+*/
+QSize QRasterPaintEngine::size() const
+{
+ Q_D(const QRasterPaintEngine);
+ return QSize(d->rasterBuffer->width(), d->rasterBuffer->height());
+}
+
+/*!
+ \internal
+*/
+#ifndef QT_NO_DEBUG
+void QRasterPaintEngine::saveBuffer(const QString &s) const
+{
+ Q_D(const QRasterPaintEngine);
+ d->rasterBuffer->bufferImage().save(s, "PNG");
+}
+#endif
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::updateMatrix(const QTransform &matrix)
+{
+ QRasterPaintEngineState *s = state();
+ // FALCON: get rid of this line, see drawImage call below.
+ s->matrix = matrix;
+ QTransform::TransformationType txop = s->matrix.type();
+
+ switch (txop) {
+
+ case QTransform::TxNone:
+ s->flags.int_xform = true;
+ break;
+
+ case QTransform::TxTranslate:
+ s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
+ && qreal(int(s->matrix.dy())) == s->matrix.dy();
+ break;
+
+ case QTransform::TxScale:
+ s->flags.int_xform = qreal(int(s->matrix.dx())) == s->matrix.dx()
+ && qreal(int(s->matrix.dy())) == s->matrix.dy()
+ && qreal(int(s->matrix.m11())) == s->matrix.m11()
+ && qreal(int(s->matrix.m22())) == s->matrix.m22();
+ break;
+
+ default: // shear / perspective...
+ s->flags.int_xform = false;
+ break;
+ }
+
+ s->flags.tx_noshear = qt_scaleForTransform(s->matrix, &s->txscale);
+
+ ensureOutlineMapper();
+
+#ifdef Q_WS_WIN
+ Q_D(QRasterPaintEngine);
+ d->isPlain45DegreeRotation = false;
+ if (txop >= QTransform::TxRotate) {
+ d->isPlain45DegreeRotation =
+ (qFuzzyIsNull(matrix.m11())
+ && qFuzzyIsNull(matrix.m12() - qreal(1))
+ && qFuzzyIsNull(matrix.m21() + qreal(1))
+ && qFuzzyIsNull(matrix.m22())
+ )
+ ||
+ (qFuzzyIsNull(matrix.m11() + qreal(1))
+ && qFuzzyIsNull(matrix.m12())
+ && qFuzzyIsNull(matrix.m21())
+ && qFuzzyIsNull(matrix.m22() + qreal(1))
+ )
+ ||
+ (qFuzzyIsNull(matrix.m11())
+ && qFuzzyIsNull(matrix.m12() + qreal(1))
+ && qFuzzyIsNull(matrix.m21() - qreal(1))
+ && qFuzzyIsNull(matrix.m22())
+ )
+ ;
+ }
+#endif
+
+}
+
+
+
+QRasterPaintEngineState::~QRasterPaintEngineState()
+{
+ if (flags.has_clip_ownership)
+ delete clip;
+}
+
+
+QRasterPaintEngineState::QRasterPaintEngineState()
+{
+ stroker = 0;
+
+ fillFlags = 0;
+ strokeFlags = 0;
+ pixmapFlags = 0;
+
+ intOpacity = 256;
+
+ txscale = 1.;
+
+ flags.fast_pen = true;
+ flags.antialiased = false;
+ flags.bilinear = false;
+ flags.fast_text = true;
+ flags.int_xform = true;
+ flags.tx_noshear = true;
+ flags.fast_images = true;
+
+ clip = 0;
+ flags.has_clip_ownership = false;
+
+ dirty = 0;
+}
+
+QRasterPaintEngineState::QRasterPaintEngineState(QRasterPaintEngineState &s)
+ : QPainterState(s)
+ , lastPen(s.lastPen)
+ , penData(s.penData)
+ , stroker(s.stroker)
+ , strokeFlags(s.strokeFlags)
+ , lastBrush(s.lastBrush)
+ , brushData(s.brushData)
+ , fillFlags(s.fillFlags)
+ , pixmapFlags(s.pixmapFlags)
+ , intOpacity(s.intOpacity)
+ , txscale(s.txscale)
+ , clip(s.clip)
+ , dirty(s.dirty)
+ , flag_bits(s.flag_bits)
+{
+ brushData.tempImage = 0;
+ penData.tempImage = 0;
+ flags.has_clip_ownership = false;
+}
+
+/*!
+ \internal
+*/
+QPainterState *QRasterPaintEngine::createState(QPainterState *orig) const
+{
+ QRasterPaintEngineState *s;
+ if (!orig)
+ s = new QRasterPaintEngineState();
+ else
+ s = new QRasterPaintEngineState(*static_cast<QRasterPaintEngineState *>(orig));
+
+ return s;
+}
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::setState(QPainterState *s)
+{
+ Q_D(QRasterPaintEngine);
+ QPaintEngineEx::setState(s);
+ d->rasterBuffer->compositionMode = s->composition_mode;
+}
+
+/*!
+ \fn QRasterPaintEngineState *QRasterPaintEngine::state()
+ \internal
+*/
+
+/*!
+ \fn const QRasterPaintEngineState *QRasterPaintEngine::state() const
+ \internal
+*/
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::penChanged()
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QRasterPaintEngine::penChanged():" << state()->pen;
+#endif
+ QRasterPaintEngineState *s = state();
+ s->strokeFlags |= DirtyPen;
+ s->dirty |= DirtyPen;
+}
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::updatePen(const QPen &pen)
+{
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QRasterPaintEngine::updatePen():" << s->pen;
+#endif
+
+ Qt::PenStyle pen_style = qpen_style(pen);
+
+ s->lastPen = pen;
+ s->strokeFlags = 0;
+
+ s->penData.clip = d->clip();
+ s->penData.setup(pen_style == Qt::NoPen ? QBrush() : pen.brush(), s->intOpacity, s->composition_mode);
+
+ if (s->strokeFlags & QRasterPaintEngine::DirtyTransform
+ || pen.brush().transform().type() >= QTransform::TxNone) {
+ d->updateMatrixData(&s->penData, pen.brush(), s->matrix);
+ }
+
+ // Slightly ugly handling of an uncommon case... We need to change
+ // the pen because it is reused in draw_midpoint to decide dashed
+ // or non-dashed.
+ if (pen_style == Qt::CustomDashLine && pen.dashPattern().size() == 0) {
+ pen_style = Qt::SolidLine;
+ s->lastPen.setStyle(Qt::SolidLine);
+ }
+
+ d->basicStroker.setJoinStyle(qpen_joinStyle(pen));
+ d->basicStroker.setCapStyle(qpen_capStyle(pen));
+ d->basicStroker.setMiterLimit(pen.miterLimit());
+
+ qreal penWidth = qpen_widthf(pen);
+ if (penWidth == 0)
+ d->basicStroker.setStrokeWidth(1);
+ else
+ d->basicStroker.setStrokeWidth(penWidth);
+
+ if(pen_style == Qt::SolidLine) {
+ s->stroker = &d->basicStroker;
+ } else if (pen_style != Qt::NoPen) {
+ if (!d->dashStroker)
+ d->dashStroker.reset(new QDashStroker(&d->basicStroker));
+ if (pen.isCosmetic()) {
+ d->dashStroker->setClipRect(d->deviceRect);
+ } else {
+ // ### I've seen this inverted devrect multiple places now...
+ QRectF clipRect = s->matrix.inverted().mapRect(QRectF(d->deviceRect));
+ d->dashStroker->setClipRect(clipRect);
+ }
+ d->dashStroker->setDashPattern(pen.dashPattern());
+ d->dashStroker->setDashOffset(pen.dashOffset());
+ s->stroker = d->dashStroker.data();
+ } else {
+ s->stroker = 0;
+ }
+
+ s->flags.fast_pen = pen_style > Qt::NoPen
+ && s->penData.blend
+ && !s->flags.antialiased
+ && (penWidth == 0 || (penWidth <= 1
+ && (s->matrix.type() <= QTransform::TxTranslate
+ || pen.isCosmetic())));
+
+ ensureState(); // needed because of tx_noshear...
+ s->flags.non_complex_pen = qpen_capStyle(s->lastPen) <= Qt::SquareCap && s->flags.tx_noshear;
+
+ s->strokeFlags = 0;
+}
+
+
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::brushOriginChanged()
+{
+ QRasterPaintEngineState *s = state();
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QRasterPaintEngine::brushOriginChanged()" << s->brushOrigin;
+#endif
+
+ s->fillFlags |= DirtyBrushOrigin;
+}
+
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::brushChanged()
+{
+ QRasterPaintEngineState *s = state();
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QRasterPaintEngine::brushChanged():" << s->brush;
+#endif
+ s->fillFlags |= DirtyBrush;
+}
+
+
+
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::updateBrush(const QBrush &brush)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QRasterPaintEngine::updateBrush()" << brush;
+#endif
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+ // must set clip prior to setup, as setup uses it...
+ s->brushData.clip = d->clip();
+ s->brushData.setup(brush, s->intOpacity, s->composition_mode);
+ if (s->fillFlags & DirtyTransform
+ || brush.transform().type() >= QTransform::TxNone)
+ d_func()->updateMatrixData(&s->brushData, brush, d->brushMatrix());
+ s->lastBrush = brush;
+ s->fillFlags = 0;
+}
+
+void QRasterPaintEngine::updateOutlineMapper()
+{
+ Q_D(QRasterPaintEngine);
+ d->outlineMapper->setMatrix(state()->matrix);
+}
+
+void QRasterPaintEngine::updateState()
+{
+ QRasterPaintEngineState *s = state();
+
+ if (s->dirty & DirtyTransform)
+ updateMatrix(s->matrix);
+
+ if (s->dirty & (DirtyPen|DirtyCompositionMode|DirtyOpacity)) {
+ const QPainter::CompositionMode mode = s->composition_mode;
+ s->flags.fast_text = (s->penData.type == QSpanData::Solid)
+ && s->intOpacity == 256
+ && (mode == QPainter::CompositionMode_Source
+ || (mode == QPainter::CompositionMode_SourceOver
+ && qAlpha(s->penData.solid.color) == 255));
+ }
+
+ s->dirty = 0;
+}
+
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::opacityChanged()
+{
+ QRasterPaintEngineState *s = state();
+
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QRasterPaintEngine::opacityChanged()" << s->opacity;
+#endif
+
+ s->fillFlags |= DirtyOpacity;
+ s->strokeFlags |= DirtyOpacity;
+ s->pixmapFlags |= DirtyOpacity;
+ s->dirty |= DirtyOpacity;
+ s->intOpacity = (int) (s->opacity * 256);
+}
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::compositionModeChanged()
+{
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QRasterPaintEngine::compositionModeChanged()" << s->composition_mode;
+#endif
+
+ s->fillFlags |= DirtyCompositionMode;
+ s->dirty |= DirtyCompositionMode;
+
+ s->strokeFlags |= DirtyCompositionMode;
+ d->rasterBuffer->compositionMode = s->composition_mode;
+
+ d->recalculateFastImages();
+}
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::renderHintsChanged()
+{
+ QRasterPaintEngineState *s = state();
+
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QRasterPaintEngine::renderHintsChanged()" << hex << s->renderHints;
+#endif
+
+ bool was_aa = s->flags.antialiased;
+ bool was_bilinear = s->flags.bilinear;
+
+ s->flags.antialiased = bool(s->renderHints & QPainter::Antialiasing);
+ s->flags.bilinear = bool(s->renderHints & QPainter::SmoothPixmapTransform);
+
+ if (was_aa != s->flags.antialiased)
+ s->strokeFlags |= DirtyHints;
+
+ if (was_bilinear != s->flags.bilinear) {
+ s->strokeFlags |= DirtyPen;
+ s->fillFlags |= DirtyBrush;
+ }
+
+ Q_D(QRasterPaintEngine);
+ d->recalculateFastImages();
+}
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::transformChanged()
+{
+ QRasterPaintEngineState *s = state();
+
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QRasterPaintEngine::transformChanged()" << s->matrix;
+#endif
+
+ s->fillFlags |= DirtyTransform;
+ s->strokeFlags |= DirtyTransform;
+
+ s->dirty |= DirtyTransform;
+
+ Q_D(QRasterPaintEngine);
+ d->recalculateFastImages();
+}
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::clipEnabledChanged()
+{
+ QRasterPaintEngineState *s = state();
+
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QRasterPaintEngine::clipEnabledChanged()" << s->clipEnabled;
+#endif
+
+ if (s->clip) {
+ s->clip->enabled = s->clipEnabled;
+ s->fillFlags |= DirtyClipEnabled;
+ s->strokeFlags |= DirtyClipEnabled;
+ s->pixmapFlags |= DirtyClipEnabled;
+ }
+}
+
+#ifdef Q_WS_QWS
+void QRasterPaintEnginePrivate::prepare(QCustomRasterPaintDevice *device)
+{
+ rasterBuffer->prepare(device);
+}
+#endif
+
+void QRasterPaintEnginePrivate::drawImage(const QPointF &pt,
+ const QImage &img,
+ SrcOverBlendFunc func,
+ const QRect &clip,
+ int alpha,
+ const QRect &sr)
+{
+ if (alpha == 0 || !clip.isValid())
+ return;
+
+ Q_ASSERT(img.depth() >= 8);
+
+ int srcBPL = img.bytesPerLine();
+ const uchar *srcBits = img.bits();
+ int srcSize = img.depth() >> 3; // This is the part that is incompatible with lower than 8-bit..
+ int iw = img.width();
+ int ih = img.height();
+
+ if (!sr.isEmpty()) {
+ iw = sr.width();
+ ih = sr.height();
+ // Adjust the image according to the source offset...
+ srcBits += ((sr.y() * srcBPL) + sr.x() * srcSize);
+ }
+
+ // adapt the x parameters
+ int x = qRound(pt.x());
+ int cx1 = clip.x();
+ int cx2 = clip.x() + clip.width();
+ if (x < cx1) {
+ int d = cx1 - x;
+ srcBits += srcSize * d;
+ iw -= d;
+ x = cx1;
+ }
+ if (x + iw > cx2) {
+ int d = x + iw - cx2;
+ iw -= d;
+ }
+ if (iw <= 0)
+ return;
+
+ // adapt the y paremeters...
+ int cy1 = clip.y();
+ int cy2 = clip.y() + clip.height();
+ int y = qRound(pt.y());
+ if (y < cy1) {
+ int d = cy1 - y;
+ srcBits += srcBPL * d;
+ ih -= d;
+ y = cy1;
+ }
+ if (y + ih > cy2) {
+ int d = y + ih - cy2;
+ ih -= d;
+ }
+ if (ih <= 0)
+ return;
+
+ // call the blend function...
+ int dstSize = rasterBuffer->bytesPerPixel();
+ int dstBPL = rasterBuffer->bytesPerLine();
+ func(rasterBuffer->buffer() + x * dstSize + y * dstBPL, dstBPL,
+ srcBits, srcBPL,
+ iw, ih,
+ alpha);
+}
+
+
+void QRasterPaintEnginePrivate::systemStateChanged()
+{
+ QRect clipRect(0, 0,
+ qMin(QT_RASTER_COORD_LIMIT, device->width()),
+ qMin(QT_RASTER_COORD_LIMIT, device->height()));
+
+ if (!systemClip.isEmpty()) {
+ QRegion clippedDeviceRgn = systemClip & clipRect;
+ deviceRect = clippedDeviceRgn.boundingRect();
+ baseClip->setClipRegion(clippedDeviceRgn);
+ } else {
+ deviceRect = clipRect;
+ baseClip->setClipRect(deviceRect);
+ }
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "systemStateChanged" << this << "deviceRect" << deviceRect << clipRect << systemClip;
+#endif
+
+ exDeviceRect = deviceRect;
+
+ Q_Q(QRasterPaintEngine);
+ q->state()->strokeFlags |= QPaintEngine::DirtyClipRegion;
+ q->state()->fillFlags |= QPaintEngine::DirtyClipRegion;
+ q->state()->pixmapFlags |= QPaintEngine::DirtyClipRegion;
+}
+
+void QRasterPaintEnginePrivate::updateMatrixData(QSpanData *spanData, const QBrush &b, const QTransform &m)
+{
+ if (b.d->style == Qt::NoBrush || b.d->style == Qt::SolidPattern)
+ return;
+
+ Q_Q(QRasterPaintEngine);
+ bool bilinear = q->state()->flags.bilinear;
+
+ if (b.d->transform.type() > QTransform::TxNone) { // FALCON: optimize
+ spanData->setupMatrix(b.transform() * m, bilinear);
+ } else {
+ if (m.type() <= QTransform::TxTranslate) {
+ // specialize setupMatrix for translation matrices
+ // to avoid needless matrix inversion
+ spanData->m11 = 1;
+ spanData->m12 = 0;
+ spanData->m13 = 0;
+ spanData->m21 = 0;
+ spanData->m22 = 1;
+ spanData->m23 = 0;
+ spanData->m33 = 1;
+ spanData->dx = -m.dx();
+ spanData->dy = -m.dy();
+ spanData->txop = m.type();
+ spanData->bilinear = bilinear;
+ spanData->fast_matrix = qAbs(m.dx()) < 1e4 && qAbs(m.dy()) < 1e4;
+ spanData->adjustSpanMethods();
+ } else {
+ spanData->setupMatrix(m, bilinear);
+ }
+ }
+}
+
+// #define QT_CLIPPING_RATIOS
+
+#ifdef QT_CLIPPING_RATIOS
+int rectClips;
+int regionClips;
+int totalClips;
+
+static void checkClipRatios(QRasterPaintEnginePrivate *d)
+{
+ if (d->clip()->hasRectClip)
+ rectClips++;
+ if (d->clip()->hasRegionClip)
+ regionClips++;
+ totalClips++;
+
+ if ((totalClips % 5000) == 0) {
+ printf("Clipping ratio: rectangular=%f%%, region=%f%%, complex=%f%%\n",
+ rectClips * 100.0 / (qreal) totalClips,
+ regionClips * 100.0 / (qreal) totalClips,
+ (totalClips - rectClips - regionClips) * 100.0 / (qreal) totalClips);
+ totalClips = 0;
+ rectClips = 0;
+ regionClips = 0;
+ }
+
+}
+#endif
+
+static void qrasterpaintengine_state_setNoClip(QRasterPaintEngineState *s)
+{
+ if (s->flags.has_clip_ownership)
+ delete s->clip;
+ s->clip = 0;
+ s->flags.has_clip_ownership = false;
+}
+
+static void qrasterpaintengine_dirty_clip(QRasterPaintEnginePrivate *d, QRasterPaintEngineState *s)
+{
+ s->fillFlags |= QPaintEngine::DirtyClipPath;
+ s->strokeFlags |= QPaintEngine::DirtyClipPath;
+ s->pixmapFlags |= QPaintEngine::DirtyClipPath;
+
+ d->solid_color_filler.clip = d->clip();
+ d->solid_color_filler.adjustSpanMethods();
+
+#ifdef QT_DEBUG_DRAW
+ dumpClip(d->rasterBuffer->width(), d->rasterBuffer->height(), &*d->clip());
+#endif
+
+}
+
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QRasterPaintEngine::clip(): " << path << op;
+
+ if (path.elements()) {
+ for (int i=0; i<path.elementCount(); ++i) {
+ qDebug() << " - " << path.elements()[i]
+ << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
+ }
+ } else {
+ for (int i=0; i<path.elementCount(); ++i) {
+ qDebug() << " ---- "
+ << '(' << path.points()[i*2] << ", " << path.points()[i*2+1] << ')';
+ }
+ }
+#endif
+
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ const qreal *points = path.points();
+ const QPainterPath::ElementType *types = path.elements();
+
+ // There are some cases that are not supported by clip(QRect)
+ if (op != Qt::UniteClip && (op != Qt::IntersectClip || !s->clip
+ || s->clip->hasRectClip || s->clip->hasRegionClip)) {
+ if (s->matrix.type() <= QTransform::TxScale
+ && ((path.shape() == QVectorPath::RectangleHint)
+ || (isRect(points, path.elementCount())
+ && (!types || (types[0] == QPainterPath::MoveToElement
+ && types[1] == QPainterPath::LineToElement
+ && types[2] == QPainterPath::LineToElement
+ && types[3] == QPainterPath::LineToElement))))) {
+#ifdef QT_DEBUG_DRAW
+ qDebug() << " --- optimizing vector clip to rect clip...";
+#endif
+
+ QRectF r(points[0], points[1], points[4]-points[0], points[5]-points[1]);
+ if (setClipRectInDeviceCoords(s->matrix.mapRect(r).toRect(), op))
+ return;
+ }
+ }
+
+ if (op == Qt::NoClip) {
+ qrasterpaintengine_state_setNoClip(s);
+
+ } else {
+ QClipData *base = d->baseClip.data();
+
+ // Intersect with current clip when available...
+ if (op == Qt::IntersectClip && s->clip)
+ base = s->clip;
+
+ // We always intersect, except when there is nothing to
+ // intersect with, in which case we simplify the operation to
+ // a replace...
+ Qt::ClipOperation isectOp = Qt::IntersectClip;
+ if (base == 0)
+ isectOp = Qt::ReplaceClip;
+
+ QClipData *newClip = new QClipData(d->rasterBuffer->height());
+ newClip->initialize();
+ ClipData clipData = { base, newClip, isectOp };
+ ensureOutlineMapper();
+ d->rasterize(d->outlineMapper->convertPath(path), qt_span_clip, &clipData, 0);
+
+ newClip->fixup();
+
+ if (op == Qt::UniteClip) {
+ // merge clips
+ QClipData *result = new QClipData(d->rasterBuffer->height());
+ QClipData *current = s->clip ? s->clip : new QClipData(d->rasterBuffer->height());
+ qt_merge_clip(current, newClip, result);
+ result->fixup();
+ delete newClip;
+ if (!s->clip)
+ delete current;
+ newClip = result;
+ }
+
+ if (s->flags.has_clip_ownership)
+ delete s->clip;
+
+ s->clip = newClip;
+ s->flags.has_clip_ownership = true;
+ }
+ qrasterpaintengine_dirty_clip(d, s);
+}
+
+
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QRasterPaintEngine::clip(): " << rect << op;
+#endif
+
+ QRasterPaintEngineState *s = state();
+
+ if (op == Qt::NoClip) {
+ qrasterpaintengine_state_setNoClip(s);
+
+ } else if (op == Qt::UniteClip || s->matrix.type() > QTransform::TxScale) {
+ QPaintEngineEx::clip(rect, op);
+ return;
+
+ } else if (!setClipRectInDeviceCoords(s->matrix.mapRect(rect), op)) {
+ QPaintEngineEx::clip(rect, op);
+ return;
+ }
+}
+
+
+bool QRasterPaintEngine::setClipRectInDeviceCoords(const QRect &r, Qt::ClipOperation op)
+{
+ Q_D(QRasterPaintEngine);
+ QRect clipRect = r & d->deviceRect;
+ QRasterPaintEngineState *s = state();
+
+ if (op == Qt::ReplaceClip || s->clip == 0) {
+
+ // No current clip, hence we intersect with sysclip and be
+ // done with it...
+ QRegion clipRegion = systemClip();
+ QClipData *clip = new QClipData(d->rasterBuffer->height());
+
+ if (clipRegion.isEmpty())
+ clip->setClipRect(clipRect);
+ else
+ clip->setClipRegion(clipRegion & clipRect);
+
+ if (s->flags.has_clip_ownership)
+ delete s->clip;
+
+ s->clip = clip;
+ s->clip->enabled = true;
+ s->flags.has_clip_ownership = true;
+
+ } else if (op == Qt::IntersectClip){ // intersect clip with current clip
+ QClipData *base = s->clip;
+
+ Q_ASSERT(base);
+ if (base->hasRectClip || base->hasRegionClip) {
+ if (!s->flags.has_clip_ownership) {
+ s->clip = new QClipData(d->rasterBuffer->height());
+ s->flags.has_clip_ownership = true;
+ }
+ if (base->hasRectClip)
+ s->clip->setClipRect(base->clipRect & clipRect);
+ else
+ s->clip->setClipRegion(base->clipRegion & clipRect);
+ s->clip->enabled = true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+
+ qrasterpaintengine_dirty_clip(d, s);
+ return true;
+}
+
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::clip(const QRegion &region, Qt::ClipOperation op)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QRasterPaintEngine::clip(): " << region << op;
+#endif
+
+ Q_D(QRasterPaintEngine);
+
+ if (region.rectCount() == 1) {
+ clip(region.boundingRect(), op);
+ return;
+ }
+
+ QRasterPaintEngineState *s = state();
+ const QClipData *clip = d->clip();
+ const QClipData *baseClip = d->baseClip.data();
+
+ if (op == Qt::NoClip) {
+ qrasterpaintengine_state_setNoClip(s);
+ } else if (s->matrix.type() > QTransform::TxScale
+ || op == Qt::UniteClip
+ || (op == Qt::IntersectClip && !clip->hasRectClip && !clip->hasRegionClip)
+ || (op == Qt::ReplaceClip && !baseClip->hasRectClip && !baseClip->hasRegionClip)) {
+ QPaintEngineEx::clip(region, op);
+ } else {
+ const QClipData *curClip;
+ QClipData *newClip;
+
+ if (op == Qt::IntersectClip)
+ curClip = clip;
+ else
+ curClip = baseClip;
+
+ if (s->flags.has_clip_ownership) {
+ newClip = s->clip;
+ Q_ASSERT(newClip);
+ } else {
+ newClip = new QClipData(d->rasterBuffer->height());
+ s->clip = newClip;
+ s->flags.has_clip_ownership = true;
+ }
+
+ QRegion r = s->matrix.map(region);
+ if (curClip->hasRectClip)
+ newClip->setClipRegion(r & curClip->clipRect);
+ else if (curClip->hasRegionClip)
+ newClip->setClipRegion(r & curClip->clipRegion);
+
+ qrasterpaintengine_dirty_clip(d, s);
+ }
+}
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::fillPath(const QPainterPath &path, QSpanData *fillData)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << " --- fillPath, bounds=" << path.boundingRect();
+#endif
+
+ if (!fillData->blend)
+ return;
+
+ Q_D(QRasterPaintEngine);
+
+ const QRectF controlPointRect = path.controlPointRect();
+
+ QRasterPaintEngineState *s = state();
+ const QRect deviceRect = s->matrix.mapRect(controlPointRect).toRect();
+ ProcessSpans blend = d->getBrushFunc(deviceRect, fillData);
+ const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
+ || deviceRect.right() > QT_RASTER_COORD_LIMIT
+ || deviceRect.top() < -QT_RASTER_COORD_LIMIT
+ || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
+
+ if (!s->flags.antialiased && !do_clip) {
+ d->initializeRasterizer(fillData);
+ d->rasterizer->rasterize(path * s->matrix, path.fillRule());
+ return;
+ }
+
+ ensureOutlineMapper();
+ d->rasterize(d->outlineMapper->convertPath(path), blend, fillData, d->rasterBuffer.data());
+}
+
+static void fillRect_normalized(const QRect &r, QSpanData *data,
+ QRasterPaintEnginePrivate *pe)
+{
+ int x1, x2, y1, y2;
+
+ bool rectClipped = true;
+
+ if (data->clip) {
+ x1 = qMax(r.x(), data->clip->xmin);
+ x2 = qMin(r.x() + r.width(), data->clip->xmax);
+ y1 = qMax(r.y(), data->clip->ymin);
+ y2 = qMin(r.y() + r.height(), data->clip->ymax);
+ rectClipped = data->clip->hasRectClip;
+
+ } else if (pe) {
+ x1 = qMax(r.x(), pe->deviceRect.x());
+ x2 = qMin(r.x() + r.width(), pe->deviceRect.x() + pe->deviceRect.width());
+ y1 = qMax(r.y(), pe->deviceRect.y());
+ y2 = qMin(r.y() + r.height(), pe->deviceRect.y() + pe->deviceRect.height());
+ } else {
+ x1 = qMax(r.x(), 0);
+ x2 = qMin(r.x() + r.width(), data->rasterBuffer->width());
+ y1 = qMax(r.y(), 0);
+ y2 = qMin(r.y() + r.height(), data->rasterBuffer->height());
+ }
+
+ if (x2 <= x1 || y2 <= y1)
+ return;
+
+ const int width = x2 - x1;
+ const int height = y2 - y1;
+
+ bool isUnclipped = rectClipped
+ || (pe && pe->isUnclipped_normalized(QRect(x1, y1, width, height)));
+
+ if (pe && isUnclipped) {
+ const QPainter::CompositionMode mode = pe->rasterBuffer->compositionMode;
+
+ if (data->fillRect && (mode == QPainter::CompositionMode_Source
+ || (mode == QPainter::CompositionMode_SourceOver
+ && qAlpha(data->solid.color) == 255)))
+ {
+ data->fillRect(data->rasterBuffer, x1, y1, width, height,
+ data->solid.color);
+ return;
+ }
+ }
+
+ ProcessSpans blend = isUnclipped ? data->unclipped_blend : data->blend;
+
+ const int nspans = 256;
+ QT_FT_Span spans[nspans];
+
+ Q_ASSERT(data->blend);
+ int y = y1;
+ while (y < y2) {
+ int n = qMin(nspans, y2 - y);
+ int i = 0;
+ while (i < n) {
+ spans[i].x = x1;
+ spans[i].len = width;
+ spans[i].y = y + i;
+ spans[i].coverage = 255;
+ ++i;
+ }
+
+ blend(n, spans, data);
+ y += n;
+ }
+}
+
+/*!
+ \reimp
+*/
+void QRasterPaintEngine::drawRects(const QRect *rects, int rectCount)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug(" - QRasterPaintEngine::drawRect(), rectCount=%d", rectCount);
+#endif
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ // Fill
+ ensureBrush();
+ if (s->brushData.blend) {
+ if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxTranslate) {
+ const QRect *r = rects;
+ const QRect *lastRect = rects + rectCount;
+
+ int offset_x = int(s->matrix.dx());
+ int offset_y = int(s->matrix.dy());
+ while (r < lastRect) {
+ QRect rect = r->normalized();
+ QRect rr = rect.translated(offset_x, offset_y);
+ fillRect_normalized(rr, &s->brushData, d);
+ ++r;
+ }
+ } else {
+ QRectVectorPath path;
+ for (int i=0; i<rectCount; ++i) {
+ path.set(rects[i]);
+ fill(path, s->brush);
+ }
+ }
+ }
+
+ ensurePen();
+ if (s->penData.blend) {
+ if (s->flags.fast_pen && s->lastPen.brush().isOpaque()) {
+ const QRect *r = rects;
+ const QRect *lastRect = rects + rectCount;
+ while (r < lastRect) {
+ int left = r->x();
+ int right = r->x() + r->width();
+ int top = r->y();
+ int bottom = r->y() + r->height();
+
+#ifdef Q_WS_MAC
+ int pts[] = { top, left,
+ top, right,
+ bottom, right,
+ bottom, left };
+#else
+ int pts[] = { left, top,
+ right, top,
+ right, bottom,
+ left, bottom };
+#endif
+
+ strokePolygonCosmetic((QPoint *) pts, 4, WindingMode);
+ ++r;
+ }
+ } else {
+ QRectVectorPath path;
+ for (int i = 0; i < rectCount; ++i) {
+ path.set(rects[i]);
+ stroke(path, s->pen);
+ }
+ }
+ }
+}
+
+/*!
+ \reimp
+*/
+void QRasterPaintEngine::drawRects(const QRectF *rects, int rectCount)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug(" - QRasterPaintEngine::drawRect(), rectCount=%d", rectCount);
+#endif
+#ifdef QT_FAST_SPANS
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ ensureState();
+
+ if (s->flags.tx_noshear) {
+ ensureBrush();
+ if (s->brushData.blend) {
+ d->initializeRasterizer(&s->brushData);
+ for (int i = 0; i < rectCount; ++i) {
+ const QRectF &rect = rects[i].normalized();
+ if (rect.isEmpty())
+ continue;
+ const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
+ const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
+ d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
+ }
+ }
+
+ ensurePen();
+ if (s->penData.blend) {
+ qreal width = s->pen.isCosmetic()
+ ? (s->lastPen.widthF() == 0 ? 1 : s->lastPen.widthF())
+ : s->lastPen.widthF() * s->txscale;
+
+ if (s->flags.fast_pen && s->lastPen.brush().isOpaque()) {
+ for (int i = 0; i < rectCount; ++i) {
+ const QRectF &r = rects[i];
+ qreal left = r.x();
+ qreal right = r.x() + r.width();
+ qreal top = r.y();
+ qreal bottom = r.y() + r.height();
+ qreal pts[] = { left, top,
+ right, top,
+ right, bottom,
+ left, bottom };
+ strokePolygonCosmetic((QPointF *) pts, 4, WindingMode);
+ }
+ } else if (width <= 1 && qpen_style(s->lastPen) == Qt::SolidLine) {
+ d->initializeRasterizer(&s->penData);
+
+ for (int i = 0; i < rectCount; ++i) {
+ const QRectF &rect = rects[i].normalized();
+ if (rect.isEmpty()) {
+ qreal pts[] = { rect.left(), rect.top(), rect.right(), rect.bottom() };
+ QVectorPath vp(pts, 2, 0, QVectorPath::LinesHint);
+ QPaintEngineEx::stroke(vp, s->lastPen);
+ } else {
+ const QPointF tl = s->matrix.map(rect.topLeft());
+ const QPointF tr = s->matrix.map(rect.topRight());
+ const QPointF bl = s->matrix.map(rect.bottomLeft());
+ const QPointF br = s->matrix.map(rect.bottomRight());
+ const qreal w = width / (rect.width() * s->txscale);
+ const qreal h = width / (rect.height() * s->txscale);
+ d->rasterizer->rasterizeLine(tl, tr, w); // top
+ d->rasterizer->rasterizeLine(bl, br, w); // bottom
+ d->rasterizer->rasterizeLine(bl, tl, h); // left
+ d->rasterizer->rasterizeLine(br, tr, h); // right
+ }
+ }
+ } else {
+ for (int i = 0; i < rectCount; ++i) {
+ const QRectF &r = rects[i];
+ qreal left = r.x();
+ qreal right = r.x() + r.width();
+ qreal top = r.y();
+ qreal bottom = r.y() + r.height();
+ qreal pts[] = { left, top,
+ right, top,
+ right, bottom,
+ left, bottom,
+ left, top };
+ QVectorPath vp(pts, 5, 0, QVectorPath::RectangleHint);
+ QPaintEngineEx::stroke(vp, s->lastPen);
+ }
+ }
+ }
+
+ return;
+ }
+#endif // QT_FAST_SPANS
+ QPaintEngineEx::drawRects(rects, rectCount);
+}
+
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::stroke(const QVectorPath &path, const QPen &pen)
+{
+ QRasterPaintEngineState *s = state();
+ ensurePen(pen);
+ if (!s->penData.blend)
+ return;
+
+ if (s->flags.fast_pen && !path.isCurved()
+ && s->lastPen.brush().isOpaque()) {
+ int count = path.elementCount();
+ QPointF *points = (QPointF *) path.points();
+ const QPainterPath::ElementType *types = path.elements();
+ if (types) {
+ int first = 0;
+ int last;
+ while (first < count) {
+ while (first < count && types[first] != QPainterPath::MoveToElement) ++first;
+ last = first + 1;
+ while (last < count && types[last] == QPainterPath::LineToElement) ++last;
+ strokePolygonCosmetic(points + first, last - first,
+ path.hasImplicitClose() && last == count // only close last one..
+ ? WindingMode
+ : PolylineMode);
+ first = last;
+ }
+ } else {
+ strokePolygonCosmetic(points, count,
+ path.hasImplicitClose()
+ ? WindingMode
+ : PolylineMode);
+ }
+
+ } else if (s->flags.non_complex_pen && path.shape() == QVectorPath::LinesHint) {
+ qreal width = s->lastPen.isCosmetic()
+ ? (qpen_widthf(s->lastPen) == 0 ? 1 : qpen_widthf(s->lastPen))
+ : qpen_widthf(s->lastPen) * s->txscale;
+ int dashIndex = 0;
+ qreal dashOffset = s->lastPen.dashOffset();
+ bool inDash = true;
+ qreal patternLength = 0;
+ const QVector<qreal> pattern = s->lastPen.dashPattern();
+ for (int i = 0; i < pattern.size(); ++i)
+ patternLength += pattern.at(i);
+
+ if (patternLength > 0) {
+ int n = qFloor(dashOffset / patternLength);
+ dashOffset -= n * patternLength;
+ while (dashOffset >= pattern.at(dashIndex)) {
+ dashOffset -= pattern.at(dashIndex);
+ if (++dashIndex >= pattern.size())
+ dashIndex = 0;
+ inDash = !inDash;
+ }
+ }
+
+ Q_D(QRasterPaintEngine);
+ d->initializeRasterizer(&s->penData);
+ int lineCount = path.elementCount() / 2;
+ const QLineF *lines = reinterpret_cast<const QLineF *>(path.points());
+
+ for (int i = 0; i < lineCount; ++i) {
+ if (lines[i].p1() == lines[i].p2()) {
+ if (s->lastPen.capStyle() != Qt::FlatCap) {
+ QPointF p = lines[i].p1();
+ QLineF line = s->matrix.map(QLineF(QPointF(p.x() - width*0.5, p.y()),
+ QPointF(p.x() + width*0.5, p.y())));
+ d->rasterizer->rasterizeLine(line.p1(), line.p2(), 1);
+ }
+ continue;
+ }
+
+ const QLineF line = s->matrix.map(lines[i]);
+ if (qpen_style(s->lastPen) == Qt::SolidLine) {
+ d->rasterizer->rasterizeLine(line.p1(), line.p2(),
+ width / line.length(),
+ s->lastPen.capStyle() == Qt::SquareCap);
+ } else {
+ d->rasterizeLine_dashed(line, width,
+ &dashIndex, &dashOffset, &inDash);
+ }
+ }
+ }
+ else
+ QPaintEngineEx::stroke(path, pen);
+}
+
+static inline QRect toNormalizedFillRect(const QRectF &rect)
+{
+ int x1 = qRound(rect.x());
+ int y1 = qRound(rect.y());
+ int x2 = qRound(rect.right());
+ int y2 = qRound(rect.bottom());
+
+ if (x2 < x1)
+ qSwap(x1, x2);
+ if (y2 < y1)
+ qSwap(y1, y2);
+
+ return QRect(x1, y1, x2 - x1, y2 - y1);
+}
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::fill(const QVectorPath &path, const QBrush &brush)
+{
+ if (path.isEmpty())
+ return;
+#ifdef QT_DEBUG_DRAW
+ QRectF rf = path.controlPointRect();
+ qDebug() << "QRasterPaintEngine::fill(): "
+ << "size=" << path.elementCount()
+ << ", hints=" << hex << path.hints()
+ << rf << brush;
+#endif
+
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ ensureBrush(brush);
+ if (!s->brushData.blend)
+ return;
+
+ if (path.shape() == QVectorPath::RectangleHint) {
+ if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxScale) {
+ const qreal *p = path.points();
+ QPointF tl = QPointF(p[0], p[1]) * s->matrix;
+ QPointF br = QPointF(p[4], p[5]) * s->matrix;
+ fillRect_normalized(toNormalizedFillRect(QRectF(tl, br)), &s->brushData, d);
+ return;
+ }
+ ensureState();
+ if (s->flags.tx_noshear) {
+ d->initializeRasterizer(&s->brushData);
+ // ### Is normalizing really necessary here?
+ const qreal *p = path.points();
+ QRectF r = QRectF(p[0], p[1], p[2] - p[0], p[7] - p[1]).normalized();
+ if (!r.isEmpty()) {
+ const QPointF a = s->matrix.map((r.topLeft() + r.bottomLeft()) * 0.5f);
+ const QPointF b = s->matrix.map((r.topRight() + r.bottomRight()) * 0.5f);
+ d->rasterizer->rasterizeLine(a, b, r.height() / r.width());
+ }
+ return;
+ }
+ }
+
+ if (path.shape() == QVectorPath::EllipseHint) {
+ if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxScale) {
+ const qreal *p = path.points();
+ QPointF tl = QPointF(p[0], p[1]) * s->matrix;
+ QPointF br = QPointF(p[4], p[5]) * s->matrix;
+ QRectF r = s->matrix.mapRect(QRectF(tl, br));
+
+ ProcessSpans penBlend = d->getPenFunc(r, &s->penData);
+ ProcessSpans brushBlend = d->getBrushFunc(r, &s->brushData);
+ const QRect brect = QRect(int(r.x()), int(r.y()),
+ int_dim(r.x(), r.width()),
+ int_dim(r.y(), r.height()));
+ if (brect == r) {
+ drawEllipse_midpoint_i(brect, d->deviceRect, penBlend, brushBlend,
+ &s->penData, &s->brushData);
+ return;
+ }
+ }
+ }
+
+ // ### Optimize for non transformed ellipses and rectangles...
+ QRectF cpRect = path.controlPointRect();
+ const QRect deviceRect = s->matrix.mapRect(cpRect).toRect();
+ ProcessSpans blend = d->getBrushFunc(deviceRect, &s->brushData);
+
+ // ### Falcon
+// const bool do_clip = (deviceRect.left() < -QT_RASTER_COORD_LIMIT
+// || deviceRect.right() > QT_RASTER_COORD_LIMIT
+// || deviceRect.top() < -QT_RASTER_COORD_LIMIT
+// || deviceRect.bottom() > QT_RASTER_COORD_LIMIT);
+
+ // ### Falonc: implement....
+// if (!s->flags.antialiased && !do_clip) {
+// d->initializeRasterizer(&s->brushData);
+// d->rasterizer->rasterize(path * d->matrix, path.fillRule());
+// return;
+// }
+
+ ensureOutlineMapper();
+ d->rasterize(d->outlineMapper->convertPath(path), blend, &s->brushData, d->rasterBuffer.data());
+}
+
+void QRasterPaintEngine::fillRect(const QRectF &r, QSpanData *data)
+{
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ if (!s->flags.antialiased) {
+ uint txop = s->matrix.type();
+ if (txop == QTransform::TxNone) {
+ fillRect_normalized(toNormalizedFillRect(r), data, d);
+ return;
+ } else if (txop == QTransform::TxTranslate) {
+ const QRect rr = toNormalizedFillRect(r.translated(s->matrix.dx(), s->matrix.dy()));
+ fillRect_normalized(rr, data, d);
+ return;
+ } else if (txop == QTransform::TxScale) {
+ const QRect rr = toNormalizedFillRect(s->matrix.mapRect(r));
+ fillRect_normalized(rr, data, d);
+ return;
+ }
+ }
+ ensureState();
+ if (s->flags.tx_noshear) {
+ d->initializeRasterizer(data);
+ QRectF nr = r.normalized();
+ if (!nr.isEmpty()) {
+ const QPointF a = s->matrix.map((nr.topLeft() + nr.bottomLeft()) * 0.5f);
+ const QPointF b = s->matrix.map((nr.topRight() + nr.bottomRight()) * 0.5f);
+ d->rasterizer->rasterizeLine(a, b, nr.height() / nr.width());
+ }
+ return;
+ }
+
+ QPainterPath path;
+ path.addRect(r);
+ ensureOutlineMapper();
+ fillPath(path, data);
+}
+
+/*!
+ \reimp
+*/
+void QRasterPaintEngine::fillRect(const QRectF &r, const QBrush &brush)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QRasterPaintEngine::fillRecct(): " << r << brush;
+#endif
+ QRasterPaintEngineState *s = state();
+
+ ensureBrush(brush);
+ if (!s->brushData.blend)
+ return;
+
+ fillRect(r, &s->brushData);
+}
+
+/*!
+ \reimp
+*/
+void QRasterPaintEngine::fillRect(const QRectF &r, const QColor &color)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QRasterPaintEngine::fillRect(): " << r << color;
+#endif
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color.rgba(), s->intOpacity));
+ if ((d->solid_color_filler.solid.color & 0xff000000) == 0
+ && s->composition_mode == QPainter::CompositionMode_SourceOver) {
+ return;
+ }
+ d->solid_color_filler.clip = d->clip();
+ d->solid_color_filler.adjustSpanMethods();
+ fillRect(r, &d->solid_color_filler);
+}
+
+static inline bool isAbove(const QPointF *a, const QPointF *b)
+{
+ return a->y() < b->y();
+}
+
+static bool splitPolygon(const QPointF *points, int pointCount, QVector<QPointF> *upper, QVector<QPointF> *lower)
+{
+ Q_ASSERT(upper);
+ Q_ASSERT(lower);
+
+ Q_ASSERT(pointCount >= 2);
+
+ QVector<const QPointF *> sorted;
+ sorted.reserve(pointCount);
+
+ upper->reserve(pointCount * 3 / 4);
+ lower->reserve(pointCount * 3 / 4);
+
+ for (int i = 0; i < pointCount; ++i)
+ sorted << points + i;
+
+ qSort(sorted.begin(), sorted.end(), isAbove);
+
+ qreal splitY = sorted.at(sorted.size() / 2)->y();
+
+ const QPointF *end = points + pointCount;
+ const QPointF *last = end - 1;
+
+ QVector<QPointF> *bin[2] = { upper, lower };
+
+ for (const QPointF *p = points; p < end; ++p) {
+ int side = p->y() < splitY;
+ int lastSide = last->y() < splitY;
+
+ if (side != lastSide) {
+ if (qFuzzyCompare(p->y(), splitY)) {
+ bin[!side]->append(*p);
+ } else if (qFuzzyCompare(last->y(), splitY)) {
+ bin[side]->append(*last);
+ } else {
+ QPointF delta = *p - *last;
+ QPointF intersection(p->x() + delta.x() * (splitY - p->y()) / delta.y(), splitY);
+
+ bin[0]->append(intersection);
+ bin[1]->append(intersection);
+ }
+ }
+
+ bin[side]->append(*p);
+
+ last = p;
+ }
+
+ // give up if we couldn't reduce the point count
+ return upper->size() < pointCount && lower->size() < pointCount;
+}
+
+/*!
+ \internal
+ */
+void QRasterPaintEngine::fillPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
+{
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ const int maxPoints = 0xffff;
+
+ // max amount of points that raster engine can reliably handle
+ if (pointCount > maxPoints) {
+ QVector<QPointF> upper, lower;
+
+ if (splitPolygon(points, pointCount, &upper, &lower)) {
+ fillPolygon(upper.constData(), upper.size(), mode);
+ fillPolygon(lower.constData(), lower.size(), mode);
+ } else
+ qWarning("Polygon too complex for filling.");
+
+ return;
+ }
+
+ // Compose polygon fill..,
+ QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
+ ensureOutlineMapper();
+ QT_FT_Outline *outline = d->outlineMapper->convertPath(vp);
+
+ // scanconvert.
+ ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
+ &s->brushData);
+ d->rasterize(outline, brushBlend, &s->brushData, d->rasterBuffer.data());
+}
+
+/*!
+ \reimp
+*/
+void QRasterPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
+{
+ QRasterPaintEngineState *s = state();
+
+#ifdef QT_DEBUG_DRAW
+ qDebug(" - QRasterPaintEngine::drawPolygon(F), pointCount=%d", pointCount);
+ for (int i=0; i<pointCount; ++i)
+ qDebug() << " - " << points[i];
+#endif
+ Q_ASSERT(pointCount >= 2);
+
+ if (mode != PolylineMode && isRect((qreal *) points, pointCount)) {
+ QRectF r(points[0], points[2]);
+ drawRects(&r, 1);
+ return;
+ }
+
+ ensurePen();
+ ensureBrush();
+ if (mode != PolylineMode) {
+ // Do the fill...
+ if (s->brushData.blend) {
+ fillPolygon(points, pointCount, mode);
+ }
+ }
+
+ // Do the outline...
+ if (s->penData.blend) {
+ if (s->flags.fast_pen && s->lastPen.brush().isOpaque())
+ strokePolygonCosmetic(points, pointCount, mode);
+ else {
+ QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
+ QPaintEngineEx::stroke(vp, s->lastPen);
+ }
+ }
+}
+
+/*!
+ \reimp
+*/
+void QRasterPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
+{
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+#ifdef QT_DEBUG_DRAW
+ qDebug(" - QRasterPaintEngine::drawPolygon(I), pointCount=%d", pointCount);
+ for (int i=0; i<pointCount; ++i)
+ qDebug() << " - " << points[i];
+#endif
+ Q_ASSERT(pointCount >= 2);
+ if (mode != PolylineMode && isRect((int *) points, pointCount)) {
+ QRect r(points[0].x(),
+ points[0].y(),
+ points[2].x() - points[0].x(),
+ points[2].y() - points[0].y());
+ drawRects(&r, 1);
+ return;
+ }
+
+ ensureState();
+ ensurePen();
+ if (!(s->flags.int_xform && s->flags.fast_pen && (!s->penData.blend || s->pen.brush().isOpaque()))) {
+ // this calls the float version
+ QPaintEngineEx::drawPolygon(points, pointCount, mode);
+ return;
+ }
+
+ // Do the fill
+ if (mode != PolylineMode) {
+ ensureBrush();
+ if (s->brushData.blend) {
+ // Compose polygon fill..,
+ ensureOutlineMapper();
+ d->outlineMapper->beginOutline(mode == WindingMode ? Qt::WindingFill : Qt::OddEvenFill);
+ d->outlineMapper->moveTo(*points);
+ const QPoint *p = points;
+ const QPoint *ep = points + pointCount - 1;
+ do {
+ d->outlineMapper->lineTo(*(++p));
+ } while (p < ep);
+ d->outlineMapper->endOutline();
+
+ // scanconvert.
+ ProcessSpans brushBlend = d->getBrushFunc(d->outlineMapper->controlPointRect,
+ &s->brushData);
+ d->rasterize(d->outlineMapper->outline(), brushBlend, &s->brushData, d->rasterBuffer.data());
+ }
+ }
+
+ // Do the outline...
+ if (s->penData.blend) {
+ if (s->flags.fast_pen && s->lastPen.brush().isOpaque())
+ strokePolygonCosmetic(points, pointCount, mode);
+ else {
+ int count = pointCount * 2;
+ QVarLengthArray<qreal> fpoints(count);
+#ifdef Q_WS_MAC
+ for (int i=0; i<count; i+=2) {
+ fpoints[i] = ((int *) points)[i+1];
+ fpoints[i+1] = ((int *) points)[i];
+ }
+#else
+ for (int i=0; i<count; ++i)
+ fpoints[i] = ((int *) points)[i];
+#endif
+ QVectorPath vp((qreal *) fpoints.data(), pointCount, 0, QVectorPath::polygonFlags(mode));
+ QPaintEngineEx::stroke(vp, s->lastPen);
+ }
+ }
+}
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::strokePolygonCosmetic(const QPointF *points, int pointCount, PolygonDrawMode mode)
+{
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ Q_ASSERT(s->penData.blend);
+ Q_ASSERT(s->flags.fast_pen);
+
+ bool needs_closing = mode != PolylineMode && points[0] != points[pointCount-1];
+
+ // Use fast path for 0 width / trivial pens.
+ QIntRect devRect;
+ devRect.set(d->deviceRect);
+
+ LineDrawMode mode_for_last = (s->lastPen.capStyle() != Qt::FlatCap
+ ? LineDrawIncludeLastPixel
+ : LineDrawNormal);
+ int dashOffset = int(s->lastPen.dashOffset());
+
+ // Draw all the line segments.
+ for (int i=1; i<pointCount; ++i) {
+
+ QPointF lp1 = points[i-1] * s->matrix;
+ QPointF lp2 = points[i] * s->matrix;
+
+ const QRectF brect(lp1, lp2);
+ ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
+ if (qpen_style(s->lastPen) == Qt::SolidLine) {
+ drawLine_midpoint_i(qFloor(lp1.x()), qFloor(lp1.y()),
+ qFloor(lp2.x()), qFloor(lp2.y()),
+ penBlend, &s->penData,
+ i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel,
+ devRect);
+ } else {
+ drawLine_midpoint_dashed_i(qFloor(lp1.x()), qFloor(lp1.y()),
+ qFloor(lp2.x()), qFloor(lp2.y()),
+ &s->lastPen,
+ penBlend, &s->penData,
+ i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel,
+ devRect, &dashOffset);
+ }
+ }
+
+ // Polygons are implicitly closed.
+ if (needs_closing) {
+ QPointF lp1 = points[pointCount-1] * s->matrix;
+ QPointF lp2 = points[0] * s->matrix;
+
+ const QRectF brect(lp1, lp2);
+ ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
+ if (qpen_style(s->lastPen) == Qt::SolidLine) {
+ drawLine_midpoint_i(qFloor(lp1.x()), qFloor(lp1.y()),
+ qFloor(lp2.x()), qFloor(lp2.y()),
+ penBlend, &s->penData,
+ LineDrawIncludeLastPixel,
+ devRect);
+ } else {
+ drawLine_midpoint_dashed_i(qFloor(lp1.x()), qFloor(lp1.y()),
+ qFloor(lp2.x()), qFloor(lp2.y()),
+ &s->lastPen,
+ penBlend, &s->penData,
+ LineDrawIncludeLastPixel,
+ devRect, &dashOffset);
+ }
+ }
+
+}
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::strokePolygonCosmetic(const QPoint *points, int pointCount, PolygonDrawMode mode)
+{
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ // We assert here because this function is called from drawRects
+ // and drawPolygon and they already do ensurePen(), so we skip that
+ // here to avoid duplicate checks..
+ Q_ASSERT(s->penData.blend);
+
+ bool needs_closing = mode != PolylineMode && points[0] != points[pointCount-1];
+
+ QIntRect devRect;
+ devRect.set(d->deviceRect);
+
+ LineDrawMode mode_for_last = (s->lastPen.capStyle() != Qt::FlatCap
+ ? LineDrawIncludeLastPixel
+ : LineDrawNormal);
+
+ int m11 = int(s->matrix.m11());
+ int m22 = int(s->matrix.m22());
+ int dx = int(s->matrix.dx());
+ int dy = int(s->matrix.dy());
+ int m13 = int(s->matrix.m13());
+ int m23 = int(s->matrix.m23());
+ bool affine = !m13 && !m23;
+
+ int dashOffset = int(s->lastPen.dashOffset());
+
+ if (affine) {
+ // Draw all the line segments.
+ for (int i=1; i<pointCount; ++i) {
+ const QPoint lp1 = points[i-1] * s->matrix;
+ const QPoint lp2 = points[i] * s->matrix;
+ const QRect brect(lp1, lp2);
+ ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
+
+ if (qpen_style(s->lastPen) == Qt::SolidLine)
+ drawLine_midpoint_i(lp1.x(), lp1.y(),
+ lp2.x(), lp2.y(),
+ penBlend, &s->penData,
+ i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel,
+ devRect);
+ else
+ drawLine_midpoint_dashed_i(lp1.x(), lp1.y(),
+ lp2.x(), lp2.y(),
+ &s->lastPen,
+ penBlend, &s->penData,
+ i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel,
+ devRect, &dashOffset);
+
+ }
+
+ // Polygons are implicitly closed.
+ if (needs_closing) {
+ const QPoint lp1 = points[pointCount - 1] * s->matrix;
+ const QPoint lp2 = points[0] * s->matrix;
+ const QRect brect(lp1, lp2);
+ ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
+
+ if (qpen_style(s->lastPen) == Qt::SolidLine)
+ drawLine_midpoint_i(lp1.x(), lp1.y(),
+ lp2.x(), lp2.y(),
+ penBlend, &s->penData, LineDrawIncludeLastPixel,
+ devRect);
+ else
+ drawLine_midpoint_dashed_i(lp1.x(), lp1.y(),
+ lp2.x(), lp2.y(),
+ &s->lastPen,
+ penBlend, &s->penData, LineDrawIncludeLastPixel,
+ devRect, &dashOffset);
+ }
+ } else {
+ // Draw all the line segments.
+ for (int i=1; i<pointCount; ++i) {
+ int x1 = points[i-1].x() * m11 + dx;
+ int y1 = points[i-1].y() * m22 + dy;
+ qreal w = m13*points[i-1].x() + m23*points[i-1].y() + 1.;
+ w = 1/w;
+ x1 = int(x1*w);
+ y1 = int(y1*w);
+ int x2 = points[i].x() * m11 + dx;
+ int y2 = points[i].y() * m22 + dy;
+ w = m13*points[i].x() + m23*points[i].y() + 1.;
+ w = 1/w;
+ x2 = int(x2*w);
+ y2 = int(y2*w);
+
+ const QRect brect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
+ ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
+ if (qpen_style(s->lastPen) == Qt::SolidLine)
+ drawLine_midpoint_i(x1, y1, x2, y2,
+ penBlend, &s->penData,
+ i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel,
+ devRect);
+ else
+ drawLine_midpoint_dashed_i(x1, y1, x2, y2,
+ &s->lastPen,
+ penBlend, &s->penData,
+ i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel,
+ devRect, &dashOffset);
+
+ }
+
+ int x1 = points[pointCount-1].x() * m11 + dx;
+ int y1 = points[pointCount-1].y() * m22 + dy;
+ qreal w = m13*points[pointCount-1].x() + m23*points[pointCount-1].y() + 1.;
+ w = 1/w;
+ x1 = int(x1*w);
+ y1 = int(y1*w);
+ int x2 = points[0].x() * m11 + dx;
+ int y2 = points[0].y() * m22 + dy;
+ w = m13*points[0].x() + m23*points[0].y() + 1.;
+ w = 1/w;
+ x2 = int(x2 * w);
+ y2 = int(y2 * w);
+ // Polygons are implicitly closed.
+
+ if (needs_closing) {
+ const QRect brect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
+ ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
+ if (qpen_style(s->lastPen) == Qt::SolidLine)
+ drawLine_midpoint_i(x1, y1, x2, y2,
+ penBlend, &s->penData, LineDrawIncludeLastPixel,
+ devRect);
+ else
+ drawLine_midpoint_dashed_i(x1, y1, x2, y2,
+ &s->lastPen,
+ penBlend, &s->penData, LineDrawIncludeLastPixel,
+ devRect, &dashOffset);
+ }
+ }
+}
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pixmap)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << " - QRasterPaintEngine::drawPixmap(), pos=" << pos << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
+#endif
+
+ QPixmapData *pd = pixmap.pixmapData();
+ if (pd->classId() == QPixmapData::RasterClass) {
+ const QImage &image = static_cast<QRasterPixmapData *>(pd)->image;
+ if (image.depth() == 1) {
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+ if (s->matrix.type() <= QTransform::TxTranslate) {
+ ensurePen();
+ drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
+ } else {
+ drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
+ }
+ } else {
+ QRasterPaintEngine::drawImage(pos, image);
+ }
+ } else {
+ const QImage image = pixmap.toImage();
+ if (pixmap.depth() == 1) {
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+ if (s->matrix.type() <= QTransform::TxTranslate) {
+ ensurePen();
+ drawBitmap(pos + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
+ } else {
+ drawImage(pos, d->rasterBuffer->colorizeBitmap(image, s->pen.color()));
+ }
+ } else {
+ QRasterPaintEngine::drawImage(pos, image);
+ }
+ }
+}
+
+/*!
+ \reimp
+*/
+void QRasterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, const QRectF &sr)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << " - QRasterPaintEngine::drawPixmap(), r=" << r << " sr=" << sr << " pixmap=" << pixmap.size() << "depth=" << pixmap.depth();
+#endif
+
+ QPixmapData* pd = pixmap.pixmapData();
+ if (pd->classId() == QPixmapData::RasterClass) {
+ const QImage &image = static_cast<QRasterPixmapData *>(pd)->image;
+ if (image.depth() == 1) {
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+ if (s->matrix.type() <= QTransform::TxTranslate
+ && r.size() == sr.size()
+ && r.size() == pixmap.size()) {
+ ensurePen();
+ drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
+ return;
+ } else {
+ drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), sr);
+ }
+ } else {
+ drawImage(r, image, sr);
+ }
+ } else {
+ QRect clippedSource = sr.toAlignedRect().intersected(pixmap.rect());
+ const QImage image = pd->toImage(clippedSource);
+ QRectF translatedSource = sr.translated(-clippedSource.topLeft());
+ if (image.depth() == 1) {
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+ if (s->matrix.type() <= QTransform::TxTranslate
+ && r.size() == sr.size()
+ && r.size() == pixmap.size()) {
+ ensurePen();
+ drawBitmap(r.topLeft() + QPointF(s->matrix.dx(), s->matrix.dy()), image, &s->penData);
+ return;
+ } else {
+ drawImage(r, d->rasterBuffer->colorizeBitmap(image, s->pen.color()), translatedSource);
+ }
+ } else {
+ drawImage(r, image, translatedSource);
+ }
+ }
+}
+
+// assumes that rect has positive width and height
+static inline const QRect toRect_normalized(const QRectF &rect)
+{
+ const int x = qRound(rect.x());
+ const int y = qRound(rect.y());
+ const int w = int(rect.width() + qreal(0.5));
+ const int h = int(rect.height() + qreal(0.5));
+
+ return QRect(x, y, w, h);
+}
+
+static inline int fast_ceil_positive(const qreal &v)
+{
+ const int iv = int(v);
+ if (v - iv == 0)
+ return iv;
+ else
+ return iv + 1;
+}
+
+static inline const QRect toAlignedRect_positive(const QRectF &rect)
+{
+ const int xmin = int(rect.x());
+ const int xmax = int(fast_ceil_positive(rect.right()));
+ const int ymin = int(rect.y());
+ const int ymax = int(fast_ceil_positive(rect.bottom()));
+ return QRect(xmin, ymin, xmax - xmin, ymax - ymin);
+}
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::drawImage(const QPointF &p, const QImage &img)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << " - QRasterPaintEngine::drawImage(), p=" << p << " image=" << img.size() << "depth=" << img.depth();
+#endif
+
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ if (s->matrix.type() > QTransform::TxTranslate) {
+ drawImage(QRectF(p.x(), p.y(), img.width(), img.height()),
+ img,
+ QRectF(0, 0, img.width(), img.height()));
+ } else {
+
+ const QClipData *clip = d->clip();
+ QPointF pt(p.x() + s->matrix.dx(), p.y() + s->matrix.dy());
+
+ if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
+ SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
+ if (func) {
+ if (!clip) {
+ d->drawImage(pt, img, func, d->deviceRect, s->intOpacity);
+ return;
+ } else if (clip->hasRectClip) {
+ d->drawImage(pt, img, func, clip->clipRect, s->intOpacity);
+ return;
+ }
+ }
+ }
+
+
+
+ d->image_filler.clip = clip;
+ d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, img.rect());
+ if (!d->image_filler.blend)
+ return;
+ d->image_filler.dx = -pt.x();
+ d->image_filler.dy = -pt.y();
+ QRect rr = img.rect().translated(qRound(pt.x()), qRound(pt.y()));
+
+ fillRect_normalized(rr, &d->image_filler, d);
+ }
+
+}
+
+QRectF qt_mapRect_non_normalizing(const QRectF &r, const QTransform &t)
+{
+ return QRectF(r.topLeft() * t, r.bottomRight() * t);
+}
+
+namespace {
+ enum RotationType {
+ Rotation90,
+ Rotation180,
+ Rotation270,
+ NoRotation
+ };
+
+ inline RotationType qRotationType(const QTransform &transform)
+ {
+ QTransform::TransformationType type = transform.type();
+
+ if (type > QTransform::TxRotate)
+ return NoRotation;
+
+ if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(-1))
+ && qFuzzyCompare(transform.m21(), qreal(1)) && qFuzzyIsNull(transform.m22()))
+ return Rotation90;
+
+ if (type == QTransform::TxScale && qFuzzyCompare(transform.m11(), qreal(-1)) && qFuzzyIsNull(transform.m12())
+ && qFuzzyIsNull(transform.m21()) && qFuzzyCompare(transform.m22(), qreal(-1)))
+ return Rotation180;
+
+ if (type == QTransform::TxRotate && qFuzzyIsNull(transform.m11()) && qFuzzyCompare(transform.m12(), qreal(1))
+ && qFuzzyCompare(transform.m21(), qreal(-1)) && qFuzzyIsNull(transform.m22()))
+ return Rotation270;
+
+ return NoRotation;
+ }
+
+ inline bool isPixelAligned(const QRectF &rect) {
+ return QRectF(rect.toRect()) == rect;
+ }
+}
+
+/*!
+ \reimp
+*/
+void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
+ Qt::ImageConversionFlags)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << " - QRasterPaintEngine::drawImage(), r=" << r << " sr=" << sr << " image=" << img.size() << "depth=" << img.depth();
+#endif
+
+ if (r.isEmpty())
+ return;
+
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+ int sr_l = qFloor(sr.left());
+ int sr_r = qCeil(sr.right()) - 1;
+ int sr_t = qFloor(sr.top());
+ int sr_b = qCeil(sr.bottom()) - 1;
+
+ if (s->matrix.type() <= QTransform::TxScale && !s->flags.antialiased && sr_l == sr_r && sr_t == sr_b) {
+ QTransform old = s->matrix;
+
+ // Do whatever fillRect() does, but without premultiplying the color if it's already premultiplied.
+ QRgb color = img.pixel(sr_l, sr_t);
+ switch (img.format()) {
+ case QImage::Format_ARGB32_Premultiplied:
+ case QImage::Format_ARGB8565_Premultiplied:
+ case QImage::Format_ARGB6666_Premultiplied:
+ case QImage::Format_ARGB8555_Premultiplied:
+ case QImage::Format_ARGB4444_Premultiplied:
+ // Combine premultiplied color with the opacity set on the painter.
+ d->solid_color_filler.solid.color =
+ ((((color & 0x00ff00ff) * s->intOpacity) >> 8) & 0x00ff00ff)
+ | ((((color & 0xff00ff00) >> 8) * s->intOpacity) & 0xff00ff00);
+ break;
+ default:
+ d->solid_color_filler.solid.color = PREMUL(ARGB_COMBINE_ALPHA(color, s->intOpacity));
+ break;
+ }
+
+ if ((d->solid_color_filler.solid.color & 0xff000000) == 0
+ && s->composition_mode == QPainter::CompositionMode_SourceOver) {
+ return;
+ }
+
+ d->solid_color_filler.clip = d->clip();
+ d->solid_color_filler.adjustSpanMethods();
+ fillRect(r, &d->solid_color_filler);
+
+ s->matrix = old;
+ return;
+ }
+
+ bool stretch_sr = r.width() != sr.width() || r.height() != sr.height();
+
+ const QClipData *clip = d->clip();
+
+ if (s->matrix.type() > QTransform::TxTranslate
+ && !stretch_sr
+ && (!clip || clip->hasRectClip)
+ && s->intOpacity == 256
+ && (d->rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver
+ || d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)
+ && d->rasterBuffer->format == img.format()
+ && (d->rasterBuffer->format == QImage::Format_RGB16
+ || d->rasterBuffer->format == QImage::Format_RGB32
+ || (d->rasterBuffer->format == QImage::Format_ARGB32_Premultiplied
+ && d->rasterBuffer->compositionMode == QPainter::CompositionMode_Source)))
+ {
+ RotationType rotationType = qRotationType(s->matrix);
+
+ if (rotationType != NoRotation && qMemRotateFunctions[d->rasterBuffer->format][rotationType] && img.rect().contains(sr.toAlignedRect())) {
+ QRectF transformedTargetRect = s->matrix.mapRect(r);
+
+ if ((!(s->renderHints & QPainter::SmoothPixmapTransform) && !(s->renderHints & QPainter::Antialiasing))
+ || (isPixelAligned(transformedTargetRect) && isPixelAligned(sr)))
+ {
+ QRect clippedTransformedTargetRect = transformedTargetRect.toRect().intersected(clip ? clip->clipRect : d->deviceRect);
+ if (clippedTransformedTargetRect.isNull())
+ return;
+
+ QRectF clippedTargetRect = s->matrix.inverted().mapRect(QRectF(clippedTransformedTargetRect));
+
+ QRect clippedSourceRect
+ = QRectF(sr.x() + clippedTargetRect.x() - r.x(), sr.y() + clippedTargetRect.y() - r.y(),
+ clippedTargetRect.width(), clippedTargetRect.height()).toRect();
+
+ uint dbpl = d->rasterBuffer->bytesPerLine();
+ uint sbpl = img.bytesPerLine();
+
+ uchar *dst = d->rasterBuffer->buffer();
+ uint bpp = img.depth() >> 3;
+
+ const uchar *srcBase = img.bits() + clippedSourceRect.y() * sbpl + clippedSourceRect.x() * bpp;
+ uchar *dstBase = dst + clippedTransformedTargetRect.y() * dbpl + clippedTransformedTargetRect.x() * bpp;
+
+ uint cw = clippedSourceRect.width();
+ uint ch = clippedSourceRect.height();
+
+ qMemRotateFunctions[d->rasterBuffer->format][rotationType](srcBase, cw, ch, sbpl, dstBase, dbpl);
+
+ return;
+ }
+ }
+ }
+
+ if (s->matrix.type() > QTransform::TxTranslate || stretch_sr) {
+
+ QRectF targetBounds = s->matrix.mapRect(r);
+ bool exceedsPrecision = targetBounds.width() > 0xffff
+ || targetBounds.height() > 0xffff;
+
+ if (!exceedsPrecision && d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
+ if (s->matrix.type() > QTransform::TxScale) {
+ SrcOverTransformFunc func = qTransformFunctions[d->rasterBuffer->format][img.format()];
+ if (func && (!clip || clip->hasRectClip)) {
+ func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(), img.bits(),
+ img.bytesPerLine(), r, sr, !clip ? d->deviceRect : clip->clipRect,
+ s->matrix, s->intOpacity);
+ return;
+ }
+ } else {
+ SrcOverScaleFunc func = qScaleFunctions[d->rasterBuffer->format][img.format()];
+ if (func && (!clip || clip->hasRectClip)) {
+ func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(),
+ img.bits(), img.bytesPerLine(),
+ qt_mapRect_non_normalizing(r, s->matrix), sr,
+ !clip ? d->deviceRect : clip->clipRect,
+ s->intOpacity);
+ return;
+ }
+ }
+ }
+
+ QTransform copy = s->matrix;
+ copy.translate(r.x(), r.y());
+ if (stretch_sr)
+ copy.scale(r.width() / sr.width(), r.height() / sr.height());
+ copy.translate(-sr.x(), -sr.y());
+
+ d->image_filler_xform.clip = clip;
+ d->image_filler_xform.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
+ if (!d->image_filler_xform.blend)
+ return;
+ d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
+
+ if (!s->flags.antialiased && s->matrix.type() == QTransform::TxScale) {
+ QRectF rr = s->matrix.mapRect(r);
+
+ const int x1 = qRound(rr.x());
+ const int y1 = qRound(rr.y());
+ const int x2 = qRound(rr.right());
+ const int y2 = qRound(rr.bottom());
+
+ fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler_xform, d);
+ return;
+ }
+
+#ifdef QT_FAST_SPANS
+ ensureState();
+ if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
+ d->initializeRasterizer(&d->image_filler_xform);
+ d->rasterizer->setAntialiased(s->flags.antialiased);
+
+ const QRectF &rect = r.normalized();
+ const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
+ const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
+
+ if (s->flags.tx_noshear)
+ d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
+ else
+ d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
+ return;
+ }
+#endif
+ QPainterPath path;
+ path.addRect(r);
+ QTransform m = s->matrix;
+ s->matrix = QTransform(m.m11(), m.m12(), m.m13(),
+ m.m21(), m.m22(), m.m23(),
+ m.m31(), m.m32(), m.m33());
+ fillPath(path, &d->image_filler_xform);
+ s->matrix = m;
+ } else {
+ if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) {
+ SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()];
+ if (func) {
+ QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy());
+ if (!clip) {
+ d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect());
+ return;
+ } else if (clip->hasRectClip) {
+ d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect());
+ return;
+ }
+ }
+ }
+
+ d->image_filler.clip = clip;
+ d->image_filler.initTexture(&img, s->intOpacity, QTextureData::Plain, toAlignedRect_positive(sr));
+ if (!d->image_filler.blend)
+ return;
+ d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
+ d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
+
+ QRectF rr = r;
+ rr.translate(s->matrix.dx(), s->matrix.dy());
+
+ const int x1 = qRound(rr.x());
+ const int y1 = qRound(rr.y());
+ const int x2 = qRound(rr.right());
+ const int y2 = qRound(rr.bottom());
+
+ fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler, d);
+ }
+}
+
+/*!
+ \reimp
+*/
+void QRasterPaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &sr)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << " - QRasterPaintEngine::drawTiledPixmap(), r=" << r << "pixmap=" << pixmap.size();
+#endif
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ QImage image;
+
+ QPixmapData *pd = pixmap.pixmapData();
+ if (pd->classId() == QPixmapData::RasterClass) {
+ image = static_cast<QRasterPixmapData *>(pd)->image;
+ } else {
+ image = pixmap.toImage();
+ }
+
+ if (image.depth() == 1)
+ image = d->rasterBuffer->colorizeBitmap(image, s->pen.color());
+
+ if (s->matrix.type() > QTransform::TxTranslate) {
+ QTransform copy = s->matrix;
+ copy.translate(r.x(), r.y());
+ copy.translate(-sr.x(), -sr.y());
+ d->image_filler_xform.clip = d->clip();
+ d->image_filler_xform.initTexture(&image, s->intOpacity, QTextureData::Tiled);
+ if (!d->image_filler_xform.blend)
+ return;
+ d->image_filler_xform.setupMatrix(copy, s->flags.bilinear);
+
+#ifdef QT_FAST_SPANS
+ ensureState();
+ if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
+ d->initializeRasterizer(&d->image_filler_xform);
+ d->rasterizer->setAntialiased(s->flags.antialiased);
+
+ const QRectF &rect = r.normalized();
+ const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
+ const QPointF b = s->matrix.map((rect.topRight() + rect.bottomRight()) * 0.5f);
+ if (s->flags.tx_noshear)
+ d->rasterizer->rasterizeLine(a, b, rect.height() / rect.width());
+ else
+ d->rasterizer->rasterizeLine(a, b, qAbs((s->matrix.m22() * rect.height()) / (s->matrix.m11() * rect.width())));
+ return;
+ }
+#endif
+ QPainterPath path;
+ path.addRect(r);
+ fillPath(path, &d->image_filler_xform);
+ } else {
+ d->image_filler.clip = d->clip();
+
+ d->image_filler.initTexture(&image, s->intOpacity, QTextureData::Tiled);
+ if (!d->image_filler.blend)
+ return;
+ d->image_filler.dx = -(r.x() + s->matrix.dx()) + sr.x();
+ d->image_filler.dy = -(r.y() + s->matrix.dy()) + sr.y();
+
+ QRectF rr = r;
+ rr.translate(s->matrix.dx(), s->matrix.dy());
+ fillRect_normalized(rr.toRect().normalized(), &d->image_filler, d);
+ }
+}
+
+
+//QWS hack
+static inline bool monoVal(const uchar* s, int x)
+{
+ return (s[x>>3] << (x&7)) & 0x80;
+}
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h)
+{
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ if (!s->penData.blend)
+ return;
+
+ QRasterBuffer *rb = d->rasterBuffer.data();
+
+ const QRect rect(rx, ry, w, h);
+ const QClipData *clip = d->clip();
+ bool unclipped = false;
+ if (clip) {
+ // inlined QRect::intersects
+ const bool intersects = qMax(clip->xmin, rect.left()) <= qMin(clip->xmax - 1, rect.right())
+ && qMax(clip->ymin, rect.top()) <= qMin(clip->ymax - 1, rect.bottom());
+
+ if (clip->hasRectClip) {
+ unclipped = rx > clip->xmin
+ && rx + w < clip->xmax
+ && ry > clip->ymin
+ && ry + h < clip->ymax;
+ }
+
+ if (!intersects)
+ return;
+ } else {
+ // inlined QRect::intersects
+ const bool intersects = qMax(0, rect.left()) <= qMin(rb->width() - 1, rect.right())
+ && qMax(0, rect.top()) <= qMin(rb->height() - 1, rect.bottom());
+ if (!intersects)
+ return;
+
+ // inlined QRect::contains
+ const bool contains = rect.left() >= 0 && rect.right() < rb->width()
+ && rect.top() >= 0 && rect.bottom() < rb->height();
+
+ unclipped = contains && d->isUnclipped_normalized(rect);
+ }
+
+ ProcessSpans blend = unclipped ? s->penData.unclipped_blend : s->penData.blend;
+ const uchar * scanline = static_cast<const uchar *>(src);
+
+ if (s->flags.fast_text) {
+ if (unclipped) {
+ if (depth == 1) {
+ if (s->penData.bitmapBlit) {
+ s->penData.bitmapBlit(rb, rx, ry, s->penData.solid.color,
+ scanline, w, h, bpl);
+ return;
+ }
+ } else if (depth == 8) {
+ if (s->penData.alphamapBlit) {
+ s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
+ scanline, w, h, bpl, 0);
+ return;
+ }
+ } else if (depth == 32) {
+ // (A)RGB Alpha mask where the alpha component is not used.
+ if (s->penData.alphaRGBBlit) {
+ s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
+ (const uint *) scanline, w, h, bpl / 4, 0);
+ return;
+ }
+ }
+ } else if (d->deviceDepth == 32 && (depth == 8 || depth == 32)) {
+ // (A)RGB Alpha mask where the alpha component is not used.
+ if (!clip) {
+ int nx = qMax(0, rx);
+ int ny = qMax(0, ry);
+
+ // Move scanline pointer to compensate for moved x and y
+ int xdiff = nx - rx;
+ int ydiff = ny - ry;
+ scanline += ydiff * bpl;
+ scanline += xdiff * (depth == 32 ? 4 : 1);
+
+ w -= xdiff;
+ h -= ydiff;
+
+ if (nx + w > d->rasterBuffer->width())
+ w = d->rasterBuffer->width() - nx;
+ if (ny + h > d->rasterBuffer->height())
+ h = d->rasterBuffer->height() - ny;
+
+ rx = nx;
+ ry = ny;
+ }
+ if (depth == 8 && s->penData.alphamapBlit) {
+ s->penData.alphamapBlit(rb, rx, ry, s->penData.solid.color,
+ scanline, w, h, bpl, clip);
+ } else if (depth == 32 && s->penData.alphaRGBBlit) {
+ s->penData.alphaRGBBlit(rb, rx, ry, s->penData.solid.color,
+ (const uint *) scanline, w, h, bpl / 4, clip);
+ }
+ return;
+ }
+ }
+
+ int x0 = 0;
+ if (rx < 0) {
+ x0 = -rx;
+ w -= x0;
+ }
+
+ int y0 = 0;
+ if (ry < 0) {
+ y0 = -ry;
+ scanline += bpl * y0;
+ h -= y0;
+ }
+
+ w = qMin(w, rb->width() - qMax(0, rx));
+ h = qMin(h, rb->height() - qMax(0, ry));
+
+ if (w <= 0 || h <= 0)
+ return;
+
+ const int NSPANS = 256;
+ QSpan spans[NSPANS];
+ int current = 0;
+
+ const int x1 = x0 + w;
+ const int y1 = y0 + h;
+
+ if (depth == 1) {
+ for (int y = y0; y < y1; ++y) {
+ for (int x = x0; x < x1; ) {
+ if (!monoVal(scanline, x)) {
+ ++x;
+ continue;
+ }
+
+ if (current == NSPANS) {
+ blend(current, spans, &s->penData);
+ current = 0;
+ }
+ spans[current].x = x + rx;
+ spans[current].y = y + ry;
+ spans[current].coverage = 255;
+ int len = 1;
+ ++x;
+ // extend span until we find a different one.
+ while (x < x1 && monoVal(scanline, x)) {
+ ++x;
+ ++len;
+ }
+ spans[current].len = len;
+ ++current;
+ }
+ scanline += bpl;
+ }
+ } else if (depth == 8) {
+ for (int y = y0; y < y1; ++y) {
+ for (int x = x0; x < x1; ) {
+ // Skip those with 0 coverage
+ if (scanline[x] == 0) {
+ ++x;
+ continue;
+ }
+
+ if (current == NSPANS) {
+ blend(current, spans, &s->penData);
+ current = 0;
+ }
+ int coverage = scanline[x];
+ spans[current].x = x + rx;
+ spans[current].y = y + ry;
+ spans[current].coverage = coverage;
+ int len = 1;
+ ++x;
+
+ // extend span until we find a different one.
+ while (x < x1 && scanline[x] == coverage) {
+ ++x;
+ ++len;
+ }
+ spans[current].len = len;
+ ++current;
+ }
+ scanline += bpl;
+ }
+ } else { // 32-bit alpha...
+ uint *sl = (uint *) src;
+ for (int y = y0; y < y1; ++y) {
+ for (int x = x0; x < x1; ) {
+ // Skip those with 0 coverage
+ if ((sl[x] & 0x00ffffff) == 0) {
+ ++x;
+ continue;
+ }
+
+ if (current == NSPANS) {
+ blend(current, spans, &s->penData);
+ current = 0;
+ }
+ uint rgbCoverage = sl[x];
+ int coverage = qGreen(rgbCoverage);
+ spans[current].x = x + rx;
+ spans[current].y = y + ry;
+ spans[current].coverage = coverage;
+ int len = 1;
+ ++x;
+
+ // extend span until we find a different one.
+ while (x < x1 && sl[x] == rgbCoverage) {
+ ++x;
+ ++len;
+ }
+ spans[current].len = len;
+ ++current;
+ }
+ sl += bpl / sizeof(uint);
+ }
+ }
+// qDebug() << "alphaPenBlt: num spans=" << current
+// << "span:" << spans->x << spans->y << spans->len << spans->coverage;
+ // Call span func for current set of spans.
+ if (current != 0)
+ blend(current, spans, &s->penData);
+}
+
+bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs,
+ const QFixedPoint *positions, QFontEngine *fontEngine)
+{
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+#if !defined(QT_NO_FREETYPE)
+ if (fontEngine->type() == QFontEngine::Freetype) {
+ QFontEngineFT *fe = static_cast<QFontEngineFT *>(fontEngine);
+ QFontEngineFT::GlyphFormat neededFormat =
+ painter()->device()->devType() == QInternal::Widget
+ ? fe->defaultGlyphFormat()
+ : QFontEngineFT::Format_A8;
+
+ if (d_func()->mono_surface
+ || fe->isBitmapFont() // alphaPenBlt can handle mono, too
+ )
+ neededFormat = QFontEngineFT::Format_Mono;
+
+ if (neededFormat == QFontEngineFT::Format_None)
+ neededFormat = QFontEngineFT::Format_A8;
+
+ QFontEngineFT::QGlyphSet *gset = fe->defaultGlyphs();
+ if (s->matrix.type() >= QTransform::TxScale) {
+ if (s->matrix.isAffine())
+ gset = fe->loadTransformedGlyphSet(s->matrix);
+ else
+ gset = 0;
+ }
+
+ if (!gset || gset->outline_drawing
+ || !fe->loadGlyphs(gset, glyphs, numGlyphs, positions, neededFormat))
+ return false;
+
+ FT_Face lockedFace = 0;
+
+ int depth;
+ switch (neededFormat) {
+ case QFontEngineFT::Format_Mono:
+ depth = 1;
+ break;
+ case QFontEngineFT::Format_A8:
+ depth = 8;
+ break;
+ case QFontEngineFT::Format_A32:
+ depth = 32;
+ break;
+ default:
+ Q_ASSERT(false);
+ depth = 0;
+ };
+
+ for (int i = 0; i < numGlyphs; i++) {
+ QFixed spp = fe->subPixelPositionForX(positions[i].x);
+ QFontEngineFT::Glyph *glyph = gset->getGlyph(glyphs[i], spp);
+
+ if (!glyph || glyph->format != neededFormat) {
+ if (!lockedFace)
+ lockedFace = fe->lockFace();
+ glyph = fe->loadGlyph(gset, glyphs[i], spp, neededFormat);
+ }
+
+ if (!glyph || !glyph->data)
+ continue;
+
+ int pitch;
+ switch (neededFormat) {
+ case QFontEngineFT::Format_Mono:
+ pitch = ((glyph->width + 31) & ~31) >> 3;
+ break;
+ case QFontEngineFT::Format_A8:
+ pitch = (glyph->width + 3) & ~3;
+ break;
+ case QFontEngineFT::Format_A32:
+ pitch = glyph->width * 4;
+ break;
+ default:
+ Q_ASSERT(false);
+ pitch = 0;
+ };
+
+ alphaPenBlt(glyph->data, pitch, depth,
+ qFloor(positions[i].x) + glyph->x,
+ qFloor(positions[i].y) - glyph->y,
+ glyph->width, glyph->height);
+ }
+ if (lockedFace)
+ fe->unlockFace();
+ } else
+#endif
+ {
+ QFontEngineGlyphCache::Type glyphType = fontEngine->glyphFormat >= 0 ? QFontEngineGlyphCache::Type(fontEngine->glyphFormat) : d->glyphCacheType;
+
+ QImageTextureGlyphCache *cache =
+ static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(0, glyphType, s->matrix));
+ if (!cache) {
+ cache = new QImageTextureGlyphCache(glyphType, s->matrix);
+ fontEngine->setGlyphCache(0, cache);
+ }
+
+ cache->populate(fontEngine, numGlyphs, glyphs, positions);
+ cache->fillInPendingGlyphs();
+
+ const QImage &image = cache->image();
+ int bpl = image.bytesPerLine();
+
+ int depth = image.depth();
+ int rightShift = 0;
+ int leftShift = 0;
+ if (depth == 32)
+ leftShift = 2; // multiply by 4
+ else if (depth == 1)
+ rightShift = 3; // divide by 8
+
+ int margin = cache->glyphMargin();
+ const uchar *bits = image.bits();
+ for (int i=0; i<numGlyphs; ++i) {
+
+ QFixed subPixelPosition = cache->subPixelPositionForX(positions[i].x);
+ QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition);
+ const QTextureGlyphCache::Coord &c = cache->coords[glyph];
+ if (c.isNull())
+ continue;
+
+ int x = qFloor(positions[i].x) + c.baseLineX - margin;
+ int y = qFloor(positions[i].y) - c.baseLineY - margin;
+
+ // printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n",
+ // c.x, c.y,
+ // c.w, c.h,
+ // c.baseLineX, c.baseLineY,
+ // glyphs[i],
+ // x, y,
+ // positions[i].x.toInt(), positions[i].y.toInt());
+
+ alphaPenBlt(bits + ((c.x << leftShift) >> rightShift) + c.y * bpl, bpl, depth, x, y, c.w, c.h);
+ }
+ }
+ return true;
+}
+
+#if defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE)
+void QRasterPaintEngine::drawGlyphsS60(const QPointF &p, const QTextItemInt &ti)
+{
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ QFontEngine *fontEngine = ti.fontEngine;
+ if (fontEngine->type() != QFontEngine::S60FontEngine) {
+ QPaintEngineEx::drawTextItem(p, ti);
+ return;
+ }
+
+ QFontEngineS60 *fe = static_cast<QFontEngineS60 *>(fontEngine);
+
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> glyphs;
+ QTransform matrix = s->matrix;
+ matrix.translate(p.x(), p.y());
+ if (matrix.type() == QTransform::TxScale)
+ fe->setFontScale(matrix.m11());
+ ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
+
+ for (int i=0; i<glyphs.size(); ++i) {
+ TOpenFontCharMetrics tmetrics;
+ const TUint8 *glyphBitmapBytes;
+ TSize glyphBitmapSize;
+ fe->getCharacterData(glyphs[i], tmetrics, glyphBitmapBytes, glyphBitmapSize);
+ const int x = qFloor(positions[i].x + tmetrics.HorizBearingX());
+ const int y = qFloor(positions[i].y - tmetrics.HorizBearingY());
+ alphaPenBlt(glyphBitmapBytes, glyphBitmapSize.iWidth, 8, x, y, glyphBitmapSize.iWidth, glyphBitmapSize.iHeight);
+ }
+
+ if (matrix.type() == QTransform::TxScale)
+ fe->setFontScale(1.0);
+
+ return;
+}
+#endif // Q_OS_SYMBIAN && QT_NO_FREETYPE
+
+/*!
+ * Returns true if the rectangle is completely within the current clip
+ * state of the paint engine.
+ */
+bool QRasterPaintEnginePrivate::isUnclipped_normalized(const QRect &r) const
+{
+ const QClipData *cl = clip();
+ if (!cl) {
+ // inline contains() for performance (we know the rects are normalized)
+ const QRect &r1 = deviceRect;
+ return (r.left() >= r1.left() && r.right() <= r1.right()
+ && r.top() >= r1.top() && r.bottom() <= r1.bottom());
+ }
+
+
+ if (cl->hasRectClip) {
+ // currently all painting functions clips to deviceRect internally
+ if (cl->clipRect == deviceRect)
+ return true;
+
+ // inline contains() for performance (we know the rects are normalized)
+ const QRect &r1 = cl->clipRect;
+ return (r.left() >= r1.left() && r.right() <= r1.right()
+ && r.top() >= r1.top() && r.bottom() <= r1.bottom());
+ } else {
+ return qt_region_strictContains(cl->clipRegion, r);
+ }
+}
+
+bool QRasterPaintEnginePrivate::isUnclipped(const QRect &rect,
+ int penWidth) const
+{
+ Q_Q(const QRasterPaintEngine);
+ const QRasterPaintEngineState *s = q->state();
+ const QClipData *cl = clip();
+ if (!cl) {
+ QRect r = rect.normalized();
+ // inline contains() for performance (we know the rects are normalized)
+ const QRect &r1 = deviceRect;
+ return (r.left() >= r1.left() && r.right() <= r1.right()
+ && r.top() >= r1.top() && r.bottom() <= r1.bottom());
+ }
+
+
+ // currently all painting functions that call this function clip to deviceRect internally
+ if (cl->hasRectClip && cl->clipRect == deviceRect)
+ return true;
+
+ if (s->flags.antialiased)
+ ++penWidth;
+
+ QRect r = rect.normalized();
+ if (penWidth > 0) {
+ r.setX(r.x() - penWidth);
+ r.setY(r.y() - penWidth);
+ r.setWidth(r.width() + 2 * penWidth);
+ r.setHeight(r.height() + 2 * penWidth);
+ }
+
+ if (cl->hasRectClip) {
+ // inline contains() for performance (we know the rects are normalized)
+ const QRect &r1 = cl->clipRect;
+ return (r.left() >= r1.left() && r.right() <= r1.right()
+ && r.top() >= r1.top() && r.bottom() <= r1.bottom());
+ } else {
+ return qt_region_strictContains(cl->clipRegion, r);
+ }
+}
+
+inline bool QRasterPaintEnginePrivate::isUnclipped(const QRectF &rect,
+ int penWidth) const
+{
+ return isUnclipped(rect.normalized().toAlignedRect(), penWidth);
+}
+
+inline ProcessSpans
+QRasterPaintEnginePrivate::getBrushFunc(const QRect &rect,
+ const QSpanData *data) const
+{
+ return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
+}
+
+inline ProcessSpans
+QRasterPaintEnginePrivate::getBrushFunc(const QRectF &rect,
+ const QSpanData *data) const
+{
+ return isUnclipped(rect, 0) ? data->unclipped_blend : data->blend;
+}
+
+inline ProcessSpans
+QRasterPaintEnginePrivate::getPenFunc(const QRect &rect,
+ const QSpanData *data) const
+{
+ Q_Q(const QRasterPaintEngine);
+ const QRasterPaintEngineState *s = q->state();
+
+ if (!s->flags.fast_pen && s->matrix.type() > QTransform::TxTranslate)
+ return data->blend;
+ const int penWidth = s->flags.fast_pen ? 1 : qCeil(s->pen.widthF());
+ return isUnclipped(rect, penWidth) ? data->unclipped_blend : data->blend;
+}
+
+inline ProcessSpans
+QRasterPaintEnginePrivate::getPenFunc(const QRectF &rect,
+ const QSpanData *data) const
+{
+ Q_Q(const QRasterPaintEngine);
+ const QRasterPaintEngineState *s = q->state();
+
+ if (!s->flags.fast_pen && s->matrix.type() > QTransform::TxTranslate)
+ return data->blend;
+ const int penWidth = s->flags.fast_pen ? 1 : qCeil(s->lastPen.widthF());
+ return isUnclipped(rect, penWidth) ? data->unclipped_blend : data->blend;
+}
+
+/*!
+ \reimp
+*/
+void QRasterPaintEngine::drawStaticTextItem(QStaticTextItem *textItem)
+{
+ ensurePen();
+ ensureState();
+
+ drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions,
+ textItem->fontEngine());
+}
+
+/*!
+ \reimp
+*/
+void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
+{
+ const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
+ QRasterPaintEngineState *s = state();
+
+#ifdef QT_DEBUG_DRAW
+ Q_D(QRasterPaintEngine);
+ fprintf(stderr," - QRasterPaintEngine::drawTextItem(), (%.2f,%.2f), string=%s ct=%d\n",
+ p.x(), p.y(), QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data(),
+ d->glyphCacheType);
+#endif
+
+ ensurePen();
+ ensureState();
+
+#if defined (Q_WS_WIN) || defined(Q_WS_MAC)
+
+ bool drawCached = true;
+
+ if (s->matrix.type() >= QTransform::TxProject)
+ drawCached = false;
+
+ // don't try to cache huge fonts
+ const qreal pixelSize = ti.fontEngine->fontDef.pixelSize;
+ if (pixelSize * pixelSize * qAbs(s->matrix.determinant()) >= 64 * 64)
+ drawCached = false;
+
+ // ### Remove the TestFontEngine and Box engine crap, in these
+ // ### cases we should delegate painting to the font engine
+ // ### directly...
+
+#if defined(Q_WS_WIN) && !defined(Q_WS_WINCE)
+ QFontEngine::Type fontEngineType = ti.fontEngine->type();
+ // qDebug() << "type" << fontEngineType << s->matrix.type();
+ if ((fontEngineType == QFontEngine::Win && !((QFontEngineWin *) ti.fontEngine)->ttf && s->matrix.type() > QTransform::TxTranslate)
+ || (s->matrix.type() <= QTransform::TxTranslate
+ && (fontEngineType == QFontEngine::TestFontEngine
+ || fontEngineType == QFontEngine::Box))) {
+ drawCached = false;
+ }
+#else
+ if (s->matrix.type() > QTransform::TxTranslate)
+ drawCached = false;
+#endif
+ if (drawCached) {
+ QRasterPaintEngineState *s = state();
+
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> glyphs;
+
+ QTransform matrix = s->matrix;
+ matrix.translate(p.x(), p.y());
+
+ ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
+
+ drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine);
+ return;
+ }
+
+#elif defined (Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) // Q_WS_WIN || Q_WS_MAC
+ if (s->matrix.type() <= QTransform::TxTranslate
+ || (s->matrix.type() == QTransform::TxScale
+ && (qFuzzyCompare(s->matrix.m11(), s->matrix.m22())))) {
+ drawGlyphsS60(p, ti);
+ return;
+ }
+#else // Q_WS_WIN || Q_WS_MAC
+
+ QFontEngine *fontEngine = ti.fontEngine;
+
+#if defined(Q_WS_QWS)
+ if (fontEngine->type() == QFontEngine::Box) {
+ fontEngine->draw(this, qFloor(p.x()), qFloor(p.y()), ti);
+ return;
+ }
+
+ if (s->matrix.type() < QTransform::TxScale
+ && (fontEngine->type() == QFontEngine::QPF1 || fontEngine->type() == QFontEngine::QPF2
+ || (fontEngine->type() == QFontEngine::Proxy
+ && !(static_cast<QProxyFontEngine *>(fontEngine)->drawAsOutline()))
+ )) {
+ fontEngine->draw(this, qFloor(p.x()), qFloor(p.y()), ti);
+ return;
+ }
+#endif // Q_WS_QWS
+
+#ifdef Q_WS_QPA
+ if (s->matrix.type() < QTransform::TxScale) {
+
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> glyphs;
+ QTransform matrix = state()->transform();
+
+ qreal _x = qFloor(p.x());
+ qreal _y = qFloor(p.y());
+ matrix.translate(_x, _y);
+
+ fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
+ if (glyphs.size() == 0)
+ return;
+
+ for(int i = 0; i < glyphs.size(); i++) {
+ QImage img = fontEngine->alphaMapForGlyph(glyphs[i]);
+ glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[i]);
+ alphaPenBlt(img.bits(), img.bytesPerLine(), img.depth(),
+ qRound(positions[i].x + metrics.x),
+ qRound(positions[i].y + metrics.y),
+ img.width(), img.height());
+ }
+ return;
+ }
+#endif //Q_WS_QPA
+
+#if (defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)) && !defined(QT_NO_FREETYPE)
+
+#if defined(Q_WS_QWS) && !defined(QT_NO_QWS_QPF2)
+ if (fontEngine->type() == QFontEngine::QPF2) {
+ QFontEngine *renderingEngine = static_cast<QFontEngineQPF *>(fontEngine)->renderingEngine();
+ if (renderingEngine)
+ fontEngine = renderingEngine;
+ }
+#endif
+
+ if (fontEngine->type() != QFontEngine::Freetype) {
+ QPaintEngineEx::drawTextItem(p, ti);
+ return;
+ }
+
+ QFontEngineFT *fe = static_cast<QFontEngineFT *>(fontEngine);
+
+ QTransform matrix = s->matrix;
+ matrix.translate(p.x(), p.y());
+
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> glyphs;
+ fe->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
+ if (glyphs.size() == 0)
+ return;
+
+ if (!drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), fontEngine))
+ QPaintEngine::drawTextItem(p, ti);
+
+ return;
+#endif
+#endif
+
+ QPaintEngineEx::drawTextItem(p, ti);
+}
+
+/*!
+ \reimp
+*/
+void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount)
+{
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ ensurePen();
+ qreal pw = s->lastPen.widthF();
+ if (!s->flags.fast_pen && (s->matrix.type() > QTransform::TxTranslate || pw > 1)) {
+ QPaintEngineEx::drawPoints(points, pointCount);
+
+ } else {
+ if (!s->penData.blend)
+ return;
+
+ QVarLengthArray<QT_FT_Span, 4096> array(pointCount);
+ QT_FT_Span span = { 0, 1, 0, 255 };
+ const QPointF *end = points + pointCount;
+ qreal trans_x, trans_y;
+ int x, y;
+ int left = d->deviceRect.x();
+ int right = left + d->deviceRect.width();
+ int top = d->deviceRect.y();
+ int bottom = top + d->deviceRect.height();
+ int count = 0;
+ while (points < end) {
+ s->matrix.map(points->x(), points->y(), &trans_x, &trans_y);
+ x = qFloor(trans_x);
+ y = qFloor(trans_y);
+ if (x >= left && x < right && y >= top && y < bottom) {
+ if (count > 0) {
+ const QT_FT_Span &last = array[count - 1];
+ // spans must be sorted on y (primary) and x (secondary)
+ if (y < last.y || (y == last.y && x < last.x)) {
+ s->penData.blend(count, array.constData(), &s->penData);
+ count = 0;
+ }
+ }
+
+ span.x = x;
+ span.y = y;
+ array[count++] = span;
+ }
+ ++points;
+ }
+
+ if (count > 0)
+ s->penData.blend(count, array.constData(), &s->penData);
+ }
+}
+
+
+void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
+{
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ ensurePen();
+ double pw = s->lastPen.widthF();
+ if (!s->flags.fast_pen && (s->matrix.type() > QTransform::TxTranslate || pw > 1)) {
+ QPaintEngineEx::drawPoints(points, pointCount);
+
+ } else {
+ if (!s->penData.blend)
+ return;
+
+ QVarLengthArray<QT_FT_Span, 4096> array(pointCount);
+ QT_FT_Span span = { 0, 1, 0, 255 };
+ const QPoint *end = points + pointCount;
+ qreal trans_x, trans_y;
+ int x, y;
+ int left = d->deviceRect.x();
+ int right = left + d->deviceRect.width();
+ int top = d->deviceRect.y();
+ int bottom = top + d->deviceRect.height();
+ int count = 0;
+ while (points < end) {
+ s->matrix.map(points->x(), points->y(), &trans_x, &trans_y);
+ x = qFloor(trans_x);
+ y = qFloor(trans_y);
+ if (x >= left && x < right && y >= top && y < bottom) {
+ if (count > 0) {
+ const QT_FT_Span &last = array[count - 1];
+ // spans must be sorted on y (primary) and x (secondary)
+ if (y < last.y || (y == last.y && x < last.x)) {
+ s->penData.blend(count, array.constData(), &s->penData);
+ count = 0;
+ }
+ }
+
+ span.x = x;
+ span.y = y;
+ array[count++] = span;
+ }
+ ++points;
+ }
+
+ if (count > 0)
+ s->penData.blend(count, array.constData(), &s->penData);
+ }
+}
+
+/*!
+ \reimp
+*/
+void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << " - QRasterPaintEngine::drawLine()";
+#endif
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ ensurePen();
+ if (s->flags.fast_pen) {
+ QIntRect bounds; bounds.set(d->deviceRect);
+ LineDrawMode mode = s->lastPen.capStyle() == Qt::FlatCap
+ ? LineDrawNormal
+ : LineDrawIncludeLastPixel;
+
+ int m11 = int(s->matrix.m11());
+ int m22 = int(s->matrix.m22());
+ int dx = qFloor(s->matrix.dx());
+ int dy = qFloor(s->matrix.dy());
+ for (int i=0; i<lineCount; ++i) {
+ int dashOffset = int(s->lastPen.dashOffset());
+ if (s->flags.int_xform) {
+ const QLine &l = lines[i];
+ int x1 = l.x1() * m11 + dx;
+ int y1 = l.y1() * m22 + dy;
+ int x2 = l.x2() * m11 + dx;
+ int y2 = l.y2() * m22 + dy;
+
+ const QRect brect(QPoint(x1, y1), QPoint(x2, y2));
+ ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
+ if (qpen_style(s->lastPen) == Qt::SolidLine)
+ drawLine_midpoint_i(x1, y1, x2, y2,
+ penBlend, &s->penData, mode, bounds);
+ else
+ drawLine_midpoint_dashed_i(x1, y1, x2, y2,
+ &s->lastPen, penBlend,
+ &s->penData, mode, bounds,
+ &dashOffset);
+ } else {
+ QLineF line = lines[i] * s->matrix;
+ const QRectF brect(QPointF(line.x1(), line.y1()),
+ QPointF(line.x2(), line.y2()));
+ ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
+ if (qpen_style(s->lastPen) == Qt::SolidLine)
+ drawLine_midpoint_i(int(line.x1()), int(line.y1()),
+ int(line.x2()), int(line.y2()),
+ penBlend, &s->penData, mode, bounds);
+ else
+ drawLine_midpoint_dashed_i(int(line.x1()), int(line.y1()),
+ int(line.x2()), int(line.y2()),
+ &s->lastPen, penBlend,
+ &s->penData, mode, bounds,
+ &dashOffset);
+ }
+ }
+ } else if (s->penData.blend) {
+ QPaintEngineEx::drawLines(lines, lineCount);
+ }
+}
+
+void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line,
+ qreal width,
+ int *dashIndex,
+ qreal *dashOffset,
+ bool *inDash)
+{
+ Q_Q(QRasterPaintEngine);
+ QRasterPaintEngineState *s = q->state();
+
+ const QPen &pen = s->lastPen;
+ const bool squareCap = (pen.capStyle() == Qt::SquareCap);
+ const QVector<qreal> pattern = pen.dashPattern();
+
+ qreal patternLength = 0;
+ for (int i = 0; i < pattern.size(); ++i)
+ patternLength += pattern.at(i);
+
+ if (patternLength <= 0)
+ return;
+
+ qreal length = line.length();
+ Q_ASSERT(length > 0);
+ while (length > 0) {
+ const bool rasterize = *inDash;
+ qreal dash = (pattern.at(*dashIndex) - *dashOffset) * width;
+ QLineF l = line;
+
+ if (dash >= length) {
+ dash = length;
+ *dashOffset += dash / width;
+ length = 0;
+ } else {
+ *dashOffset = 0;
+ *inDash = !(*inDash);
+ if (++*dashIndex >= pattern.size())
+ *dashIndex = 0;
+ length -= dash;
+ l.setLength(dash);
+ line.setP1(l.p2());
+ }
+
+ if (rasterize && dash > 0)
+ rasterizer->rasterizeLine(l.p1(), l.p2(), width / dash, squareCap);
+ }
+}
+
+/*!
+ \reimp
+*/
+void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << " - QRasterPaintEngine::drawLine()";
+#endif
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ ensurePen();
+ if (!s->penData.blend)
+ return;
+ if (s->flags.fast_pen) {
+ QIntRect bounds;
+ bounds.set(d->deviceRect);
+ LineDrawMode mode = s->lastPen.capStyle() == Qt::FlatCap
+ ? LineDrawNormal
+ : LineDrawIncludeLastPixel;
+
+ for (int i=0; i<lineCount; ++i) {
+ int dashOffset = int(s->lastPen.dashOffset());
+ QLineF line = lines[i] * s->matrix;
+ const QRectF brect(QPointF(line.x1(), line.y1()),
+ QPointF(line.x2(), line.y2()));
+ ProcessSpans penBlend = d->getPenFunc(brect, &s->penData);
+ if (qpen_style(s->lastPen) == Qt::SolidLine)
+ drawLine_midpoint_i(int(line.x1()), int(line.y1()),
+ int(line.x2()), int(line.y2()),
+ penBlend, &s->penData, mode, bounds);
+ else
+ drawLine_midpoint_dashed_i(int(line.x1()), int(line.y1()),
+ int(line.x2()), int(line.y2()),
+ &s->lastPen,
+ penBlend, &s->penData, mode,
+ bounds, &dashOffset);
+ }
+ } else {
+ QPaintEngineEx::drawLines(lines, lineCount);
+ }
+}
+
+
+/*!
+ \reimp
+*/
+void QRasterPaintEngine::drawEllipse(const QRectF &rect)
+{
+ Q_D(QRasterPaintEngine);
+ QRasterPaintEngineState *s = state();
+
+ ensurePen();
+ if (((qpen_style(s->lastPen) == Qt::SolidLine && s->flags.fast_pen)
+ || (qpen_style(s->lastPen) == Qt::NoPen && !s->flags.antialiased))
+ && qMax(rect.width(), rect.height()) < QT_RASTER_COORD_LIMIT
+ && !rect.isEmpty()
+ && s->matrix.type() <= QTransform::TxScale) // no shear
+ {
+ ensureBrush();
+ const QRectF r = s->matrix.mapRect(rect);
+ ProcessSpans penBlend = d->getPenFunc(r, &s->penData);
+ ProcessSpans brushBlend = d->getBrushFunc(r, &s->brushData);
+ const QRect brect = QRect(int(r.x()), int(r.y()),
+ int_dim(r.x(), r.width()),
+ int_dim(r.y(), r.height()));
+ if (brect == r) {
+ drawEllipse_midpoint_i(brect, d->deviceRect, penBlend, brushBlend,
+ &s->penData, &s->brushData);
+ return;
+ }
+ }
+ QPaintEngineEx::drawEllipse(rect);
+}
+
+/*!
+ \internal
+*/
+#ifdef Q_WS_MAC
+void QRasterPaintEngine::setCGContext(CGContextRef ctx)
+{
+ Q_D(QRasterPaintEngine);
+ d->cgContext = ctx;
+}
+
+/*!
+ \internal
+*/
+CGContextRef QRasterPaintEngine::getCGContext() const
+{
+ Q_D(const QRasterPaintEngine);
+ return d->cgContext;
+}
+#endif
+
+#ifdef Q_WS_WIN
+/*!
+ \internal
+*/
+void QRasterPaintEngine::setDC(HDC hdc) {
+ Q_D(QRasterPaintEngine);
+ d->hdc = hdc;
+}
+
+/*!
+ \internal
+*/
+HDC QRasterPaintEngine::getDC() const
+{
+ Q_D(const QRasterPaintEngine);
+ return d->hdc;
+}
+
+/*!
+ \internal
+*/
+void QRasterPaintEngine::releaseDC(HDC) const
+{
+}
+
+#endif
+
+/*!
+ \internal
+*/
+QPoint QRasterPaintEngine::coordinateOffset() const
+{
+ return QPoint(0, 0);
+}
+
+/*!
+ Draws the given color \a spans with the specified \a color. The \a
+ count parameter specifies the number of spans.
+
+ The default implementation does nothing; reimplement this function
+ to draw the given color \a spans with the specified \a color. Note
+ that this function \e must be reimplemented if the framebuffer is
+ not memory-mapped.
+
+ \sa drawBufferSpan()
+*/
+#if defined(Q_WS_QWS) && !defined(QT_NO_RASTERCALLBACKS)
+void QRasterPaintEngine::drawColorSpans(const QSpan *spans, int count, uint color)
+{
+ Q_UNUSED(spans);
+ Q_UNUSED(count);
+ Q_UNUSED(color);
+ qFatal("QRasterPaintEngine::drawColorSpans must be reimplemented on "
+ "a non memory-mapped device");
+}
+
+/*!
+ \fn void QRasterPaintEngine::drawBufferSpan(const uint *buffer, int size, int x, int y, int length, uint alpha)
+
+ Draws the given \a buffer.
+
+ The default implementation does nothing; reimplement this function
+ to draw a buffer that contains more than one color. Note that this
+ function \e must be reimplemented if the framebuffer is not
+ memory-mapped.
+
+ The \a size parameter specifies the total size of the given \a
+ buffer, while the \a length parameter specifies the number of
+ pixels to draw. The buffer's position is given by (\a x, \a
+ y). The provided \a alpha value is added to each pixel in the
+ buffer when drawing.
+
+ \sa drawColorSpans()
+*/
+void QRasterPaintEngine::drawBufferSpan(const uint *buffer, int bufsize,
+ int x, int y, int length, uint const_alpha)
+{
+ Q_UNUSED(buffer);
+ Q_UNUSED(bufsize);
+ Q_UNUSED(x);
+ Q_UNUSED(y);
+ Q_UNUSED(length);
+ Q_UNUSED(const_alpha);
+ qFatal("QRasterPaintEngine::drawBufferSpan must be reimplemented on "
+ "a non memory-mapped device");
+}
+#endif // Q_WS_QWS
+
+void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fg)
+{
+ Q_ASSERT(fg);
+ if (!fg->blend)
+ return;
+ Q_D(QRasterPaintEngine);
+
+ Q_ASSERT(image.depth() == 1);
+
+ const int spanCount = 256;
+ QT_FT_Span spans[spanCount];
+ int n = 0;
+
+ // Boundaries
+ int w = image.width();
+ int h = image.height();
+ int ymax = qMin(qRound(pos.y() + h), d->rasterBuffer->height());
+ int ymin = qMax(qRound(pos.y()), 0);
+ int xmax = qMin(qRound(pos.x() + w), d->rasterBuffer->width());
+ int xmin = qMax(qRound(pos.x()), 0);
+
+ int x_offset = xmin - qRound(pos.x());
+
+ QImage::Format format = image.format();
+ for (int y = ymin; y < ymax; ++y) {
+ const uchar *src = image.scanLine(y - qRound(pos.y()));
+ if (format == QImage::Format_MonoLSB) {
+ for (int x = 0; x < xmax - xmin; ++x) {
+ int src_x = x + x_offset;
+ uchar pixel = src[src_x >> 3];
+ if (!pixel) {
+ x += 7 - (src_x%8);
+ continue;
+ }
+ if (pixel & (0x1 << (src_x & 7))) {
+ spans[n].x = xmin + x;
+ spans[n].y = y;
+ spans[n].coverage = 255;
+ int len = 1;
+ while (src_x < w-1 && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) {
+ ++src_x;
+ ++len;
+ }
+ spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
+ x += len;
+ ++n;
+ if (n == spanCount) {
+ fg->blend(n, spans, fg);
+ n = 0;
+ }
+ }
+ }
+ } else {
+ for (int x = 0; x < xmax - xmin; ++x) {
+ int src_x = x + x_offset;
+ uchar pixel = src[src_x >> 3];
+ if (!pixel) {
+ x += 7 - (src_x%8);
+ continue;
+ }
+ if (pixel & (0x80 >> (x & 7))) {
+ spans[n].x = xmin + x;
+ spans[n].y = y;
+ spans[n].coverage = 255;
+ int len = 1;
+ while (src_x < w-1 && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) {
+ ++src_x;
+ ++len;
+ }
+ spans[n].len = ((len + spans[n].x) > xmax) ? (xmax - spans[n].x) : len;
+ x += len;
+ ++n;
+ if (n == spanCount) {
+ fg->blend(n, spans, fg);
+ n = 0;
+ }
+ }
+ }
+ }
+ }
+ if (n) {
+ fg->blend(n, spans, fg);
+ n = 0;
+ }
+}
+
+/*!
+ \enum QRasterPaintEngine::ClipType
+ \internal
+
+ \value RectClip Indicates that the currently set clip is a single rectangle.
+ \value ComplexClip Indicates that the currently set clip is a combination of several shapes.
+*/
+
+/*!
+ \internal
+ Returns the type of the clip currently set.
+*/
+QRasterPaintEngine::ClipType QRasterPaintEngine::clipType() const
+{
+ Q_D(const QRasterPaintEngine);
+
+ const QClipData *clip = d->clip();
+ if (!clip || clip->hasRectClip)
+ return RectClip;
+ else
+ return ComplexClip;
+}
+
+/*!
+ \internal
+ Returns the bounding rect of the currently set clip.
+*/
+QRect QRasterPaintEngine::clipBoundingRect() const
+{
+ Q_D(const QRasterPaintEngine);
+
+ const QClipData *clip = d->clip();
+
+ if (!clip)
+ return d->deviceRect;
+
+ if (clip->hasRectClip)
+ return clip->clipRect;
+
+ return QRect(clip->xmin, clip->ymin, clip->xmax - clip->xmin, clip->ymax - clip->ymin);
+}
+
+static void qt_merge_clip(const QClipData *c1, const QClipData *c2, QClipData *result)
+{
+ Q_ASSERT(c1->clipSpanHeight == c2->clipSpanHeight && c1->clipSpanHeight == result->clipSpanHeight);
+
+ QVarLengthArray<short, 4096> buffer;
+
+ QClipData::ClipLine *c1ClipLines = const_cast<QClipData *>(c1)->clipLines();
+ QClipData::ClipLine *c2ClipLines = const_cast<QClipData *>(c2)->clipLines();
+ result->initialize();
+
+ for (int y = 0; y < c1->clipSpanHeight; ++y) {
+ const QSpan *c1_spans = c1ClipLines[y].spans;
+ int c1_count = c1ClipLines[y].count;
+ const QSpan *c2_spans = c2ClipLines[y].spans;
+ int c2_count = c2ClipLines[y].count;
+
+ if (c1_count == 0 && c2_count == 0)
+ continue;
+ if (c1_count == 0) {
+ result->appendSpans(c2_spans, c2_count);
+ continue;
+ } else if (c2_count == 0) {
+ result->appendSpans(c1_spans, c1_count);
+ continue;
+ }
+
+ // we need to merge the two
+
+ // find required length
+ int max = qMax(c1_spans[c1_count - 1].x + c1_spans[c1_count - 1].len,
+ c2_spans[c2_count - 1].x + c2_spans[c2_count - 1].len);
+ buffer.resize(max);
+ memset(buffer.data(), 0, buffer.size() * sizeof(short));
+
+ // Fill with old spans.
+ for (int i = 0; i < c1_count; ++i) {
+ const QSpan *cs = c1_spans + i;
+ for (int j=cs->x; j<cs->x + cs->len; ++j)
+ buffer[j] = cs->coverage;
+ }
+
+ // Fill with new spans
+ for (int i = 0; i < c2_count; ++i) {
+ const QSpan *cs = c2_spans + i;
+ for (int j = cs->x; j < cs->x + cs->len; ++j) {
+ buffer[j] += cs->coverage;
+ if (buffer[j] > 255)
+ buffer[j] = 255;
+ }
+ }
+
+ int x = 0;
+ while (x<max) {
+
+ // Skip to next span
+ while (x < max && buffer[x] == 0) ++x;
+ if (x >= max) break;
+
+ int sx = x;
+ int coverage = buffer[x];
+
+ // Find length of span
+ while (x < max && buffer[x] == coverage)
+ ++x;
+
+ result->appendSpan(sx, x - sx, y, coverage);
+ }
+ }
+}
+
+void QRasterPaintEnginePrivate::initializeRasterizer(QSpanData *data)
+{
+ Q_Q(QRasterPaintEngine);
+ QRasterPaintEngineState *s = q->state();
+
+ rasterizer->setAntialiased(s->flags.antialiased);
+
+ QRect clipRect(deviceRect);
+ ProcessSpans blend;
+ // ### get from optimized rectbased QClipData
+
+ const QClipData *c = clip();
+ if (c) {
+ const QRect r(QPoint(c->xmin, c->ymin),
+ QSize(c->xmax - c->xmin, c->ymax - c->ymin));
+ clipRect = clipRect.intersected(r);
+ blend = data->blend;
+ } else {
+ blend = data->unclipped_blend;
+ }
+
+ rasterizer->setClipRect(clipRect);
+ rasterizer->initialize(blend, data);
+}
+
+void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
+ ProcessSpans callback,
+ QSpanData *spanData, QRasterBuffer *rasterBuffer)
+{
+ if (!callback || !outline)
+ return;
+
+ Q_Q(QRasterPaintEngine);
+ QRasterPaintEngineState *s = q->state();
+
+ if (!s->flags.antialiased) {
+ initializeRasterizer(spanData);
+
+ const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
+ ? Qt::WindingFill
+ : Qt::OddEvenFill;
+
+ rasterizer->rasterize(outline, fillRule);
+ return;
+ }
+
+ rasterize(outline, callback, (void *)spanData, rasterBuffer);
+}
+
+extern "C" {
+ int q_gray_rendered_spans(QT_FT_Raster raster);
+}
+
+void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline,
+ ProcessSpans callback,
+ void *userData, QRasterBuffer *)
+{
+ if (!callback || !outline)
+ return;
+
+ Q_Q(QRasterPaintEngine);
+ QRasterPaintEngineState *s = q->state();
+
+ if (!s->flags.antialiased) {
+ rasterizer->setAntialiased(s->flags.antialiased);
+ rasterizer->setClipRect(deviceRect);
+ rasterizer->initialize(callback, userData);
+
+ const Qt::FillRule fillRule = outline->flags == QT_FT_OUTLINE_NONE
+ ? Qt::WindingFill
+ : Qt::OddEvenFill;
+
+ rasterizer->rasterize(outline, fillRule);
+ return;
+ }
+
+ // Initial size for raster pool is MINIMUM_POOL_SIZE so as to
+ // minimize memory reallocations. However if initial size for
+ // raster pool is changed for lower value, reallocations will
+ // occur normally.
+ const int rasterPoolInitialSize = MINIMUM_POOL_SIZE;
+ int rasterPoolSize = rasterPoolInitialSize;
+ unsigned char *rasterPoolBase;
+#if defined(Q_WS_WIN64)
+ rasterPoolBase =
+ // We make use of setjmp and longjmp in qgrayraster.c which requires
+ // 16-byte alignment, hence we hardcode this requirement here..
+ (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
+#else
+ unsigned char rasterPoolOnStack[rasterPoolInitialSize];
+ rasterPoolBase = rasterPoolOnStack;
+#endif
+ Q_CHECK_PTR(rasterPoolBase);
+
+ qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
+
+ void *data = userData;
+
+ QT_FT_BBox clip_box = { deviceRect.x(),
+ deviceRect.y(),
+ deviceRect.x() + deviceRect.width(),
+ deviceRect.y() + deviceRect.height() };
+
+ QT_FT_Raster_Params rasterParams;
+ rasterParams.target = 0;
+ rasterParams.source = outline;
+ rasterParams.flags = QT_FT_RASTER_FLAG_CLIP;
+ rasterParams.gray_spans = 0;
+ rasterParams.black_spans = 0;
+ rasterParams.bit_test = 0;
+ rasterParams.bit_set = 0;
+ rasterParams.user = data;
+ rasterParams.clip_box = clip_box;
+
+ bool done = false;
+ int error;
+
+ int rendered_spans = 0;
+
+ while (!done) {
+
+ rasterParams.flags |= (QT_FT_RASTER_FLAG_AA | QT_FT_RASTER_FLAG_DIRECT);
+ rasterParams.gray_spans = callback;
+ rasterParams.skip_spans = rendered_spans;
+ error = qt_ft_grays_raster.raster_render(*grayRaster.data(), &rasterParams);
+
+ // Out of memory, reallocate some more and try again...
+ if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c
+ int new_size = rasterPoolSize * 2;
+ if (new_size > 1024 * 1024) {
+ qWarning("QPainter: Rasterization of primitive failed");
+ break;
+ }
+
+ rendered_spans += q_gray_rendered_spans(*grayRaster.data());
+
+#if defined(Q_WS_WIN64)
+ _aligned_free(rasterPoolBase);
+#else
+ if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
+ free(rasterPoolBase);
+#endif
+
+ rasterPoolSize = new_size;
+ rasterPoolBase =
+#if defined(Q_WS_WIN64)
+ // We make use of setjmp and longjmp in qgrayraster.c which requires
+ // 16-byte alignment, hence we hardcode this requirement here..
+ (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2);
+#else
+ (unsigned char *) malloc(rasterPoolSize);
+#endif
+ Q_CHECK_PTR(rasterPoolBase); // note: we just freed the old rasterPoolBase. I hope it's not fatal.
+
+ qt_ft_grays_raster.raster_done(*grayRaster.data());
+ qt_ft_grays_raster.raster_new(grayRaster.data());
+ qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize);
+ } else {
+ done = true;
+ }
+ }
+
+#if defined(Q_WS_WIN64)
+ _aligned_free(rasterPoolBase);
+#else
+ if (rasterPoolBase != rasterPoolOnStack) // initially on the stack
+ free(rasterPoolBase);
+#endif
+}
+
+void QRasterPaintEnginePrivate::recalculateFastImages()
+{
+ Q_Q(QRasterPaintEngine);
+ QRasterPaintEngineState *s = q->state();
+
+ s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform)
+ && s->matrix.type() <= QTransform::TxShear;
+}
+
+bool QRasterPaintEnginePrivate::canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const
+{
+ Q_Q(const QRasterPaintEngine);
+ const QRasterPaintEngineState *s = q->state();
+
+ return s->flags.fast_images
+ && (mode == QPainter::CompositionMode_SourceOver
+ || (mode == QPainter::CompositionMode_Source
+ && !image.hasAlphaChannel()));
+}
+
+QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color)
+{
+ Q_ASSERT(image.depth() == 1);
+
+ QImage sourceImage = image.convertToFormat(QImage::Format_MonoLSB);
+ QImage dest = QImage(sourceImage.size(), QImage::Format_ARGB32_Premultiplied);
+
+ QRgb fg = PREMUL(color.rgba());
+ QRgb bg = 0;
+
+ int height = sourceImage.height();
+ int width = sourceImage.width();
+ for (int y=0; y<height; ++y) {
+ uchar *source = sourceImage.scanLine(y);
+ QRgb *target = reinterpret_cast<QRgb *>(dest.scanLine(y));
+ if (!source || !target)
+ QT_THROW(std::bad_alloc()); // we must have run out of memory
+ for (int x=0; x < width; ++x)
+ target[x] = (source[x>>3] >> (x&7)) & 1 ? fg : bg;
+ }
+ return dest;
+}
+
+QRasterBuffer::~QRasterBuffer()
+{
+}
+
+void QRasterBuffer::init()
+{
+ compositionMode = QPainter::CompositionMode_SourceOver;
+ monoDestinationWithClut = false;
+ destColor0 = 0;
+ destColor1 = 0;
+}
+
+QImage::Format QRasterBuffer::prepare(QImage *image)
+{
+ m_buffer = (uchar *)image->bits();
+ m_width = qMin(QT_RASTER_COORD_LIMIT, image->width());
+ m_height = qMin(QT_RASTER_COORD_LIMIT, image->height());
+ bytes_per_pixel = image->depth()/8;
+ bytes_per_line = image->bytesPerLine();
+
+ format = image->format();
+ drawHelper = qDrawHelper + format;
+ if (image->depth() == 1 && image->colorTable().size() == 2) {
+ monoDestinationWithClut = true;
+ destColor0 = PREMUL(image->colorTable()[0]);
+ destColor1 = PREMUL(image->colorTable()[1]);
+ }
+
+ return format;
+}
+
+void QRasterBuffer::resetBuffer(int val)
+{
+ memset(m_buffer, val, m_height*bytes_per_line);
+}
+
+
+#if defined(Q_WS_QWS)
+void QRasterBuffer::prepare(QCustomRasterPaintDevice *device)
+{
+ m_buffer = reinterpret_cast<uchar*>(device->memory());
+ m_width = qMin(QT_RASTER_COORD_LIMIT, device->width());
+ m_height = qMin(QT_RASTER_COORD_LIMIT, device->height());
+ bytes_per_pixel = device->depth() / 8;
+ bytes_per_line = device->bytesPerLine();
+ format = device->format();
+#ifndef QT_NO_RASTERCALLBACKS
+ if (!m_buffer)
+ drawHelper = qDrawHelperCallback + format;
+ else
+#endif
+ drawHelper = qDrawHelper + format;
+}
+
+int QCustomRasterPaintDevice::metric(PaintDeviceMetric m) const
+{
+ switch (m) {
+ case PdmWidth:
+ return widget->frameGeometry().width();
+ case PdmHeight:
+ return widget->frameGeometry().height();
+ default:
+ break;
+ }
+
+ return qt_paint_device_metric(widget, m);
+}
+
+int QCustomRasterPaintDevice::bytesPerLine() const
+{
+ return (width() * depth() + 7) / 8;
+}
+
+#elif defined(Q_OS_SYMBIAN)
+
+void QRasterBuffer::prepareBuffer(int /* width */, int /* height */)
+{
+}
+
+#endif // Q_OS_SYMBIAN
+
+/*!
+ \class QCustomRasterPaintDevice
+ \preliminary
+ \ingroup qws
+ \since 4.2
+
+ \brief The QCustomRasterPaintDevice class is provided to activate
+ hardware accelerated paint engines in Qt for Embedded Linux.
+
+ Note that this class is only available in \l{Qt for Embedded Linux}.
+
+ In \l{Qt for Embedded Linux}, painting is a pure software
+ implementation. But starting with Qt 4.2, it is
+ possible to add an accelerated graphics driver to take advantage
+ of available hardware resources.
+
+ Hardware acceleration is accomplished by creating a custom screen
+ driver, accelerating the copying from memory to the screen, and
+ implementing a custom paint engine accelerating the various
+ painting operations. Then a custom paint device (derived from the
+ QCustomRasterPaintDevice class) and a custom window surface
+ (derived from QWSWindowSurface) must be implemented to make
+ \l{Qt for Embedded Linux} aware of the accelerated driver.
+
+ See the \l {Adding an Accelerated Graphics Driver to Qt for Embedded Linux}
+ documentation for details.
+
+ \sa QRasterPaintEngine, QPaintDevice
+*/
+
+/*!
+ \fn QCustomRasterPaintDevice::QCustomRasterPaintDevice(QWidget *widget)
+
+ Constructs a custom raster based paint device for the given
+ top-level \a widget.
+*/
+
+/*!
+ \fn int QCustomRasterPaintDevice::bytesPerLine() const
+
+ Returns the number of bytes per line in the framebuffer. Note that
+ this number might be larger than the framebuffer width.
+*/
+
+/*!
+ \fn int QCustomRasterPaintDevice::devType() const
+ \internal
+*/
+
+/*!
+ \fn QImage::Format QCustomRasterPaintDevice::format() const
+
+ Returns the format of the device's memory buffet.
+
+ The default format is QImage::Format_ARGB32_Premultiplied. The
+ only other valid format is QImage::Format_RGB16.
+*/
+
+/*!
+ \fn void * QCustomRasterPaintDevice::memory () const
+
+ Returns a pointer to the paint device's memory buffer, or 0 if no
+ such buffer exists.
+*/
+
+/*!
+ \fn int QCustomRasterPaintDevice::metric ( PaintDeviceMetric m ) const
+ \reimp
+*/
+
+/*!
+ \fn QSize QCustomRasterPaintDevice::size () const
+ \internal
+*/
+
+
+QClipData::QClipData(int height)
+{
+ clipSpanHeight = height;
+ m_clipLines = 0;
+
+ allocated = 0;
+ m_spans = 0;
+ xmin = xmax = ymin = ymax = 0;
+ count = 0;
+
+ enabled = true;
+ hasRectClip = hasRegionClip = false;
+}
+
+QClipData::~QClipData()
+{
+ if (m_clipLines)
+ free(m_clipLines);
+ if (m_spans)
+ free(m_spans);
+}
+
+void QClipData::initialize()
+{
+ if (m_spans)
+ return;
+
+ if (!m_clipLines)
+ m_clipLines = (ClipLine *)calloc(sizeof(ClipLine), clipSpanHeight);
+
+ Q_CHECK_PTR(m_clipLines);
+ QT_TRY {
+ m_spans = (QSpan *)malloc(clipSpanHeight*sizeof(QSpan));
+ allocated = clipSpanHeight;
+ Q_CHECK_PTR(m_spans);
+
+ QT_TRY {
+ if (hasRectClip) {
+ int y = 0;
+ while (y < ymin) {
+ m_clipLines[y].spans = 0;
+ m_clipLines[y].count = 0;
+ ++y;
+ }
+
+ const int len = clipRect.width();
+ count = 0;
+ while (y < ymax) {
+ QSpan *span = m_spans + count;
+ span->x = xmin;
+ span->len = len;
+ span->y = y;
+ span->coverage = 255;
+ ++count;
+
+ m_clipLines[y].spans = span;
+ m_clipLines[y].count = 1;
+ ++y;
+ }
+
+ while (y < clipSpanHeight) {
+ m_clipLines[y].spans = 0;
+ m_clipLines[y].count = 0;
+ ++y;
+ }
+ } else if (hasRegionClip) {
+
+ const QVector<QRect> rects = clipRegion.rects();
+ const int numRects = rects.size();
+
+ { // resize
+ const int maxSpans = (ymax - ymin) * numRects;
+ if (maxSpans > allocated) {
+ m_spans = q_check_ptr((QSpan *)realloc(m_spans, maxSpans * sizeof(QSpan)));
+ allocated = maxSpans;
+ }
+ }
+
+ int y = 0;
+ int firstInBand = 0;
+ count = 0;
+ while (firstInBand < numRects) {
+ const int currMinY = rects.at(firstInBand).y();
+ const int currMaxY = currMinY + rects.at(firstInBand).height();
+
+ while (y < currMinY) {
+ m_clipLines[y].spans = 0;
+ m_clipLines[y].count = 0;
+ ++y;
+ }
+
+ int lastInBand = firstInBand;
+ while (lastInBand + 1 < numRects && rects.at(lastInBand+1).top() == y)
+ ++lastInBand;
+
+ while (y < currMaxY) {
+
+ m_clipLines[y].spans = m_spans + count;
+ m_clipLines[y].count = lastInBand - firstInBand + 1;
+
+ for (int r = firstInBand; r <= lastInBand; ++r) {
+ const QRect &currRect = rects.at(r);
+ QSpan *span = m_spans + count;
+ span->x = currRect.x();
+ span->len = currRect.width();
+ span->y = y;
+ span->coverage = 255;
+ ++count;
+ }
+ ++y;
+ }
+
+ firstInBand = lastInBand + 1;
+ }
+
+ Q_ASSERT(count <= allocated);
+
+ while (y < clipSpanHeight) {
+ m_clipLines[y].spans = 0;
+ m_clipLines[y].count = 0;
+ ++y;
+ }
+
+ }
+ } QT_CATCH(...) {
+ free(m_spans); // have to free m_spans again or someone might think that we were successfully initialized.
+ m_spans = 0;
+ QT_RETHROW;
+ }
+ } QT_CATCH(...) {
+ free(m_clipLines); // same for clipLines
+ m_clipLines = 0;
+ QT_RETHROW;
+ }
+}
+
+void QClipData::fixup()
+{
+ Q_ASSERT(m_spans);
+
+ if (count == 0) {
+ ymin = ymax = xmin = xmax = 0;
+ return;
+ }
+
+ int y = -1;
+ ymin = m_spans[0].y;
+ ymax = m_spans[count-1].y + 1;
+ xmin = INT_MAX;
+ xmax = 0;
+
+ const int firstLeft = m_spans[0].x;
+ const int firstRight = m_spans[0].x + m_spans[0].len;
+ bool isRect = true;
+
+ for (int i = 0; i < count; ++i) {
+ QT_FT_Span_& span = m_spans[i];
+
+ if (span.y != y) {
+ if (span.y != y + 1 && y != -1)
+ isRect = false;
+ y = span.y;
+ m_clipLines[y].spans = &span;
+ m_clipLines[y].count = 1;
+ } else
+ ++m_clipLines[y].count;
+
+ const int spanLeft = span.x;
+ const int spanRight = spanLeft + span.len;
+
+ if (spanLeft < xmin)
+ xmin = spanLeft;
+
+ if (spanRight > xmax)
+ xmax = spanRight;
+
+ if (spanLeft != firstLeft || spanRight != firstRight)
+ isRect = false;
+ }
+
+ if (isRect) {
+ hasRectClip = true;
+ clipRect.setRect(xmin, ymin, xmax - xmin, ymax - ymin);
+ }
+}
+
+/*
+ Convert \a rect to clip spans.
+ */
+void QClipData::setClipRect(const QRect &rect)
+{
+ if (hasRectClip && rect == clipRect)
+ return;
+
+// qDebug() << "setClipRect" << clipSpanHeight << count << allocated << rect;
+ hasRectClip = true;
+ hasRegionClip = false;
+ clipRect = rect;
+
+ xmin = rect.x();
+ xmax = rect.x() + rect.width();
+ ymin = qMin(rect.y(), clipSpanHeight);
+ ymax = qMin(rect.y() + rect.height(), clipSpanHeight);
+
+ if (m_spans) {
+ free(m_spans);
+ m_spans = 0;
+ }
+
+// qDebug() << xmin << xmax << ymin << ymax;
+}
+
+/*
+ Convert \a region to clip spans.
+ */
+void QClipData::setClipRegion(const QRegion &region)
+{
+ if (region.rectCount() == 1) {
+ setClipRect(region.rects().at(0));
+ return;
+ }
+
+ hasRegionClip = true;
+ hasRectClip = false;
+ clipRegion = region;
+
+ { // set bounding rect
+ const QRect rect = region.boundingRect();
+ xmin = rect.x();
+ xmax = rect.x() + rect.width();
+ ymin = rect.y();
+ ymax = rect.y() + rect.height();
+ }
+
+ if (m_spans) {
+ free(m_spans);
+ m_spans = 0;
+ }
+
+}
+
+/*!
+ \internal
+ spans must be sorted on y
+*/
+static const QSpan *qt_intersect_spans(const QClipData *clip, int *currentClip,
+ const QSpan *spans, const QSpan *end,
+ QSpan **outSpans, int available)
+{
+ const_cast<QClipData *>(clip)->initialize();
+
+ QSpan *out = *outSpans;
+
+ const QSpan *clipSpans = clip->m_spans + *currentClip;
+ const QSpan *clipEnd = clip->m_spans + clip->count;
+
+ while (available && spans < end ) {
+ if (clipSpans >= clipEnd) {
+ spans = end;
+ break;
+ }
+ if (clipSpans->y > spans->y) {
+ ++spans;
+ continue;
+ }
+ if (spans->y != clipSpans->y) {
+ if (spans->y < clip->count && clip->m_clipLines[spans->y].spans)
+ clipSpans = clip->m_clipLines[spans->y].spans;
+ else
+ ++clipSpans;
+ continue;
+ }
+ Q_ASSERT(spans->y == clipSpans->y);
+
+ int sx1 = spans->x;
+ int sx2 = sx1 + spans->len;
+ int cx1 = clipSpans->x;
+ int cx2 = cx1 + clipSpans->len;
+
+ if (cx1 < sx1 && cx2 < sx1) {
+ ++clipSpans;
+ continue;
+ } else if (sx1 < cx1 && sx2 < cx1) {
+ ++spans;
+ continue;
+ }
+ int x = qMax(sx1, cx1);
+ int len = qMin(sx2, cx2) - x;
+ if (len) {
+ out->x = qMax(sx1, cx1);
+ out->len = qMin(sx2, cx2) - out->x;
+ out->y = spans->y;
+ out->coverage = qt_div_255(spans->coverage * clipSpans->coverage);
+ ++out;
+ --available;
+ }
+ if (sx2 < cx2) {
+ ++spans;
+ } else {
+ ++clipSpans;
+ }
+ }
+
+ *outSpans = out;
+ *currentClip = clipSpans - clip->m_spans;
+ return spans;
+}
+
+static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userData)
+{
+// qDebug() << "qt_span_fill_clipped" << spanCount;
+ QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
+
+ Q_ASSERT(fillData->blend && fillData->unclipped_blend);
+
+ const int NSPANS = 256;
+ QSpan cspans[NSPANS];
+ int currentClip = 0;
+ const QSpan *end = spans + spanCount;
+ while (spans < end) {
+ QSpan *clipped = cspans;
+ spans = qt_intersect_spans(fillData->clip, &currentClip, spans, end, &clipped, NSPANS);
+// qDebug() << "processed " << processed << "clipped" << clipped-cspans
+// << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage;
+
+ if (clipped - cspans)
+ fillData->unclipped_blend(clipped - cspans, cspans, fillData);
+ }
+}
+
+/*
+ \internal
+ Clip spans to \a{clip}-rectangle.
+ Returns number of unclipped spans
+*/
+static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
+ const QRect &clip)
+{
+ const short minx = clip.left();
+ const short miny = clip.top();
+ const short maxx = clip.right();
+ const short maxy = clip.bottom();
+
+ int n = 0;
+ for (int i = 0; i < numSpans; ++i) {
+ if (spans[i].y > maxy)
+ break;
+ if (spans[i].y < miny
+ || spans[i].x > maxx
+ || spans[i].x + spans[i].len <= minx) {
+ continue;
+ }
+ if (spans[i].x < minx) {
+ spans[n].len = qMin(spans[i].len - (minx - spans[i].x), maxx - minx + 1);
+ spans[n].x = minx;
+ } else {
+ spans[n].x = spans[i].x;
+ spans[n].len = qMin(spans[i].len, ushort(maxx - spans[n].x + 1));
+ }
+ if (spans[n].len == 0)
+ continue;
+ spans[n].y = spans[i].y;
+ spans[n].coverage = spans[i].coverage;
+ ++n;
+ }
+ return n;
+}
+
+
+static void qt_span_fill_clipRect(int count, const QSpan *spans,
+ void *userData)
+{
+ QSpanData *fillData = reinterpret_cast<QSpanData *>(userData);
+ Q_ASSERT(fillData->blend && fillData->unclipped_blend);
+
+ Q_ASSERT(fillData->clip);
+ Q_ASSERT(!fillData->clip->clipRect.isEmpty());
+
+ // hw: check if this const_cast<> is safe!!!
+ count = qt_intersect_spans(const_cast<QSpan*>(spans), count,
+ fillData->clip->clipRect);
+ if (count > 0)
+ fillData->unclipped_blend(count, spans, fillData);
+}
+
+static void qt_span_clip(int count, const QSpan *spans, void *userData)
+{
+ ClipData *clipData = reinterpret_cast<ClipData *>(userData);
+
+// qDebug() << " qt_span_clip: " << count << clipData->operation;
+// for (int i = 0; i < qMin(count, 10); ++i) {
+// qDebug() << " " << spans[i].x << spans[i].y << spans[i].len << spans[i].coverage;
+// }
+
+ switch (clipData->operation) {
+
+ case Qt::IntersectClip:
+ {
+ QClipData *newClip = clipData->newClip;
+ newClip->initialize();
+
+ int currentClip = 0;
+ const QSpan *end = spans + count;
+ while (spans < end) {
+ QSpan *newspans = newClip->m_spans + newClip->count;
+ spans = qt_intersect_spans(clipData->oldClip, &currentClip, spans, end,
+ &newspans, newClip->allocated - newClip->count);
+ newClip->count = newspans - newClip->m_spans;
+ if (spans < end) {
+ newClip->m_spans = q_check_ptr((QSpan *)realloc(newClip->m_spans, newClip->allocated*2*sizeof(QSpan)));
+ newClip->allocated *= 2;
+ }
+ }
+ }
+ break;
+
+ case Qt::UniteClip:
+ case Qt::ReplaceClip:
+ clipData->newClip->appendSpans(spans, count);
+ break;
+ case Qt::NoClip:
+ break;
+ }
+}
+
+#ifndef QT_NO_DEBUG
+QImage QRasterBuffer::bufferImage() const
+{
+ QImage image(m_width, m_height, QImage::Format_ARGB32_Premultiplied);
+
+ for (int y = 0; y < m_height; ++y) {
+ uint *span = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
+
+ for (int x=0; x<m_width; ++x) {
+ uint argb = span[x];
+ image.setPixel(x, y, argb);
+ }
+ }
+ return image;
+}
+#endif
+
+
+void QRasterBuffer::flushToARGBImage(QImage *target) const
+{
+ int w = qMin(m_width, target->width());
+ int h = qMin(m_height, target->height());
+
+ for (int y=0; y<h; ++y) {
+ uint *sourceLine = (uint *)const_cast<QRasterBuffer *>(this)->scanLine(y);
+ QRgb *dest = (QRgb *) target->scanLine(y);
+ for (int x=0; x<w; ++x) {
+ QRgb pixel = sourceLine[x];
+ int alpha = qAlpha(pixel);
+ if (!alpha) {
+ dest[x] = 0;
+ } else {
+ dest[x] = (alpha << 24)
+ | ((255*qRed(pixel)/alpha) << 16)
+ | ((255*qGreen(pixel)/alpha) << 8)
+ | ((255*qBlue(pixel)/alpha) << 0);
+ }
+ }
+ }
+}
+
+
+class QGradientCache
+{
+ struct CacheInfo
+ {
+ inline CacheInfo(QGradientStops s, int op, QGradient::InterpolationMode mode) :
+ stops(s), opacity(op), interpolationMode(mode) {}
+ uint buffer[GRADIENT_STOPTABLE_SIZE];
+ QGradientStops stops;
+ int opacity;
+ QGradient::InterpolationMode interpolationMode;
+ };
+
+ typedef QMultiHash<quint64, CacheInfo> QGradientColorTableHash;
+
+public:
+ inline const uint *getBuffer(const QGradient &gradient, int opacity) {
+ quint64 hash_val = 0;
+
+ QGradientStops stops = gradient.stops();
+ for (int i = 0; i < stops.size() && i <= 2; i++)
+ hash_val += stops[i].second.rgba();
+
+ QMutexLocker lock(&mutex);
+ QGradientColorTableHash::const_iterator it = cache.constFind(hash_val);
+
+ if (it == cache.constEnd())
+ return addCacheElement(hash_val, gradient, opacity);
+ else {
+ do {
+ const CacheInfo &cache_info = it.value();
+ if (cache_info.stops == stops && cache_info.opacity == opacity && cache_info.interpolationMode == gradient.interpolationMode())
+ return cache_info.buffer;
+ ++it;
+ } while (it != cache.constEnd() && it.key() == hash_val);
+ // an exact match for these stops and opacity was not found, create new cache
+ return addCacheElement(hash_val, gradient, opacity);
+ }
+ }
+
+ inline int paletteSize() const { return GRADIENT_STOPTABLE_SIZE; }
+protected:
+ inline int maxCacheSize() const { return 60; }
+ inline void generateGradientColorTable(const QGradient& g,
+ uint *colorTable,
+ int size, int opacity) const;
+ uint *addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) {
+ if (cache.size() == maxCacheSize()) {
+ // may remove more than 1, but OK
+ cache.erase(cache.begin() + (qrand() % maxCacheSize()));
+ }
+ CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode());
+ generateGradientColorTable(gradient, cache_entry.buffer, paletteSize(), opacity);
+ return cache.insert(hash_val, cache_entry).value().buffer;
+ }
+
+ QGradientColorTableHash cache;
+ QMutex mutex;
+};
+
+void QGradientCache::generateGradientColorTable(const QGradient& gradient, uint *colorTable, int size, int opacity) const
+{
+ QGradientStops stops = gradient.stops();
+ int stopCount = stops.count();
+ Q_ASSERT(stopCount > 0);
+
+ bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation);
+
+ uint current_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity);
+ if (stopCount == 1) {
+ current_color = PREMUL(current_color);
+ for (int i = 0; i < size; ++i)
+ colorTable[i] = current_color;
+ return;
+ }
+
+ // The position where the gradient begins and ends
+ qreal begin_pos = stops[0].first;
+ qreal end_pos = stops[stopCount-1].first;
+
+ int pos = 0; // The position in the color table.
+ uint next_color;
+
+ qreal incr = 1 / qreal(size); // the double increment.
+ qreal dpos = 1.5 * incr; // current position in gradient stop list (0 to 1)
+
+ // Up to first point
+ colorTable[pos++] = PREMUL(current_color);
+ while (dpos <= begin_pos) {
+ colorTable[pos] = colorTable[pos - 1];
+ ++pos;
+ dpos += incr;
+ }
+
+ int current_stop = 0; // We always interpolate between current and current + 1.
+
+ qreal t; // position between current left and right stops
+ qreal t_delta; // the t increment per entry in the color table
+
+ if (dpos < end_pos) {
+ // Gradient area
+ while (dpos > stops[current_stop+1].first)
+ ++current_stop;
+
+ if (current_stop != 0)
+ current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
+ next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
+
+ if (colorInterpolation) {
+ current_color = PREMUL(current_color);
+ next_color = PREMUL(next_color);
+ }
+
+ qreal diff = stops[current_stop+1].first - stops[current_stop].first;
+ qreal c = (diff == 0) ? qreal(0) : 256 / diff;
+ t = (dpos - stops[current_stop].first) * c;
+ t_delta = incr * c;
+
+ while (true) {
+ Q_ASSERT(current_stop < stopCount);
+
+ int dist = qRound(t);
+ int idist = 256 - dist;
+
+ if (colorInterpolation)
+ colorTable[pos] = INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist);
+ else
+ colorTable[pos] = PREMUL(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
+
+ ++pos;
+ dpos += incr;
+
+ if (dpos >= end_pos)
+ break;
+
+ t += t_delta;
+
+ int skip = 0;
+ while (dpos > stops[current_stop+skip+1].first)
+ ++skip;
+
+ if (skip != 0) {
+ current_stop += skip;
+ if (skip == 1)
+ current_color = next_color;
+ else
+ current_color = ARGB_COMBINE_ALPHA(stops[current_stop].second.rgba(), opacity);
+ next_color = ARGB_COMBINE_ALPHA(stops[current_stop+1].second.rgba(), opacity);
+
+ if (colorInterpolation) {
+ if (skip != 1)
+ current_color = PREMUL(current_color);
+ next_color = PREMUL(next_color);
+ }
+
+ qreal diff = stops[current_stop+1].first - stops[current_stop].first;
+ qreal c = (diff == 0) ? qreal(0) : 256 / diff;
+ t = (dpos - stops[current_stop].first) * c;
+ t_delta = incr * c;
+ }
+ }
+ }
+
+ // After last point
+ current_color = PREMUL(ARGB_COMBINE_ALPHA(stops[stopCount - 1].second.rgba(), opacity));
+ while (pos < size - 1) {
+ colorTable[pos] = current_color;
+ ++pos;
+ }
+
+ // Make sure the last color stop is represented at the end of the table
+ colorTable[size - 1] = current_color;
+}
+
+Q_GLOBAL_STATIC(QGradientCache, qt_gradient_cache)
+
+
+void QSpanData::init(QRasterBuffer *rb, const QRasterPaintEngine *pe)
+{
+ rasterBuffer = rb;
+#ifdef Q_WS_QWS
+ rasterEngine = const_cast<QRasterPaintEngine *>(pe);
+#endif
+ type = None;
+ txop = 0;
+ bilinear = false;
+ m11 = m22 = m33 = 1.;
+ m12 = m13 = m21 = m23 = dx = dy = 0.0;
+ clip = pe ? pe->d_func()->clip() : 0;
+}
+
+Q_GUI_EXPORT extern QImage qt_imageForBrush(int brushStyle, bool invert);
+
+void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode)
+{
+ Qt::BrushStyle brushStyle = qbrush_style(brush);
+ switch (brushStyle) {
+ case Qt::SolidPattern: {
+ type = Solid;
+ QColor c = qbrush_color(brush);
+ QRgb rgba = c.rgba();
+ solid.color = PREMUL(ARGB_COMBINE_ALPHA(rgba, alpha));
+ if ((solid.color & 0xff000000) == 0
+ && compositionMode == QPainter::CompositionMode_SourceOver) {
+ type = None;
+ }
+ break;
+ }
+
+ case Qt::LinearGradientPattern:
+ {
+ type = LinearGradient;
+ const QLinearGradient *g = static_cast<const QLinearGradient *>(brush.gradient());
+ gradient.alphaColor = !brush.isOpaque() || alpha != 256;
+ gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
+ gradient.spread = g->spread();
+
+ QLinearGradientData &linearData = gradient.linear;
+
+ linearData.origin.x = g->start().x();
+ linearData.origin.y = g->start().y();
+ linearData.end.x = g->finalStop().x();
+ linearData.end.y = g->finalStop().y();
+ break;
+ }
+
+ case Qt::RadialGradientPattern:
+ {
+ type = RadialGradient;
+ const QRadialGradient *g = static_cast<const QRadialGradient *>(brush.gradient());
+ gradient.alphaColor = !brush.isOpaque() || alpha != 256;
+ gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
+ gradient.spread = g->spread();
+
+ QRadialGradientData &radialData = gradient.radial;
+
+ QPointF center = g->center();
+ radialData.center.x = center.x();
+ radialData.center.y = center.y();
+ QPointF focal = g->focalPoint();
+ radialData.focal.x = focal.x();
+ radialData.focal.y = focal.y();
+ radialData.radius = g->radius();
+ }
+ break;
+
+ case Qt::ConicalGradientPattern:
+ {
+ type = ConicalGradient;
+ const QConicalGradient *g = static_cast<const QConicalGradient *>(brush.gradient());
+ gradient.alphaColor = !brush.isOpaque() || alpha != 256;
+ gradient.colorTable = const_cast<uint*>(qt_gradient_cache()->getBuffer(*g, alpha));
+ gradient.spread = QGradient::RepeatSpread;
+
+ QConicalGradientData &conicalData = gradient.conical;
+
+ QPointF center = g->center();
+ conicalData.center.x = center.x();
+ conicalData.center.y = center.y();
+ conicalData.angle = g->angle() * 2 * Q_PI / 360.0;
+ }
+ break;
+
+ case Qt::Dense1Pattern:
+ case Qt::Dense2Pattern:
+ case Qt::Dense3Pattern:
+ case Qt::Dense4Pattern:
+ case Qt::Dense5Pattern:
+ case Qt::Dense6Pattern:
+ case Qt::Dense7Pattern:
+ case Qt::HorPattern:
+ case Qt::VerPattern:
+ case Qt::CrossPattern:
+ case Qt::BDiagPattern:
+ case Qt::FDiagPattern:
+ case Qt::DiagCrossPattern:
+ type = Texture;
+ if (!tempImage)
+ tempImage = new QImage();
+ *tempImage = rasterBuffer->colorizeBitmap(qt_imageForBrush(brushStyle, true), brush.color());
+ initTexture(tempImage, alpha, QTextureData::Tiled);
+ break;
+ case Qt::TexturePattern:
+ type = Texture;
+ if (!tempImage)
+ tempImage = new QImage();
+
+ if (qHasPixmapTexture(brush) && brush.texture().isQBitmap())
+ *tempImage = rasterBuffer->colorizeBitmap(brush.textureImage(), brush.color());
+ else
+ *tempImage = brush.textureImage();
+ initTexture(tempImage, alpha, QTextureData::Tiled, tempImage->rect());
+ break;
+
+ case Qt::NoBrush:
+ default:
+ type = None;
+ break;
+ }
+ adjustSpanMethods();
+}
+
+void QSpanData::adjustSpanMethods()
+{
+ bitmapBlit = 0;
+ alphamapBlit = 0;
+ alphaRGBBlit = 0;
+
+ fillRect = 0;
+
+ switch(type) {
+ case None:
+ unclipped_blend = 0;
+ break;
+ case Solid:
+ unclipped_blend = rasterBuffer->drawHelper->blendColor;
+ bitmapBlit = rasterBuffer->drawHelper->bitmapBlit;
+ alphamapBlit = rasterBuffer->drawHelper->alphamapBlit;
+ alphaRGBBlit = rasterBuffer->drawHelper->alphaRGBBlit;
+ fillRect = rasterBuffer->drawHelper->fillRect;
+ break;
+ case LinearGradient:
+ case RadialGradient:
+ case ConicalGradient:
+ unclipped_blend = rasterBuffer->drawHelper->blendGradient;
+ break;
+ case Texture:
+#ifdef Q_WS_QWS
+#ifndef QT_NO_RASTERCALLBACKS
+ if (!rasterBuffer->buffer())
+ unclipped_blend = qBlendTextureCallback;
+ else
+#endif
+ unclipped_blend = qBlendTexture;
+#else
+ unclipped_blend = qBlendTexture;
+#endif
+ if (!texture.imageData)
+ unclipped_blend = 0;
+
+ break;
+ }
+ // setup clipping
+ if (!unclipped_blend) {
+ blend = 0;
+ } else if (!clip) {
+ blend = unclipped_blend;
+ } else if (clip->hasRectClip) {
+ blend = clip->clipRect.isEmpty() ? 0 : qt_span_fill_clipRect;
+ } else {
+ blend = qt_span_fill_clipped;
+ }
+}
+
+void QSpanData::setupMatrix(const QTransform &matrix, int bilin)
+{
+ QTransform delta;
+ // make sure we round off correctly in qdrawhelper.cpp
+ delta.translate(1.0 / 65536, 1.0 / 65536);
+
+ QTransform inv = (delta * matrix).inverted();
+ m11 = inv.m11();
+ m12 = inv.m12();
+ m13 = inv.m13();
+ m21 = inv.m21();
+ m22 = inv.m22();
+ m23 = inv.m23();
+ m33 = inv.m33();
+ dx = inv.dx();
+ dy = inv.dy();
+ txop = inv.type();
+ bilinear = bilin;
+
+ const bool affine = !m13 && !m23;
+ fast_matrix = affine
+ && m11 * m11 + m21 * m21 < 1e4
+ && m12 * m12 + m22 * m22 < 1e4
+ && qAbs(dx) < 1e4
+ && qAbs(dy) < 1e4;
+
+ adjustSpanMethods();
+}
+
+extern const QVector<QRgb> *qt_image_colortable(const QImage &image);
+
+void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _type, const QRect &sourceRect)
+{
+ const QImageData *d = const_cast<QImage *>(image)->data_ptr();
+ if (!d || d->height == 0) {
+ texture.imageData = 0;
+ texture.width = 0;
+ texture.height = 0;
+ texture.x1 = 0;
+ texture.y1 = 0;
+ texture.x2 = 0;
+ texture.y2 = 0;
+ texture.bytesPerLine = 0;
+ texture.format = QImage::Format_Invalid;
+ texture.colorTable = 0;
+ texture.hasAlpha = alpha != 256;
+ } else {
+ texture.imageData = d->data;
+ texture.width = d->width;
+ texture.height = d->height;
+
+ if (sourceRect.isNull()) {
+ texture.x1 = 0;
+ texture.y1 = 0;
+ texture.x2 = texture.width;
+ texture.y2 = texture.height;
+ } else {
+ texture.x1 = sourceRect.x();
+ texture.y1 = sourceRect.y();
+ texture.x2 = qMin(texture.x1 + sourceRect.width(), d->width);
+ texture.y2 = qMin(texture.y1 + sourceRect.height(), d->height);
+ }
+
+ texture.bytesPerLine = d->bytes_per_line;
+
+ texture.format = d->format;
+ texture.colorTable = (d->format <= QImage::Format_Indexed8 && !d->colortable.isEmpty()) ? &d->colortable : 0;
+ texture.hasAlpha = image->hasAlphaChannel() || alpha != 256;
+ }
+ texture.const_alpha = alpha;
+ texture.type = _type;
+
+ adjustSpanMethods();
+}
+
+#ifdef Q_WS_WIN
+
+
+#endif
+
+
+/*!
+ \internal
+
+ Draws a line using the floating point midpoint algorithm. The line
+ \a line is already in device coords at this point.
+*/
+
+static void drawLine_midpoint_i(int x1, int y1, int x2, int y2, ProcessSpans span_func, QSpanData *data,
+ LineDrawMode style, const QIntRect &devRect)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << " - drawLine_midpoint_i" << QLine(QPoint(x1, y1), QPoint(x2, y2));
+#endif
+
+ int x, y;
+ int dx, dy, d, incrE, incrNE;
+
+ dx = x2 - x1;
+ dy = y2 - y1;
+
+ const int NSPANS = 256;
+ QT_FT_Span spans[NSPANS];
+ int current = 0;
+ bool ordered = true;
+
+ if (dy == 0) {
+ // specialcase horizontal lines
+ if (y1 >= devRect.y1 && y1 < devRect.y2) {
+ int start = qMax(devRect.x1, qMin(x1, x2));
+ int stop = qMax(x1, x2) + 1;
+ int stop_clipped = qMin(devRect.x2, stop);
+ int len = stop_clipped - start;
+ if (style == LineDrawNormal && stop == stop_clipped)
+ len--;
+ if (len > 0) {
+ spans[0].x = ushort(start);
+ spans[0].len = ushort(len);
+ spans[0].y = y1;
+ spans[0].coverage = 255;
+ span_func(1, spans, data);
+ }
+ }
+ return;
+ } else if (dx == 0) {
+ // specialcase vertical lines
+ if (x1 >= devRect.x1 && x1 < devRect.x2) {
+ int start = qMax(devRect.y1, qMin(y1, y2));
+ int stop = qMax(y1, y2) + 1;
+ int stop_clipped = qMin(devRect.y2, stop);
+ int len = stop_clipped - start;
+ if (style == LineDrawNormal && stop == stop_clipped)
+ len--;
+ // hw: create spans directly instead to possibly avoid clipping
+ if (len > 0)
+ fillRect_normalized(QRect(x1, start, 1, len).normalized(), data, 0);
+ }
+ return;
+ }
+
+
+ if (qAbs(dx) >= qAbs(dy)) { /* if x is the major axis: */
+
+ if (x2 < x1) { /* if coordinates are out of order */
+ qt_swap_int(x1, x2);
+ dx = -dx;
+
+ qt_swap_int(y1, y2);
+ dy = -dy;
+ }
+
+ int x_lower_limit = - 128;
+ if (x1 < x_lower_limit) {
+ int cy = dy * (x_lower_limit - x1) / dx + y1;
+ drawLine_midpoint_i(x_lower_limit, cy, x2, y2, span_func, data, style, devRect);
+ return;
+ }
+
+ if (style == LineDrawNormal)
+ --x2;
+
+ // In the loops below we increment before call the span function so
+ // we need to stop one pixel before
+ x2 = qMin(x2, devRect.x2 - 1);
+
+ // completely clipped, so abort
+ if (x2 <= x1) {
+ return;
+ }
+
+ int x = x1;
+ int y = y1;
+
+ if (y2 <= y1)
+ ordered = false;
+
+ {
+ const int index = (ordered ? current : NSPANS - 1 - current);
+ spans[index].coverage = 255;
+ spans[index].x = x;
+ spans[index].y = y;
+
+ if (x >= devRect.x1 && y >= devRect.y1 && y < devRect.y2)
+ spans[index].len = 1;
+ else
+ spans[index].len = 0;
+ }
+
+ if (y2 > y1) { // 315 -> 360 and 135 -> 180 (unit circle degrees)
+ y2 = qMin(y2, devRect.y2 - 1);
+
+ incrE = dy * 2;
+ d = incrE - dx;
+ incrNE = (dy - dx) * 2;
+
+ if (y > y2)
+ goto flush_and_return;
+
+ while (x < x2) {
+ ++x;
+ if (d > 0) {
+ if (spans[current].len > 0)
+ ++current;
+ if (current == NSPANS) {
+ span_func(NSPANS, spans, data);
+ current = 0;
+ }
+
+ ++y;
+ d += incrNE;
+ if (y > y2)
+ goto flush_and_return;
+
+ spans[current].len = 0;
+ spans[current].coverage = 255;
+ spans[current].x = x;
+ spans[current].y = y;
+ } else {
+ d += incrE;
+ if (x == devRect.x1)
+ spans[current].x = devRect.x1;
+ }
+
+ if (x < devRect.x1 || y < devRect.y1)
+ continue;
+
+ Q_ASSERT(x<devRect.x2);
+ Q_ASSERT(y<devRect.y2);
+ Q_ASSERT(spans[current].y == y);
+ spans[current].len++;
+ }
+ if (spans[current].len > 0) {
+ ++current;
+ }
+ } else { // 0-45 and 180->225 (unit circle degrees)
+
+ y1 = qMin(y1, devRect.y2 - 1);
+
+ incrE = dy * 2;
+ d = incrE + dx;
+ incrNE = (dy + dx) * 2;
+
+ if (y < devRect.y1)
+ goto flush_and_return;
+
+ while (x < x2) {
+ ++x;
+ if (d < 0) {
+ if (spans[NSPANS - 1 - current].len > 0)
+ ++current;
+ if (current == NSPANS) {
+ span_func(NSPANS, spans, data);
+ current = 0;
+ }
+
+ --y;
+ d += incrNE;
+ if (y < devRect.y1)
+ goto flush_and_return;
+
+ const int index = NSPANS - 1 - current;
+ spans[index].len = 0;
+ spans[index].coverage = 255;
+ spans[index].x = x;
+ spans[index].y = y;
+ } else {
+ d += incrE;
+ if (x == devRect.x1)
+ spans[NSPANS - 1 - current].x = devRect.x1;
+ }
+
+ if (x < devRect.x1 || y > y1)
+ continue;
+
+ Q_ASSERT(x<devRect.x2 && y<devRect.y2);
+ Q_ASSERT(spans[NSPANS - 1 - current].y == y);
+ spans[NSPANS - 1 - current].len++;
+ }
+ if (spans[NSPANS - 1 - current].len > 0) {
+ ++current;
+ }
+ }
+
+ } else {
+
+ // if y is the major axis:
+
+ if (y2 < y1) { /* if coordinates are out of order */
+ qt_swap_int(y1, y2);
+ dy = -dy;
+
+ qt_swap_int(x1, x2);
+ dx = -dx;
+ }
+
+ int y_lower_limit = - 128;
+ if (y1 < y_lower_limit) {
+ int cx = dx * (y_lower_limit - y1) / dy + x1;
+ drawLine_midpoint_i(cx, y_lower_limit, x2, y2, span_func, data, style, devRect);
+ return;
+ }
+
+ if (style == LineDrawNormal)
+ --y2;
+
+ // In the loops below we increment before call the span function so
+ // we need to stop one pixel before
+ y2 = qMin(y2, devRect.y2 - 1);
+
+ // completely clipped, so abort
+ if (y2 <= y1) {
+ return;
+ }
+
+ x = x1;
+ y = y1;
+
+ if (x>=devRect.x1 && y>=devRect.y1 && x < devRect.x2) {
+ Q_ASSERT(x >= devRect.x1 && y >= devRect.y1 && x < devRect.x2 && y < devRect.y2);
+ if (current == NSPANS) {
+ span_func(NSPANS, spans, data);
+ current = 0;
+ }
+ spans[current].len = 1;
+ spans[current].coverage = 255;
+ spans[current].x = x;
+ spans[current].y = y;
+ ++current;
+ }
+
+ if (x2 > x1) { // 90 -> 135 and 270 -> 315 (unit circle degrees)
+ x2 = qMin(x2, devRect.x2 - 1);
+ incrE = dx * 2;
+ d = incrE - dy;
+ incrNE = (dx - dy) * 2;
+
+ if (x > x2)
+ goto flush_and_return;
+
+ while (y < y2) {
+ if (d > 0) {
+ ++x;
+ d += incrNE;
+ if (x > x2)
+ goto flush_and_return;
+ } else {
+ d += incrE;
+ }
+ ++y;
+ if (x < devRect.x1 || y < devRect.y1)
+ continue;
+ Q_ASSERT(x<devRect.x2 && y<devRect.y2);
+ if (current == NSPANS) {
+ span_func(NSPANS, spans, data);
+ current = 0;
+ }
+ spans[current].len = 1;
+ spans[current].coverage = 255;
+ spans[current].x = x;
+ spans[current].y = y;
+ ++current;
+ }
+ } else { // 45 -> 90 and 225 -> 270 (unit circle degrees)
+ x1 = qMin(x1, devRect.x2 - 1);
+ incrE = dx * 2;
+ d = incrE + dy;
+ incrNE = (dx + dy) * 2;
+
+ if (x < devRect.x1)
+ goto flush_and_return;
+
+ while (y < y2) {
+ if (d < 0) {
+ --x;
+ d += incrNE;
+ if (x < devRect.x1)
+ goto flush_and_return;
+ } else {
+ d += incrE;
+ }
+ ++y;
+ if (y < devRect.y1 || x > x1)
+ continue;
+ Q_ASSERT(x>=devRect.x1 && x<devRect.x2 && y>=devRect.y1 && y<devRect.y2);
+ if (current == NSPANS) {
+ span_func(NSPANS, spans, data);
+ current = 0;
+ }
+ spans[current].len = 1;
+ spans[current].coverage = 255;
+ spans[current].x = x;
+ spans[current].y = y;
+ ++current;
+ }
+ }
+ }
+flush_and_return:
+ if (current > 0)
+ span_func(current, ordered ? spans : spans + (NSPANS - current), data);
+}
+
+static void offset_pattern(int offset, bool *inDash, int *dashIndex, int *currentOffset, const QVarLengthArray<qreal> &pattern)
+{
+ while (offset--) {
+ if (--*currentOffset == 0) {
+ *inDash = !*inDash;
+ *dashIndex = ((*dashIndex + 1) % pattern.size());
+ *currentOffset = int(pattern[*dashIndex]);
+ }
+ }
+}
+
+static void drawLine_midpoint_dashed_i(int x1, int y1, int x2, int y2,
+ QPen *pen,
+ ProcessSpans span_func, QSpanData *data,
+ LineDrawMode style, const QIntRect &devRect,
+ int *patternOffset)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << " - drawLine_midpoint_dashed_i" << x1 << y1 << x2 << y2 << *patternOffset;
+#endif
+
+ int x, y;
+ int dx, dy, d, incrE, incrNE;
+
+ dx = x2 - x1;
+ dy = y2 - y1;
+
+ Q_ASSERT(*patternOffset >= 0);
+
+ const QVector<qreal> penPattern = pen->dashPattern();
+ QVarLengthArray<qreal> pattern(penPattern.size());
+
+ int patternLength = 0;
+ for (int i = 0; i < penPattern.size(); ++i)
+ patternLength += qMax<qreal>(1.0, (penPattern.at(i)));
+
+ // pattern must be reversed if coordinates are out of order
+ int reverseLength = -1;
+ if (dy == 0 && x1 > x2)
+ reverseLength = x1 - x2;
+ else if (dx == 0 && y1 > y2)
+ reverseLength = y1 - y2;
+ else if (qAbs(dx) >= qAbs(dy) && x2 < x1) // x major axis
+ reverseLength = qAbs(dx);
+ else if (qAbs(dy) >= qAbs(dx) && y2 < y1) // y major axis
+ reverseLength = qAbs(dy);
+
+ const bool reversed = (reverseLength > -1);
+ if (reversed) { // reverse pattern
+ for (int i = 0; i < penPattern.size(); ++i)
+ pattern[penPattern.size() - 1 - i] = qMax<qreal>(1.0, penPattern.at(i));
+
+ *patternOffset = (patternLength - 1 - *patternOffset);
+ *patternOffset += patternLength - (reverseLength % patternLength);
+ *patternOffset = *patternOffset % patternLength;
+ } else {
+ for (int i = 0; i < penPattern.size(); ++i)
+ pattern[i] = qMax<qreal>(1.0, penPattern.at(i));
+ }
+
+ int dashIndex = 0;
+ bool inDash = !reversed;
+ int currPattern = int(pattern[dashIndex]);
+
+ // adjust pattern for offset
+ offset_pattern(*patternOffset, &inDash, &dashIndex, &currPattern, pattern);
+
+ const int NSPANS = 256;
+ QT_FT_Span spans[NSPANS];
+ int current = 0;
+ bool ordered = true;
+
+ if (dy == 0) {
+ // specialcase horizontal lines
+ if (y1 >= devRect.y1 && y1 < devRect.y2) {
+ int start_unclipped = qMin(x1, x2);
+ int start = qMax(devRect.x1, start_unclipped);
+ int stop = qMax(x1, x2) + 1;
+ int stop_clipped = qMin(devRect.x2, stop);
+ int len = stop_clipped - start;
+ if (style == LineDrawNormal && stop == stop_clipped)
+ len--;
+
+ // adjust pattern for starting offset
+ offset_pattern(start - start_unclipped, &inDash, &dashIndex, &currPattern, pattern);
+
+ if (len > 0) {
+ int x = start;
+ while (x < stop_clipped) {
+ if (current == NSPANS) {
+ span_func(NSPANS, spans, data);
+ current = 0;
+ }
+ const int dash = qMin(currPattern, stop_clipped - x);
+ if (inDash) {
+ spans[current].x = ushort(x);
+ spans[current].len = ushort(dash);
+ spans[current].y = y1;
+ spans[current].coverage = 255;
+ ++current;
+ }
+ if (dash < currPattern) {
+ currPattern -= dash;
+ } else {
+ dashIndex = (dashIndex + 1) % pattern.size();
+ currPattern = int(pattern[dashIndex]);
+ inDash = !inDash;
+ }
+ x += dash;
+ }
+ }
+ }
+ goto flush_and_return;
+ } else if (dx == 0) {
+ if (x1 >= devRect.x1 && x1 < devRect.x2) {
+ int start_unclipped = qMin(y1, y2);
+ int start = qMax(devRect.y1, start_unclipped);
+ int stop = qMax(y1, y2) + 1;
+ int stop_clipped = qMin(devRect.y2, stop);
+ if (style == LineDrawNormal && stop == stop_clipped)
+ --stop;
+ else
+ stop = stop_clipped;
+
+ // adjust pattern for starting offset
+ offset_pattern(start - start_unclipped, &inDash, &dashIndex, &currPattern, pattern);
+
+ // loop over dashes
+ int y = start;
+ while (y < stop) {
+ const int dash = qMin(currPattern, stop - y);
+ if (inDash) {
+ for (int i = 0; i < dash; ++i) {
+ if (current == NSPANS) {
+ span_func(NSPANS, spans, data);
+ current = 0;
+ }
+ spans[current].x = x1;
+ spans[current].len = 1;
+ spans[current].coverage = 255;
+ spans[current].y = ushort(y + i);
+ ++current;
+ }
+ }
+ if (dash < currPattern) {
+ currPattern -= dash;
+ } else {
+ dashIndex = (dashIndex + 1) % pattern.size();
+ currPattern = int(pattern[dashIndex]);
+ inDash = !inDash;
+ }
+ y += dash;
+ }
+ }
+ goto flush_and_return;
+ }
+
+ if (qAbs(dx) >= qAbs(dy)) { /* if x is the major axis: */
+
+ if (x2 < x1) { /* if coordinates are out of order */
+ qt_swap_int(x1, x2);
+ dx = -dx;
+
+ qt_swap_int(y1, y2);
+ dy = -dy;
+ }
+
+ if (style == LineDrawNormal)
+ --x2;
+
+ // In the loops below we increment before call the span function so
+ // we need to stop one pixel before
+ x2 = qMin(x2, devRect.x2 - 1);
+
+ // completely clipped, so abort
+ if (x2 <= x1)
+ goto flush_and_return;
+
+ int x = x1;
+ int y = y1;
+
+ if (x >= devRect.x1 && y >= devRect.y1 && y < devRect.y2) {
+ Q_ASSERT(x < devRect.x2);
+ if (inDash) {
+ if (current == NSPANS) {
+ span_func(NSPANS, spans, data);
+ current = 0;
+ }
+ spans[current].len = 1;
+ spans[current].coverage = 255;
+ spans[current].x = x;
+ spans[current].y = y;
+ ++current;
+ }
+ if (--currPattern <= 0) {
+ inDash = !inDash;
+ dashIndex = (dashIndex + 1) % pattern.size();
+ currPattern = int(pattern[dashIndex]);
+ }
+ }
+
+ if (y2 > y1) { // 315 -> 360 and 135 -> 180 (unit circle degrees)
+ y2 = qMin(y2, devRect.y2 - 1);
+
+ incrE = dy * 2;
+ d = incrE - dx;
+ incrNE = (dy - dx) * 2;
+
+ if (y > y2)
+ goto flush_and_return;
+
+ while (x < x2) {
+ if (d > 0) {
+ ++y;
+ d += incrNE;
+ if (y > y2)
+ goto flush_and_return;
+ } else {
+ d += incrE;
+ }
+ ++x;
+
+ const bool skip = x < devRect.x1 || y < devRect.y1;
+ Q_ASSERT(skip || (x < devRect.x2 && y < devRect.y2));
+ if (inDash && !skip) {
+ if (current == NSPANS) {
+ span_func(NSPANS, spans, data);
+ current = 0;
+ }
+ spans[current].len = 1;
+ spans[current].coverage = 255;
+ spans[current].x = x;
+ spans[current].y = y;
+ ++current;
+ }
+ if (--currPattern <= 0) {
+ inDash = !inDash;
+ dashIndex = (dashIndex + 1) % pattern.size();
+ currPattern = int(pattern[dashIndex]);
+ }
+ }
+ } else { // 0-45 and 180->225 (unit circle degrees)
+ y1 = qMin(y1, devRect.y2 - 1);
+
+ incrE = dy * 2;
+ d = incrE + dx;
+ incrNE = (dy + dx) * 2;
+
+ if (y < devRect.y1)
+ goto flush_and_return;
+
+ while (x < x2) {
+ if (d < 0) {
+ if (current > 0) {
+ span_func(current, spans, data);
+ current = 0;
+ }
+
+ --y;
+ d += incrNE;
+ if (y < devRect.y1)
+ goto flush_and_return;
+ } else {
+ d += incrE;
+ }
+ ++x;
+
+ const bool skip = x < devRect.x1 || y > y1;
+ Q_ASSERT(skip || (x < devRect.x2 && y < devRect.y2));
+ if (inDash && !skip) {
+ if (current == NSPANS) {
+ span_func(NSPANS, spans, data);
+ current = 0;
+ }
+ spans[current].len = 1;
+ spans[current].coverage = 255;
+ spans[current].x = x;
+ spans[current].y = y;
+ ++current;
+ }
+ if (--currPattern <= 0) {
+ inDash = !inDash;
+ dashIndex = (dashIndex + 1) % pattern.size();
+ currPattern = int(pattern[dashIndex]);
+ }
+ }
+ }
+ } else {
+
+ // if y is the major axis:
+
+ if (y2 < y1) { /* if coordinates are out of order */
+ qt_swap_int(y1, y2);
+ dy = -dy;
+
+ qt_swap_int(x1, x2);
+ dx = -dx;
+ }
+
+ if (style == LineDrawNormal)
+ --y2;
+
+ // In the loops below we increment before call the span function so
+ // we need to stop one pixel before
+ y2 = qMin(y2, devRect.y2 - 1);
+
+ // completely clipped, so abort
+ if (y2 <= y1)
+ goto flush_and_return;
+
+ x = x1;
+ y = y1;
+
+ if (x>=devRect.x1 && y>=devRect.y1 && x < devRect.x2) {
+ Q_ASSERT(x < devRect.x2);
+ if (inDash) {
+ if (current == NSPANS) {
+ span_func(NSPANS, spans, data);
+ current = 0;
+ }
+ spans[current].len = 1;
+ spans[current].coverage = 255;
+ spans[current].x = x;
+ spans[current].y = y;
+ ++current;
+ }
+ if (--currPattern <= 0) {
+ inDash = !inDash;
+ dashIndex = (dashIndex + 1) % pattern.size();
+ currPattern = int(pattern[dashIndex]);
+ }
+ }
+
+ if (x2 > x1) { // 90 -> 135 and 270 -> 315 (unit circle degrees)
+ x2 = qMin(x2, devRect.x2 - 1);
+ incrE = dx * 2;
+ d = incrE - dy;
+ incrNE = (dx - dy) * 2;
+
+ if (x > x2)
+ goto flush_and_return;
+
+ while (y < y2) {
+ if (d > 0) {
+ ++x;
+ d += incrNE;
+ if (x > x2)
+ goto flush_and_return;
+ } else {
+ d += incrE;
+ }
+ ++y;
+ const bool skip = x < devRect.x1 || y < devRect.y1;
+ Q_ASSERT(skip || (x < devRect.x2 && y < devRect.y2));
+ if (inDash && !skip) {
+ if (current == NSPANS) {
+ span_func(NSPANS, spans, data);
+ current = 0;
+ }
+ spans[current].len = 1;
+ spans[current].coverage = 255;
+ spans[current].x = x;
+ spans[current].y = y;
+ ++current;
+ }
+ if (--currPattern <= 0) {
+ inDash = !inDash;
+ dashIndex = (dashIndex + 1) % pattern.size();
+ currPattern = int(pattern[dashIndex]);
+ }
+ }
+ } else { // 45 -> 90 and 225 -> 270 (unit circle degrees)
+ x1 = qMin(x1, devRect.x2 - 1);
+ incrE = dx * 2;
+ d = incrE + dy;
+ incrNE = (dx + dy) * 2;
+
+ if (x < devRect.x1)
+ goto flush_and_return;
+
+ while (y < y2) {
+ if (d < 0) {
+ --x;
+ d += incrNE;
+ if (x < devRect.x1)
+ goto flush_and_return;
+ } else {
+ d += incrE;
+ }
+ ++y;
+ const bool skip = y < devRect.y1 || x > x1;
+ Q_ASSERT(skip || (x >= devRect.x1 && x < devRect.x2 && y < devRect.y2));
+ if (inDash && !skip) {
+ if (current == NSPANS) {
+ span_func(NSPANS, spans, data);
+ current = 0;
+ }
+ spans[current].len = 1;
+ spans[current].coverage = 255;
+ spans[current].x = x;
+ spans[current].y = y;
+ ++current;
+ }
+ if (--currPattern <= 0) {
+ inDash = !inDash;
+ dashIndex = (dashIndex + 1) % pattern.size();
+ currPattern = int(pattern[dashIndex]);
+ }
+ }
+ }
+ }
+flush_and_return:
+ if (current > 0)
+ span_func(current, ordered ? spans : spans + (NSPANS - current), data);
+
+ // adjust offset
+ if (reversed) {
+ *patternOffset = (patternLength - 1 - *patternOffset);
+ } else {
+ *patternOffset = 0;
+ for (int i = 0; i <= dashIndex; ++i)
+ *patternOffset += int(pattern[i]);
+ *patternOffset += patternLength - currPattern - 1;
+ *patternOffset = (*patternOffset % patternLength);
+ }
+}
+
+/*!
+ \internal
+ \a x and \a y is relative to the midpoint of \a rect.
+*/
+static inline void drawEllipsePoints(int x, int y, int length,
+ const QRect &rect,
+ const QRect &clip,
+ ProcessSpans pen_func, ProcessSpans brush_func,
+ QSpanData *pen_data, QSpanData *brush_data)
+{
+ if (length == 0)
+ return;
+
+ QT_FT_Span outline[4];
+ const int midx = rect.x() + (rect.width() + 1) / 2;
+ const int midy = rect.y() + (rect.height() + 1) / 2;
+
+ x = x + midx;
+ y = midy - y;
+
+ // topleft
+ outline[0].x = midx + (midx - x) - (length - 1) - (rect.width() & 0x1);
+ outline[0].len = qMin(length, x - outline[0].x);
+ outline[0].y = y;
+ outline[0].coverage = 255;
+
+ // topright
+ outline[1].x = x;
+ outline[1].len = length;
+ outline[1].y = y;
+ outline[1].coverage = 255;
+
+ // bottomleft
+ outline[2].x = outline[0].x;
+ outline[2].len = outline[0].len;
+ outline[2].y = midy + (midy - y) - (rect.height() & 0x1);
+ outline[2].coverage = 255;
+
+ // bottomright
+ outline[3].x = x;
+ outline[3].len = length;
+ outline[3].y = outline[2].y;
+ outline[3].coverage = 255;
+
+ if (brush_func && outline[0].x + outline[0].len < outline[1].x) {
+ QT_FT_Span fill[2];
+
+ // top fill
+ fill[0].x = outline[0].x + outline[0].len - 1;
+ fill[0].len = qMax(0, outline[1].x - fill[0].x);
+ fill[0].y = outline[1].y;
+ fill[0].coverage = 255;
+
+ // bottom fill
+ fill[1].x = outline[2].x + outline[2].len - 1;
+ fill[1].len = qMax(0, outline[3].x - fill[1].x);
+ fill[1].y = outline[3].y;
+ fill[1].coverage = 255;
+
+ int n = (fill[0].y >= fill[1].y ? 1 : 2);
+ n = qt_intersect_spans(fill, n, clip);
+ if (n > 0)
+ brush_func(n, fill, brush_data);
+ }
+ if (pen_func) {
+ int n = (outline[1].y >= outline[2].y ? 2 : 4);
+ n = qt_intersect_spans(outline, n, clip);
+ if (n > 0)
+ pen_func(n, outline, pen_data);
+ }
+}
+
+/*!
+ \internal
+ Draws an ellipse using the integer point midpoint algorithm.
+*/
+static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip,
+ ProcessSpans pen_func, ProcessSpans brush_func,
+ QSpanData *pen_data, QSpanData *brush_data)
+{
+ const qreal a = qreal(rect.width()) / 2;
+ const qreal b = qreal(rect.height()) / 2;
+ qreal d = b*b - (a*a*b) + 0.25*a*a;
+
+ int x = 0;
+ int y = (rect.height() + 1) / 2;
+ int startx = x;
+
+ // region 1
+ while (a*a*(2*y - 1) > 2*b*b*(x + 1)) {
+ if (d < 0) { // select E
+ d += b*b*(2*x + 3);
+ ++x;
+ } else { // select SE
+ d += b*b*(2*x + 3) + a*a*(-2*y + 2);
+ drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
+ pen_func, brush_func, pen_data, brush_data);
+ startx = ++x;
+ --y;
+ }
+ }
+ drawEllipsePoints(startx, y, x - startx + 1, rect, clip,
+ pen_func, brush_func, pen_data, brush_data);
+
+ // region 2
+ d = b*b*(x + 0.5)*(x + 0.5) + a*a*((y - 1)*(y - 1) - b*b);
+ const int miny = rect.height() & 0x1;
+ while (y > miny) {
+ if (d < 0) { // select SE
+ d += b*b*(2*x + 2) + a*a*(-2*y + 3);
+ ++x;
+ } else { // select S
+ d += a*a*(-2*y + 3);
+ }
+ --y;
+ drawEllipsePoints(x, y, 1, rect, clip,
+ pen_func, brush_func, pen_data, brush_data);
+ }
+}
+
+/*!
+ \fn void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount)
+ \overload
+
+ Draws the first \a pointCount points in the buffer \a points
+
+ The default implementation converts the first \a pointCount QPoints in \a points
+ to QPointFs and calls the floating point version of drawPoints.
+*/
+
+/*!
+ \fn void QRasterPaintEngine::drawEllipse(const QRect &rect)
+ \overload
+
+ Reimplement this function to draw the largest ellipse that can be
+ contained within rectangle \a rect.
+*/
+
+#ifdef QT_DEBUG_DRAW
+void dumpClip(int width, int height, const QClipData *clip)
+{
+ QImage clipImg(width, height, QImage::Format_ARGB32_Premultiplied);
+ clipImg.fill(0xffff0000);
+
+ int x0 = width;
+ int x1 = 0;
+ int y0 = height;
+ int y1 = 0;
+
+ ((QClipData *) clip)->spans(); // Force allocation of the spans structure...
+
+ for (int i = 0; i < clip->count; ++i) {
+ const QSpan *span = ((QClipData *) clip)->spans() + i;
+ for (int j = 0; j < span->len; ++j)
+ clipImg.setPixel(span->x + j, span->y, 0xffffff00);
+ x0 = qMin(x0, int(span->x));
+ x1 = qMax(x1, int(span->x + span->len - 1));
+
+ y0 = qMin(y0, int(span->y));
+ y1 = qMax(y1, int(span->y));
+ }
+
+ static int counter = 0;
+
+ Q_ASSERT(y0 >= 0);
+ Q_ASSERT(x0 >= 0);
+ Q_ASSERT(y1 >= 0);
+ Q_ASSERT(x1 >= 0);
+
+ fprintf(stderr,"clip %d: %d %d - %d %d\n", counter, x0, y0, x1, y1);
+ clipImg.save(QString::fromLatin1("clip-%0.png").arg(counter++));
+}
+#endif
+
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qpaintengine_raster_p.h b/src/gui/painting/qpaintengine_raster_p.h
new file mode 100644
index 0000000000..52f51fab16
--- /dev/null
+++ b/src/gui/painting/qpaintengine_raster_p.h
@@ -0,0 +1,567 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTENGINE_RASTER_P_H
+#define QPAINTENGINE_RASTER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qpaintengineex_p.h"
+#include "QtGui/qpainterpath.h"
+#include "private/qdatabuffer_p.h"
+#include "private/qdrawhelper_p.h"
+#include "private/qpaintengine_p.h"
+#include "private/qrasterizer_p.h"
+#include "private/qstroker_p.h"
+#include "private/qpainter_p.h"
+#include "private/qtextureglyphcache_p.h"
+#include "private/qoutlinemapper_p.h"
+
+#include <stdlib.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOutlineMapper;
+class QRasterPaintEnginePrivate;
+class QRasterBuffer;
+class QClipData;
+class QCustomRasterPaintDevice;
+
+class QRasterPaintEngineState : public QPainterState
+{
+public:
+ QRasterPaintEngineState(QRasterPaintEngineState &other);
+ QRasterPaintEngineState();
+ ~QRasterPaintEngineState();
+
+
+ QPen lastPen;
+ QSpanData penData;
+ QStrokerOps *stroker;
+ uint strokeFlags;
+
+ QBrush lastBrush;
+ QSpanData brushData;
+ uint fillFlags;
+
+ uint pixmapFlags;
+ int intOpacity;
+
+ qreal txscale;
+
+ QClipData *clip;
+// QRect clipRect;
+// QRegion clipRegion;
+
+// QPainter::RenderHints hints;
+// QPainter::CompositionMode compositionMode;
+
+ uint dirty;
+
+ struct Flags {
+ uint has_clip_ownership : 1; // should delete the clip member..
+ uint fast_pen : 1; // cosmetic 1-width pens, using midpoint drawlines
+ uint non_complex_pen : 1; // can use rasterizer, rather than stroker
+ uint antialiased : 1;
+ uint bilinear : 1;
+ uint fast_text : 1;
+ uint int_xform : 1;
+ uint tx_noshear : 1;
+ uint fast_images : 1;
+ };
+
+ union {
+ Flags flags;
+ uint flag_bits;
+ };
+};
+
+
+
+
+/*******************************************************************************
+ * QRasterPaintEngine
+ */
+class
+#ifdef Q_WS_QWS
+Q_GUI_EXPORT
+#endif
+QRasterPaintEngine : public QPaintEngineEx
+{
+ Q_DECLARE_PRIVATE(QRasterPaintEngine)
+public:
+
+ QRasterPaintEngine(QPaintDevice *device);
+ ~QRasterPaintEngine();
+ bool begin(QPaintDevice *device);
+ bool end();
+
+ void penChanged();
+ void brushChanged();
+ void brushOriginChanged();
+ void opacityChanged();
+ void compositionModeChanged();
+ void renderHintsChanged();
+ void transformChanged();
+ void clipEnabledChanged();
+
+ void setState(QPainterState *s);
+ QPainterState *createState(QPainterState *orig) const;
+ inline QRasterPaintEngineState *state() {
+ return static_cast<QRasterPaintEngineState *>(QPaintEngineEx::state());
+ }
+ inline const QRasterPaintEngineState *state() const {
+ return static_cast<const QRasterPaintEngineState *>(QPaintEngineEx::state());
+ }
+
+ void updateBrush(const QBrush &brush);
+ void updatePen(const QPen &pen);
+
+ void updateMatrix(const QTransform &matrix);
+
+ void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode);
+ void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode);
+ void fillPath(const QPainterPath &path, QSpanData *fillData);
+ void fillPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode);
+
+ void drawEllipse(const QRectF &rect);
+
+ void fillRect(const QRectF &rect, const QBrush &brush);
+ void fillRect(const QRectF &rect, const QColor &color);
+
+ void drawRects(const QRect *rects, int rectCount);
+ void drawRects(const QRectF *rects, int rectCount);
+
+ void drawPixmap(const QPointF &p, const QPixmap &pm);
+ void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
+ void drawImage(const QPointF &p, const QImage &img);
+ void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr,
+ Qt::ImageConversionFlags falgs = Qt::AutoColor);
+ void drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &sr);
+ void drawTextItem(const QPointF &p, const QTextItem &textItem);
+
+ void drawLines(const QLine *line, int lineCount);
+ void drawLines(const QLineF *line, int lineCount);
+
+ void drawPoints(const QPointF *points, int pointCount);
+ void drawPoints(const QPoint *points, int pointCount);
+
+ void stroke(const QVectorPath &path, const QPen &pen);
+ void fill(const QVectorPath &path, const QBrush &brush);
+
+ void strokePolygonCosmetic(const QPoint *pts, int pointCount, PolygonDrawMode mode);
+ void strokePolygonCosmetic(const QPointF *pt, int pointCount, PolygonDrawMode mode);
+
+ void clip(const QVectorPath &path, Qt::ClipOperation op);
+ void clip(const QRect &rect, Qt::ClipOperation op);
+ void clip(const QRegion &region, Qt::ClipOperation op);
+
+ void drawStaticTextItem(QStaticTextItem *textItem);
+
+ enum ClipType {
+ RectClip,
+ ComplexClip
+ };
+ ClipType clipType() const;
+ QRect clipBoundingRect() const;
+
+#ifdef Q_NO_USING_KEYWORD
+ inline void drawEllipse(const QRect &rect) { QPaintEngineEx::drawEllipse(rect); }
+#else
+ using QPaintEngineEx::drawPolygon;
+ using QPaintEngineEx::drawEllipse;
+#endif
+
+ void releaseBuffer();
+
+ QSize size() const;
+
+#ifndef QT_NO_DEBUG
+ void saveBuffer(const QString &s) const;
+#endif
+
+#ifdef Q_WS_MAC
+ void setCGContext(CGContextRef ref);
+ CGContextRef getCGContext() const;
+#endif
+
+#ifdef Q_WS_WIN
+ void setDC(HDC hdc);
+ HDC getDC() const;
+ void releaseDC(HDC hdc) const;
+#endif
+
+ void alphaPenBlt(const void* src, int bpl, int depth, int rx,int ry,int w,int h);
+
+ Type type() const { return Raster; }
+
+ QPoint coordinateOffset() const;
+
+#if defined(Q_WS_QWS) && !defined(QT_NO_RASTERCALLBACKS)
+ virtual void drawColorSpans(const QSpan *spans, int count, uint color);
+ virtual void drawBufferSpan(const uint *buffer, int bufsize,
+ int x, int y, int length, uint const_alpha);
+#endif
+
+protected:
+ QRasterPaintEngine(QRasterPaintEnginePrivate &d, QPaintDevice *);
+private:
+ friend struct QSpanData;
+ friend class QBlitterPaintEngine;
+ friend class QBlitterPaintEnginePrivate;
+ void init();
+
+ void fillRect(const QRectF &rect, QSpanData *data);
+ void drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fill);
+
+ bool drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs, const QFixedPoint *positions,
+ QFontEngine *fontEngine);
+
+#if defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE)
+ void drawGlyphsS60(const QPointF &p, const QTextItemInt &ti);
+#endif // Q_OS_SYMBIAN && QT_NO_FREETYPE
+
+ bool setClipRectInDeviceCoords(const QRect &r, Qt::ClipOperation op);
+
+ inline void ensureBrush(const QBrush &brush) {
+ if (!qbrush_fast_equals(state()->lastBrush, brush) || (brush.style() != Qt::NoBrush && state()->fillFlags))
+ updateBrush(brush);
+ }
+ inline void ensureBrush() { ensureBrush(state()->brush); }
+
+ inline void ensurePen(const QPen &pen) {
+ if (!qpen_fast_equals(state()->lastPen, pen) || (pen.style() != Qt::NoPen && state()->strokeFlags))
+ updatePen(pen);
+ }
+ inline void ensurePen() { ensurePen(state()->pen); }
+
+ void updateOutlineMapper();
+ inline void ensureOutlineMapper();
+
+ void updateState();
+ inline void ensureState() {
+ if (state()->dirty)
+ updateState();
+ }
+};
+
+
+/*******************************************************************************
+ * QRasterPaintEnginePrivate
+ */
+class
+#ifdef Q_WS_QWS
+Q_GUI_EXPORT
+#endif
+QRasterPaintEnginePrivate : public QPaintEngineExPrivate
+{
+ Q_DECLARE_PUBLIC(QRasterPaintEngine)
+public:
+ QRasterPaintEnginePrivate();
+
+ void rasterizeLine_dashed(QLineF line, qreal width,
+ int *dashIndex, qreal *dashOffset, bool *inDash);
+ void rasterize(QT_FT_Outline *outline, ProcessSpans callback, QSpanData *spanData, QRasterBuffer *rasterBuffer);
+ void rasterize(QT_FT_Outline *outline, ProcessSpans callback, void *userData, QRasterBuffer *rasterBuffer);
+ void updateMatrixData(QSpanData *spanData, const QBrush &brush, const QTransform &brushMatrix);
+
+ void systemStateChanged();
+
+ void drawImage(const QPointF &pt, const QImage &img, SrcOverBlendFunc func,
+ const QRect &clip, int alpha, const QRect &sr = QRect());
+
+ QTransform brushMatrix() const {
+ Q_Q(const QRasterPaintEngine);
+ const QRasterPaintEngineState *s = q->state();
+ QTransform m(s->matrix);
+ m.translate(s->brushOrigin.x(), s->brushOrigin.y());
+ return m;
+ }
+
+ bool isUnclipped_normalized(const QRect &rect) const;
+ bool isUnclipped(const QRect &rect, int penWidth) const;
+ bool isUnclipped(const QRectF &rect, int penWidth) const;
+ ProcessSpans getPenFunc(const QRect &rect, const QSpanData *data) const;
+ ProcessSpans getPenFunc(const QRectF &rect, const QSpanData *data) const;
+ ProcessSpans getBrushFunc(const QRect &rect, const QSpanData *data) const;
+ ProcessSpans getBrushFunc(const QRectF &rect, const QSpanData *data) const;
+
+#ifdef Q_WS_QWS
+ void prepare(QCustomRasterPaintDevice *);
+#endif
+
+ inline const QClipData *clip() const;
+
+ void initializeRasterizer(QSpanData *data);
+
+ void recalculateFastImages();
+ bool canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const;
+
+ QPaintDevice *device;
+ QScopedPointer<QOutlineMapper> outlineMapper;
+ QScopedPointer<QRasterBuffer> rasterBuffer;
+
+#if defined (Q_WS_WIN)
+ HDC hdc;
+#elif defined(Q_WS_MAC)
+ CGContextRef cgContext;
+#endif
+
+ QRect deviceRect;
+
+ QStroker basicStroker;
+ QScopedPointer<QDashStroker> dashStroker;
+
+ QScopedPointer<QT_FT_Raster> grayRaster;
+
+ QDataBuffer<QLineF> cachedLines;
+ QSpanData image_filler;
+ QSpanData image_filler_xform;
+ QSpanData solid_color_filler;
+
+
+ QFontEngineGlyphCache::Type glyphCacheType;
+
+ QScopedPointer<QClipData> baseClip;
+
+ int deviceDepth;
+
+ uint mono_surface : 1;
+ uint outlinemapper_xform_dirty : 1;
+
+#ifdef Q_WS_WIN
+ uint isPlain45DegreeRotation : 1;
+#endif
+
+ QScopedPointer<QRasterizer> rasterizer;
+};
+
+
+class
+#ifdef Q_WS_QWS
+Q_GUI_EXPORT
+#endif
+QClipData {
+public:
+ QClipData(int height);
+ ~QClipData();
+
+ int clipSpanHeight;
+ struct ClipLine {
+ int count;
+ QSpan *spans;
+ } *m_clipLines;
+
+ void initialize();
+
+ inline ClipLine *clipLines() {
+ if (!m_clipLines)
+ initialize();
+ return m_clipLines;
+ }
+
+ inline QSpan *spans() {
+ if (!m_spans)
+ initialize();
+ return m_spans;
+ }
+
+ int allocated;
+ int count;
+ QSpan *m_spans;
+ int xmin, xmax, ymin, ymax;
+
+ QRect clipRect;
+ QRegion clipRegion;
+
+ uint enabled : 1;
+ uint hasRectClip : 1;
+ uint hasRegionClip : 1;
+
+ void appendSpan(int x, int length, int y, int coverage);
+ void appendSpans(const QSpan *s, int num);
+
+ // ### Should optimize and actually kill the QSpans if the rect is
+ // ### a subset of The current region. Thus the "fast" clipspan
+ // ### callback can be used
+ void setClipRect(const QRect &rect);
+ void setClipRegion(const QRegion &region);
+ void fixup();
+};
+
+inline void QClipData::appendSpan(int x, int length, int y, int coverage)
+{
+ Q_ASSERT(m_spans); // initialize() has to be called prior to adding spans..
+
+ if (count == allocated) {
+ allocated *= 2;
+ m_spans = (QSpan *)realloc(m_spans, allocated*sizeof(QSpan));
+ }
+ m_spans[count].x = x;
+ m_spans[count].len = length;
+ m_spans[count].y = y;
+ m_spans[count].coverage = coverage;
+ ++count;
+}
+
+inline void QClipData::appendSpans(const QSpan *s, int num)
+{
+ Q_ASSERT(m_spans);
+
+ if (count + num > allocated) {
+ do {
+ allocated *= 2;
+ } while (count + num > allocated);
+ m_spans = (QSpan *)realloc(m_spans, allocated*sizeof(QSpan));
+ }
+ memcpy(m_spans+count, s, num*sizeof(QSpan));
+ count += num;
+}
+
+#ifdef Q_WS_QWS
+class Q_GUI_EXPORT QCustomRasterPaintDevice : public QPaintDevice
+{
+public:
+ QCustomRasterPaintDevice(QWidget *w) : widget(w) {}
+
+ int devType() const { return QInternal::CustomRaster; }
+
+ virtual int metric(PaintDeviceMetric m) const;
+
+ virtual void* memory() const { return 0; }
+
+ virtual QImage::Format format() const {
+ return QImage::Format_ARGB32_Premultiplied;
+ }
+
+ virtual int bytesPerLine() const;
+
+ virtual QSize size() const {
+ return static_cast<QRasterPaintEngine*>(paintEngine())->size();
+ }
+
+private:
+ QWidget *widget;
+};
+#endif // Q_WS_QWS
+
+/*******************************************************************************
+ * QRasterBuffer
+ */
+class
+#ifdef Q_WS_QWS
+Q_GUI_EXPORT
+#endif
+QRasterBuffer
+{
+public:
+ QRasterBuffer() : m_width(0), m_height(0), m_buffer(0) { init(); }
+
+ ~QRasterBuffer();
+
+ void init();
+
+ QImage::Format prepare(QImage *image);
+ QImage::Format prepare(QPixmap *pix);
+#ifdef Q_WS_QWS
+ void prepare(QCustomRasterPaintDevice *device);
+#endif
+ void prepare(int w, int h);
+ void prepareBuffer(int w, int h);
+
+ void resetBuffer(int val=0);
+
+ uchar *scanLine(int y) { Q_ASSERT(y>=0); Q_ASSERT(y<m_height); return m_buffer + y * bytes_per_line; }
+
+#ifndef QT_NO_DEBUG
+ QImage bufferImage() const;
+#endif
+
+ void flushToARGBImage(QImage *image) const;
+
+ int width() const { return m_width; }
+ int height() const { return m_height; }
+ int bytesPerLine() const { return bytes_per_line; }
+ int bytesPerPixel() const { return bytes_per_pixel; }
+
+ uchar *buffer() const { return m_buffer; }
+
+ bool monoDestinationWithClut;
+ QRgb destColor0;
+ QRgb destColor1;
+
+ QPainter::CompositionMode compositionMode;
+ QImage::Format format;
+ DrawHelper *drawHelper;
+ QImage colorizeBitmap(const QImage &image, const QColor &color);
+
+private:
+ int m_width;
+ int m_height;
+ int bytes_per_line;
+ int bytes_per_pixel;
+ uchar *m_buffer;
+};
+
+inline void QRasterPaintEngine::ensureOutlineMapper() {
+ if (d_func()->outlinemapper_xform_dirty)
+ updateOutlineMapper();
+}
+
+inline const QClipData *QRasterPaintEnginePrivate::clip() const {
+ Q_Q(const QRasterPaintEngine);
+ if (q->state() && q->state()->clip && q->state()->clip->enabled)
+ return q->state()->clip;
+ return baseClip.data();
+}
+
+
+QT_END_NAMESPACE
+#endif // QPAINTENGINE_RASTER_P_H
diff --git a/src/gui/painting/qpaintengine_s60.cpp b/src/gui/painting/qpaintengine_s60.cpp
new file mode 100644
index 0000000000..ca303be03d
--- /dev/null
+++ b/src/gui/painting/qpaintengine_s60.cpp
@@ -0,0 +1,145 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <private/qpaintengine_s60_p.h>
+#include <private/qpixmap_s60_p.h>
+#include <private/qt_s60_p.h>
+#include <private/qvolatileimage_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QS60PaintEnginePrivate : public QRasterPaintEnginePrivate
+{
+public:
+ QS60PaintEnginePrivate() {}
+};
+
+QS60PaintEngine::QS60PaintEngine(QPaintDevice *device, QS60PixmapData *data)
+ : QRasterPaintEngine(*(new QS60PaintEnginePrivate), device), pixmapData(data)
+{
+}
+
+bool QS60PaintEngine::begin(QPaintDevice *device)
+{
+ Q_D(QS60PaintEngine);
+
+ if (pixmapData->classId() == QPixmapData::RasterClass) {
+ pixmapData->beginDataAccess();
+ bool ret = QRasterPaintEngine::begin(device);
+ // Make sure QPaintEngine::paintDevice() returns the proper device.
+ // QRasterPaintEngine changes pdev to QImage in case of RasterClass QPixmapData
+ // which is incorrect in Symbian.
+ d->pdev = device;
+ return ret;
+ }
+
+ return QRasterPaintEngine::begin(device);
+}
+
+bool QS60PaintEngine::end()
+{
+ if (pixmapData->classId() == QPixmapData::RasterClass) {
+ bool ret = QRasterPaintEngine::end();
+ pixmapData->endDataAccess();
+ return ret;
+ }
+ return QRasterPaintEngine::end();
+}
+
+void QS60PaintEngine::drawPixmap(const QPointF &p, const QPixmap &pm)
+{
+ if (pm.pixmapData()->classId() == QPixmapData::RasterClass) {
+ QS60PixmapData *srcData = static_cast<QS60PixmapData *>(pm.pixmapData());
+ srcData->beginDataAccess();
+ QRasterPaintEngine::drawPixmap(p, pm);
+ srcData->endDataAccess();
+ } else {
+ void *nativeData = pm.pixmapData()->toNativeType(QPixmapData::VolatileImage);
+ if (nativeData) {
+ QVolatileImage *img = static_cast<QVolatileImage *>(nativeData);
+ img->beginDataAccess();
+ QRasterPaintEngine::drawImage(p, img->imageRef());
+ img->endDataAccess(true);
+ } else {
+ QRasterPaintEngine::drawPixmap(p, pm);
+ }
+ }
+}
+
+void QS60PaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
+{
+ if (pm.pixmapData()->classId() == QPixmapData::RasterClass) {
+ QS60PixmapData *srcData = static_cast<QS60PixmapData *>(pm.pixmapData());
+ srcData->beginDataAccess();
+ QRasterPaintEngine::drawPixmap(r, pm, sr);
+ srcData->endDataAccess();
+ } else {
+ void *nativeData = pm.pixmapData()->toNativeType(QPixmapData::VolatileImage);
+ if (nativeData) {
+ QVolatileImage *img = static_cast<QVolatileImage *>(nativeData);
+ img->beginDataAccess();
+ QRasterPaintEngine::drawImage(r, img->imageRef(), sr);
+ img->endDataAccess(true);
+ } else {
+ QRasterPaintEngine::drawPixmap(r, pm, sr);
+ }
+ }
+}
+
+void QS60PaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &sr)
+{
+ if (pm.pixmapData()->classId() == QPixmapData::RasterClass) {
+ QS60PixmapData *srcData = static_cast<QS60PixmapData *>(pm.pixmapData());
+ srcData->beginDataAccess();
+ QRasterPaintEngine::drawTiledPixmap(r, pm, sr);
+ srcData->endDataAccess();
+ } else {
+ QRasterPaintEngine::drawTiledPixmap(r, pm, sr);
+ }
+}
+
+void QS60PaintEngine::prepare(QImage *image)
+{
+ QRasterBuffer *buffer = d_func()->rasterBuffer.data();
+ if (buffer)
+ buffer->prepare(image);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qpaintengine_s60_p.h b/src/gui/painting/qpaintengine_s60_p.h
new file mode 100644
index 0000000000..a62bdac97c
--- /dev/null
+++ b/src/gui/painting/qpaintengine_s60_p.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTENGINE_S60_P_H
+#define QPAINTENGINE_S60_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qpaintengine_raster_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QS60PaintEnginePrivate;
+class QS60PixmapData;
+
+class QS60PaintEngine : public QRasterPaintEngine
+{
+ Q_DECLARE_PRIVATE(QS60PaintEngine)
+
+public:
+ QS60PaintEngine(QPaintDevice *device, QS60PixmapData* data);
+ bool begin(QPaintDevice *device);
+ bool end();
+
+ void drawPixmap(const QPointF &p, const QPixmap &pm);
+ void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
+ void drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &sr);
+
+ void prepare(QImage* image);
+
+private:
+ QS60PixmapData *pixmapData;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPAINTENGINE_S60_P_H
diff --git a/src/gui/painting/qpaintengine_x11.cpp b/src/gui/painting/qpaintengine_x11.cpp
new file mode 100644
index 0000000000..94828fba53
--- /dev/null
+++ b/src/gui/painting/qpaintengine_x11.cpp
@@ -0,0 +1,2507 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+
+#include "private/qpixmap_x11_p.h"
+
+#include "qapplication.h"
+#include "qdebug.h"
+#include "qfont.h"
+#include "qwidget.h"
+#include "qbitmap.h"
+#include "qpixmapcache.h"
+#include "qtextcodec.h"
+#include "qcoreevent.h"
+#include "qiodevice.h"
+#include <qmath.h>
+
+#include "qpainter_p.h"
+#include <qtextlayout.h>
+#include <qvarlengtharray.h>
+#include <private/qfont_p.h>
+#include <private/qtextengine_p.h>
+#include <private/qpaintengine_x11_p.h>
+#include <private/qfontengine_x11_p.h>
+#include <private/qwidget_p.h>
+#include <private/qpainterpath_p.h>
+
+#include "qpen.h"
+#include "qcolor.h"
+#include "qcolormap.h"
+
+#include <private/qpaintengine_p.h>
+#include "qpaintengine_x11_p.h"
+
+#include <private/qt_x11_p.h>
+#include <private/qnumeric_p.h>
+#include <limits.h>
+
+#ifndef QT_NO_XRENDER
+#include <private/qtessellator_p.h>
+#endif
+
+#include <private/qstylehelper_p.h>
+
+QT_BEGIN_NAMESPACE
+
+extern Drawable qt_x11Handle(const QPaintDevice *pd);
+extern const QX11Info *qt_x11Info(const QPaintDevice *pd);
+extern QPixmap qt_pixmapForBrush(int brushStyle, bool invert); //in qbrush.cpp
+extern QPixmap qt_toX11Pixmap(const QPixmap &pixmap);
+
+// use the same rounding as in qrasterizer.cpp (6 bit fixed point)
+static const qreal aliasedCoordinateDelta = 0.5 - 0.015625;
+
+#undef X11 // defined in qt_x11_p.h
+/*!
+ Returns the X11 specific pen GC for the painter \a p. Note that
+ QPainter::begin() must be called before this function returns a
+ valid GC.
+*/
+Q_GUI_EXPORT GC qt_x11_get_pen_gc(QPainter *p)
+{
+ if (p && p->paintEngine()
+ && p->paintEngine()->isActive()
+ && p->paintEngine()->type() == QPaintEngine::X11) {
+ return static_cast<QX11PaintEngine *>(p->paintEngine())->d_func()->gc;
+ }
+ return 0;
+}
+
+/*!
+ Returns the X11 specific brush GC for the painter \a p. Note that
+ QPainter::begin() must be called before this function returns a
+ valid GC.
+*/
+Q_GUI_EXPORT GC qt_x11_get_brush_gc(QPainter *p)
+{
+ if (p && p->paintEngine()
+ && p->paintEngine()->isActive()
+ && p->paintEngine()->type() == QPaintEngine::X11) {
+ return static_cast<QX11PaintEngine *>(p->paintEngine())->d_func()->gc_brush;
+ }
+ return 0;
+}
+#define X11 qt_x11Data
+
+#ifndef QT_NO_XRENDER
+static const int compositionModeToRenderOp[QPainter::CompositionMode_Xor + 1] = {
+ PictOpOver, //CompositionMode_SourceOver,
+ PictOpOverReverse, //CompositionMode_DestinationOver,
+ PictOpClear, //CompositionMode_Clear,
+ PictOpSrc, //CompositionMode_Source,
+ PictOpDst, //CompositionMode_Destination,
+ PictOpIn, //CompositionMode_SourceIn,
+ PictOpInReverse, //CompositionMode_DestinationIn,
+ PictOpOut, //CompositionMode_SourceOut,
+ PictOpOutReverse, //CompositionMode_DestinationOut,
+ PictOpAtop, //CompositionMode_SourceAtop,
+ PictOpAtopReverse, //CompositionMode_DestinationAtop,
+ PictOpXor //CompositionMode_Xor
+};
+
+static inline int qpainterOpToXrender(QPainter::CompositionMode mode)
+{
+ Q_ASSERT(mode <= QPainter::CompositionMode_Xor);
+ return compositionModeToRenderOp[mode];
+}
+#endif
+
+// hack, so we don't have to make QRegion::clipRectangles() public or include
+// X11 headers in qregion.h
+Q_GUI_EXPORT void *qt_getClipRects(const QRegion &r, int &num)
+{
+ return r.clipRectangles(num);
+}
+
+static inline void x11SetClipRegion(Display *dpy, GC gc, GC gc2,
+#ifndef QT_NO_XRENDER
+ Picture picture,
+#else
+ Qt::HANDLE picture,
+#endif
+ const QRegion &r)
+{
+ int num;
+ XRectangle *rects = (XRectangle *)qt_getClipRects(r, num);
+
+ if (gc)
+ XSetClipRectangles( dpy, gc, 0, 0, rects, num, YXBanded );
+ if (gc2)
+ XSetClipRectangles( dpy, gc2, 0, 0, rects, num, YXBanded );
+
+#ifndef QT_NO_XRENDER
+ if (picture)
+ XRenderSetPictureClipRectangles(dpy, picture, 0, 0, rects, num);
+#else
+ Q_UNUSED(picture);
+#endif // QT_NO_XRENDER
+}
+
+
+static inline void x11ClearClipRegion(Display *dpy, GC gc, GC gc2,
+#ifndef QT_NO_XRENDER
+ Picture picture
+#else
+ Qt::HANDLE picture
+#endif
+ )
+{
+ if (gc)
+ XSetClipMask(dpy, gc, XNone);
+ if (gc2)
+ XSetClipMask(dpy, gc2, XNone);
+
+#ifndef QT_NO_XRENDER
+ if (picture) {
+ XRenderPictureAttributes attrs;
+ attrs.clip_mask = XNone;
+ XRenderChangePicture (dpy, picture, CPClipMask, &attrs);
+ }
+#else
+ Q_UNUSED(picture);
+#endif // QT_NO_XRENDER
+}
+
+
+#define DITHER_SIZE 16
+static const uchar base_dither_matrix[DITHER_SIZE][DITHER_SIZE] = {
+ { 0,192, 48,240, 12,204, 60,252, 3,195, 51,243, 15,207, 63,255 },
+ { 128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127 },
+ { 32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223 },
+ { 160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95 },
+ { 8,200, 56,248, 4,196, 52,244, 11,203, 59,251, 7,199, 55,247 },
+ { 136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119 },
+ { 40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215 },
+ { 168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87 },
+ { 2,194, 50,242, 14,206, 62,254, 1,193, 49,241, 13,205, 61,253 },
+ { 130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125 },
+ { 34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221 },
+ { 162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93 },
+ { 10,202, 58,250, 6,198, 54,246, 9,201, 57,249, 5,197, 53,245 },
+ { 138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117 },
+ { 42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213 },
+ { 170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85 }
+};
+
+static QPixmap qt_patternForAlpha(uchar alpha, int screen)
+{
+ QPixmap pm;
+ QString key = QLatin1Literal("$qt-alpha-brush$")
+ % HexString<uchar>(alpha)
+ % HexString<int>(screen);
+
+ if (!QPixmapCache::find(key, pm)) {
+ // #### why not use a mono image here????
+ QImage pattern(DITHER_SIZE, DITHER_SIZE, QImage::Format_ARGB32);
+ pattern.fill(0xffffffff);
+ for (int y = 0; y < DITHER_SIZE; ++y) {
+ for (int x = 0; x < DITHER_SIZE; ++x) {
+ if (base_dither_matrix[x][y] <= alpha)
+ pattern.setPixel(x, y, 0x00000000);
+ }
+ }
+ pm = QBitmap::fromImage(pattern);
+ pm.x11SetScreen(screen);
+ QPixmapCache::insert(key, pm);
+ }
+ return pm;
+}
+
+#if !defined(QT_NO_XRENDER)
+
+class QXRenderTessellator : public QTessellator
+{
+public:
+ QXRenderTessellator() : traps(0), allocated(0), size(0) {}
+ ~QXRenderTessellator() { free(traps); }
+ XTrapezoid *traps;
+ int allocated;
+ int size;
+ void addTrap(const Trapezoid &trap);
+ QRect tessellate(const QPointF *points, int nPoints, bool winding) {
+ size = 0;
+ setWinding(winding);
+ return QTessellator::tessellate(points, nPoints).toRect();
+ }
+ void done() {
+ if (allocated > 64) {
+ free(traps);
+ traps = 0;
+ allocated = 0;
+ }
+ }
+};
+
+void QXRenderTessellator::addTrap(const Trapezoid &trap)
+{
+ if (size == allocated) {
+ allocated = qMax(2*allocated, 64);
+ traps = q_check_ptr((XTrapezoid *)realloc(traps, allocated * sizeof(XTrapezoid)));
+ }
+ traps[size].top = Q27Dot5ToXFixed(trap.top);
+ traps[size].bottom = Q27Dot5ToXFixed(trap.bottom);
+ traps[size].left.p1.x = Q27Dot5ToXFixed(trap.topLeft->x);
+ traps[size].left.p1.y = Q27Dot5ToXFixed(trap.topLeft->y);
+ traps[size].left.p2.x = Q27Dot5ToXFixed(trap.bottomLeft->x);
+ traps[size].left.p2.y = Q27Dot5ToXFixed(trap.bottomLeft->y);
+ traps[size].right.p1.x = Q27Dot5ToXFixed(trap.topRight->x);
+ traps[size].right.p1.y = Q27Dot5ToXFixed(trap.topRight->y);
+ traps[size].right.p2.x = Q27Dot5ToXFixed(trap.bottomRight->x);
+ traps[size].right.p2.y = Q27Dot5ToXFixed(trap.bottomRight->y);
+ ++size;
+}
+
+#endif // !defined(QT_NO_XRENDER)
+
+
+#ifndef QT_NO_XRENDER
+static Picture getPatternFill(int screen, const QBrush &b)
+{
+ if (!X11->use_xrender)
+ return XNone;
+
+ XRenderColor color = X11->preMultiply(b.color());
+ XRenderColor bg_color;
+
+ bg_color = X11->preMultiply(QColor(0, 0, 0, 0));
+
+ for (int i = 0; i < X11->pattern_fill_count; ++i) {
+ if (X11->pattern_fills[i].screen == screen
+ && X11->pattern_fills[i].opaque == false
+ && X11->pattern_fills[i].style == b.style()
+ && X11->pattern_fills[i].color.alpha == color.alpha
+ && X11->pattern_fills[i].color.red == color.red
+ && X11->pattern_fills[i].color.green == color.green
+ && X11->pattern_fills[i].color.blue == color.blue
+ && X11->pattern_fills[i].bg_color.alpha == bg_color.alpha
+ && X11->pattern_fills[i].bg_color.red == bg_color.red
+ && X11->pattern_fills[i].bg_color.green == bg_color.green
+ && X11->pattern_fills[i].bg_color.blue == bg_color.blue)
+ return X11->pattern_fills[i].picture;
+ }
+ // none found, replace one
+ int i = qrand() % 16;
+
+ if (X11->pattern_fills[i].screen != screen && X11->pattern_fills[i].picture) {
+ XRenderFreePicture (X11->display, X11->pattern_fills[i].picture);
+ X11->pattern_fills[i].picture = 0;
+ }
+
+ if (!X11->pattern_fills[i].picture) {
+ Pixmap pixmap = XCreatePixmap (X11->display, RootWindow (X11->display, screen), 8, 8, 32);
+ XRenderPictureAttributes attrs;
+ attrs.repeat = True;
+ X11->pattern_fills[i].picture = XRenderCreatePicture (X11->display, pixmap,
+ XRenderFindStandardFormat(X11->display, PictStandardARGB32),
+ CPRepeat, &attrs);
+ XFreePixmap (X11->display, pixmap);
+ }
+
+ X11->pattern_fills[i].screen = screen;
+ X11->pattern_fills[i].color = color;
+ X11->pattern_fills[i].bg_color = bg_color;
+ X11->pattern_fills[i].opaque = false;
+ X11->pattern_fills[i].style = b.style();
+
+ XRenderFillRectangle(X11->display, PictOpSrc, X11->pattern_fills[i].picture, &bg_color, 0, 0, 8, 8);
+
+ QPixmap pattern(qt_pixmapForBrush(b.style(), true));
+ XRenderPictureAttributes attrs;
+ attrs.repeat = true;
+ XRenderChangePicture(X11->display, pattern.x11PictureHandle(), CPRepeat, &attrs);
+
+ Picture fill_fg = X11->getSolidFill(screen, b.color());
+ XRenderComposite(X11->display, PictOpOver, fill_fg, pattern.x11PictureHandle(),
+ X11->pattern_fills[i].picture,
+ 0, 0, 0, 0, 0, 0, 8, 8);
+
+ return X11->pattern_fills[i].picture;
+}
+
+static void qt_render_bitmap(Display *dpy, int scrn, Picture src, Picture dst,
+ int sx, int sy, int x, int y, int sw, int sh,
+ const QPen &pen)
+{
+ Picture fill_fg = X11->getSolidFill(scrn, pen.color());
+ XRenderComposite(dpy, PictOpOver,
+ fill_fg, src, dst, sx, sy, sx, sy, x, y, sw, sh);
+}
+#endif
+
+void QX11PaintEnginePrivate::init()
+{
+ dpy = 0;
+ scrn = 0;
+ hd = 0;
+ picture = 0;
+ xinfo = 0;
+#ifndef QT_NO_XRENDER
+ current_brush = 0;
+ composition_mode = PictOpOver;
+ tessellator = new QXRenderTessellator;
+#endif
+}
+
+void QX11PaintEnginePrivate::setupAdaptedOrigin(const QPoint &p)
+{
+ if (adapted_pen_origin)
+ XSetTSOrigin(dpy, gc, p.x(), p.y());
+ if (adapted_brush_origin)
+ XSetTSOrigin(dpy, gc_brush, p.x(), p.y());
+}
+
+void QX11PaintEnginePrivate::resetAdaptedOrigin()
+{
+ if (adapted_pen_origin)
+ XSetTSOrigin(dpy, gc, 0, 0);
+ if (adapted_brush_origin)
+ XSetTSOrigin(dpy, gc_brush, 0, 0);
+}
+
+void QX11PaintEnginePrivate::clipPolygon_dev(const QPolygonF &poly, QPolygonF *clipped_poly)
+{
+ int clipped_count = 0;
+ qt_float_point *clipped_points = 0;
+ polygonClipper.clipPolygon((qt_float_point *) poly.data(), poly.size(),
+ &clipped_points, &clipped_count);
+ clipped_poly->resize(clipped_count);
+ for (int i=0; i<clipped_count; ++i)
+ (*clipped_poly)[i] = *((QPointF *)(&clipped_points[i]));
+}
+
+void QX11PaintEnginePrivate::systemStateChanged()
+{
+ Q_Q(QX11PaintEngine);
+ QPainter *painter = q->state ? static_cast<QPainterState *>(q->state)->painter : 0;
+ if (painter && painter->hasClipping()) {
+ if (q->testDirty(QPaintEngine::DirtyTransform))
+ q->updateMatrix(q->state->transform());
+ QPolygonF clip_poly_dev(matrix.map(painter->clipPath().toFillPolygon()));
+ QPolygonF clipped_poly_dev;
+ clipPolygon_dev(clip_poly_dev, &clipped_poly_dev);
+ q->updateClipRegion_dev(QRegion(clipped_poly_dev.toPolygon()), Qt::ReplaceClip);
+ } else {
+ q->updateClipRegion_dev(QRegion(), Qt::NoClip);
+ }
+}
+
+static QPaintEngine::PaintEngineFeatures qt_decide_features()
+{
+ QPaintEngine::PaintEngineFeatures features =
+ QPaintEngine::PrimitiveTransform
+ | QPaintEngine::PatternBrush
+ | QPaintEngine::AlphaBlend
+ | QPaintEngine::PainterPaths
+ | QPaintEngine::RasterOpModes;
+
+ if (X11->use_xrender) {
+ features |= QPaintEngine::Antialiasing;
+ features |= QPaintEngine::PorterDuff;
+ features |= QPaintEngine::MaskedBrush;
+#if 0
+ if (X11->xrender_version > 10) {
+ features |= QPaintEngine::LinearGradientFill;
+ // ###
+ }
+#endif
+ }
+
+ return features;
+}
+
+/*
+ * QX11PaintEngine members
+ */
+
+QX11PaintEngine::QX11PaintEngine()
+ : QPaintEngine(*(new QX11PaintEnginePrivate), qt_decide_features())
+{
+ d_func()->init();
+}
+
+QX11PaintEngine::QX11PaintEngine(QX11PaintEnginePrivate &dptr)
+ : QPaintEngine(dptr, qt_decide_features())
+{
+ d_func()->init();
+}
+
+QX11PaintEngine::~QX11PaintEngine()
+{
+#ifndef QT_NO_XRENDER
+ Q_D(QX11PaintEngine);
+ delete d->tessellator;
+#endif
+}
+
+bool QX11PaintEngine::begin(QPaintDevice *pdev)
+{
+ Q_D(QX11PaintEngine);
+ d->xinfo = qt_x11Info(pdev);
+ QWidget *w = d->pdev->devType() == QInternal::Widget ? static_cast<QWidget *>(d->pdev) : 0;
+ const bool isAlienWidget = w && !w->internalWinId() && w->testAttribute(Qt::WA_WState_Created);
+#ifndef QT_NO_XRENDER
+ if (w) {
+ if (isAlienWidget)
+ d->picture = (::Picture)w->nativeParentWidget()->x11PictureHandle();
+ else
+ d->picture = (::Picture)w->x11PictureHandle();
+ } else if (pdev->devType() == QInternal::Pixmap) {
+ const QPixmap *pm = static_cast<const QPixmap *>(pdev);
+ QX11PixmapData *data = static_cast<QX11PixmapData*>(pm->data.data());
+ if (X11->use_xrender && data->depth() != 32 && data->x11_mask)
+ data->convertToARGB32();
+ d->picture = (::Picture)static_cast<const QPixmap *>(pdev)->x11PictureHandle();
+ }
+#else
+ d->picture = 0;
+#endif
+ d->hd = !isAlienWidget ? qt_x11Handle(pdev) : qt_x11Handle(w->nativeParentWidget());
+
+ Q_ASSERT(d->xinfo != 0);
+ d->dpy = d->xinfo->display(); // get display variable
+ d->scrn = d->xinfo->screen(); // get screen variable
+
+ d->crgn = QRegion();
+ d->gc = XCreateGC(d->dpy, d->hd, 0, 0);
+ d->gc_brush = XCreateGC(d->dpy, d->hd, 0, 0);
+ d->has_alpha_brush = false;
+ d->has_alpha_pen = false;
+ d->has_clipping = false;
+ d->has_complex_xform = false;
+ d->has_scaling_xform = false;
+ d->has_non_scaling_xform = true;
+ d->xform_scale = 1;
+ d->has_custom_pen = false;
+ d->matrix = QTransform();
+ d->pdev_depth = d->pdev->depth();
+ d->render_hints = 0;
+ d->txop = QTransform::TxNone;
+ d->use_path_fallback = false;
+#if !defined(QT_NO_XRENDER)
+ d->composition_mode = PictOpOver;
+#endif
+ d->xlibMaxLinePoints = 32762; // a safe number used to avoid, call to XMaxRequestSize(d->dpy) - 3;
+ d->opacity = 1;
+
+ // Set up the polygon clipper. Note: This will only work in
+ // polyline mode as long as we have a buffer zone, since a
+ // polyline may be clipped into several non-connected polylines.
+ const int BUFFERZONE = 1000;
+ QRect devClipRect(-BUFFERZONE, -BUFFERZONE,
+ pdev->width() + 2*BUFFERZONE, pdev->height() + 2*BUFFERZONE);
+ d->polygonClipper.setBoundingRect(devClipRect);
+
+ if (isAlienWidget) {
+ // Set system clip for alien widgets painting outside the paint event.
+ // This is not a problem with native windows since the windowing system
+ // will handle the clip.
+ QWidgetPrivate *wd = w->d_func();
+ QRegion widgetClip(wd->clipRect());
+ wd->clipToEffectiveMask(widgetClip);
+ wd->subtractOpaqueSiblings(widgetClip);
+ widgetClip.translate(w->mapTo(w->nativeParentWidget(), QPoint()));
+ setSystemClip(widgetClip);
+ }
+
+ QPixmap::x11SetDefaultScreen(d->xinfo->screen());
+
+ if (w && w->testAttribute(Qt::WA_PaintUnclipped)) { // paint direct on device
+ updatePen(QPen(Qt::black));
+ updateBrush(QBrush(Qt::white), QPoint());
+ XSetSubwindowMode(d->dpy, d->gc, IncludeInferiors);
+ XSetSubwindowMode(d->dpy, d->gc_brush, IncludeInferiors);
+#ifndef QT_NO_XRENDER
+ XRenderPictureAttributes attrs;
+ attrs.subwindow_mode = IncludeInferiors;
+ XRenderChangePicture(d->dpy, d->picture, CPSubwindowMode, &attrs);
+#endif
+ }
+
+ setDirty(QPaintEngine::DirtyClipRegion);
+ setDirty(QPaintEngine::DirtyPen);
+ setDirty(QPaintEngine::DirtyBrush);
+ setDirty(QPaintEngine::DirtyBackground);
+
+ return true;
+}
+
+bool QX11PaintEngine::end()
+{
+ Q_D(QX11PaintEngine);
+
+#if !defined(QT_NO_XRENDER)
+ if (d->picture) {
+ // reset clipping/subwindow mode on our render picture
+ XRenderPictureAttributes attrs;
+ attrs.subwindow_mode = ClipByChildren;
+ attrs.clip_mask = XNone;
+ XRenderChangePicture(d->dpy, d->picture, CPClipMask|CPSubwindowMode, &attrs);
+ }
+#endif
+
+ if (d->gc_brush && d->pdev->painters < 2) {
+ XFreeGC(d->dpy, d->gc_brush);
+ d->gc_brush = 0;
+ }
+
+ if (d->gc && d->pdev->painters < 2) {
+ XFreeGC(d->dpy, d->gc);
+ d->gc = 0;
+ }
+
+ // Restore system clip for alien widgets painting outside the paint event.
+ if (d->pdev->devType() == QInternal::Widget && !static_cast<QWidget *>(d->pdev)->internalWinId())
+ setSystemClip(QRegion());
+
+ return true;
+}
+
+static bool clipLine(QLineF *line, const QRect &rect)
+{
+ qreal x1 = line->x1();
+ qreal x2 = line->x2();
+ qreal y1 = line->y1();
+ qreal y2 = line->y2();
+
+ qreal left = rect.x();
+ qreal right = rect.x() + rect.width() - 1;
+ qreal top = rect.y();
+ qreal bottom = rect.y() + rect.height() - 1;
+
+ enum { Left, Right, Top, Bottom };
+ // clip the lines, after cohen-sutherland, see e.g. http://www.nondot.org/~sabre/graphpro/line6.html
+ int p1 = ((x1 < left) << Left)
+ | ((x1 > right) << Right)
+ | ((y1 < top) << Top)
+ | ((y1 > bottom) << Bottom);
+ int p2 = ((x2 < left) << Left)
+ | ((x2 > right) << Right)
+ | ((y2 < top) << Top)
+ | ((y2 > bottom) << Bottom);
+
+ if (p1 & p2)
+ // completely outside
+ return false;
+
+ if (p1 | p2) {
+ qreal dx = x2 - x1;
+ qreal dy = y2 - y1;
+
+ // clip x coordinates
+ if (x1 < left) {
+ y1 += dy/dx * (left - x1);
+ x1 = left;
+ } else if (x1 > right) {
+ y1 -= dy/dx * (x1 - right);
+ x1 = right;
+ }
+ if (x2 < left) {
+ y2 += dy/dx * (left - x2);
+ x2 = left;
+ } else if (x2 > right) {
+ y2 -= dy/dx * (x2 - right);
+ x2 = right;
+ }
+ p1 = ((y1 < top) << Top)
+ | ((y1 > bottom) << Bottom);
+ p2 = ((y2 < top) << Top)
+ | ((y2 > bottom) << Bottom);
+ if (p1 & p2)
+ return false;
+ // clip y coordinates
+ if (y1 < top) {
+ x1 += dx/dy * (top - y1);
+ y1 = top;
+ } else if (y1 > bottom) {
+ x1 -= dx/dy * (y1 - bottom);
+ y1 = bottom;
+ }
+ if (y2 < top) {
+ x2 += dx/dy * (top - y2);
+ y2 = top;
+ } else if (y2 > bottom) {
+ x2 -= dx/dy * (y2 - bottom);
+ y2 = bottom;
+ }
+ *line = QLineF(QPointF(x1, y1), QPointF(x2, y2));
+ }
+ return true;
+}
+
+void QX11PaintEngine::drawLines(const QLine *lines, int lineCount)
+{
+ Q_ASSERT(lines);
+ Q_ASSERT(lineCount);
+ Q_D(QX11PaintEngine);
+ if (d->has_alpha_brush
+ || d->has_alpha_pen
+ || d->has_custom_pen
+ || (d->cpen.widthF() > 0 && d->has_complex_xform
+ && !d->has_non_scaling_xform)
+ || (d->render_hints & QPainter::Antialiasing)) {
+ for (int i = 0; i < lineCount; ++i) {
+ QPainterPath path(lines[i].p1());
+ path.lineTo(lines[i].p2());
+ drawPath(path);
+ }
+ return;
+ }
+
+ if (d->has_pen) {
+ for (int i = 0; i < lineCount; ++i) {
+ QLineF linef;
+ if (d->txop == QTransform::TxNone) {
+ linef = lines[i];
+ } else {
+ linef = d->matrix.map(QLineF(lines[i]));
+ }
+ if (clipLine(&linef, d->polygonClipper.boundingRect())) {
+ int x1 = qRound(linef.x1() + aliasedCoordinateDelta);
+ int y1 = qRound(linef.y1() + aliasedCoordinateDelta);
+ int x2 = qRound(linef.x2() + aliasedCoordinateDelta);
+ int y2 = qRound(linef.y2() + aliasedCoordinateDelta);
+
+ XDrawLine(d->dpy, d->hd, d->gc, x1, y1, x2, y2);
+ }
+ }
+ }
+}
+
+void QX11PaintEngine::drawLines(const QLineF *lines, int lineCount)
+{
+ Q_ASSERT(lines);
+ Q_ASSERT(lineCount);
+ Q_D(QX11PaintEngine);
+ if (d->has_alpha_brush
+ || d->has_alpha_pen
+ || d->has_custom_pen
+ || (d->cpen.widthF() > 0 && d->has_complex_xform
+ && !d->has_non_scaling_xform)
+ || (d->render_hints & QPainter::Antialiasing)) {
+ for (int i = 0; i < lineCount; ++i) {
+ QPainterPath path(lines[i].p1());
+ path.lineTo(lines[i].p2());
+ drawPath(path);
+ }
+ return;
+ }
+
+ if (d->has_pen) {
+ for (int i = 0; i < lineCount; ++i) {
+ QLineF linef = d->matrix.map(lines[i]);
+ if (clipLine(&linef, d->polygonClipper.boundingRect())) {
+ int x1 = qRound(linef.x1() + aliasedCoordinateDelta);
+ int y1 = qRound(linef.y1() + aliasedCoordinateDelta);
+ int x2 = qRound(linef.x2() + aliasedCoordinateDelta);
+ int y2 = qRound(linef.y2() + aliasedCoordinateDelta);
+
+ XDrawLine(d->dpy, d->hd, d->gc, x1, y1, x2, y2);
+ }
+ }
+ }
+}
+
+static inline QLine clipStraightLine(const QRect &clip, const QLine &l)
+{
+ if (l.p1().x() == l.p2().x()) {
+ int x = qBound(clip.left(), l.p1().x(), clip.right());
+ int y1 = qBound(clip.top(), l.p1().y(), clip.bottom());
+ int y2 = qBound(clip.top(), l.p2().y(), clip.bottom());
+
+ return QLine(x, y1, x, y2);
+ } else {
+ Q_ASSERT(l.p1().y() == l.p2().y());
+
+ int x1 = qBound(clip.left(), l.p1().x(), clip.right());
+ int x2 = qBound(clip.left(), l.p2().x(), clip.right());
+ int y = qBound(clip.top(), l.p1().y(), clip.bottom());
+
+ return QLine(x1, y, x2, y);
+ }
+}
+
+void QX11PaintEngine::drawRects(const QRectF *rects, int rectCount)
+{
+ Q_D(QX11PaintEngine);
+ Q_ASSERT(rects);
+ Q_ASSERT(rectCount);
+
+ if (rectCount != 1
+ || d->has_pen
+ || d->has_alpha_brush
+ || d->has_complex_xform
+ || d->has_custom_pen
+ || d->cbrush.style() != Qt::SolidPattern)
+ {
+ QPaintEngine::drawRects(rects, rectCount);
+ return;
+ }
+
+ QPoint alignedOffset;
+ if (d->txop == QTransform::TxTranslate) {
+ QPointF offset(d->matrix.dx(), d->matrix.dy());
+ alignedOffset = offset.toPoint();
+ if (offset != QPointF(alignedOffset)) {
+ QPaintEngine::drawRects(rects, rectCount);
+ return;
+ }
+ }
+
+ const QRectF& r = rects[0];
+ QRect alignedRect = r.toAlignedRect();
+ if (r != QRectF(alignedRect)) {
+ QPaintEngine::drawRects(rects, rectCount);
+ return;
+ }
+ alignedRect.translate(alignedOffset);
+
+ QRect clip(d->polygonClipper.boundingRect());
+ alignedRect = alignedRect.intersected(clip);
+ if (alignedRect.isEmpty())
+ return;
+
+ // simple-case:
+ // the rectangle is pixel-aligned
+ // the fill brush is just a solid non-alpha color
+ // the painter transform is only integer translation
+ // ignore: antialiasing and just XFillRectangles directly
+ XRectangle xrect;
+ xrect.x = short(alignedRect.x());
+ xrect.y = short(alignedRect.y());
+ xrect.width = ushort(alignedRect.width());
+ xrect.height = ushort(alignedRect.height());
+ XFillRectangles(d->dpy, d->hd, d->gc_brush, &xrect, 1);
+}
+
+void QX11PaintEngine::drawRects(const QRect *rects, int rectCount)
+{
+ Q_D(QX11PaintEngine);
+ Q_ASSERT(rects);
+ Q_ASSERT(rectCount);
+
+ if (d->has_alpha_pen
+ || d->has_complex_xform
+ || d->has_custom_pen
+ || (d->render_hints & QPainter::Antialiasing))
+ {
+ for (int i = 0; i < rectCount; ++i) {
+ QPainterPath path;
+ path.addRect(rects[i]);
+ drawPath(path);
+ }
+ return;
+ }
+
+ QRect clip(d->polygonClipper.boundingRect());
+ QPoint offset(qRound(d->matrix.dx()), qRound(d->matrix.dy()));
+#if !defined(QT_NO_XRENDER)
+ ::Picture pict = d->picture;
+
+ if (X11->use_xrender && pict && d->has_brush && d->pdev_depth != 1
+ && (d->has_texture || d->has_alpha_brush))
+ {
+ XRenderColor xc;
+ if (!d->has_texture && !d->has_pattern)
+ xc = X11->preMultiply(d->cbrush.color());
+
+ for (int i = 0; i < rectCount; ++i) {
+ QRect r(rects[i]);
+ if (d->txop == QTransform::TxTranslate)
+ r.translate(offset);
+
+ if (r.width() == 0 || r.height() == 0) {
+ if (d->has_pen) {
+ const QLine l = clipStraightLine(clip, QLine(r.left(), r.top(), r.left() + r.width(), r.top() + r.height()));
+ XDrawLine(d->dpy, d->hd, d->gc, l.p1().x(), l.p1().y(), l.p2().x(), l.p2().y());
+ }
+ continue;
+ }
+
+ r = r.intersected(clip);
+ if (r.isEmpty())
+ continue;
+ if (d->has_texture || d->has_pattern) {
+ XRenderComposite(d->dpy, d->composition_mode, d->current_brush, 0, pict,
+ qRound(r.x() - d->bg_origin.x()), qRound(r.y() - d->bg_origin.y()),
+ 0, 0, r.x(), r.y(), r.width(), r.height());
+ } else {
+ XRenderFillRectangle(d->dpy, d->composition_mode, pict, &xc, r.x(), r.y(), r.width(), r.height());
+ }
+ if (d->has_pen)
+ XDrawRectangle(d->dpy, d->hd, d->gc, r.x(), r.y(), r.width(), r.height());
+ }
+ } else
+#endif // !QT_NO_XRENDER
+ {
+ if (d->has_brush && d->has_pen) {
+ for (int i = 0; i < rectCount; ++i) {
+ QRect r(rects[i]);
+ if (d->txop == QTransform::TxTranslate)
+ r.translate(offset);
+
+ if (r.width() == 0 || r.height() == 0) {
+ const QLine l = clipStraightLine(clip, QLine(r.left(), r.top(), r.left() + r.width(), r.top() + r.height()));
+ XDrawLine(d->dpy, d->hd, d->gc, l.p1().x(), l.p1().y(), l.p2().x(), l.p2().y());
+ continue;
+ }
+
+ r = r.intersected(clip);
+ if (r.isEmpty())
+ continue;
+ d->setupAdaptedOrigin(r.topLeft());
+ XFillRectangle(d->dpy, d->hd, d->gc_brush, r.x(), r.y(), r.width(), r.height());
+ XDrawRectangle(d->dpy, d->hd, d->gc, r.x(), r.y(), r.width(), r.height());
+ }
+ d->resetAdaptedOrigin();
+ } else {
+ QVarLengthArray<XRectangle> xrects(rectCount);
+ int numClipped = rectCount;
+ for (int i = 0; i < rectCount; ++i) {
+ QRect r(rects[i]);
+ if (d->txop == QTransform::TxTranslate)
+ r.translate(offset);
+
+ if (r.width() == 0 || r.height() == 0) {
+ --numClipped;
+ if (d->has_pen) {
+ const QLine l = clipStraightLine(clip, QLine(r.left(), r.top(), r.left() + r.width(), r.top() + r.height()));
+ XDrawLine(d->dpy, d->hd, d->gc, l.p1().x(), l.p1().y(), l.p2().x(), l.p2().y());
+ }
+ continue;
+ }
+
+ r = r.intersected(clip);
+ if (r.isEmpty()) {
+ --numClipped;
+ continue;
+ }
+ xrects[i].x = short(r.x());
+ xrects[i].y = short(r.y());
+ xrects[i].width = ushort(r.width());
+ xrects[i].height = ushort(r.height());
+ }
+ if (numClipped) {
+ d->setupAdaptedOrigin(rects[0].topLeft());
+ if (d->has_brush)
+ XFillRectangles(d->dpy, d->hd, d->gc_brush, xrects.data(), numClipped);
+ else if (d->has_pen)
+ XDrawRectangles(d->dpy, d->hd, d->gc, xrects.data(), numClipped);
+ d->resetAdaptedOrigin();
+ }
+ }
+ }
+}
+
+static inline void setCapStyle(int cap_style, GC gc)
+{
+ ulong mask = GCCapStyle;
+ XGCValues vals;
+ vals.cap_style = cap_style;
+ XChangeGC(X11->display, gc, mask, &vals);
+}
+
+void QX11PaintEngine::drawPoints(const QPoint *points, int pointCount)
+{
+ Q_ASSERT(points);
+ Q_ASSERT(pointCount);
+ Q_D(QX11PaintEngine);
+
+ if (!d->has_pen)
+ return;
+
+ // use the same test here as in drawPath to ensure that we don't use the path fallback
+ // and end up in XDrawLines for pens with width <= 1
+ if (d->cpen.widthF() > 1.0f
+ || (X11->use_xrender && (d->has_alpha_pen || (d->render_hints & QPainter::Antialiasing)))
+ || (!d->cpen.isCosmetic() && d->txop > QTransform::TxTranslate))
+ {
+ Qt::PenCapStyle capStyle = d->cpen.capStyle();
+ if (capStyle == Qt::FlatCap) {
+ setCapStyle(CapProjecting, d->gc);
+ d->cpen.setCapStyle(Qt::SquareCap);
+ }
+ const QPoint *end = points + pointCount;
+ while (points < end) {
+ QPainterPath path;
+ path.moveTo(*points);
+ path.lineTo(points->x()+.005, points->y());
+ drawPath(path);
+ ++points;
+ }
+
+ if (capStyle == Qt::FlatCap) {
+ setCapStyle(CapButt, d->gc);
+ d->cpen.setCapStyle(capStyle);
+ }
+ return;
+ }
+
+ static const int BUF_SIZE = 1024;
+ XPoint xPoints[BUF_SIZE];
+ int i = 0, j = 0;
+ while (i < pointCount) {
+ while (i < pointCount && j < BUF_SIZE) {
+ const QPoint &xformed = d->matrix.map(points[i]);
+ int x = xformed.x();
+ int y = xformed.y();
+ if (x >= SHRT_MIN && y >= SHRT_MIN && x < SHRT_MAX && y < SHRT_MAX) {
+ xPoints[j].x = x;
+ xPoints[j].y = y;
+ ++j;
+ }
+ ++i;
+ }
+ if (j)
+ XDrawPoints(d->dpy, d->hd, d->gc, xPoints, j, CoordModeOrigin);
+
+ j = 0;
+ }
+}
+
+void QX11PaintEngine::drawPoints(const QPointF *points, int pointCount)
+{
+ Q_ASSERT(points);
+ Q_ASSERT(pointCount);
+ Q_D(QX11PaintEngine);
+
+ if (!d->has_pen)
+ return;
+
+ // use the same test here as in drawPath to ensure that we don't use the path fallback
+ // and end up in XDrawLines for pens with width <= 1
+ if (d->cpen.widthF() > 1.0f
+ || (X11->use_xrender && (d->has_alpha_pen || (d->render_hints & QPainter::Antialiasing)))
+ || (!d->cpen.isCosmetic() && d->txop > QTransform::TxTranslate))
+ {
+ Qt::PenCapStyle capStyle = d->cpen.capStyle();
+ if (capStyle == Qt::FlatCap) {
+ setCapStyle(CapProjecting, d->gc);
+ d->cpen.setCapStyle(Qt::SquareCap);
+ }
+
+ const QPointF *end = points + pointCount;
+ while (points < end) {
+ QPainterPath path;
+ path.moveTo(*points);
+ path.lineTo(points->x() + 0.005, points->y());
+ drawPath(path);
+ ++points;
+ }
+ if (capStyle == Qt::FlatCap) {
+ setCapStyle(CapButt, d->gc);
+ d->cpen.setCapStyle(capStyle);
+ }
+ return;
+ }
+
+ static const int BUF_SIZE = 1024;
+ XPoint xPoints[BUF_SIZE];
+ int i = 0, j = 0;
+ while (i < pointCount) {
+ while (i < pointCount && j < BUF_SIZE) {
+ const QPointF &xformed = d->matrix.map(points[i]);
+ int x = qFloor(xformed.x());
+ int y = qFloor(xformed.y());
+
+ if (x >= SHRT_MIN && y >= SHRT_MIN && x < SHRT_MAX && y < SHRT_MAX) {
+ xPoints[j].x = x;
+ xPoints[j].y = y;
+ ++j;
+ }
+ ++i;
+ }
+ if (j)
+ XDrawPoints(d->dpy, d->hd, d->gc, xPoints, j, CoordModeOrigin);
+
+ j = 0;
+ }
+}
+
+QPainter::RenderHints QX11PaintEngine::supportedRenderHints() const
+{
+#if !defined(QT_NO_XRENDER)
+ if (X11->use_xrender)
+ return QPainter::Antialiasing;
+#endif
+ return QFlag(0);
+}
+
+void QX11PaintEngine::updateState(const QPaintEngineState &state)
+{
+ Q_D(QX11PaintEngine);
+ QPaintEngine::DirtyFlags flags = state.state();
+
+
+ if (flags & DirtyOpacity) {
+ d->opacity = state.opacity();
+ // Force update pen/brush as to get proper alpha colors propagated
+ flags |= DirtyPen;
+ flags |= DirtyBrush;
+ }
+
+ if (flags & DirtyTransform) updateMatrix(state.transform());
+ if (flags & DirtyPen) updatePen(state.pen());
+ if (flags & (DirtyBrush | DirtyBrushOrigin)) updateBrush(state.brush(), state.brushOrigin());
+ if (flags & DirtyFont) updateFont(state.font());
+
+ if (state.state() & DirtyClipEnabled) {
+ if (state.isClipEnabled()) {
+ QPolygonF clip_poly_dev(d->matrix.map(painter()->clipPath().toFillPolygon()));
+ QPolygonF clipped_poly_dev;
+ d->clipPolygon_dev(clip_poly_dev, &clipped_poly_dev);
+ updateClipRegion_dev(QRegion(clipped_poly_dev.toPolygon()), Qt::ReplaceClip);
+ } else {
+ updateClipRegion_dev(QRegion(), Qt::NoClip);
+ }
+ }
+
+ if (flags & DirtyClipPath) {
+ QPolygonF clip_poly_dev(d->matrix.map(state.clipPath().toFillPolygon()));
+ QPolygonF clipped_poly_dev;
+ d->clipPolygon_dev(clip_poly_dev, &clipped_poly_dev);
+ updateClipRegion_dev(QRegion(clipped_poly_dev.toPolygon(), state.clipPath().fillRule()),
+ state.clipOperation());
+ } else if (flags & DirtyClipRegion) {
+ extern QPainterPath qt_regionToPath(const QRegion &region);
+ QPainterPath clip_path = qt_regionToPath(state.clipRegion());
+ QPolygonF clip_poly_dev(d->matrix.map(clip_path.toFillPolygon()));
+ QPolygonF clipped_poly_dev;
+ d->clipPolygon_dev(clip_poly_dev, &clipped_poly_dev);
+ updateClipRegion_dev(QRegion(clipped_poly_dev.toPolygon()), state.clipOperation());
+ }
+ if (flags & DirtyHints) updateRenderHints(state.renderHints());
+ if (flags & DirtyCompositionMode) {
+ int function = GXcopy;
+ if (state.compositionMode() >= QPainter::RasterOp_SourceOrDestination) {
+ switch (state.compositionMode()) {
+ case QPainter::RasterOp_SourceOrDestination:
+ function = GXor;
+ break;
+ case QPainter::RasterOp_SourceAndDestination:
+ function = GXand;
+ break;
+ case QPainter::RasterOp_SourceXorDestination:
+ function = GXxor;
+ break;
+ case QPainter::RasterOp_NotSourceAndNotDestination:
+ function = GXnor;
+ break;
+ case QPainter::RasterOp_NotSourceOrNotDestination:
+ function = GXnand;
+ break;
+ case QPainter::RasterOp_NotSourceXorDestination:
+ function = GXequiv;
+ break;
+ case QPainter::RasterOp_NotSource:
+ function = GXcopyInverted;
+ break;
+ case QPainter::RasterOp_SourceAndNotDestination:
+ function = GXandReverse;
+ break;
+ case QPainter::RasterOp_NotSourceAndDestination:
+ function = GXandInverted;
+ break;
+ default:
+ function = GXcopy;
+ }
+ }
+#if !defined(QT_NO_XRENDER)
+ else {
+ d->composition_mode =
+ qpainterOpToXrender(state.compositionMode());
+ }
+#endif
+ XSetFunction(X11->display, d->gc, function);
+ XSetFunction(X11->display, d->gc_brush, function);
+ }
+ d->decidePathFallback();
+ d->decideCoordAdjust();
+}
+
+void QX11PaintEngine::updateRenderHints(QPainter::RenderHints hints)
+{
+ Q_D(QX11PaintEngine);
+ d->render_hints = hints;
+
+#if !defined(QT_NO_XRENDER)
+ if (X11->use_xrender && d->picture) {
+ XRenderPictureAttributes attrs;
+ attrs.poly_edge = (hints & QPainter::Antialiasing) ? PolyEdgeSmooth : PolyEdgeSharp;
+ XRenderChangePicture(d->dpy, d->picture, CPPolyEdge, &attrs);
+ }
+#endif
+}
+
+void QX11PaintEngine::updatePen(const QPen &pen)
+{
+ Q_D(QX11PaintEngine);
+ d->cpen = pen;
+ int cp = CapButt;
+ int jn = JoinMiter;
+ int ps = pen.style();
+
+ if (d->opacity < 1.0) {
+ QColor c = d->cpen.color();
+ c.setAlpha(qRound(c.alpha()*d->opacity));
+ d->cpen.setColor(c);
+ }
+
+ d->has_pen = (ps != Qt::NoPen);
+ d->has_alpha_pen = (pen.color().alpha() != 255);
+
+ switch (pen.capStyle()) {
+ case Qt::SquareCap:
+ cp = CapProjecting;
+ break;
+ case Qt::RoundCap:
+ cp = CapRound;
+ break;
+ case Qt::FlatCap:
+ default:
+ cp = CapButt;
+ break;
+ }
+ switch (pen.joinStyle()) {
+ case Qt::BevelJoin:
+ jn = JoinBevel;
+ break;
+ case Qt::RoundJoin:
+ jn = JoinRound;
+ break;
+ case Qt::MiterJoin:
+ default:
+ jn = JoinMiter;
+ break;
+ }
+
+ d->adapted_pen_origin = false;
+
+ char dashes[10]; // custom pen dashes
+ int dash_len = 0; // length of dash list
+ int xStyle = LineSolid;
+
+ /*
+ We are emulating Windows here. Windows treats cpen.width() == 1
+ (or 0) as a very special case. The fudge variable unifies this
+ case with the general case.
+ */
+ qreal pen_width = pen.widthF();
+ int scale = qRound(pen_width < 1 ? 1 : pen_width);
+ int space = (pen_width < 1 && pen_width > 0 ? 1 : (2 * scale));
+ int dot = 1 * scale;
+ int dash = 4 * scale;
+
+ d->has_custom_pen = false;
+
+ switch (ps) {
+ case Qt::NoPen:
+ case Qt::SolidLine:
+ xStyle = LineSolid;
+ break;
+ case Qt::DashLine:
+ dashes[0] = dash;
+ dashes[1] = space;
+ dash_len = 2;
+ xStyle = LineOnOffDash;
+ break;
+ case Qt::DotLine:
+ dashes[0] = dot;
+ dashes[1] = space;
+ dash_len = 2;
+ xStyle = LineOnOffDash;
+ break;
+ case Qt::DashDotLine:
+ dashes[0] = dash;
+ dashes[1] = space;
+ dashes[2] = dot;
+ dashes[3] = space;
+ dash_len = 4;
+ xStyle = LineOnOffDash;
+ break;
+ case Qt::DashDotDotLine:
+ dashes[0] = dash;
+ dashes[1] = space;
+ dashes[2] = dot;
+ dashes[3] = space;
+ dashes[4] = dot;
+ dashes[5] = space;
+ dash_len = 6;
+ xStyle = LineOnOffDash;
+ break;
+ case Qt::CustomDashLine:
+ d->has_custom_pen = true;
+ break;
+ }
+
+ ulong mask = GCForeground | GCBackground | GCGraphicsExposures | GCLineWidth
+ | GCCapStyle | GCJoinStyle | GCLineStyle;
+ XGCValues vals;
+ vals.graphics_exposures = false;
+ if (d->pdev_depth == 1) {
+ vals.foreground = qGray(pen.color().rgb()) > 127 ? 0 : 1;
+ vals.background = qGray(QColor(Qt::transparent).rgb()) > 127 ? 0 : 1;
+ } else if (d->pdev->devType() == QInternal::Pixmap && d->pdev_depth == 32
+ && X11->use_xrender) {
+ vals.foreground = pen.color().rgba();
+ vals.background = QColor(Qt::transparent).rgba();
+ } else {
+ QColormap cmap = QColormap::instance(d->scrn);
+ vals.foreground = cmap.pixel(pen.color());
+ vals.background = cmap.pixel(QColor(Qt::transparent));
+ }
+
+
+ vals.line_width = qRound(pen.widthF());
+ vals.cap_style = cp;
+ vals.join_style = jn;
+ vals.line_style = xStyle;
+
+ XChangeGC(d->dpy, d->gc, mask, &vals);
+
+ if (dash_len) { // make dash list
+ XSetDashes(d->dpy, d->gc, 0, dashes, dash_len);
+ }
+
+ if (!d->has_clipping) { // if clipping is set the paintevent clip region is merged with the clip region
+ QRegion sysClip = systemClip();
+ if (!sysClip.isEmpty())
+ x11SetClipRegion(d->dpy, d->gc, 0, d->picture, sysClip);
+ else
+ x11ClearClipRegion(d->dpy, d->gc, 0, d->picture);
+ }
+}
+
+void QX11PaintEngine::updateBrush(const QBrush &brush, const QPointF &origin)
+{
+ Q_D(QX11PaintEngine);
+ d->cbrush = brush;
+ d->bg_origin = origin;
+ d->adapted_brush_origin = false;
+#if !defined(QT_NO_XRENDER)
+ d->current_brush = 0;
+#endif
+ if (d->opacity < 1.0) {
+ QColor c = d->cbrush.color();
+ c.setAlpha(qRound(c.alpha()*d->opacity));
+ d->cbrush.setColor(c);
+ }
+
+ int s = FillSolid;
+ int bs = d->cbrush.style();
+ d->has_brush = (bs != Qt::NoBrush);
+ d->has_pattern = bs >= Qt::Dense1Pattern && bs <= Qt::DiagCrossPattern;
+ d->has_texture = bs == Qt::TexturePattern;
+ d->has_alpha_brush = brush.color().alpha() != 255;
+ d->has_alpha_texture = d->has_texture && d->cbrush.texture().hasAlphaChannel();
+
+ ulong mask = GCForeground | GCBackground | GCGraphicsExposures
+ | GCLineStyle | GCCapStyle | GCJoinStyle | GCFillStyle;
+ XGCValues vals;
+ vals.graphics_exposures = false;
+ if (d->pdev_depth == 1) {
+ vals.foreground = qGray(d->cbrush.color().rgb()) > 127 ? 0 : 1;
+ vals.background = qGray(QColor(Qt::transparent).rgb()) > 127 ? 0 : 1;
+ } else if (X11->use_xrender && d->pdev->devType() == QInternal::Pixmap
+ && d->pdev_depth == 32) {
+ vals.foreground = d->cbrush.color().rgba();
+ vals.background = QColor(Qt::transparent).rgba();
+ } else {
+ QColormap cmap = QColormap::instance(d->scrn);
+ vals.foreground = cmap.pixel(d->cbrush.color());
+ vals.background = cmap.pixel(QColor(Qt::transparent));
+
+ if (!X11->use_xrender && d->has_brush && !d->has_pattern && !brush.isOpaque()) {
+ QPixmap pattern = qt_patternForAlpha(brush.color().alpha(), d->scrn);
+ mask |= GCStipple;
+ vals.stipple = pattern.handle();
+ s = FillStippled;
+ d->adapted_brush_origin = true;
+ }
+ }
+ vals.cap_style = CapButt;
+ vals.join_style = JoinMiter;
+ vals.line_style = LineSolid;
+
+ if (d->has_pattern || d->has_texture) {
+ if (bs == Qt::TexturePattern) {
+ d->brush_pm = qt_toX11Pixmap(d->cbrush.texture());
+#if !defined(QT_NO_XRENDER)
+ if (X11->use_xrender) {
+ XRenderPictureAttributes attrs;
+ attrs.repeat = true;
+ XRenderChangePicture(d->dpy, d->brush_pm.x11PictureHandle(), CPRepeat, &attrs);
+ QX11PixmapData *data = static_cast<QX11PixmapData*>(d->brush_pm.data.data());
+ if (data->mask_picture)
+ XRenderChangePicture(d->dpy, data->mask_picture, CPRepeat, &attrs);
+ }
+#endif
+ } else {
+ d->brush_pm = qt_toX11Pixmap(qt_pixmapForBrush(bs, true));
+ }
+ d->brush_pm.x11SetScreen(d->scrn);
+ if (d->brush_pm.depth() == 1) {
+ mask |= GCStipple;
+ vals.stipple = d->brush_pm.handle();
+ s = FillStippled;
+#if !defined(QT_NO_XRENDER)
+ if (X11->use_xrender) {
+ d->bitmap_texture = QPixmap(d->brush_pm.size());
+ d->bitmap_texture.fill(Qt::transparent);
+ d->bitmap_texture = qt_toX11Pixmap(d->bitmap_texture);
+ d->bitmap_texture.x11SetScreen(d->scrn);
+
+ ::Picture src = X11->getSolidFill(d->scrn, d->cbrush.color());
+ XRenderComposite(d->dpy, PictOpSrc, src, d->brush_pm.x11PictureHandle(),
+ d->bitmap_texture.x11PictureHandle(),
+ 0, 0, d->brush_pm.width(), d->brush_pm.height(),
+ 0, 0, d->brush_pm.width(), d->brush_pm.height());
+
+ XRenderPictureAttributes attrs;
+ attrs.repeat = true;
+ XRenderChangePicture(d->dpy, d->bitmap_texture.x11PictureHandle(), CPRepeat, &attrs);
+
+ d->current_brush = d->bitmap_texture.x11PictureHandle();
+ }
+#endif
+ } else {
+ mask |= GCTile;
+#ifndef QT_NO_XRENDER
+ if (d->pdev_depth == 32 && d->brush_pm.depth() != 32) {
+ d->brush_pm.detach();
+ QX11PixmapData *brushData = static_cast<QX11PixmapData*>(d->brush_pm.data.data());
+ brushData->convertToARGB32();
+ }
+#endif
+ vals.tile = (d->brush_pm.depth() == d->pdev_depth
+ ? d->brush_pm.handle()
+ : static_cast<QX11PixmapData*>(d->brush_pm.data.data())->x11ConvertToDefaultDepth());
+ s = FillTiled;
+#if !defined(QT_NO_XRENDER)
+ d->current_brush = d->cbrush.texture().x11PictureHandle();
+#endif
+ }
+
+ mask |= GCTileStipXOrigin | GCTileStipYOrigin;
+ vals.ts_x_origin = qRound(origin.x());
+ vals.ts_y_origin = qRound(origin.y());
+ }
+#if !defined(QT_NO_XRENDER)
+ else if (d->has_alpha_brush) {
+ d->current_brush = X11->getSolidFill(d->scrn, d->cbrush.color());
+ }
+#endif
+
+ vals.fill_style = s;
+ XChangeGC(d->dpy, d->gc_brush, mask, &vals);
+ if (!d->has_clipping) {
+ QRegion sysClip = systemClip();
+ if (!sysClip.isEmpty())
+ x11SetClipRegion(d->dpy, d->gc_brush, 0, d->picture, sysClip);
+ else
+ x11ClearClipRegion(d->dpy, d->gc_brush, 0, d->picture);
+ }
+}
+
+void QX11PaintEngine::drawEllipse(const QRectF &rect)
+{
+ QRect aligned = rect.toAlignedRect();
+ if (aligned == rect)
+ drawEllipse(aligned);
+ else
+ QPaintEngine::drawEllipse(rect);
+}
+
+void QX11PaintEngine::drawEllipse(const QRect &rect)
+{
+ if (rect.isEmpty()) {
+ drawRects(&rect, 1);
+ return;
+ }
+
+ Q_D(QX11PaintEngine);
+ QRect devclip(SHRT_MIN, SHRT_MIN, SHRT_MAX*2 - 1, SHRT_MAX*2 - 1);
+ QRect r(rect);
+ if (d->txop < QTransform::TxRotate) {
+ r = d->matrix.mapRect(rect);
+ } else if (d->txop == QTransform::TxRotate && rect.width() == rect.height()) {
+ QPainterPath path;
+ path.addEllipse(rect);
+ r = d->matrix.map(path).boundingRect().toRect();
+ }
+
+ if (d->has_alpha_brush || d->has_alpha_pen || d->has_custom_pen || (d->render_hints & QPainter::Antialiasing)
+ || d->has_alpha_texture || devclip.intersected(r) != r
+ || (d->has_complex_xform
+ && !(d->has_non_scaling_xform && rect.width() == rect.height())))
+ {
+ QPainterPath path;
+ path.addEllipse(rect);
+ drawPath(path);
+ return;
+ }
+
+ int x = r.x();
+ int y = r.y();
+ int w = r.width();
+ int h = r.height();
+ if (w < 1 || h < 1)
+ return;
+ if (w == 1 && h == 1) {
+ XDrawPoint(d->dpy, d->hd, d->has_pen ? d->gc : d->gc_brush, x, y);
+ return;
+ }
+ d->setupAdaptedOrigin(rect.topLeft());
+ if (d->has_brush) { // draw filled ellipse
+ XFillArc(d->dpy, d->hd, d->gc_brush, x, y, w, h, 0, 360*64);
+ if (!d->has_pen) // make smoother outline
+ XDrawArc(d->dpy, d->hd, d->gc_brush, x, y, w-1, h-1, 0, 360*64);
+ }
+ if (d->has_pen) // draw outline
+ XDrawArc(d->dpy, d->hd, d->gc, x, y, w, h, 0, 360*64);
+ d->resetAdaptedOrigin();
+}
+
+
+
+void QX11PaintEnginePrivate::fillPolygon_translated(const QPointF *polygonPoints, int pointCount,
+ QX11PaintEnginePrivate::GCMode gcMode,
+ QPaintEngine::PolygonDrawMode mode)
+{
+
+ QVarLengthArray<QPointF> translated_points(pointCount);
+ QPointF offset(matrix.dx(), matrix.dy());
+
+ qreal offs = adjust_coords ? aliasedCoordinateDelta : 0.0;
+ if (!X11->use_xrender || !(render_hints & QPainter::Antialiasing))
+ offset += QPointF(aliasedCoordinateDelta, aliasedCoordinateDelta);
+
+ for (int i = 0; i < pointCount; ++i) {
+ translated_points[i] = polygonPoints[i] + offset;
+
+ translated_points[i].rx() = qRound(translated_points[i].x()) + offs;
+ translated_points[i].ry() = qRound(translated_points[i].y()) + offs;
+ }
+
+ fillPolygon_dev(translated_points.data(), pointCount, gcMode, mode);
+}
+
+#ifndef QT_NO_XRENDER
+static void qt_XRenderCompositeTrapezoids(Display *dpy,
+ int op,
+ Picture src,
+ Picture dst,
+ _Xconst XRenderPictFormat *maskFormat,
+ int xSrc,
+ int ySrc,
+ const XTrapezoid *traps, int size)
+{
+ const int MAX_TRAPS = 50000;
+ while (size) {
+ int to_draw = size;
+ if (to_draw > MAX_TRAPS)
+ to_draw = MAX_TRAPS;
+ XRenderCompositeTrapezoids(dpy, op, src, dst,
+ maskFormat,
+ xSrc, ySrc,
+ traps, to_draw);
+ size -= to_draw;
+ traps += to_draw;
+ }
+}
+#endif
+
+void QX11PaintEnginePrivate::fillPolygon_dev(const QPointF *polygonPoints, int pointCount,
+ QX11PaintEnginePrivate::GCMode gcMode,
+ QPaintEngine::PolygonDrawMode mode)
+{
+ Q_Q(QX11PaintEngine);
+
+ int clippedCount = 0;
+ qt_float_point *clippedPoints = 0;
+
+#ifndef QT_NO_XRENDER
+ //can change if we switch to pen if gcMode != BrushGC
+ bool has_fill_texture = has_texture;
+ bool has_fill_pattern = has_pattern;
+ ::Picture src;
+#endif
+ QBrush fill;
+ GC fill_gc;
+ if (gcMode == BrushGC) {
+ fill = cbrush;
+ fill_gc = gc_brush;
+#ifndef QT_NO_XRENDER
+ if (current_brush)
+ src = current_brush;
+ else
+ src = X11->getSolidFill(scrn, fill.color());
+#endif
+ } else {
+ fill = QBrush(cpen.brush());
+ fill_gc = gc;
+#ifndef QT_NO_XRENDER
+ //we use the pens brush
+ has_fill_texture = (fill.style() == Qt::TexturePattern);
+ has_fill_pattern = (fill.style() >= Qt::Dense1Pattern && fill.style() <= Qt::DiagCrossPattern);
+ if (has_fill_texture)
+ src = fill.texture().x11PictureHandle();
+ else if (has_fill_pattern)
+ src = getPatternFill(scrn, fill);
+ else
+ src = X11->getSolidFill(scrn, fill.color());
+#endif
+ }
+
+ polygonClipper.clipPolygon((qt_float_point *) polygonPoints, pointCount,
+ &clippedPoints, &clippedCount);
+
+#ifndef QT_NO_XRENDER
+ bool solid_fill = fill.color().alpha() == 255;
+ if (has_fill_texture && fill.texture().depth() == 1 && solid_fill) {
+ has_fill_texture = false;
+ has_fill_pattern = true;
+ }
+
+ bool antialias = render_hints & QPainter::Antialiasing;
+
+ if (X11->use_xrender
+ && picture
+ && !has_fill_pattern
+ && (clippedCount > 0)
+ && (fill.style() != Qt::NoBrush)
+ && ((has_fill_texture && fill.texture().hasAlpha()) || antialias || !solid_fill || has_alpha_pen != has_alpha_brush))
+ {
+ QRect br = tessellator->tessellate((QPointF *)clippedPoints, clippedCount,
+ mode == QPaintEngine::WindingMode);
+ if (tessellator->size > 0) {
+ XRenderPictureAttributes attrs;
+ attrs.poly_edge = antialias ? PolyEdgeSmooth : PolyEdgeSharp;
+ XRenderChangePicture(dpy, picture, CPPolyEdge, &attrs);
+ int x_offset = int(XFixedToDouble(tessellator->traps[0].left.p1.x) - bg_origin.x());
+ int y_offset = int(XFixedToDouble(tessellator->traps[0].left.p1.y) - bg_origin.y());
+ qt_XRenderCompositeTrapezoids(dpy, composition_mode, src, picture,
+ antialias
+ ? XRenderFindStandardFormat(dpy, PictStandardA8)
+ : XRenderFindStandardFormat(dpy, PictStandardA1),
+ x_offset, y_offset,
+ tessellator->traps, tessellator->size);
+ tessellator->done();
+ }
+ } else
+#endif
+ if (fill.style() != Qt::NoBrush) {
+ if (clippedCount > 200000) {
+ QPolygon poly;
+ for (int i = 0; i < clippedCount; ++i)
+ poly << QPoint(qFloor(clippedPoints[i].x), qFloor(clippedPoints[i].y));
+
+ const QRect bounds = poly.boundingRect();
+ const QRect aligned = bounds
+ & QRect(QPoint(), QSize(pdev->width(), pdev->height()));
+
+ QImage img(aligned.size(), QImage::Format_ARGB32_Premultiplied);
+ img.fill(0);
+
+ QPainter painter(&img);
+ painter.translate(-aligned.x(), -aligned.y());
+ painter.setPen(Qt::NoPen);
+ painter.setBrush(fill);
+ if (gcMode == BrushGC)
+ painter.setBrushOrigin(q->painter()->brushOrigin());
+ painter.drawPolygon(poly);
+ painter.end();
+
+ q->drawImage(aligned, img, img.rect(), Qt::AutoColor);
+ } else if (clippedCount > 0) {
+ QVarLengthArray<XPoint> xpoints(clippedCount);
+ for (int i = 0; i < clippedCount; ++i) {
+ xpoints[i].x = qFloor(clippedPoints[i].x);
+ xpoints[i].y = qFloor(clippedPoints[i].y);
+ }
+ if (mode == QPaintEngine::WindingMode)
+ XSetFillRule(dpy, fill_gc, WindingRule);
+ setupAdaptedOrigin(QPoint(xpoints[0].x, xpoints[0].y));
+ XFillPolygon(dpy, hd, fill_gc,
+ xpoints.data(), clippedCount,
+ mode == QPaintEngine::ConvexMode ? Convex : Complex, CoordModeOrigin);
+ resetAdaptedOrigin();
+ if (mode == QPaintEngine::WindingMode)
+ XSetFillRule(dpy, fill_gc, EvenOddRule);
+ }
+ }
+}
+
+void QX11PaintEnginePrivate::strokePolygon_translated(const QPointF *polygonPoints, int pointCount, bool close)
+{
+ QVarLengthArray<QPointF> translated_points(pointCount);
+ QPointF offset(matrix.dx(), matrix.dy());
+ for (int i = 0; i < pointCount; ++i)
+ translated_points[i] = polygonPoints[i] + offset;
+ strokePolygon_dev(translated_points.data(), pointCount, close);
+}
+
+void QX11PaintEnginePrivate::strokePolygon_dev(const QPointF *polygonPoints, int pointCount, bool close)
+{
+ int clippedCount = 0;
+ qt_float_point *clippedPoints = 0;
+ polygonClipper.clipPolygon((qt_float_point *) polygonPoints, pointCount,
+ &clippedPoints, &clippedCount, close);
+
+ if (clippedCount > 0) {
+ QVarLengthArray<XPoint> xpoints(clippedCount);
+ for (int i = 0; i < clippedCount; ++i) {
+ xpoints[i].x = qRound(clippedPoints[i].x + aliasedCoordinateDelta);
+ xpoints[i].y = qRound(clippedPoints[i].y + aliasedCoordinateDelta);
+ }
+ uint numberPoints = qMin(clippedCount, xlibMaxLinePoints);
+ XPoint *pts = xpoints.data();
+ XDrawLines(dpy, hd, gc, pts, numberPoints, CoordModeOrigin);
+ pts += numberPoints;
+ clippedCount -= numberPoints;
+ numberPoints = qMin(clippedCount, xlibMaxLinePoints-1);
+ while (clippedCount) {
+ XDrawLines(dpy, hd, gc, pts-1, numberPoints+1, CoordModeOrigin);
+ pts += numberPoints;
+ clippedCount -= numberPoints;
+ numberPoints = qMin(clippedCount, xlibMaxLinePoints-1);
+ }
+ }
+}
+
+void QX11PaintEngine::drawPolygon(const QPointF *polygonPoints, int pointCount, PolygonDrawMode mode)
+{
+ Q_D(QX11PaintEngine);
+ if (d->use_path_fallback) {
+ QPainterPath path(polygonPoints[0]);
+ for (int i = 1; i < pointCount; ++i)
+ path.lineTo(polygonPoints[i]);
+ if (mode == PolylineMode) {
+ QBrush oldBrush = d->cbrush;
+ d->cbrush = QBrush(Qt::NoBrush);
+ path.setFillRule(Qt::WindingFill);
+ drawPath(path);
+ d->cbrush = oldBrush;
+ } else {
+ path.setFillRule(mode == OddEvenMode ? Qt::OddEvenFill : Qt::WindingFill);
+ path.closeSubpath();
+ drawPath(path);
+ }
+ return;
+ }
+ if (mode != PolylineMode && d->has_brush)
+ d->fillPolygon_translated(polygonPoints, pointCount, QX11PaintEnginePrivate::BrushGC, mode);
+
+ if (d->has_pen)
+ d->strokePolygon_translated(polygonPoints, pointCount, mode != PolylineMode);
+}
+
+
+void QX11PaintEnginePrivate::fillPath(const QPainterPath &path, QX11PaintEnginePrivate::GCMode gc_mode, bool transform)
+{
+ qreal offs = adjust_coords ? aliasedCoordinateDelta : 0.0;
+
+ QPainterPath clippedPath;
+ QPainterPath clipPath;
+ clipPath.addRect(polygonClipper.boundingRect());
+
+ if (transform)
+ clippedPath = (path*matrix).intersected(clipPath);
+ else
+ clippedPath = path.intersected(clipPath);
+
+ QList<QPolygonF> polys = clippedPath.toFillPolygons();
+ for (int i = 0; i < polys.size(); ++i) {
+ QVarLengthArray<QPointF> translated_points(polys.at(i).size());
+
+ for (int j = 0; j < polys.at(i).size(); ++j) {
+ translated_points[j] = polys.at(i).at(j);
+ if (!X11->use_xrender || !(render_hints & QPainter::Antialiasing)) {
+ translated_points[j].rx() = qRound(translated_points[j].rx() + aliasedCoordinateDelta) + offs;
+ translated_points[j].ry() = qRound(translated_points[j].ry() + aliasedCoordinateDelta) + offs;
+ }
+ }
+
+ fillPolygon_dev(translated_points.data(), polys.at(i).size(), gc_mode,
+ path.fillRule() == Qt::OddEvenFill ? QPaintEngine::OddEvenMode : QPaintEngine::WindingMode);
+ }
+}
+
+void QX11PaintEngine::drawPath(const QPainterPath &path)
+{
+ Q_D(QX11PaintEngine);
+ if (path.isEmpty())
+ return;
+ QTransform old_matrix = d->matrix;
+
+ if (d->has_brush)
+ d->fillPath(path, QX11PaintEnginePrivate::BrushGC, true);
+ if (d->has_pen
+ && ((X11->use_xrender && (d->has_alpha_pen || (d->render_hints & QPainter::Antialiasing)))
+ || (!d->cpen.isCosmetic() && d->txop > QTransform::TxTranslate
+ && !d->has_non_scaling_xform)
+ || (d->cpen.style() == Qt::CustomDashLine))) {
+ QPainterPathStroker stroker;
+ if (d->cpen.style() == Qt::CustomDashLine) {
+ stroker.setDashPattern(d->cpen.dashPattern());
+ stroker.setDashOffset(d->cpen.dashOffset());
+ } else {
+ stroker.setDashPattern(d->cpen.style());
+ }
+ stroker.setCapStyle(d->cpen.capStyle());
+ stroker.setJoinStyle(d->cpen.joinStyle());
+ QPainterPath stroke;
+ qreal width = d->cpen.widthF();
+ QPolygonF poly;
+ QRectF deviceRect(0, 0, d->pdev->width(), d->pdev->height());
+ // necessary to get aliased alphablended primitives to be drawn correctly
+ if (d->cpen.isCosmetic() || d->has_scaling_xform) {
+ if (d->cpen.isCosmetic())
+ stroker.setWidth(width == 0 ? 1 : width);
+ else
+ stroker.setWidth(width * d->xform_scale);
+ stroker.d_ptr->stroker.setClipRect(deviceRect);
+ stroke = stroker.createStroke(path * d->matrix);
+ if (stroke.isEmpty())
+ return;
+ stroke.setFillRule(Qt::WindingFill);
+ d->fillPath(stroke, QX11PaintEnginePrivate::PenGC, false);
+ } else {
+ stroker.setWidth(width);
+ stroker.d_ptr->stroker.setClipRect(d->matrix.inverted().mapRect(deviceRect));
+ stroke = stroker.createStroke(path);
+ if (stroke.isEmpty())
+ return;
+ stroke.setFillRule(Qt::WindingFill);
+ d->fillPath(stroke, QX11PaintEnginePrivate::PenGC, true);
+ }
+ } else if (d->has_pen) {
+ // if we have a cosmetic pen - use XDrawLine() for speed
+ QList<QPolygonF> polys = path.toSubpathPolygons(d->matrix);
+ for (int i = 0; i < polys.size(); ++i)
+ d->strokePolygon_dev(polys.at(i).data(), polys.at(i).size(), false);
+ }
+}
+
+Q_GUI_EXPORT void qt_x11_drawImage(const QRect &rect, const QPoint &pos, const QImage &image,
+ Drawable hd, GC gc, Display *dpy, Visual *visual, int depth)
+{
+ Q_ASSERT(image.format() == QImage::Format_RGB32);
+ Q_ASSERT(image.depth() == 32);
+
+ XImage *xi;
+ // Note: this code assumes either RGB or BGR, 8 bpc server layouts
+ const uint red_mask = (uint) visual->red_mask;
+ bool bgr_layout = (red_mask == 0xff);
+
+ const int w = rect.width();
+ const int h = rect.height();
+
+ QImage im;
+ int image_byte_order = ImageByteOrder(X11->display);
+ if ((QSysInfo::ByteOrder == QSysInfo::BigEndian && ((image_byte_order == LSBFirst) || bgr_layout))
+ || (image_byte_order == MSBFirst && QSysInfo::ByteOrder == QSysInfo::LittleEndian)
+ || (image_byte_order == LSBFirst && bgr_layout))
+ {
+ im = image.copy(rect);
+ const int iw = im.bytesPerLine() / 4;
+ uint *data = (uint *)im.bits();
+ for (int i=0; i < h; i++) {
+ uint *p = data;
+ uint *end = p + w;
+ if (bgr_layout && image_byte_order == MSBFirst && QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
+ while (p < end) {
+ *p = ((*p << 8) & 0xffffff00) | ((*p >> 24) & 0x000000ff);
+ p++;
+ }
+ } else if ((image_byte_order == LSBFirst && QSysInfo::ByteOrder == QSysInfo::BigEndian)
+ || (image_byte_order == MSBFirst && QSysInfo::ByteOrder == QSysInfo::LittleEndian)) {
+ while (p < end) {
+ *p = ((*p << 24) & 0xff000000) | ((*p << 8) & 0x00ff0000)
+ | ((*p >> 8) & 0x0000ff00) | ((*p >> 24) & 0x000000ff);
+ p++;
+ }
+ } else if ((image_byte_order == MSBFirst && QSysInfo::ByteOrder == QSysInfo::BigEndian)
+ || (image_byte_order == LSBFirst && bgr_layout))
+ {
+ while (p < end) {
+ *p = ((*p << 16) & 0x00ff0000) | ((*p >> 16) & 0x000000ff)
+ | ((*p ) & 0xff00ff00);
+ p++;
+ }
+ }
+ data += iw;
+ }
+ xi = XCreateImage(dpy, visual, depth, ZPixmap,
+ 0, (char *) im.bits(), w, h, 32, im.bytesPerLine());
+ } else {
+ xi = XCreateImage(dpy, visual, depth, ZPixmap,
+ 0, (char *) image.scanLine(rect.y())+rect.x()*sizeof(uint), w, h, 32, image.bytesPerLine());
+ }
+ XPutImage(dpy, hd, gc, xi, 0, 0, pos.x(), pos.y(), w, h);
+ xi->data = 0; // QImage owns these bits
+ XDestroyImage(xi);
+}
+
+void QX11PaintEngine::drawImage(const QRectF &r, const QImage &image, const QRectF &sr, Qt::ImageConversionFlags flags)
+{
+ Q_D(QX11PaintEngine);
+
+ if (image.format() == QImage::Format_RGB32
+ && d->pdev_depth >= 24 && image.depth() == 32
+ && r.size() == sr.size())
+ {
+ int sx = qRound(sr.x());
+ int sy = qRound(sr.y());
+ int x = qRound(r.x());
+ int y = qRound(r.y());
+ int w = qRound(r.width());
+ int h = qRound(r.height());
+
+ qt_x11_drawImage(QRect(sx, sy, w, h), QPoint(x, y), image, d->hd, d->gc, d->dpy,
+ (Visual *)d->xinfo->visual(), d->pdev_depth);
+ } else {
+ QPaintEngine::drawImage(r, image, sr, flags);
+ }
+}
+
+void QX11PaintEngine::drawPixmap(const QRectF &r, const QPixmap &px, const QRectF &_sr)
+{
+ Q_D(QX11PaintEngine);
+ QRectF sr = _sr;
+ int x = qRound(r.x());
+ int y = qRound(r.y());
+ int sx = qRound(sr.x());
+ int sy = qRound(sr.y());
+ int sw = qRound(sr.width());
+ int sh = qRound(sr.height());
+
+ QPixmap pixmap = qt_toX11Pixmap(px);
+ if(pixmap.isNull())
+ return;
+
+ if ((d->xinfo && d->xinfo->screen() != pixmap.x11Info().screen())
+ || (pixmap.x11Info().screen() != DefaultScreen(X11->display))) {
+ QPixmap* p = const_cast<QPixmap *>(&pixmap);
+ p->x11SetScreen(d->xinfo ? d->xinfo->screen() : DefaultScreen(X11->display));
+ }
+
+ QPixmap::x11SetDefaultScreen(pixmap.x11Info().screen());
+
+#ifndef QT_NO_XRENDER
+ ::Picture src_pict = static_cast<QX11PixmapData*>(pixmap.data.data())->picture;
+ if (src_pict && d->picture) {
+ const int pDepth = pixmap.depth();
+ if (pDepth == 1 && (d->has_alpha_pen)) {
+ qt_render_bitmap(d->dpy, d->scrn, src_pict, d->picture,
+ sx, sy, x, y, sw, sh, d->cpen);
+ return;
+ } else if (pDepth != 1 && (pDepth == 32 || pDepth != d->pdev_depth)) {
+ XRenderComposite(d->dpy, d->composition_mode,
+ src_pict, 0, d->picture, sx, sy, 0, 0, x, y, sw, sh);
+ return;
+ }
+ }
+#endif
+
+ bool mono_src = pixmap.depth() == 1;
+ bool mono_dst = d->pdev_depth == 1;
+ bool restore_clip = false;
+
+ if (static_cast<QX11PixmapData*>(pixmap.data.data())->x11_mask) { // pixmap has a mask
+ QBitmap comb(sw, sh);
+ GC cgc = XCreateGC(d->dpy, comb.handle(), 0, 0);
+ XSetForeground(d->dpy, cgc, 0);
+ XFillRectangle(d->dpy, comb.handle(), cgc, 0, 0, sw, sh);
+ XSetBackground(d->dpy, cgc, 0);
+ XSetForeground(d->dpy, cgc, 1);
+ if (!d->crgn.isEmpty()) {
+ int num;
+ XRectangle *rects = (XRectangle *)qt_getClipRects(d->crgn, num);
+ XSetClipRectangles(d->dpy, cgc, -x, -y, rects, num, Unsorted);
+ } else if (d->has_clipping) {
+ XSetClipRectangles(d->dpy, cgc, 0, 0, 0, 0, Unsorted);
+ }
+ XSetFillStyle(d->dpy, cgc, FillOpaqueStippled);
+ XSetTSOrigin(d->dpy, cgc, -sx, -sy);
+ XSetStipple(d->dpy, cgc,
+ static_cast<QX11PixmapData*>(pixmap.data.data())->x11_mask);
+ XFillRectangle(d->dpy, comb.handle(), cgc, 0, 0, sw, sh);
+ XFreeGC(d->dpy, cgc);
+
+ XSetClipOrigin(d->dpy, d->gc, x, y);
+ XSetClipMask(d->dpy, d->gc, comb.handle());
+ restore_clip = true;
+ }
+
+ if (mono_src) {
+ if (!d->crgn.isEmpty()) {
+ Pixmap comb = XCreatePixmap(d->dpy, d->hd, sw, sh, 1);
+ GC cgc = XCreateGC(d->dpy, comb, 0, 0);
+ XSetForeground(d->dpy, cgc, 0);
+ XFillRectangle(d->dpy, comb, cgc, 0, 0, sw, sh);
+ int num;
+ XRectangle *rects = (XRectangle *)qt_getClipRects(d->crgn, num);
+ XSetClipRectangles(d->dpy, cgc, -x, -y, rects, num, Unsorted);
+ XCopyArea(d->dpy, pixmap.handle(), comb, cgc, sx, sy, sw, sh, 0, 0);
+ XFreeGC(d->dpy, cgc);
+
+ XSetClipMask(d->dpy, d->gc, comb);
+ XSetClipOrigin(d->dpy, d->gc, x, y);
+ XFreePixmap(d->dpy, comb);
+ } else {
+ XSetClipMask(d->dpy, d->gc, pixmap.handle());
+ XSetClipOrigin(d->dpy, d->gc, x - sx, y - sy);
+ }
+
+ if (mono_dst) {
+ XSetForeground(d->dpy, d->gc, qGray(d->cpen.color().rgb()) > 127 ? 0 : 1);
+ } else {
+ QColormap cmap = QColormap::instance(d->scrn);
+ XSetForeground(d->dpy, d->gc, cmap.pixel(d->cpen.color()));
+ }
+ XFillRectangle(d->dpy, d->hd, d->gc, x, y, sw, sh);
+ restore_clip = true;
+ } else if (mono_dst && !mono_src) {
+ QBitmap bitmap(pixmap);
+ XCopyArea(d->dpy, bitmap.handle(), d->hd, d->gc, sx, sy, sw, sh, x, y);
+ } else {
+ XCopyArea(d->dpy, pixmap.handle(), d->hd, d->gc, sx, sy, sw, sh, x, y);
+ }
+
+ if (d->pdev->devType() == QInternal::Pixmap) {
+ const QPixmap *px = static_cast<const QPixmap*>(d->pdev);
+ Pixmap src_mask = static_cast<QX11PixmapData*>(pixmap.data.data())->x11_mask;
+ Pixmap dst_mask = static_cast<QX11PixmapData*>(px->data.data())->x11_mask;
+ if (dst_mask) {
+ GC cgc = XCreateGC(d->dpy, dst_mask, 0, 0);
+ if (src_mask) { // copy src mask into dst mask
+ XCopyArea(d->dpy, src_mask, dst_mask, cgc, sx, sy, sw, sh, x, y);
+ } else { // no src mask, but make sure the area copied is opaque in dest
+ XSetBackground(d->dpy, cgc, 0);
+ XSetForeground(d->dpy, cgc, 1);
+ XFillRectangle(d->dpy, dst_mask, cgc, x, y, sw, sh);
+ }
+ XFreeGC(d->dpy, cgc);
+ }
+ }
+
+ if (restore_clip) {
+ XSetClipOrigin(d->dpy, d->gc, 0, 0);
+ int num;
+ XRectangle *rects = (XRectangle *)qt_getClipRects(d->crgn, num);
+ if (num == 0)
+ XSetClipMask(d->dpy, d->gc, XNone);
+ else
+ XSetClipRectangles(d->dpy, d->gc, 0, 0, rects, num, Unsorted);
+ }
+}
+
+void QX11PaintEngine::updateMatrix(const QTransform &mtx)
+{
+ Q_D(QX11PaintEngine);
+ d->txop = mtx.type();
+ d->matrix = mtx;
+
+ d->has_complex_xform = (d->txop > QTransform::TxTranslate);
+
+ extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale);
+ bool scaling = qt_scaleForTransform(d->matrix, &d->xform_scale);
+ d->has_scaling_xform = scaling && d->xform_scale != 1.0;
+ d->has_non_scaling_xform = scaling && d->xform_scale == 1.0;
+}
+
+/*
+ NB! the clip region is expected to be in dev coordinates
+*/
+void QX11PaintEngine::updateClipRegion_dev(const QRegion &clipRegion, Qt::ClipOperation op)
+{
+ Q_D(QX11PaintEngine);
+ QRegion sysClip = systemClip();
+ if (op == Qt::NoClip) {
+ d->has_clipping = false;
+ d->crgn = sysClip;
+ if (!sysClip.isEmpty()) {
+ x11SetClipRegion(d->dpy, d->gc, d->gc_brush, d->picture, sysClip);
+ } else {
+ x11ClearClipRegion(d->dpy, d->gc, d->gc_brush, d->picture);
+ }
+ return;
+ }
+
+ switch (op) {
+ case Qt::IntersectClip:
+ if (d->has_clipping) {
+ d->crgn &= clipRegion;
+ break;
+ }
+ // fall through
+ case Qt::ReplaceClip:
+ if (!sysClip.isEmpty())
+ d->crgn = clipRegion.intersected(sysClip);
+ else
+ d->crgn = clipRegion;
+ break;
+ case Qt::UniteClip:
+ d->crgn |= clipRegion;
+ if (!sysClip.isEmpty())
+ d->crgn = d->crgn.intersected(sysClip);
+ break;
+ default:
+ break;
+ }
+ d->has_clipping = true;
+ x11SetClipRegion(d->dpy, d->gc, d->gc_brush, d->picture, d->crgn);
+}
+
+void QX11PaintEngine::updateFont(const QFont &)
+{
+}
+
+Qt::HANDLE QX11PaintEngine::handle() const
+{
+ Q_D(const QX11PaintEngine);
+ Q_ASSERT(isActive());
+ Q_ASSERT(d->hd);
+ return d->hd;
+}
+
+extern void qt_draw_tile(QPaintEngine *, qreal, qreal, qreal, qreal, const QPixmap &,
+ qreal, qreal);
+
+void QX11PaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &p)
+{
+ int x = qRound(r.x());
+ int y = qRound(r.y());
+ int w = qRound(r.width());
+ int h = qRound(r.height());
+ int sx = qRound(p.x());
+ int sy = qRound(p.y());
+
+ bool mono_src = pixmap.depth() == 1;
+ Q_D(QX11PaintEngine);
+
+ if ((d->xinfo && d->xinfo->screen() != pixmap.x11Info().screen())
+ || (pixmap.x11Info().screen() != DefaultScreen(X11->display))) {
+ QPixmap* p = const_cast<QPixmap *>(&pixmap);
+ p->x11SetScreen(d->xinfo ? d->xinfo->screen() : DefaultScreen(X11->display));
+ }
+
+ QPixmap::x11SetDefaultScreen(pixmap.x11Info().screen());
+
+#ifndef QT_NO_XRENDER
+ if (X11->use_xrender && d->picture && pixmap.x11PictureHandle()) {
+#if 0
+ // ### Qt 5: enable this
+ XRenderPictureAttributes attrs;
+ attrs.repeat = true;
+ XRenderChangePicture(d->dpy, pixmap.x11PictureHandle(), CPRepeat, &attrs);
+
+ if (mono_src) {
+ qt_render_bitmap(d->dpy, d->scrn, pixmap.x11PictureHandle(), d->picture,
+ sx, sy, x, y, w, h, d->cpen);
+ } else {
+ XRenderComposite(d->dpy, d->composition_mode,
+ pixmap.x11PictureHandle(), XNone, d->picture,
+ sx, sy, 0, 0, x, y, w, h);
+ }
+#else
+ const int numTiles = (w / pixmap.width()) * (h / pixmap.height());
+ if (numTiles < 100) {
+ // this is essentially qt_draw_tile(), inlined for
+ // the XRenderComposite call
+ int yPos, xPos, drawH, drawW, yOff, xOff;
+ yPos = y;
+ yOff = sy;
+ while(yPos < y + h) {
+ drawH = pixmap.height() - yOff; // Cropping first row
+ if (yPos + drawH > y + h) // Cropping last row
+ drawH = y + h - yPos;
+ xPos = x;
+ xOff = sx;
+ while(xPos < x + w) {
+ drawW = pixmap.width() - xOff; // Cropping first column
+ if (xPos + drawW > x + w) // Cropping last column
+ drawW = x + w - xPos;
+ if (mono_src) {
+ qt_render_bitmap(d->dpy, d->scrn, pixmap.x11PictureHandle(), d->picture,
+ xOff, yOff, xPos, yPos, drawW, drawH, d->cpen);
+ } else {
+ XRenderComposite(d->dpy, d->composition_mode,
+ pixmap.x11PictureHandle(), XNone, d->picture,
+ xOff, yOff, 0, 0, xPos, yPos, drawW, drawH);
+ }
+ xPos += drawW;
+ xOff = 0;
+ }
+ yPos += drawH;
+ yOff = 0;
+ }
+ } else {
+ w = qMin(w, d->pdev->width() - x);
+ h = qMin(h, d->pdev->height() - y);
+ if (w <= 0 || h <= 0)
+ return;
+
+ const int pw = w + sx;
+ const int ph = h + sy;
+ QPixmap pm(pw, ph);
+ if (pixmap.hasAlpha() || mono_src)
+ pm.fill(Qt::transparent);
+
+ const int mode = pixmap.hasAlpha() ? PictOpOver : PictOpSrc;
+ const ::Picture pmPicture = pm.x11PictureHandle();
+
+ // first tile
+ XRenderComposite(d->dpy, mode,
+ pixmap.x11PictureHandle(), XNone, pmPicture,
+ 0, 0, 0, 0, 0, 0, qMin(pw, pixmap.width()), qMin(ph, pixmap.height()));
+
+ // first row of tiles
+ int xPos = pixmap.width();
+ const int sh = qMin(ph, pixmap.height());
+ while (xPos < pw) {
+ const int sw = qMin(xPos, pw - xPos);
+ XRenderComposite(d->dpy, mode,
+ pmPicture, XNone, pmPicture,
+ 0, 0, 0, 0, xPos, 0, sw, sh);
+ xPos *= 2;
+ }
+
+ // remaining rows
+ int yPos = pixmap.height();
+ const int sw = pw;
+ while (yPos < ph) {
+ const int sh = qMin(yPos, ph - yPos);
+ XRenderComposite(d->dpy, mode,
+ pmPicture, XNone, pmPicture,
+ 0, 0, 0, 0, 0, yPos, sw, sh);
+ yPos *= 2;
+ }
+
+ // composite
+ if (mono_src)
+ qt_render_bitmap(d->dpy, d->scrn, pmPicture, d->picture,
+ sx, sy, x, y, w, h, d->cpen);
+ else
+ XRenderComposite(d->dpy, d->composition_mode,
+ pmPicture, XNone, d->picture,
+ sx, sy, 0, 0, x, y, w, h);
+ }
+#endif
+ } else
+#endif // !QT_NO_XRENDER
+ if (pixmap.depth() > 1 && !static_cast<QX11PixmapData*>(pixmap.data.data())->x11_mask) {
+ XSetTile(d->dpy, d->gc, pixmap.handle());
+ XSetFillStyle(d->dpy, d->gc, FillTiled);
+ XSetTSOrigin(d->dpy, d->gc, x-sx, y-sy);
+ XFillRectangle(d->dpy, d->hd, d->gc, x, y, w, h);
+ XSetTSOrigin(d->dpy, d->gc, 0, 0);
+ XSetFillStyle(d->dpy, d->gc, FillSolid);
+ } else {
+ qt_draw_tile(this, x, y, w, h, pixmap, sx, sy);
+ }
+}
+
+void QX11PaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
+{
+ const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
+
+ switch(ti.fontEngine->type()) {
+ case QFontEngine::TestFontEngine:
+ case QFontEngine::Box:
+ d_func()->drawBoxTextItem(p, ti);
+ break;
+ case QFontEngine::XLFD:
+ drawXLFD(p, ti);
+ break;
+#ifndef QT_NO_FONTCONFIG
+ case QFontEngine::Freetype:
+ drawFreetype(p, ti);
+ break;
+#endif
+ default:
+ Q_ASSERT(false);
+ }
+}
+
+void QX11PaintEngine::drawXLFD(const QPointF &p, const QTextItemInt &ti)
+{
+ Q_D(QX11PaintEngine);
+
+ if (d->txop > QTransform::TxTranslate) {
+ // XServer or font don't support server side transformations, need to do it by hand
+ QPaintEngine::drawTextItem(p, ti);
+ return;
+ }
+
+ if (!ti.glyphs.numGlyphs)
+ return;
+
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> glyphs;
+ QTransform matrix = d->matrix;
+ matrix.translate(p.x(), p.y());
+ ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
+ if (glyphs.size() == 0)
+ return;
+
+ QFontEngineXLFD *xlfd = static_cast<QFontEngineXLFD *>(ti.fontEngine);
+ Qt::HANDLE font_id = xlfd->fontStruct()->fid;
+
+ XSetFont(d->dpy, d->gc, font_id);
+
+ const QFixed offs = QFixed::fromReal(aliasedCoordinateDelta);
+ for (int i = 0; i < glyphs.size(); i++) {
+ int xp = qRound(positions[i].x + offs);
+ int yp = qRound(positions[i].y + offs);
+ if (xp < SHRT_MAX && xp > SHRT_MIN && yp > SHRT_MIN && yp < SHRT_MAX) {
+ XChar2b ch;
+ ch.byte1 = glyphs[i] >> 8;
+ ch.byte2 = glyphs[i] & 0xff;
+ XDrawString16(d->dpy, d->hd, d->gc, xp, yp, &ch, 1);
+ }
+ }
+}
+
+#ifndef QT_NO_FONTCONFIG
+static QPainterPath path_for_glyphs(const QVarLengthArray<glyph_t> &glyphs,
+ const QVarLengthArray<QFixedPoint> &positions,
+ const QFontEngineFT *ft)
+{
+ QPainterPath path;
+ path.setFillRule(Qt::WindingFill);
+ ft->lockFace();
+ int i = 0;
+ while (i < glyphs.size()) {
+ QFontEngineFT::Glyph *glyph = ft->loadGlyph(glyphs[i], QFontEngineFT::Format_Mono);
+ // #### fix case where we don't get a glyph
+ if (!glyph)
+ break;
+
+ Q_ASSERT(glyph->format == QFontEngineFT::Format_Mono);
+ int n = 0;
+ int h = glyph->height;
+ int xp = qRound(positions[i].x);
+ int yp = qRound(positions[i].y);
+
+ xp += glyph->x;
+ yp += -glyph->y + glyph->height;
+ int pitch = ((glyph->width + 31) & ~31) >> 3;
+
+ uchar *src = glyph->data;
+ while (h--) {
+ for (int x = 0; x < glyph->width; ++x) {
+ bool set = src[x >> 3] & (0x80 >> (x & 7));
+ if (set) {
+ QRect r(xp + x, yp - h, 1, 1);
+ while (x < glyph->width-1 && src[(x+1) >> 3] & (0x80 >> ((x+1) & 7))) {
+ ++x;
+ r.setRight(r.right()+1);
+ }
+
+ path.addRect(r);
+ ++n;
+ }
+ }
+ src += pitch;
+ }
+ ++i;
+ }
+ ft->unlockFace();
+ return path;
+}
+
+void QX11PaintEngine::drawFreetype(const QPointF &p, const QTextItemInt &ti)
+{
+ Q_D(QX11PaintEngine);
+ if (!ti.glyphs.numGlyphs)
+ return;
+
+ QFontEngineX11FT *ft = static_cast<QFontEngineX11FT *>(ti.fontEngine);
+
+ if (!d->cpen.isSolid()) {
+ QPaintEngine::drawTextItem(p, ti);
+ return;
+ }
+
+ const bool xrenderPath = (X11->use_xrender
+ && !(d->pdev->devType() == QInternal::Pixmap
+ && static_cast<const QPixmap *>(d->pdev)->data->pixelType() == QPixmapData::BitmapType));
+
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> glyphs;
+ QTransform matrix;
+
+ if (xrenderPath)
+ matrix = d->matrix;
+ matrix.translate(p.x(), p.y());
+ ft->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
+ if (glyphs.count() == 0)
+ return;
+
+#ifndef QT_NO_XRENDER
+ QFontEngineFT::QGlyphSet *set = ft->defaultGlyphs();
+ if (d->txop >= QTransform::TxScale && xrenderPath)
+ set = ft->loadTransformedGlyphSet(d->matrix);
+
+ if (!set || set->outline_drawing
+ || !ft->loadGlyphs(set, glyphs.constData(), glyphs.size(), positions.constData(), QFontEngineFT::Format_Render))
+ {
+ QPaintEngine::drawTextItem(p, ti);
+ return;
+ }
+
+ if (xrenderPath) {
+ GlyphSet glyphSet = set->id;
+ const QColor &pen = d->cpen.color();
+ ::Picture src = X11->getSolidFill(d->scrn, pen);
+ XRenderPictFormat *maskFormat = 0;
+ if (ft->xglyph_format != PictStandardA1)
+ maskFormat = XRenderFindStandardFormat(X11->display, ft->xglyph_format);
+
+ enum { t_min = SHRT_MIN, t_max = SHRT_MAX };
+
+ int i = 0;
+ for (; i < glyphs.size()
+ && (positions[i].x < t_min || positions[i].x > t_max
+ || positions[i].y < t_min || positions[i].y > t_max);
+ ++i)
+ ;
+
+ if (i >= glyphs.size())
+ return;
+ ++i;
+
+ QFixed xp = positions[i - 1].x;
+ QFixed yp = positions[i - 1].y;
+ QFixed offs = QFixed::fromReal(aliasedCoordinateDelta);
+
+ XGlyphElt32 elt;
+ elt.glyphset = glyphSet;
+ elt.chars = &glyphs[i - 1];
+ elt.nchars = 1;
+ elt.xOff = qRound(xp + offs);
+ elt.yOff = qRound(yp + offs);
+ for (; i < glyphs.size(); ++i) {
+ if (positions[i].x < t_min || positions[i].x > t_max
+ || positions[i].y < t_min || positions[i].y > t_max) {
+ break;
+ }
+ QFontEngineFT::Glyph *g = ft->cachedGlyph(glyphs[i - 1]);
+ if (g
+ && positions[i].x == xp + g->advance
+ && positions[i].y == yp
+ && elt.nchars < 253 // don't draw more than 253 characters as some X servers
+ // hang with it
+ ) {
+ elt.nchars++;
+ xp += g->advance;
+ } else {
+ xp = positions[i].x;
+ yp = positions[i].y;
+
+ XRenderCompositeText32(X11->display, PictOpOver, src, d->picture,
+ maskFormat, 0, 0, 0, 0,
+ &elt, 1);
+ elt.chars = &glyphs[i];
+ elt.nchars = 1;
+ elt.xOff = qRound(xp + offs);
+ elt.yOff = qRound(yp + offs);
+ }
+ }
+ XRenderCompositeText32(X11->display, PictOpOver, src, d->picture,
+ maskFormat, 0, 0, 0, 0,
+ &elt, 1);
+
+ return;
+
+ }
+#endif
+
+ QPainterPath path = path_for_glyphs(glyphs, positions, ft);
+ if (path.elementCount() <= 1)
+ return;
+ Q_ASSERT((path.elementCount() % 5) == 0);
+ if (d->txop >= QTransform::TxScale) {
+ painter()->save();
+ painter()->setBrush(d->cpen.brush());
+ painter()->setPen(Qt::NoPen);
+ painter()->drawPath(path);
+ painter()->restore();
+ return;
+ }
+
+ const int rectcount = 256;
+ XRectangle rects[rectcount];
+ int num_rects = 0;
+
+ QPoint delta(qRound(d->matrix.dx()), qRound(d->matrix.dy()));
+ QRect clip(d->polygonClipper.boundingRect());
+ for (int i=0; i < path.elementCount(); i+=5) {
+ int x = qRound(path.elementAt(i).x);
+ int y = qRound(path.elementAt(i).y);
+ int w = qRound(path.elementAt(i+1).x) - x;
+ int h = qRound(path.elementAt(i+2).y) - y;
+
+ QRect rect = QRect(x + delta.x(), y + delta.y(), w, h);
+ rect = rect.intersected(clip);
+ if (rect.isEmpty())
+ continue;
+
+ rects[num_rects].x = short(rect.x());
+ rects[num_rects].y = short(rect.y());
+ rects[num_rects].width = ushort(rect.width());
+ rects[num_rects].height = ushort(rect.height());
+ ++num_rects;
+ if (num_rects == rectcount) {
+ XFillRectangles(d->dpy, d->hd, d->gc, rects, num_rects);
+ num_rects = 0;
+ }
+ }
+ if (num_rects > 0)
+ XFillRectangles(d->dpy, d->hd, d->gc, rects, num_rects);
+
+}
+#endif // !QT_NO_XRENDER
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qpaintengine_x11_p.h b/src/gui/painting/qpaintengine_x11_p.h
new file mode 100644
index 0000000000..897c69f122
--- /dev/null
+++ b/src/gui/painting/qpaintengine_x11_p.h
@@ -0,0 +1,246 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTENGINE_X11_P_H
+#define QPAINTENGINE_X11_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtGui/qpaintengine.h"
+#include "QtGui/qregion.h"
+#include "QtGui/qpen.h"
+#include "QtCore/qpoint.h"
+#include "private/qpaintengine_p.h"
+#include "private/qpainter_p.h"
+#include "private/qpolygonclipper_p.h"
+
+typedef unsigned long Picture;
+
+QT_BEGIN_NAMESPACE
+
+class QX11PaintEnginePrivate;
+class QFontEngineFT;
+class QXRenderTessellator;
+
+struct qt_float_point
+{
+ qreal x, y;
+};
+
+class QX11PaintEngine : public QPaintEngine
+{
+ Q_DECLARE_PRIVATE(QX11PaintEngine)
+public:
+ QX11PaintEngine();
+ ~QX11PaintEngine();
+
+ bool begin(QPaintDevice *pdev);
+ bool end();
+
+ void updateState(const QPaintEngineState &state);
+
+ void updatePen(const QPen &pen);
+ void updateBrush(const QBrush &brush, const QPointF &pt);
+ void updateRenderHints(QPainter::RenderHints hints);
+ void updateFont(const QFont &font);
+ void updateMatrix(const QTransform &matrix);
+ void updateClipRegion_dev(const QRegion &region, Qt::ClipOperation op);
+
+ void drawLines(const QLine *lines, int lineCount);
+ void drawLines(const QLineF *lines, int lineCount);
+
+ void drawRects(const QRect *rects, int rectCount);
+ void drawRects(const QRectF *rects, int rectCount);
+
+ void drawPoints(const QPoint *points, int pointCount);
+ void drawPoints(const QPointF *points, int pointCount);
+
+ void drawEllipse(const QRect &r);
+ void drawEllipse(const QRectF &r);
+
+ virtual void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode);
+ inline void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
+ { QPaintEngine::drawPolygon(points, pointCount, mode); }
+
+ void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
+ void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s);
+ void drawPath(const QPainterPath &path);
+ void drawTextItem(const QPointF &p, const QTextItem &textItem);
+ void drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
+ Qt::ImageConversionFlags flags = Qt::AutoColor);
+
+ virtual Qt::HANDLE handle() const;
+ inline Type type() const { return QPaintEngine::X11; }
+
+ QPainter::RenderHints supportedRenderHints() const;
+
+protected:
+ QX11PaintEngine(QX11PaintEnginePrivate &dptr);
+
+ void drawXLFD(const QPointF &p, const QTextItemInt &si);
+#ifndef QT_NO_FONTCONFIG
+ void drawFreetype(const QPointF &p, const QTextItemInt &si);
+#endif
+
+ friend class QPixmap;
+ friend class QFontEngineBox;
+ friend Q_GUI_EXPORT GC qt_x11_get_pen_gc(QPainter *);
+ friend Q_GUI_EXPORT GC qt_x11_get_brush_gc(QPainter *);
+
+private:
+ Q_DISABLE_COPY(QX11PaintEngine)
+};
+
+class QX11PaintEnginePrivate : public QPaintEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QX11PaintEngine)
+public:
+ QX11PaintEnginePrivate()
+ {
+ scrn = -1;
+ hd = 0;
+ picture = 0;
+ gc = gc_brush = 0;
+ dpy = 0;
+ xinfo = 0;
+ txop = QTransform::TxNone;
+ has_clipping = false;
+ render_hints = 0;
+ xform_scale = 1;
+#ifndef QT_NO_XRENDER
+ tessellator = 0;
+#endif
+ }
+ enum GCMode {
+ PenGC,
+ BrushGC
+ };
+
+ void init();
+ void fillPolygon_translated(const QPointF *points, int pointCount, GCMode gcMode,
+ QPaintEngine::PolygonDrawMode mode);
+ void fillPolygon_dev(const QPointF *points, int pointCount, GCMode gcMode,
+ QPaintEngine::PolygonDrawMode mode);
+ void fillPath(const QPainterPath &path, GCMode gcmode, bool transform);
+ void strokePolygon_dev(const QPointF *points, int pointCount, bool close);
+ void strokePolygon_translated(const QPointF *points, int pointCount, bool close);
+ void setupAdaptedOrigin(const QPoint &p);
+ void resetAdaptedOrigin();
+ void decidePathFallback() {
+ use_path_fallback = has_alpha_brush
+ || has_alpha_pen
+ || has_custom_pen
+ || has_complex_xform
+ || (render_hints & QPainter::Antialiasing);
+ }
+ void decideCoordAdjust() {
+ adjust_coords = !(render_hints & QPainter::Antialiasing)
+ && (has_alpha_pen
+ || (has_alpha_brush && has_pen && !has_alpha_pen)
+ || (cpen.style() > Qt::SolidLine));
+ }
+ void clipPolygon_dev(const QPolygonF &poly, QPolygonF *clipped_poly);
+ void systemStateChanged();
+
+ Display *dpy;
+ int scrn;
+ int pdev_depth;
+ Qt::HANDLE hd;
+ QPixmap brush_pm;
+#if !defined (QT_NO_XRENDER)
+ Qt::HANDLE picture;
+ Qt::HANDLE current_brush;
+ QPixmap bitmap_texture;
+ int composition_mode;
+#else
+ Qt::HANDLE picture;
+#endif
+ GC gc;
+ GC gc_brush;
+
+ QPen cpen;
+ QBrush cbrush;
+ QRegion crgn;
+ QTransform matrix;
+ qreal opacity;
+
+ uint has_complex_xform : 1;
+ uint has_scaling_xform : 1;
+ uint has_non_scaling_xform : 1;
+ uint has_custom_pen : 1;
+ uint use_path_fallback : 1;
+ uint adjust_coords : 1;
+ uint has_clipping : 1;
+ uint adapted_brush_origin : 1;
+ uint adapted_pen_origin : 1;
+ uint has_pen : 1;
+ uint has_brush : 1;
+ uint has_texture : 1;
+ uint has_alpha_texture : 1;
+ uint has_pattern : 1;
+ uint has_alpha_pen : 1;
+ uint has_alpha_brush : 1;
+ uint render_hints;
+
+ const QX11Info *xinfo;
+ QPointF bg_origin;
+ QTransform::TransformationType txop;
+ qreal xform_scale;
+ QPolygonClipper<qt_float_point, qt_float_point, float> polygonClipper;
+
+ int xlibMaxLinePoints;
+#ifndef QT_NO_XRENDER
+ QXRenderTessellator *tessellator;
+#endif
+};
+
+QT_END_NAMESPACE
+
+#endif // QPAINTENGINE_X11_P_H
diff --git a/src/gui/painting/qpaintengineex.cpp b/src/gui/painting/qpaintengineex.cpp
new file mode 100644
index 0000000000..509fb77d25
--- /dev/null
+++ b/src/gui/painting/qpaintengineex.cpp
@@ -0,0 +1,1015 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpaintengineex_p.h"
+#include "qpainter_p.h"
+#include "qstroker_p.h"
+#include "qbezier_p.h"
+#include <private/qpainterpath_p.h>
+
+#include <qvarlengtharray.h>
+#include <qdebug.h>
+
+
+QT_BEGIN_NAMESPACE
+
+/*******************************************************************************
+ *
+ * class QVectorPath
+ *
+ */
+QVectorPath::~QVectorPath()
+{
+ if (m_hints & ShouldUseCacheHint) {
+ CacheEntry *e = m_cache;
+ while (e) {
+ if (e->data)
+ e->cleanup(e->engine, e->data);
+ CacheEntry *n = e->next;
+ delete e;
+ e = n;
+ }
+ }
+}
+
+
+QRectF QVectorPath::controlPointRect() const
+{
+ if (m_hints & ControlPointRect)
+ return QRectF(QPointF(m_cp_rect.x1, m_cp_rect.y1), QPointF(m_cp_rect.x2, m_cp_rect.y2));
+
+ if (m_count == 0) {
+ m_cp_rect.x1 = m_cp_rect.x2 = m_cp_rect.y1 = m_cp_rect.y2 = 0;
+ m_hints |= ControlPointRect;
+ return QRectF(QPointF(m_cp_rect.x1, m_cp_rect.y1), QPointF(m_cp_rect.x2, m_cp_rect.y2));
+ }
+ Q_ASSERT(m_points && m_count > 0);
+
+ const qreal *pts = m_points;
+ m_cp_rect.x1 = m_cp_rect.x2 = *pts;
+ ++pts;
+ m_cp_rect.y1 = m_cp_rect.y2 = *pts;
+ ++pts;
+
+ const qreal *epts = m_points + (m_count << 1);
+ while (pts < epts) {
+ qreal x = *pts;
+ if (x < m_cp_rect.x1) m_cp_rect.x1 = x;
+ else if (x > m_cp_rect.x2) m_cp_rect.x2 = x;
+ ++pts;
+
+ qreal y = *pts;
+ if (y < m_cp_rect.y1) m_cp_rect.y1 = y;
+ else if (y > m_cp_rect.y2) m_cp_rect.y2 = y;
+ ++pts;
+ }
+
+ m_hints |= ControlPointRect;
+ return QRectF(QPointF(m_cp_rect.x1, m_cp_rect.y1), QPointF(m_cp_rect.x2, m_cp_rect.y2));
+}
+
+
+QVectorPath::CacheEntry *QVectorPath::addCacheData(QPaintEngineEx *engine, void *data,
+ qvectorpath_cache_cleanup cleanup) const{
+ Q_ASSERT(!lookupCacheData(engine));
+ if ((m_hints & IsCachedHint) == 0) {
+ m_cache = 0;
+ m_hints |= IsCachedHint;
+ }
+ CacheEntry *e = new CacheEntry;
+ e->engine = engine;
+ e->data = data;
+ e->cleanup = cleanup;
+ e->next = m_cache;
+ m_cache = e;
+ return m_cache;
+}
+
+
+const QVectorPath &qtVectorPathForPath(const QPainterPath &path)
+{
+ Q_ASSERT(path.d_func());
+ return path.d_func()->vectorPath();
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug Q_GUI_EXPORT &operator<<(QDebug &s, const QVectorPath &path)
+{
+ QRectF rf = path.controlPointRect();
+ s << "QVectorPath(size:" << path.elementCount()
+ << " hints:" << hex << path.hints()
+ << rf << ')';
+ return s;
+}
+#endif
+
+/*******************************************************************************
+ *
+ * class QPaintEngineExPrivate:
+ *
+ */
+
+
+struct StrokeHandler {
+ StrokeHandler(int reserve) : pts(reserve), types(reserve) {}
+ QDataBuffer<qreal> pts;
+ QDataBuffer<QPainterPath::ElementType> types;
+};
+
+
+QPaintEngineExPrivate::QPaintEngineExPrivate()
+ : dasher(&stroker),
+ strokeHandler(0),
+ activeStroker(0),
+ strokerPen(Qt::NoPen)
+{
+}
+
+
+QPaintEngineExPrivate::~QPaintEngineExPrivate()
+{
+ delete strokeHandler;
+}
+
+
+void QPaintEngineExPrivate::replayClipOperations()
+{
+ Q_Q(QPaintEngineEx);
+
+ QPainter *p = q->painter();
+ if (!p || !p->d_ptr)
+ return;
+
+ QList<QPainterClipInfo> clipInfo = p->d_ptr->state->clipInfo;
+
+ QTransform transform = q->state()->matrix;
+
+ for (int i = 0; i < clipInfo.size(); ++i) {
+ const QPainterClipInfo &info = clipInfo.at(i);
+
+ if (info.matrix != q->state()->matrix) {
+ q->state()->matrix = info.matrix;
+ q->transformChanged();
+ }
+
+ switch (info.clipType) {
+ case QPainterClipInfo::RegionClip:
+ q->clip(info.region, info.operation);
+ break;
+ case QPainterClipInfo::PathClip:
+ q->clip(info.path, info.operation);
+ break;
+ case QPainterClipInfo::RectClip:
+ q->clip(info.rect, info.operation);
+ break;
+ case QPainterClipInfo::RectFClip: {
+ qreal right = info.rectf.x() + info.rectf.width();
+ qreal bottom = info.rectf.y() + info.rectf.height();
+ qreal pts[] = { info.rectf.x(), info.rectf.y(),
+ right, info.rectf.y(),
+ right, bottom,
+ info.rectf.x(), bottom };
+ QVectorPath vp(pts, 4, 0, QVectorPath::RectangleHint);
+ q->clip(vp, info.operation);
+ break;
+ }
+ }
+ }
+
+ if (transform != q->state()->matrix) {
+ q->state()->matrix = transform;
+ q->transformChanged();
+ }
+}
+
+
+bool QPaintEngineExPrivate::hasClipOperations() const
+{
+ Q_Q(const QPaintEngineEx);
+
+ QPainter *p = q->painter();
+ if (!p || !p->d_ptr)
+ return false;
+
+ QList<QPainterClipInfo> clipInfo = p->d_ptr->state->clipInfo;
+
+ return !clipInfo.isEmpty();
+}
+
+/*******************************************************************************
+ *
+ * class QPaintEngineEx:
+ *
+ */
+
+static QPainterPath::ElementType qpaintengineex_ellipse_types[] = {
+ QPainterPath::MoveToElement,
+ QPainterPath::CurveToElement,
+ QPainterPath::CurveToDataElement,
+ QPainterPath::CurveToDataElement,
+
+ QPainterPath::CurveToElement,
+ QPainterPath::CurveToDataElement,
+ QPainterPath::CurveToDataElement,
+
+ QPainterPath::CurveToElement,
+ QPainterPath::CurveToDataElement,
+ QPainterPath::CurveToDataElement,
+
+ QPainterPath::CurveToElement,
+ QPainterPath::CurveToDataElement,
+ QPainterPath::CurveToDataElement
+};
+
+static QPainterPath::ElementType qpaintengineex_line_types_16[] = {
+ QPainterPath::MoveToElement, QPainterPath::LineToElement,
+ QPainterPath::MoveToElement, QPainterPath::LineToElement,
+ QPainterPath::MoveToElement, QPainterPath::LineToElement,
+ QPainterPath::MoveToElement, QPainterPath::LineToElement,
+ QPainterPath::MoveToElement, QPainterPath::LineToElement,
+ QPainterPath::MoveToElement, QPainterPath::LineToElement,
+ QPainterPath::MoveToElement, QPainterPath::LineToElement,
+ QPainterPath::MoveToElement, QPainterPath::LineToElement,
+ QPainterPath::MoveToElement, QPainterPath::LineToElement,
+ QPainterPath::MoveToElement, QPainterPath::LineToElement,
+ QPainterPath::MoveToElement, QPainterPath::LineToElement,
+ QPainterPath::MoveToElement, QPainterPath::LineToElement,
+ QPainterPath::MoveToElement, QPainterPath::LineToElement,
+ QPainterPath::MoveToElement, QPainterPath::LineToElement,
+ QPainterPath::MoveToElement, QPainterPath::LineToElement,
+ QPainterPath::MoveToElement, QPainterPath::LineToElement
+};
+
+static QPainterPath::ElementType qpaintengineex_rect4_types_32[] = {
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 1
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 2
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 3
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 4
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 5
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 6
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 7
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 8
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 9
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 10
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 11
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 12
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 13
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 14
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 15
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 16
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 17
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 18
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 19
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 20
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 21
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 22
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 23
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 24
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 25
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 26
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 27
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 28
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 29
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 30
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 31
+ QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 32
+};
+
+
+static QPainterPath::ElementType qpaintengineex_roundedrect_types[] = {
+ QPainterPath::MoveToElement,
+ QPainterPath::LineToElement,
+ QPainterPath::CurveToElement,
+ QPainterPath::CurveToDataElement,
+ QPainterPath::CurveToDataElement,
+ QPainterPath::LineToElement,
+ QPainterPath::CurveToElement,
+ QPainterPath::CurveToDataElement,
+ QPainterPath::CurveToDataElement,
+ QPainterPath::LineToElement,
+ QPainterPath::CurveToElement,
+ QPainterPath::CurveToDataElement,
+ QPainterPath::CurveToDataElement,
+ QPainterPath::LineToElement,
+ QPainterPath::CurveToElement,
+ QPainterPath::CurveToDataElement,
+ QPainterPath::CurveToDataElement
+};
+
+
+
+static void qpaintengineex_moveTo(qreal x, qreal y, void *data) {
+ ((StrokeHandler *) data)->pts.add(x);
+ ((StrokeHandler *) data)->pts.add(y);
+ ((StrokeHandler *) data)->types.add(QPainterPath::MoveToElement);
+}
+
+static void qpaintengineex_lineTo(qreal x, qreal y, void *data) {
+ ((StrokeHandler *) data)->pts.add(x);
+ ((StrokeHandler *) data)->pts.add(y);
+ ((StrokeHandler *) data)->types.add(QPainterPath::LineToElement);
+}
+
+static void qpaintengineex_cubicTo(qreal c1x, qreal c1y, qreal c2x, qreal c2y, qreal ex, qreal ey, void *data) {
+ ((StrokeHandler *) data)->pts.add(c1x);
+ ((StrokeHandler *) data)->pts.add(c1y);
+ ((StrokeHandler *) data)->types.add(QPainterPath::CurveToElement);
+
+ ((StrokeHandler *) data)->pts.add(c2x);
+ ((StrokeHandler *) data)->pts.add(c2y);
+ ((StrokeHandler *) data)->types.add(QPainterPath::CurveToDataElement);
+
+ ((StrokeHandler *) data)->pts.add(ex);
+ ((StrokeHandler *) data)->pts.add(ey);
+ ((StrokeHandler *) data)->types.add(QPainterPath::CurveToDataElement);
+}
+
+QPaintEngineEx::QPaintEngineEx()
+ : QPaintEngine(*new QPaintEngineExPrivate, AllFeatures)
+{
+ extended = true;
+}
+
+QPaintEngineEx::QPaintEngineEx(QPaintEngineExPrivate &data)
+ : QPaintEngine(data, AllFeatures)
+{
+ extended = true;
+}
+
+QPainterState *QPaintEngineEx::createState(QPainterState *orig) const
+{
+ if (!orig)
+ return new QPainterState;
+ return new QPainterState(orig);
+}
+
+Q_GUI_EXPORT extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp
+
+void QPaintEngineEx::stroke(const QVectorPath &path, const QPen &pen)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QPaintEngineEx::stroke()" << pen;
+#endif
+
+ Q_D(QPaintEngineEx);
+
+ if (path.isEmpty())
+ return;
+
+ if (!d->strokeHandler) {
+ d->strokeHandler = new StrokeHandler(path.elementCount()+4);
+ d->stroker.setMoveToHook(qpaintengineex_moveTo);
+ d->stroker.setLineToHook(qpaintengineex_lineTo);
+ d->stroker.setCubicToHook(qpaintengineex_cubicTo);
+ }
+
+ if (!qpen_fast_equals(pen, d->strokerPen)) {
+ d->strokerPen = pen;
+ d->stroker.setJoinStyle(pen.joinStyle());
+ d->stroker.setCapStyle(pen.capStyle());
+ d->stroker.setMiterLimit(pen.miterLimit());
+ qreal penWidth = pen.widthF();
+ if (penWidth == 0)
+ d->stroker.setStrokeWidth(1);
+ else
+ d->stroker.setStrokeWidth(penWidth);
+
+ Qt::PenStyle style = pen.style();
+ if (style == Qt::SolidLine) {
+ d->activeStroker = &d->stroker;
+ } else if (style == Qt::NoPen) {
+ d->activeStroker = 0;
+ } else {
+ d->dasher.setDashPattern(pen.dashPattern());
+ d->dasher.setDashOffset(pen.dashOffset());
+ d->activeStroker = &d->dasher;
+ }
+ }
+
+ if (!d->activeStroker) {
+ return;
+ }
+
+ if (pen.style() > Qt::SolidLine) {
+ if (pen.isCosmetic()) {
+ d->activeStroker->setClipRect(d->exDeviceRect);
+ } else {
+ QRectF clipRect = state()->matrix.inverted().mapRect(QRectF(d->exDeviceRect));
+ d->activeStroker->setClipRect(clipRect);
+ }
+ }
+
+ const QPainterPath::ElementType *types = path.elements();
+ const qreal *points = path.points();
+ int pointCount = path.elementCount();
+
+ const qreal *lastPoint = points + (pointCount<<1);
+
+ d->strokeHandler->types.reset();
+ d->strokeHandler->pts.reset();
+
+ // Some engines might decide to optimize for the non-shape hint later on...
+ uint flags = QVectorPath::WindingFill;
+
+ if (path.elementCount() > 2)
+ flags |= QVectorPath::NonConvexShapeMask;
+
+ if (d->stroker.capStyle() == Qt::RoundCap || d->stroker.joinStyle() == Qt::RoundJoin)
+ flags |= QVectorPath::CurvedShapeMask;
+
+ // ### Perspective Xforms are currently not supported...
+ if (!pen.isCosmetic()) {
+ // We include cosmetic pens in this case to avoid having to
+ // change the current transform. Normal transformed,
+ // non-cosmetic pens will be transformed as part of fill
+ // later, so they are also covered here..
+ d->activeStroker->setCurveThresholdFromTransform(state()->matrix);
+ d->activeStroker->begin(d->strokeHandler);
+ if (types) {
+ while (points < lastPoint) {
+ switch (*types) {
+ case QPainterPath::MoveToElement:
+ d->activeStroker->moveTo(points[0], points[1]);
+ points += 2;
+ ++types;
+ break;
+ case QPainterPath::LineToElement:
+ d->activeStroker->lineTo(points[0], points[1]);
+ points += 2;
+ ++types;
+ break;
+ case QPainterPath::CurveToElement:
+ d->activeStroker->cubicTo(points[0], points[1],
+ points[2], points[3],
+ points[4], points[5]);
+ points += 6;
+ types += 3;
+ flags |= QVectorPath::CurvedShapeMask;
+ break;
+ default:
+ break;
+ }
+ }
+ if (path.hasImplicitClose())
+ d->activeStroker->lineTo(path.points()[0], path.points()[1]);
+
+ } else {
+ d->activeStroker->moveTo(points[0], points[1]);
+ points += 2;
+ while (points < lastPoint) {
+ d->activeStroker->lineTo(points[0], points[1]);
+ points += 2;
+ }
+ if (path.hasImplicitClose())
+ d->activeStroker->lineTo(path.points()[0], path.points()[1]);
+ }
+ d->activeStroker->end();
+
+ if (!d->strokeHandler->types.size()) // an empty path...
+ return;
+
+ QVectorPath strokePath(d->strokeHandler->pts.data(),
+ d->strokeHandler->types.size(),
+ d->strokeHandler->types.data(),
+ flags);
+ fill(strokePath, pen.brush());
+ } else {
+ // For cosmetic pens we need a bit of trickery... We to process xform the input points
+ if (state()->matrix.type() >= QTransform::TxProject) {
+ QPainterPath painterPath = state()->matrix.map(path.convertToPainterPath());
+ d->activeStroker->strokePath(painterPath, d->strokeHandler, QTransform());
+ } else {
+ d->activeStroker->setCurveThresholdFromTransform(QTransform());
+ d->activeStroker->begin(d->strokeHandler);
+ if (types) {
+ while (points < lastPoint) {
+ switch (*types) {
+ case QPainterPath::MoveToElement: {
+ QPointF pt = (*(QPointF *) points) * state()->matrix;
+ d->activeStroker->moveTo(pt.x(), pt.y());
+ points += 2;
+ ++types;
+ break;
+ }
+ case QPainterPath::LineToElement: {
+ QPointF pt = (*(QPointF *) points) * state()->matrix;
+ d->activeStroker->lineTo(pt.x(), pt.y());
+ points += 2;
+ ++types;
+ break;
+ }
+ case QPainterPath::CurveToElement: {
+ QPointF c1 = ((QPointF *) points)[0] * state()->matrix;
+ QPointF c2 = ((QPointF *) points)[1] * state()->matrix;
+ QPointF e = ((QPointF *) points)[2] * state()->matrix;
+ d->activeStroker->cubicTo(c1.x(), c1.y(), c2.x(), c2.y(), e.x(), e.y());
+ points += 6;
+ types += 3;
+ flags |= QVectorPath::CurvedShapeMask;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ if (path.hasImplicitClose()) {
+ QPointF pt = * ((QPointF *) path.points()) * state()->matrix;
+ d->activeStroker->lineTo(pt.x(), pt.y());
+ }
+
+ } else {
+ QPointF p = ((QPointF *)points)[0] * state()->matrix;
+ d->activeStroker->moveTo(p.x(), p.y());
+ points += 2;
+ while (points < lastPoint) {
+ QPointF p = ((QPointF *)points)[0] * state()->matrix;
+ d->activeStroker->lineTo(p.x(), p.y());
+ points += 2;
+ }
+ if (path.hasImplicitClose())
+ d->activeStroker->lineTo(p.x(), p.y());
+ }
+ d->activeStroker->end();
+ }
+
+ QVectorPath strokePath(d->strokeHandler->pts.data(),
+ d->strokeHandler->types.size(),
+ d->strokeHandler->types.data(),
+ flags);
+
+ QTransform xform = state()->matrix;
+ state()->matrix = QTransform();
+ transformChanged();
+
+ QBrush brush = pen.brush();
+ if (qbrush_style(brush) != Qt::SolidPattern)
+ brush.setTransform(brush.transform() * xform);
+
+ fill(strokePath, brush);
+
+ state()->matrix = xform;
+ transformChanged();
+ }
+}
+
+void QPaintEngineEx::draw(const QVectorPath &path)
+{
+ const QBrush &brush = state()->brush;
+ if (qbrush_style(brush) != Qt::NoBrush)
+ fill(path, brush);
+
+ const QPen &pen = state()->pen;
+ if (qpen_style(pen) != Qt::NoPen && qbrush_style(qpen_brush(pen)) != Qt::NoBrush)
+ stroke(path, pen);
+}
+
+
+void QPaintEngineEx::clip(const QRect &r, Qt::ClipOperation op)
+{
+ qreal right = r.x() + r.width();
+ qreal bottom = r.y() + r.height();
+ qreal pts[] = { qreal(r.x()), qreal(r.y()),
+ right, qreal(r.y()),
+ right, bottom,
+ qreal(r.x()), bottom,
+ qreal(r.x()), qreal(r.y()) };
+ QVectorPath vp(pts, 5, 0, QVectorPath::RectangleHint);
+ clip(vp, op);
+}
+
+void QPaintEngineEx::clip(const QRegion &region, Qt::ClipOperation op)
+{
+ if (region.rectCount() == 1)
+ clip(region.boundingRect(), op);
+
+ QVector<QRect> rects = region.rects();
+ if (rects.size() <= 32) {
+ qreal pts[2*32*4];
+ int pos = 0;
+ for (QVector<QRect>::const_iterator i = rects.constBegin(); i != rects.constEnd(); ++i) {
+ qreal x1 = i->x();
+ qreal y1 = i->y();
+ qreal x2 = i->x() + i->width();
+ qreal y2 = i->y() + i->height();
+
+ pts[pos++] = x1;
+ pts[pos++] = y1;
+
+ pts[pos++] = x2;
+ pts[pos++] = y1;
+
+ pts[pos++] = x2;
+ pts[pos++] = y2;
+
+ pts[pos++] = x1;
+ pts[pos++] = y2;
+ }
+ QVectorPath vp(pts, rects.size() * 4, qpaintengineex_rect4_types_32);
+ clip(vp, op);
+ } else {
+ QVarLengthArray<qreal> pts(rects.size() * 2 * 4);
+ QVarLengthArray<QPainterPath::ElementType> types(rects.size() * 4);
+ int ppos = 0;
+ int tpos = 0;
+
+ for (QVector<QRect>::const_iterator i = rects.constBegin(); i != rects.constEnd(); ++i) {
+ qreal x1 = i->x();
+ qreal y1 = i->y();
+ qreal x2 = i->x() + i->width();
+ qreal y2 = i->y() + i->height();
+
+ pts[ppos++] = x1;
+ pts[ppos++] = y1;
+
+ pts[ppos++] = x2;
+ pts[ppos++] = y1;
+
+ pts[ppos++] = x2;
+ pts[ppos++] = y2;
+
+ pts[ppos++] = x1;
+ pts[ppos++] = y2;
+
+ types[tpos++] = QPainterPath::MoveToElement;
+ types[tpos++] = QPainterPath::LineToElement;
+ types[tpos++] = QPainterPath::LineToElement;
+ types[tpos++] = QPainterPath::LineToElement;
+ }
+
+ QVectorPath vp(pts.data(), rects.size() * 4, types.data());
+ clip(vp, op);
+ }
+
+}
+
+void QPaintEngineEx::clip(const QPainterPath &path, Qt::ClipOperation op)
+{
+ if (path.isEmpty()) {
+ QVectorPath vp(0, 0);
+ clip(vp, op);
+ } else {
+ clip(qtVectorPathForPath(path), op);
+ }
+}
+
+void QPaintEngineEx::fillRect(const QRectF &r, const QBrush &brush)
+{
+ qreal pts[] = { r.x(), r.y(), r.x() + r.width(), r.y(),
+ r.x() + r.width(), r.y() + r.height(), r.x(), r.y() + r.height() };
+ QVectorPath vp(pts, 4, 0, QVectorPath::RectangleHint);
+ fill(vp, brush);
+}
+
+void QPaintEngineEx::fillRect(const QRectF &r, const QColor &color)
+{
+ fillRect(r, QBrush(color));
+}
+
+void QPaintEngineEx::drawRects(const QRect *rects, int rectCount)
+{
+ for (int i=0; i<rectCount; ++i) {
+ const QRect &r = rects[i];
+ // ### Is there a one off here?
+ qreal right = r.x() + r.width();
+ qreal bottom = r.y() + r.height();
+ qreal pts[] = { qreal(r.x()), qreal(r.y()),
+ right, qreal(r.y()),
+ right, bottom,
+ qreal(r.x()), bottom,
+ qreal(r.x()), qreal(r.y()) };
+ QVectorPath vp(pts, 5, 0, QVectorPath::RectangleHint);
+ draw(vp);
+ }
+}
+
+void QPaintEngineEx::drawRects(const QRectF *rects, int rectCount)
+{
+ for (int i=0; i<rectCount; ++i) {
+ const QRectF &r = rects[i];
+ qreal right = r.x() + r.width();
+ qreal bottom = r.y() + r.height();
+ qreal pts[] = { r.x(), r.y(),
+ right, r.y(),
+ right, bottom,
+ r.x(), bottom,
+ r.x(), r.y() };
+ QVectorPath vp(pts, 5, 0, QVectorPath::RectangleHint);
+ draw(vp);
+ }
+}
+
+
+void QPaintEngineEx::drawRoundedRect(const QRectF &rect, qreal xRadius, qreal yRadius,
+ Qt::SizeMode mode)
+{
+ qreal x1 = rect.left();
+ qreal x2 = rect.right();
+ qreal y1 = rect.top();
+ qreal y2 = rect.bottom();
+
+ if (mode == Qt::RelativeSize) {
+ xRadius = xRadius * rect.width() / 200.;
+ yRadius = yRadius * rect.height() / 200.;
+ }
+
+ xRadius = qMin(xRadius, rect.width() / 2);
+ yRadius = qMin(yRadius, rect.height() / 2);
+
+ qreal pts[] = {
+ x1 + xRadius, y1, // MoveTo
+ x2 - xRadius, y1, // LineTo
+ x2 - (1 - KAPPA) * xRadius, y1, // CurveTo
+ x2, y1 + (1 - KAPPA) * yRadius,
+ x2, y1 + yRadius,
+ x2, y2 - yRadius, // LineTo
+ x2, y2 - (1 - KAPPA) * yRadius, // CurveTo
+ x2 - (1 - KAPPA) * xRadius, y2,
+ x2 - xRadius, y2,
+ x1 + xRadius, y2, // LineTo
+ x1 + (1 - KAPPA) * xRadius, y2, // CurveTo
+ x1, y2 - (1 - KAPPA) * yRadius,
+ x1, y2 - yRadius,
+ x1, y1 + yRadius, // LineTo
+ x1, y1 + (1 - KAPPA) * yRadius, // CurveTo
+ x1 + (1 - KAPPA) * xRadius, y1,
+ x1 + xRadius, y1
+ };
+
+ QVectorPath path(pts, 17, qpaintengineex_roundedrect_types, QVectorPath::RoundedRectHint);
+ draw(path);
+}
+
+
+
+void QPaintEngineEx::drawLines(const QLine *lines, int lineCount)
+{
+ int elementCount = lineCount << 1;
+ while (elementCount > 0) {
+ int count = qMin(elementCount, 32);
+
+ qreal pts[64];
+ int count2 = count<<1;
+#ifdef Q_WS_MAC
+ for (int i=0; i<count2; i+=2) {
+ pts[i] = ((int *) lines)[i+1];
+ pts[i+1] = ((int *) lines)[i];
+ }
+#else
+ for (int i=0; i<count2; ++i)
+ pts[i] = ((int *) lines)[i];
+#endif
+
+ QVectorPath path(pts, count, qpaintengineex_line_types_16, QVectorPath::LinesHint);
+ stroke(path, state()->pen);
+
+ elementCount -= 32;
+ lines += 16;
+ }
+}
+
+void QPaintEngineEx::drawLines(const QLineF *lines, int lineCount)
+{
+ int elementCount = lineCount << 1;
+ while (elementCount > 0) {
+ int count = qMin(elementCount, 32);
+
+ QVectorPath path((qreal *) lines, count, qpaintengineex_line_types_16,
+ QVectorPath::LinesHint);
+ stroke(path, state()->pen);
+
+ elementCount -= 32;
+ lines += 16;
+ }
+}
+
+void QPaintEngineEx::drawEllipse(const QRectF &r)
+{
+ qreal pts[26]; // QPointF[13] without constructors...
+ union {
+ qreal *ptr;
+ QPointF *points;
+ } x;
+ x.ptr = pts;
+
+ int point_count = 0;
+ x.points[0] = qt_curves_for_arc(r, 0, -360, x.points + 1, &point_count);
+ QVectorPath vp((qreal *) pts, point_count, qpaintengineex_ellipse_types, QVectorPath::EllipseHint);
+ draw(vp);
+}
+
+void QPaintEngineEx::drawEllipse(const QRect &r)
+{
+ drawEllipse(QRectF(r));
+}
+
+void QPaintEngineEx::drawPath(const QPainterPath &path)
+{
+ if (!path.isEmpty())
+ draw(qtVectorPathForPath(path));
+}
+
+
+void QPaintEngineEx::drawPoints(const QPointF *points, int pointCount)
+{
+ QPen pen = state()->pen;
+ if (pen.capStyle() == Qt::FlatCap)
+ pen.setCapStyle(Qt::SquareCap);
+
+ if (pen.brush().isOpaque()) {
+ while (pointCount > 0) {
+ int count = qMin(pointCount, 16);
+ qreal pts[64];
+ int oset = -1;
+ for (int i=0; i<count; ++i) {
+ pts[++oset] = points[i].x();
+ pts[++oset] = points[i].y();
+ pts[++oset] = points[i].x() + 1/63.;
+ pts[++oset] = points[i].y();
+ }
+ QVectorPath path(pts, count * 2, qpaintengineex_line_types_16, QVectorPath::LinesHint);
+ stroke(path, pen);
+ pointCount -= 16;
+ points += 16;
+ }
+ } else {
+ for (int i=0; i<pointCount; ++i) {
+ qreal pts[] = { points[i].x(), points[i].y(), points[i].x() + 1/63., points[i].y() };
+ QVectorPath path(pts, 2, 0);
+ stroke(path, pen);
+ }
+ }
+}
+
+void QPaintEngineEx::drawPoints(const QPoint *points, int pointCount)
+{
+ QPen pen = state()->pen;
+ if (pen.capStyle() == Qt::FlatCap)
+ pen.setCapStyle(Qt::SquareCap);
+
+ if (pen.brush().isOpaque()) {
+ while (pointCount > 0) {
+ int count = qMin(pointCount, 16);
+ qreal pts[64];
+ int oset = -1;
+ for (int i=0; i<count; ++i) {
+ pts[++oset] = points[i].x();
+ pts[++oset] = points[i].y();
+ pts[++oset] = points[i].x() + 1/63.;
+ pts[++oset] = points[i].y();
+ }
+ QVectorPath path(pts, count * 2, qpaintengineex_line_types_16, QVectorPath::LinesHint);
+ stroke(path, pen);
+ pointCount -= 16;
+ points += 16;
+ }
+ } else {
+ for (int i=0; i<pointCount; ++i) {
+ qreal pts[] = { qreal(points[i].x()), qreal(points[i].y()),
+ qreal(points[i].x() +1/63.), qreal(points[i].y()) };
+ QVectorPath path(pts, 2, 0);
+ stroke(path, pen);
+ }
+ }
+}
+
+
+void QPaintEngineEx::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
+{
+ QVectorPath path((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
+
+ if (mode == PolylineMode)
+ stroke(path, state()->pen);
+ else
+ draw(path);
+}
+
+void QPaintEngineEx::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
+{
+ int count = pointCount<<1;
+ QVarLengthArray<qreal> pts(count);
+
+#ifdef Q_WS_MAC
+ for (int i=0; i<count; i+=2) {
+ pts[i] = ((int *) points)[i+1];
+ pts[i+1] = ((int *) points)[i];
+ }
+#else
+ for (int i=0; i<count; ++i)
+ pts[i] = ((int *) points)[i];
+#endif
+
+ QVectorPath path(pts.data(), pointCount, 0, QVectorPath::polygonFlags(mode));
+
+ if (mode == PolylineMode)
+ stroke(path, state()->pen);
+ else
+ draw(path);
+
+}
+
+void QPaintEngineEx::drawPixmap(const QPointF &pos, const QPixmap &pm)
+{
+ drawPixmap(QRectF(pos, pm.size()), pm, pm.rect());
+}
+
+void QPaintEngineEx::drawImage(const QPointF &pos, const QImage &image)
+{
+ drawImage(QRectF(pos, image.size()), image, image.rect());
+}
+
+void QPaintEngineEx::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s)
+{
+ QBrush brush(state()->pen.color(), pixmap);
+ QTransform xform = QTransform::fromTranslate(r.x() - s.x(), r.y() - s.y());
+ brush.setTransform(xform);
+
+ qreal pts[] = { r.x(), r.y(),
+ r.x() + r.width(), r.y(),
+ r.x() + r.width(), r.y() + r.height(),
+ r.x(), r.y() + r.height() };
+
+ QVectorPath path(pts, 4, 0, QVectorPath::RectangleHint);
+ fill(path, brush);
+}
+
+void QPaintEngineEx::drawPixmapFragments(const QPainter::PixmapFragment *fragments, int fragmentCount,
+ const QPixmap &pixmap, QPainter::PixmapFragmentHints /*hints*/)
+{
+ if (pixmap.isNull())
+ return;
+
+ qreal oldOpacity = state()->opacity;
+ QTransform oldTransform = state()->matrix;
+
+ for (int i = 0; i < fragmentCount; ++i) {
+ QTransform transform = oldTransform;
+ transform.translate(fragments[i].x, fragments[i].y);
+ transform.rotate(fragments[i].rotation);
+ state()->opacity = oldOpacity * fragments[i].opacity;
+ state()->matrix = transform;
+ opacityChanged();
+ transformChanged();
+
+ qreal w = fragments[i].scaleX * fragments[i].width;
+ qreal h = fragments[i].scaleY * fragments[i].height;
+ QRectF sourceRect(fragments[i].sourceLeft, fragments[i].sourceTop,
+ fragments[i].width, fragments[i].height);
+ drawPixmap(QRectF(-0.5 * w, -0.5 * h, w, h), pixmap, sourceRect);
+ }
+
+ state()->opacity = oldOpacity;
+ state()->matrix = oldTransform;
+ opacityChanged();
+ transformChanged();
+}
+
+void QPaintEngineEx::setState(QPainterState *s)
+{
+ QPaintEngine::state = s;
+}
+
+
+void QPaintEngineEx::updateState(const QPaintEngineState &)
+{
+ // do nothing...
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qpaintengineex_p.h b/src/gui/painting/qpaintengineex_p.h
new file mode 100644
index 0000000000..d12c602372
--- /dev/null
+++ b/src/gui/painting/qpaintengineex_p.h
@@ -0,0 +1,268 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTENGINEEX_P_H
+#define QPAINTENGINEEX_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/qpaintengine.h>
+#include <QtGui/qdrawutil.h>
+
+#include <private/qpaintengine_p.h>
+#include <private/qstroker_p.h>
+#include <private/qpainter_p.h>
+#include <private/qvectorpath_p.h>
+
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QPainterState;
+class QPaintEngineExPrivate;
+class QStaticTextItem;
+struct StrokeHandler;
+
+struct QIntRect {
+ int x1, y1, x2, y2;
+ inline void set(const QRect &r) {
+ x1 = r.x();
+ y1 = r.y();
+ x2 = r.right() + 1;
+ y2 = r.bottom() + 1;
+ // We will assume normalized for later...
+ Q_ASSERT(x2 >= x1);
+ Q_ASSERT(y2 >= y1);
+ }
+};
+
+class QRectVectorPath : public QVectorPath {
+public:
+ inline void set(const QRect &r) {
+ qreal left = r.x();
+ qreal right = r.x() + r.width();
+ qreal top = r.y();
+ qreal bottom = r.y() + r.height();
+ pts[0] = left;
+ pts[1] = top;
+ pts[2] = right;
+ pts[3] = top;
+ pts[4] = right;
+ pts[5] = bottom;
+ pts[6] = left;
+ pts[7] = bottom;
+ }
+
+ inline void set(const QRectF &r) {
+ qreal left = r.x();
+ qreal right = r.x() + r.width();
+ qreal top = r.y();
+ qreal bottom = r.y() + r.height();
+ pts[0] = left;
+ pts[1] = top;
+ pts[2] = right;
+ pts[3] = top;
+ pts[4] = right;
+ pts[5] = bottom;
+ pts[6] = left;
+ pts[7] = bottom;
+ }
+ inline QRectVectorPath(const QRect &r)
+ : QVectorPath(pts, 4, 0, QVectorPath::RectangleHint | QVectorPath::ImplicitClose)
+ {
+ set(r);
+ }
+ inline QRectVectorPath(const QRectF &r)
+ : QVectorPath(pts, 4, 0, QVectorPath::RectangleHint | QVectorPath::ImplicitClose)
+ {
+ set(r);
+ }
+ inline QRectVectorPath()
+ : QVectorPath(pts, 4, 0, QVectorPath::RectangleHint | QVectorPath::ImplicitClose)
+ { }
+
+ qreal pts[8];
+};
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug Q_GUI_EXPORT &operator<<(QDebug &, const QVectorPath &path);
+#endif
+
+class QPixmapFilter;
+
+class Q_GUI_EXPORT QPaintEngineEx : public QPaintEngine
+{
+ Q_DECLARE_PRIVATE(QPaintEngineEx)
+public:
+ QPaintEngineEx();
+
+ virtual QPainterState *createState(QPainterState *orig) const;
+
+ virtual void draw(const QVectorPath &path);
+ virtual void fill(const QVectorPath &path, const QBrush &brush) = 0;
+ virtual void stroke(const QVectorPath &path, const QPen &pen);
+
+ virtual void clip(const QVectorPath &path, Qt::ClipOperation op) = 0;
+ virtual void clip(const QRect &rect, Qt::ClipOperation op);
+ virtual void clip(const QRegion &region, Qt::ClipOperation op);
+ virtual void clip(const QPainterPath &path, Qt::ClipOperation op);
+
+ virtual void clipEnabledChanged() = 0;
+ virtual void penChanged() = 0;
+ virtual void brushChanged() = 0;
+ virtual void brushOriginChanged() = 0;
+ virtual void opacityChanged() = 0;
+ virtual void compositionModeChanged() = 0;
+ virtual void renderHintsChanged() = 0;
+ virtual void transformChanged() = 0;
+
+ virtual void fillRect(const QRectF &rect, const QBrush &brush);
+ virtual void fillRect(const QRectF &rect, const QColor &color);
+
+ virtual void drawRoundedRect(const QRectF &rect, qreal xrad, qreal yrad, Qt::SizeMode mode);
+
+ virtual void drawRects(const QRect *rects, int rectCount);
+ virtual void drawRects(const QRectF *rects, int rectCount);
+
+ virtual void drawLines(const QLine *lines, int lineCount);
+ virtual void drawLines(const QLineF *lines, int lineCount);
+
+ virtual void drawEllipse(const QRectF &r);
+ virtual void drawEllipse(const QRect &r);
+
+ virtual void drawPath(const QPainterPath &path);
+
+ virtual void drawPoints(const QPointF *points, int pointCount);
+ virtual void drawPoints(const QPoint *points, int pointCount);
+
+ virtual void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode);
+ virtual void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode);
+
+ virtual void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) = 0;
+ virtual void drawPixmap(const QPointF &pos, const QPixmap &pm);
+
+ virtual void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr,
+ Qt::ImageConversionFlags flags = Qt::AutoColor) = 0;
+ virtual void drawImage(const QPointF &pos, const QImage &image);
+
+ virtual void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s);
+
+ virtual void drawPixmapFragments(const QPainter::PixmapFragment *fragments, int fragmentCount, const QPixmap &pixmap,
+ QFlags<QPainter::PixmapFragmentHint> hints);
+
+ virtual void updateState(const QPaintEngineState &state);
+
+ virtual void drawStaticTextItem(QStaticTextItem *) = 0;
+
+ virtual void setState(QPainterState *s);
+ inline QPainterState *state() { return static_cast<QPainterState *>(QPaintEngine::state); }
+ inline const QPainterState *state() const { return static_cast<const QPainterState *>(QPaintEngine::state); }
+
+ virtual void sync() {}
+
+ virtual void beginNativePainting() {}
+ virtual void endNativePainting() {}
+
+ // Return a pixmap filter of "type" that can render the parameters
+ // in "prototype". The returned filter is owned by the engine and
+ // will be destroyed when the engine is destroyed. The "prototype"
+ // allows the engine to pick different filters based on the parameters
+ // that will be requested, and not just the "type".
+ virtual QPixmapFilter *pixmapFilter(int /*type*/, const QPixmapFilter * /*prototype*/) { return 0; }
+
+ // These flags are needed in the implementation of paint buffers.
+ enum Flags
+ {
+ DoNotEmulate = 0x01, // If set, QPainter will not wrap this engine in an emulation engine.
+ IsEmulationEngine = 0x02 // If set, this object is a QEmulationEngine.
+ };
+ virtual uint flags() const {return 0;}
+
+protected:
+ QPaintEngineEx(QPaintEngineExPrivate &data);
+};
+
+class Q_GUI_EXPORT QPaintEngineExPrivate : public QPaintEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QPaintEngineEx)
+public:
+ QPaintEngineExPrivate();
+ ~QPaintEngineExPrivate();
+
+ void replayClipOperations();
+ bool hasClipOperations() const;
+
+ QStroker stroker;
+ QDashStroker dasher;
+ StrokeHandler *strokeHandler;
+ QStrokerOps *activeStroker;
+ QPen strokerPen;
+
+ QRect exDeviceRect;
+};
+
+inline uint QVectorPath::polygonFlags(QPaintEngine::PolygonDrawMode mode) {
+ switch (mode) {
+ case QPaintEngine::ConvexMode: return ConvexPolygonHint | ImplicitClose;
+ case QPaintEngine::OddEvenMode: return PolygonHint | OddEvenFill | ImplicitClose;
+ case QPaintEngine::WindingMode: return PolygonHint | WindingFill | ImplicitClose;
+ case QPaintEngine::PolylineMode: return PolygonHint;
+ default: return 0;
+ }
+}
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp
new file mode 100644
index 0000000000..9fafba5f4f
--- /dev/null
+++ b/src/gui/painting/qpainter.cpp
@@ -0,0 +1,9572 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// QtCore
+#include <qdebug.h>
+#include <qmath.h>
+#include <qmutex.h>
+
+// QtGui
+#include "qbitmap.h"
+#include "qimage.h"
+#include "qpaintdevice.h"
+#include "qpaintengine.h"
+#include "qpainter.h"
+#include "qpainter_p.h"
+#include "qpainterpath.h"
+#include "qpicture.h"
+#include "qpixmapcache.h"
+#include "qpolygon.h"
+#include "qtextlayout.h"
+#include "qwidget.h"
+#include "qapplication.h"
+#include "qstyle.h"
+#include "qthread.h"
+#include "qvarlengtharray.h"
+#include "qstatictext.h"
+#include "qglyphs.h"
+
+#include <private/qfontengine_p.h>
+#include <private/qpaintengine_p.h>
+#include <private/qemulationpaintengine_p.h>
+#include <private/qpainterpath_p.h>
+#include <private/qtextengine_p.h>
+#include <private/qwidget_p.h>
+#include <private/qpaintengine_raster_p.h>
+#include <private/qmath_p.h>
+#include <private/qstatictext_p.h>
+#include <private/qglyphs_p.h>
+#include <private/qstylehelper_p.h>
+#include <private/qrawfont_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#define QGradient_StretchToDevice 0x10000000
+#define QPaintEngine_OpaqueBackground 0x40000000
+
+// #define QT_DEBUG_DRAW
+#ifdef QT_DEBUG_DRAW
+bool qt_show_painter_debug_output = true;
+#endif
+
+extern QPixmap qt_pixmapForBrush(int style, bool invert);
+
+void qt_format_text(const QFont &font,
+ const QRectF &_r, int tf, const QTextOption *option, const QString& str, QRectF *brect,
+ int tabstops, int* tabarray, int tabarraylen,
+ QPainter *painter);
+static void drawTextItemDecoration(QPainter *painter, const QPointF &pos, const QFontEngine *fe,
+ QTextCharFormat::UnderlineStyle underlineStyle,
+ QTextItem::RenderFlags flags, qreal width,
+ const QTextCharFormat &charFormat);
+// Helper function to calculate left most position, width and flags for decoration drawing
+Q_GUI_EXPORT void qt_draw_decoration_for_glyphs(QPainter *painter, const glyph_t *glyphArray,
+ const QFixedPoint *positions, int glyphCount,
+ QFontEngine *fontEngine, const QFont &font,
+ const QTextCharFormat &charFormat);
+
+static inline QGradient::CoordinateMode coordinateMode(const QBrush &brush)
+{
+ switch (brush.style()) {
+ case Qt::LinearGradientPattern:
+ case Qt::RadialGradientPattern:
+ case Qt::ConicalGradientPattern:
+ return brush.gradient()->coordinateMode();
+ default:
+ ;
+ }
+ return QGradient::LogicalMode;
+}
+
+/* Returns true if the gradient requires stretch to device...*/
+static inline bool check_gradient(const QBrush &brush)
+{
+ return coordinateMode(brush) == QGradient::StretchToDeviceMode;
+}
+
+extern bool qHasPixmapTexture(const QBrush &);
+
+static inline bool is_brush_transparent(const QBrush &brush) {
+ Qt::BrushStyle s = brush.style();
+ bool brushBitmap = qHasPixmapTexture(brush)
+ ? brush.texture().isQBitmap()
+ : (brush.textureImage().depth() == 1);
+ return ((s >= Qt::Dense1Pattern && s <= Qt::DiagCrossPattern)
+ || (s == Qt::TexturePattern && brushBitmap));
+}
+
+static inline bool is_pen_transparent(const QPen &pen) {
+ return pen.style() > Qt::SolidLine || is_brush_transparent(pen.brush());
+}
+
+/* Discards the emulation flags that are not relevant for line drawing
+ and returns the result
+*/
+static inline uint line_emulation(uint emulation)
+{
+ return emulation & (QPaintEngine::PrimitiveTransform
+ | QPaintEngine::AlphaBlend
+ | QPaintEngine::Antialiasing
+ | QPaintEngine::BrushStroke
+ | QPaintEngine::ConstantOpacity
+ | QGradient_StretchToDevice
+ | QPaintEngine::ObjectBoundingModeGradients
+ | QPaintEngine_OpaqueBackground);
+}
+
+static bool qt_paintengine_supports_transformations(QPaintEngine::Type type)
+{
+ return type == QPaintEngine::OpenGL2
+ || type == QPaintEngine::OpenVG
+ || type == QPaintEngine::OpenGL;
+}
+
+#ifndef QT_NO_DEBUG
+static bool qt_painter_thread_test(int devType, const char *what, bool extraCondition = false)
+{
+ switch (devType) {
+ case QInternal::Image:
+ case QInternal::Printer:
+ case QInternal::Picture:
+ // can be drawn onto these devices safely from any thread
+#ifndef Q_WS_WIN
+ if (extraCondition)
+#endif
+ break;
+ default:
+#ifdef Q_WS_X11
+ if (QApplication::testAttribute(Qt::AA_X11InitThreads))
+ return true;
+#endif
+ if (!extraCondition && QThread::currentThread() != qApp->thread()) {
+ qWarning("QPainter: It is not safe to use %s outside the GUI thread", what);
+ return false;
+ }
+ break;
+ }
+ return true;
+}
+#endif
+
+void QPainterPrivate::checkEmulation()
+{
+ Q_ASSERT(extended);
+ if (extended->flags() & QPaintEngineEx::DoNotEmulate)
+ return;
+
+ bool doEmulation = false;
+ if (state->bgMode == Qt::OpaqueMode)
+ doEmulation = true;
+
+ const QGradient *bg = state->brush.gradient();
+ if (bg && bg->coordinateMode() > QGradient::LogicalMode)
+ doEmulation = true;
+
+ const QGradient *pg = qpen_brush(state->pen).gradient();
+ if (pg && pg->coordinateMode() > QGradient::LogicalMode)
+ doEmulation = true;
+
+ if (doEmulation) {
+ if (extended != emulationEngine) {
+ if (!emulationEngine)
+ emulationEngine = new QEmulationPaintEngine(extended);
+ extended = emulationEngine;
+ extended->setState(state);
+ }
+ } else if (emulationEngine == extended) {
+ extended = emulationEngine->real_engine;
+ }
+}
+
+
+QPainterPrivate::~QPainterPrivate()
+{
+ delete emulationEngine;
+ for (int i=0; i<states.size(); ++i)
+ delete states.at(i);
+
+ if (dummyState)
+ delete dummyState;
+}
+
+
+QTransform QPainterPrivate::viewTransform() const
+{
+ if (state->VxF) {
+ qreal scaleW = qreal(state->vw)/qreal(state->ww);
+ qreal scaleH = qreal(state->vh)/qreal(state->wh);
+ return QTransform(scaleW, 0, 0, scaleH,
+ state->vx - state->wx*scaleW, state->vy - state->wy*scaleH);
+ }
+ return QTransform();
+}
+
+
+/*
+ \internal
+ Returns true if using a shared painter; otherwise false.
+*/
+bool QPainterPrivate::attachPainterPrivate(QPainter *q, QPaintDevice *pdev)
+{
+ Q_ASSERT(q);
+ Q_ASSERT(pdev);
+
+ if (pdev->devType() != QInternal::Widget)
+ return false;
+
+ QWidget *widget = static_cast<QWidget *>(pdev);
+ Q_ASSERT(widget);
+
+ // Someone either called QPainter::setRedirected in the widget's paint event
+ // right before this painter was created (or begin was called) or
+ // sent a paint event directly to the widget.
+ if (!widget->d_func()->redirectDev)
+ return false;
+
+ QPainter *sp = widget->d_func()->sharedPainter();
+ if (!sp || !sp->isActive())
+ return false;
+
+ if (sp->paintEngine()->paintDevice() != widget->d_func()->redirectDev)
+ return false;
+
+ // Check if we're attempting to paint outside a paint event.
+ if (!sp->d_ptr->engine->hasFeature(QPaintEngine::PaintOutsidePaintEvent)
+ && !widget->testAttribute(Qt::WA_PaintOutsidePaintEvent)
+ && !widget->testAttribute(Qt::WA_WState_InPaintEvent)) {
+
+ qWarning("QPainter::begin: Widget painting can only begin as a result of a paintEvent");
+ return false;
+ }
+
+ // Save the current state of the shared painter and assign
+ // the current d_ptr to the shared painter's d_ptr.
+ sp->save();
+ if (!sp->d_ptr->d_ptrs) {
+ // Allocate space for 4 d-pointers (enough for up to 4 sub-sequent
+ // redirections within the same paintEvent(), which should be enough
+ // in 99% of all cases). E.g: A renders B which renders C which renders D.
+ sp->d_ptr->d_ptrs_size = 4;
+ sp->d_ptr->d_ptrs = (QPainterPrivate **)malloc(4 * sizeof(QPainterPrivate *));
+ Q_CHECK_PTR(sp->d_ptr->d_ptrs);
+ } else if (sp->d_ptr->refcount - 1 == sp->d_ptr->d_ptrs_size) {
+ // However, to support corner cases we grow the array dynamically if needed.
+ sp->d_ptr->d_ptrs_size <<= 1;
+ const int newSize = sp->d_ptr->d_ptrs_size * sizeof(QPainterPrivate *);
+ sp->d_ptr->d_ptrs = q_check_ptr((QPainterPrivate **)realloc(sp->d_ptr->d_ptrs, newSize));
+ }
+ sp->d_ptr->d_ptrs[++sp->d_ptr->refcount - 2] = q->d_ptr.data();
+ q->d_ptr.take();
+ q->d_ptr.reset(sp->d_ptr.data());
+
+ Q_ASSERT(q->d_ptr->state);
+
+ // Now initialize the painter with correct widget properties.
+ q->initFrom(widget);
+ QPoint offset;
+ widget->d_func()->redirected(&offset);
+ offset += q->d_ptr->engine->coordinateOffset();
+
+ // Update system rect.
+ q->d_ptr->state->ww = q->d_ptr->state->vw = widget->width();
+ q->d_ptr->state->wh = q->d_ptr->state->vh = widget->height();
+
+ // Update matrix.
+ if (q->d_ptr->state->WxF) {
+ q->d_ptr->state->redirectionMatrix = q->d_ptr->state->matrix;
+ q->d_ptr->state->redirectionMatrix.translate(-offset.x(), -offset.y());
+ q->d_ptr->state->worldMatrix = QTransform();
+ q->d_ptr->state->WxF = false;
+ } else {
+ q->d_ptr->state->redirectionMatrix = QTransform::fromTranslate(-offset.x(), -offset.y());
+ }
+ q->d_ptr->updateMatrix();
+
+ QPaintEnginePrivate *enginePrivate = q->d_ptr->engine->d_func();
+ if (enginePrivate->currentClipWidget == widget) {
+ enginePrivate->systemStateChanged();
+ return true;
+ }
+
+ // Update system transform and clip.
+ enginePrivate->currentClipWidget = widget;
+ enginePrivate->setSystemTransform(q->d_ptr->state->matrix);
+ return true;
+}
+
+void QPainterPrivate::detachPainterPrivate(QPainter *q)
+{
+ Q_ASSERT(refcount > 1);
+ Q_ASSERT(q);
+
+ QPainterPrivate *original = d_ptrs[--refcount - 1];
+ if (inDestructor) {
+ inDestructor = false;
+ if (original)
+ original->inDestructor = true;
+ } else if (!original) {
+ original = new QPainterPrivate(q);
+ }
+
+ d_ptrs[refcount - 1] = 0;
+ q->restore();
+ q->d_ptr.take();
+ q->d_ptr.reset(original);
+
+ if (emulationEngine) {
+ extended = emulationEngine->real_engine;
+ delete emulationEngine;
+ emulationEngine = 0;
+ }
+}
+
+
+void QPainterPrivate::draw_helper(const QPainterPath &originalPath, DrawOperation op)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output) {
+ printf("QPainter::drawHelper\n");
+ }
+#endif
+
+ if (originalPath.isEmpty())
+ return;
+
+ QPaintEngine::PaintEngineFeatures gradientStretch =
+ QPaintEngine::PaintEngineFeatures(QGradient_StretchToDevice
+ | QPaintEngine::ObjectBoundingModeGradients);
+
+ const bool mustEmulateObjectBoundingModeGradients = extended
+ || ((state->emulationSpecifier & QPaintEngine::ObjectBoundingModeGradients)
+ && !engine->hasFeature(QPaintEngine::PatternTransform));
+
+ if (!(state->emulationSpecifier & ~gradientStretch)
+ && !mustEmulateObjectBoundingModeGradients) {
+ drawStretchedGradient(originalPath, op);
+ return;
+ } else if (state->emulationSpecifier & QPaintEngine_OpaqueBackground) {
+ drawOpaqueBackground(originalPath, op);
+ return;
+ }
+
+ Q_Q(QPainter);
+
+ qreal strokeOffsetX = 0, strokeOffsetY = 0;
+
+ QPainterPath path = originalPath * state->matrix;
+ QRectF pathBounds = path.boundingRect();
+ QRectF strokeBounds;
+ bool doStroke = (op & StrokeDraw) && (state->pen.style() != Qt::NoPen);
+ if (doStroke) {
+ qreal penWidth = state->pen.widthF();
+ if (penWidth == 0) {
+ strokeOffsetX = 1;
+ strokeOffsetY = 1;
+ } else {
+ // In case of complex xform
+ if (state->matrix.type() > QTransform::TxScale) {
+ QPainterPathStroker stroker;
+ stroker.setWidth(penWidth);
+ stroker.setJoinStyle(state->pen.joinStyle());
+ stroker.setCapStyle(state->pen.capStyle());
+ QPainterPath stroke = stroker.createStroke(originalPath);
+ strokeBounds = (stroke * state->matrix).boundingRect();
+ } else {
+ strokeOffsetX = qAbs(penWidth * state->matrix.m11() / 2.0);
+ strokeOffsetY = qAbs(penWidth * state->matrix.m22() / 2.0);
+ }
+ }
+ }
+
+ QRect absPathRect;
+ if (!strokeBounds.isEmpty()) {
+ absPathRect = strokeBounds.intersected(QRectF(0, 0, device->width(), device->height())).toAlignedRect();
+ } else {
+ absPathRect = pathBounds.adjusted(-strokeOffsetX, -strokeOffsetY, strokeOffsetX, strokeOffsetY)
+ .intersected(QRectF(0, 0, device->width(), device->height())).toAlignedRect();
+ }
+
+ if (q->hasClipping()) {
+ bool hasPerspectiveTransform = false;
+ for (int i = 0; i < state->clipInfo.size(); ++i) {
+ const QPainterClipInfo &info = state->clipInfo.at(i);
+ if (info.matrix.type() == QTransform::TxProject) {
+ hasPerspectiveTransform = true;
+ break;
+ }
+ }
+ // avoid mapping QRegions with perspective transforms
+ if (!hasPerspectiveTransform) {
+ // The trick with txinv and invMatrix is done in order to
+ // avoid transforming the clip to logical coordinates, and
+ // then back to device coordinates. This is a problem with
+ // QRegion/QRect based clips, since they use integer
+ // coordinates and converting to/from logical coordinates will
+ // lose precision.
+ bool old_txinv = txinv;
+ QTransform old_invMatrix = invMatrix;
+ txinv = true;
+ invMatrix = QTransform();
+ QPainterPath clipPath = q->clipPath();
+ QRectF r = clipPath.boundingRect().intersected(absPathRect);
+ absPathRect = r.toAlignedRect();
+ txinv = old_txinv;
+ invMatrix = old_invMatrix;
+ }
+ }
+
+// qDebug("\nQPainterPrivate::draw_helper(), x=%d, y=%d, w=%d, h=%d",
+// devMinX, devMinY, device->width(), device->height());
+// qDebug() << " - matrix" << state->matrix;
+// qDebug() << " - originalPath.bounds" << originalPath.boundingRect();
+// qDebug() << " - path.bounds" << path.boundingRect();
+
+ if (absPathRect.width() <= 0 || absPathRect.height() <= 0)
+ return;
+
+ QImage image(absPathRect.width(), absPathRect.height(), QImage::Format_ARGB32_Premultiplied);
+ image.fill(0);
+
+ QPainter p(&image);
+
+ p.d_ptr->helper_device = helper_device;
+
+ p.setOpacity(state->opacity);
+ p.translate(-absPathRect.x(), -absPathRect.y());
+ p.setTransform(state->matrix, true);
+ p.setPen(doStroke ? state->pen : QPen(Qt::NoPen));
+ p.setBrush((op & FillDraw) ? state->brush : QBrush(Qt::NoBrush));
+ p.setBackground(state->bgBrush);
+ p.setBackgroundMode(state->bgMode);
+ p.setBrushOrigin(state->brushOrigin);
+
+ p.setRenderHint(QPainter::Antialiasing, state->renderHints & QPainter::Antialiasing);
+ p.setRenderHint(QPainter::SmoothPixmapTransform,
+ state->renderHints & QPainter::SmoothPixmapTransform);
+
+ p.drawPath(originalPath);
+
+#ifndef QT_NO_DEBUG
+ static bool do_fallback_overlay = qgetenv("QT_PAINT_FALLBACK_OVERLAY").size() > 0;
+ if (do_fallback_overlay) {
+ QImage block(8, 8, QImage::Format_ARGB32_Premultiplied);
+ QPainter pt(&block);
+ pt.fillRect(0, 0, 8, 8, QColor(196, 0, 196));
+ pt.drawLine(0, 0, 8, 8);
+ pt.end();
+ p.resetTransform();
+ p.setCompositionMode(QPainter::CompositionMode_SourceAtop);
+ p.setOpacity(0.5);
+ p.fillRect(0, 0, image.width(), image.height(), QBrush(block));
+ }
+#endif
+
+ p.end();
+
+ q->save();
+ state->matrix = QTransform();
+ state->dirtyFlags |= QPaintEngine::DirtyTransform;
+ updateState(state);
+ engine->drawImage(absPathRect,
+ image,
+ QRectF(0, 0, absPathRect.width(), absPathRect.height()),
+ Qt::OrderedDither | Qt::OrderedAlphaDither);
+ q->restore();
+}
+
+void QPainterPrivate::drawOpaqueBackground(const QPainterPath &path, DrawOperation op)
+{
+ Q_Q(QPainter);
+
+ q->setBackgroundMode(Qt::TransparentMode);
+
+ if (op & FillDraw && state->brush.style() != Qt::NoBrush) {
+ q->fillPath(path, state->bgBrush.color());
+ q->fillPath(path, state->brush);
+ }
+
+ if (op & StrokeDraw && state->pen.style() != Qt::NoPen) {
+ q->strokePath(path, QPen(state->bgBrush.color(), state->pen.width()));
+ q->strokePath(path, state->pen);
+ }
+
+ q->setBackgroundMode(Qt::OpaqueMode);
+}
+
+static inline QBrush stretchGradientToUserSpace(const QBrush &brush, const QRectF &boundingRect)
+{
+ Q_ASSERT(brush.style() >= Qt::LinearGradientPattern
+ && brush.style() <= Qt::ConicalGradientPattern);
+
+ QTransform gradientToUser(boundingRect.width(), 0, 0, boundingRect.height(),
+ boundingRect.x(), boundingRect.y());
+
+ QGradient g = *brush.gradient();
+ g.setCoordinateMode(QGradient::LogicalMode);
+
+ QBrush b(g);
+ b.setTransform(gradientToUser * b.transform());
+ return b;
+}
+
+void QPainterPrivate::drawStretchedGradient(const QPainterPath &path, DrawOperation op)
+{
+ Q_Q(QPainter);
+
+ const qreal sw = helper_device->width();
+ const qreal sh = helper_device->height();
+
+ bool changedPen = false;
+ bool changedBrush = false;
+ bool needsFill = false;
+
+ const QPen pen = state->pen;
+ const QBrush brush = state->brush;
+
+ const QGradient::CoordinateMode penMode = coordinateMode(pen.brush());
+ const QGradient::CoordinateMode brushMode = coordinateMode(brush);
+
+ QRectF boundingRect;
+
+ // Draw the xformed fill if the brush is a stretch gradient.
+ if ((op & FillDraw) && brush.style() != Qt::NoBrush) {
+ if (brushMode == QGradient::StretchToDeviceMode) {
+ q->setPen(Qt::NoPen);
+ changedPen = pen.style() != Qt::NoPen;
+ q->scale(sw, sh);
+ updateState(state);
+
+ const qreal isw = 1.0 / sw;
+ const qreal ish = 1.0 / sh;
+ QTransform inv(isw, 0, 0, ish, 0, 0);
+ engine->drawPath(path * inv);
+ q->scale(isw, ish);
+ } else {
+ needsFill = true;
+
+ if (brushMode == QGradient::ObjectBoundingMode) {
+ Q_ASSERT(engine->hasFeature(QPaintEngine::PatternTransform));
+ boundingRect = path.boundingRect();
+ q->setBrush(stretchGradientToUserSpace(brush, boundingRect));
+ changedBrush = true;
+ }
+ }
+ }
+
+ if ((op & StrokeDraw) && pen.style() != Qt::NoPen) {
+ // Draw the xformed outline if the pen is a stretch gradient.
+ if (penMode == QGradient::StretchToDeviceMode) {
+ q->setPen(Qt::NoPen);
+ changedPen = true;
+
+ if (needsFill) {
+ updateState(state);
+ engine->drawPath(path);
+ }
+
+ q->scale(sw, sh);
+ q->setBrush(pen.brush());
+ changedBrush = true;
+ updateState(state);
+
+ QPainterPathStroker stroker;
+ stroker.setDashPattern(pen.style());
+ stroker.setWidth(pen.widthF());
+ stroker.setJoinStyle(pen.joinStyle());
+ stroker.setCapStyle(pen.capStyle());
+ stroker.setMiterLimit(pen.miterLimit());
+ QPainterPath stroke = stroker.createStroke(path);
+
+ const qreal isw = 1.0 / sw;
+ const qreal ish = 1.0 / sh;
+ QTransform inv(isw, 0, 0, ish, 0, 0);
+ engine->drawPath(stroke * inv);
+ q->scale(isw, ish);
+ } else {
+ if (!needsFill && brush.style() != Qt::NoBrush) {
+ q->setBrush(Qt::NoBrush);
+ changedBrush = true;
+ }
+
+ if (penMode == QGradient::ObjectBoundingMode) {
+ Q_ASSERT(engine->hasFeature(QPaintEngine::PatternTransform));
+
+ // avoid computing the bounding rect twice
+ if (!needsFill || brushMode != QGradient::ObjectBoundingMode)
+ boundingRect = path.boundingRect();
+
+ QPen p = pen;
+ p.setBrush(stretchGradientToUserSpace(pen.brush(), boundingRect));
+ q->setPen(p);
+ changedPen = true;
+ } else if (changedPen) {
+ q->setPen(pen);
+ changedPen = false;
+ }
+
+ updateState(state);
+ engine->drawPath(path);
+ }
+ } else if (needsFill) {
+ if (pen.style() != Qt::NoPen) {
+ q->setPen(Qt::NoPen);
+ changedPen = true;
+ }
+
+ updateState(state);
+ engine->drawPath(path);
+ }
+
+ if (changedPen)
+ q->setPen(pen);
+ if (changedBrush)
+ q->setBrush(brush);
+}
+
+
+void QPainterPrivate::updateMatrix()
+{
+ state->matrix = state->WxF ? state->worldMatrix : QTransform();
+ if (state->VxF)
+ state->matrix *= viewTransform();
+
+ txinv = false; // no inverted matrix
+ state->matrix *= state->redirectionMatrix;
+ if (extended)
+ extended->transformChanged();
+ else
+ state->dirtyFlags |= QPaintEngine::DirtyTransform;
+
+// printf("VxF=%d, WxF=%d\n", state->VxF, state->WxF);
+// qDebug() << " --- using matrix" << state->matrix << redirection_offset;
+}
+
+/*! \internal */
+void QPainterPrivate::updateInvMatrix()
+{
+ Q_ASSERT(txinv == false);
+ txinv = true; // creating inverted matrix
+ invMatrix = state->matrix.inverted();
+}
+
+void QPainterPrivate::updateEmulationSpecifier(QPainterState *s)
+{
+ bool alpha = false;
+ bool linearGradient = false;
+ bool radialGradient = false;
+ bool conicalGradient = false;
+ bool patternBrush = false;
+ bool xform = false;
+ bool complexXform = false;
+
+ bool skip = true;
+
+ // Pen and brush properties (we have to check both if one changes because the
+ // one that's unchanged can still be in a state which requires emulation)
+ if (s->state() & (QPaintEngine::DirtyPen | QPaintEngine::DirtyBrush | QPaintEngine::DirtyHints)) {
+ // Check Brush stroke emulation
+ if (!s->pen.isSolid() && !engine->hasFeature(QPaintEngine::BrushStroke))
+ s->emulationSpecifier |= QPaintEngine::BrushStroke;
+ else
+ s->emulationSpecifier &= ~QPaintEngine::BrushStroke;
+
+ skip = false;
+
+ QBrush penBrush = (qpen_style(s->pen) == Qt::NoPen) ? QBrush(Qt::NoBrush) : qpen_brush(s->pen);
+ Qt::BrushStyle brushStyle = qbrush_style(s->brush);
+ Qt::BrushStyle penBrushStyle = qbrush_style(penBrush);
+ alpha = (penBrushStyle != Qt::NoBrush
+ && (penBrushStyle < Qt::LinearGradientPattern && penBrush.color().alpha() != 255)
+ && !penBrush.isOpaque())
+ || (brushStyle != Qt::NoBrush
+ && (brushStyle < Qt::LinearGradientPattern && s->brush.color().alpha() != 255)
+ && !s->brush.isOpaque());
+ linearGradient = ((penBrushStyle == Qt::LinearGradientPattern) ||
+ (brushStyle == Qt::LinearGradientPattern));
+ radialGradient = ((penBrushStyle == Qt::RadialGradientPattern) ||
+ (brushStyle == Qt::RadialGradientPattern));
+ conicalGradient = ((penBrushStyle == Qt::ConicalGradientPattern) ||
+ (brushStyle == Qt::ConicalGradientPattern));
+ patternBrush = (((penBrushStyle > Qt::SolidPattern
+ && penBrushStyle < Qt::LinearGradientPattern)
+ || penBrushStyle == Qt::TexturePattern) ||
+ ((brushStyle > Qt::SolidPattern
+ && brushStyle < Qt::LinearGradientPattern)
+ || brushStyle == Qt::TexturePattern));
+
+ bool penTextureAlpha = false;
+ if (penBrush.style() == Qt::TexturePattern)
+ penTextureAlpha = qHasPixmapTexture(penBrush)
+ ? (penBrush.texture().depth() > 1) && penBrush.texture().hasAlpha()
+ : penBrush.textureImage().hasAlphaChannel();
+ bool brushTextureAlpha = false;
+ if (s->brush.style() == Qt::TexturePattern) {
+ brushTextureAlpha = qHasPixmapTexture(s->brush)
+ ? (s->brush.texture().depth() > 1) && s->brush.texture().hasAlpha()
+ : s->brush.textureImage().hasAlphaChannel();
+ }
+ if (((penBrush.style() == Qt::TexturePattern && penTextureAlpha)
+ || (s->brush.style() == Qt::TexturePattern && brushTextureAlpha))
+ && !engine->hasFeature(QPaintEngine::MaskedBrush))
+ s->emulationSpecifier |= QPaintEngine::MaskedBrush;
+ else
+ s->emulationSpecifier &= ~QPaintEngine::MaskedBrush;
+ }
+
+ if (s->state() & (QPaintEngine::DirtyHints
+ | QPaintEngine::DirtyOpacity
+ | QPaintEngine::DirtyBackgroundMode)) {
+ skip = false;
+ }
+
+ if (skip)
+ return;
+
+#if 0
+ qDebug("QPainterPrivate::updateEmulationSpecifier, state=%p\n"
+ " - alpha: %d\n"
+ " - linearGradient: %d\n"
+ " - radialGradient: %d\n"
+ " - conicalGradient: %d\n"
+ " - patternBrush: %d\n"
+ " - hints: %x\n"
+ " - xform: %d\n",
+ s,
+ alpha,
+ linearGradient,
+ radialGradient,
+ conicalGradient,
+ patternBrush,
+ uint(s->renderHints),
+ xform);
+#endif
+
+ // XForm properties
+ if (s->state() & QPaintEngine::DirtyTransform) {
+ xform = !s->matrix.isIdentity();
+ complexXform = !s->matrix.isAffine();
+ } else if (s->matrix.type() >= QTransform::TxTranslate) {
+ xform = true;
+ complexXform = !s->matrix.isAffine();
+ }
+
+ const bool brushXform = (!s->brush.transform().type() == QTransform::TxNone);
+ const bool penXform = (!s->pen.brush().transform().type() == QTransform::TxNone);
+
+ const bool patternXform = patternBrush && (xform || brushXform || penXform);
+
+ // Check alphablending
+ if (alpha && !engine->hasFeature(QPaintEngine::AlphaBlend))
+ s->emulationSpecifier |= QPaintEngine::AlphaBlend;
+ else
+ s->emulationSpecifier &= ~QPaintEngine::AlphaBlend;
+
+ // Linear gradient emulation
+ if (linearGradient && !engine->hasFeature(QPaintEngine::LinearGradientFill))
+ s->emulationSpecifier |= QPaintEngine::LinearGradientFill;
+ else
+ s->emulationSpecifier &= ~QPaintEngine::LinearGradientFill;
+
+ // Radial gradient emulation
+ if (radialGradient && !engine->hasFeature(QPaintEngine::RadialGradientFill))
+ s->emulationSpecifier |= QPaintEngine::RadialGradientFill;
+ else
+ s->emulationSpecifier &= ~QPaintEngine::RadialGradientFill;
+
+ // Conical gradient emulation
+ if (conicalGradient && !engine->hasFeature(QPaintEngine::ConicalGradientFill))
+ s->emulationSpecifier |= QPaintEngine::ConicalGradientFill;
+ else
+ s->emulationSpecifier &= ~QPaintEngine::ConicalGradientFill;
+
+ // Pattern brushes
+ if (patternBrush && !engine->hasFeature(QPaintEngine::PatternBrush))
+ s->emulationSpecifier |= QPaintEngine::PatternBrush;
+ else
+ s->emulationSpecifier &= ~QPaintEngine::PatternBrush;
+
+ // Pattern XForms
+ if (patternXform && !engine->hasFeature(QPaintEngine::PatternTransform))
+ s->emulationSpecifier |= QPaintEngine::PatternTransform;
+ else
+ s->emulationSpecifier &= ~QPaintEngine::PatternTransform;
+
+ // Primitive XForms
+ if (xform && !engine->hasFeature(QPaintEngine::PrimitiveTransform))
+ s->emulationSpecifier |= QPaintEngine::PrimitiveTransform;
+ else
+ s->emulationSpecifier &= ~QPaintEngine::PrimitiveTransform;
+
+ // Perspective XForms
+ if (complexXform && !engine->hasFeature(QPaintEngine::PerspectiveTransform))
+ s->emulationSpecifier |= QPaintEngine::PerspectiveTransform;
+ else
+ s->emulationSpecifier &= ~QPaintEngine::PerspectiveTransform;
+
+ // Constant opacity
+ if (state->opacity != 1 && !engine->hasFeature(QPaintEngine::ConstantOpacity))
+ s->emulationSpecifier |= QPaintEngine::ConstantOpacity;
+ else
+ s->emulationSpecifier &= ~QPaintEngine::ConstantOpacity;
+
+ bool gradientStretch = false;
+ bool objectBoundingMode = false;
+ if (linearGradient || conicalGradient || radialGradient) {
+ QGradient::CoordinateMode brushMode = coordinateMode(s->brush);
+ QGradient::CoordinateMode penMode = coordinateMode(s->pen.brush());
+
+ gradientStretch |= (brushMode == QGradient::StretchToDeviceMode);
+ gradientStretch |= (penMode == QGradient::StretchToDeviceMode);
+
+ objectBoundingMode |= (brushMode == QGradient::ObjectBoundingMode);
+ objectBoundingMode |= (penMode == QGradient::ObjectBoundingMode);
+ }
+ if (gradientStretch)
+ s->emulationSpecifier |= QGradient_StretchToDevice;
+ else
+ s->emulationSpecifier &= ~QGradient_StretchToDevice;
+
+ if (objectBoundingMode && !engine->hasFeature(QPaintEngine::ObjectBoundingModeGradients))
+ s->emulationSpecifier |= QPaintEngine::ObjectBoundingModeGradients;
+ else
+ s->emulationSpecifier &= ~QPaintEngine::ObjectBoundingModeGradients;
+
+ // Opaque backgrounds...
+ if (s->bgMode == Qt::OpaqueMode &&
+ (is_pen_transparent(s->pen) || is_brush_transparent(s->brush)))
+ s->emulationSpecifier |= QPaintEngine_OpaqueBackground;
+ else
+ s->emulationSpecifier &= ~QPaintEngine_OpaqueBackground;
+
+#if 0
+ //won't be correct either way because the device can already have
+ // something rendered to it in which case subsequent emulation
+ // on a fully transparent qimage and then blitting the results
+ // won't produce correct results
+ // Blend modes
+ if (state->composition_mode > QPainter::CompositionMode_Xor &&
+ !engine->hasFeature(QPaintEngine::BlendModes))
+ s->emulationSpecifier |= QPaintEngine::BlendModes;
+ else
+ s->emulationSpecifier &= ~QPaintEngine::BlendModes;
+#endif
+}
+
+void QPainterPrivate::updateStateImpl(QPainterState *newState)
+{
+ // ### we might have to call QPainter::begin() here...
+ if (!engine->state) {
+ engine->state = newState;
+ engine->setDirty(QPaintEngine::AllDirty);
+ }
+
+ if (engine->state->painter() != newState->painter)
+ // ### this could break with clip regions vs paths.
+ engine->setDirty(QPaintEngine::AllDirty);
+
+ // Upon restore, revert all changes since last save
+ else if (engine->state != newState)
+ newState->dirtyFlags |= QPaintEngine::DirtyFlags(static_cast<QPainterState *>(engine->state)->changeFlags);
+
+ // We need to store all changes made so that restore can deal with them
+ else
+ newState->changeFlags |= newState->dirtyFlags;
+
+ updateEmulationSpecifier(newState);
+
+ // Unset potential dirty background mode
+ newState->dirtyFlags &= ~(QPaintEngine::DirtyBackgroundMode
+ | QPaintEngine::DirtyBackground);
+
+ engine->state = newState;
+ engine->updateState(*newState);
+ engine->clearDirty(QPaintEngine::AllDirty);
+
+}
+
+void QPainterPrivate::updateState(QPainterState *newState)
+{
+
+ if (!newState) {
+ engine->state = newState;
+
+ } else if (newState->state() || engine->state!=newState) {
+ bool setNonCosmeticPen = (newState->renderHints & QPainter::NonCosmeticDefaultPen)
+ && newState->pen.widthF() == 0;
+ if (setNonCosmeticPen) {
+ // Override the default pen's cosmetic state if the
+ // NonCosmeticDefaultPen render hint is used.
+ QPen oldPen = newState->pen;
+ newState->pen.setWidth(1);
+ newState->pen.setCosmetic(false);
+ newState->dirtyFlags |= QPaintEngine::DirtyPen;
+
+ updateStateImpl(newState);
+
+ // Restore the state pen back to its default to preserve visible
+ // state.
+ newState->pen = oldPen;
+ } else {
+ updateStateImpl(newState);
+ }
+ }
+}
+
+
+/*!
+ \class QPainter
+ \brief The QPainter class performs low-level painting on widgets and
+ other paint devices.
+
+ \ingroup painting
+
+ \reentrant
+
+ QPainter provides highly optimized functions to do most of the
+ drawing GUI programs require. It can draw everything from simple
+ lines to complex shapes like pies and chords. It can also draw
+ aligned text and pixmaps. Normally, it draws in a "natural"
+ coordinate system, but it can also do view and world
+ transformation. QPainter can operate on any object that inherits
+ the QPaintDevice class.
+
+ The common use of QPainter is inside a widget's paint event:
+ Construct and customize (e.g. set the pen or the brush) the
+ painter. Then draw. Remember to destroy the QPainter object after
+ drawing. For example:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 0
+
+ The core functionality of QPainter is drawing, but the class also
+ provide several functions that allows you to customize QPainter's
+ settings and its rendering quality, and others that enable
+ clipping. In addition you can control how different shapes are
+ merged together by specifying the painter's composition mode.
+
+ The isActive() function indicates whether the painter is active. A
+ painter is activated by the begin() function and the constructor
+ that takes a QPaintDevice argument. The end() function, and the
+ destructor, deactivates it.
+
+ Together with the QPaintDevice and QPaintEngine classes, QPainter
+ form the basis for Qt's paint system. QPainter is the class used
+ to perform drawing operations. QPaintDevice represents a device
+ that can be painted on using a QPainter. QPaintEngine provides the
+ interface that the painter uses to draw onto different types of
+ devices. If the painter is active, device() returns the paint
+ device on which the painter paints, and paintEngine() returns the
+ paint engine that the painter is currently operating on. For more
+ information, see the \l {Paint System}.
+
+ Sometimes it is desirable to make someone else paint on an unusual
+ QPaintDevice. QPainter supports a static function to do this,
+ setRedirected().
+
+ \warning When the paintdevice is a widget, QPainter can only be
+ used inside a paintEvent() function or in a function called by
+ paintEvent(); that is unless the Qt::WA_PaintOutsidePaintEvent
+ widget attribute is set. On Mac OS X and Windows, you can only
+ paint in a paintEvent() function regardless of this attribute's
+ setting.
+
+ \tableofcontents
+
+ \section1 Settings
+
+ There are several settings that you can customize to make QPainter
+ draw according to your preferences:
+
+ \list
+
+ \o font() is the font used for drawing text. If the painter
+ isActive(), you can retrieve information about the currently set
+ font, and its metrics, using the fontInfo() and fontMetrics()
+ functions respectively.
+
+ \o brush() defines the color or pattern that is used for filling
+ shapes.
+
+ \o pen() defines the color or stipple that is used for drawing
+ lines or boundaries.
+
+ \o backgroundMode() defines whether there is a background() or
+ not, i.e it is either Qt::OpaqueMode or Qt::TransparentMode.
+
+ \o background() only applies when backgroundMode() is \l
+ Qt::OpaqueMode and pen() is a stipple. In that case, it
+ describes the color of the background pixels in the stipple.
+
+ \o brushOrigin() defines the origin of the tiled brushes, normally
+ the origin of widget's background.
+
+ \o viewport(), window(), worldTransform() make up the painter's coordinate
+ transformation system. For more information, see the \l
+ {Coordinate Transformations} section and the \l {Coordinate
+ System} documentation.
+
+ \o hasClipping() tells whether the painter clips at all. (The paint
+ device clips, too.) If the painter clips, it clips to clipRegion().
+
+ \o layoutDirection() defines the layout direction used by the
+ painter when drawing text.
+
+ \o worldMatrixEnabled() tells whether world transformation is enabled.
+
+ \o viewTransformEnabled() tells whether view transformation is
+ enabled.
+
+ \endlist
+
+ Note that some of these settings mirror settings in some paint
+ devices, e.g. QWidget::font(). The QPainter::begin() function (or
+ equivalently the QPainter constructor) copies these attributes
+ from the paint device.
+
+ You can at any time save the QPainter's state by calling the
+ save() function which saves all the available settings on an
+ internal stack. The restore() function pops them back.
+
+ \section1 Drawing
+
+ QPainter provides functions to draw most primitives: drawPoint(),
+ drawPoints(), drawLine(), drawRect(), drawRoundedRect(),
+ drawEllipse(), drawArc(), drawPie(), drawChord(), drawPolyline(),
+ drawPolygon(), drawConvexPolygon() and drawCubicBezier(). The two
+ convenience functions, drawRects() and drawLines(), draw the given
+ number of rectangles or lines in the given array of \l
+ {QRect}{QRects} or \l {QLine}{QLines} using the current pen and
+ brush.
+
+ The QPainter class also provides the fillRect() function which
+ fills the given QRect, with the given QBrush, and the eraseRect()
+ function that erases the area inside the given rectangle.
+
+ All of these functions have both integer and floating point
+ versions.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainter-basicdrawing.png
+ \o
+ \bold {Basic Drawing Example}
+
+ The \l {painting/basicdrawing}{Basic Drawing} example shows how to
+ display basic graphics primitives in a variety of styles using the
+ QPainter class.
+
+ \endtable
+
+ If you need to draw a complex shape, especially if you need to do
+ so repeatedly, consider creating a QPainterPath and drawing it
+ using drawPath().
+
+ \table 100%
+ \row
+ \o
+ \bold {Painter Paths example}
+
+ The QPainterPath class provides a container for painting
+ operations, enabling graphical shapes to be constructed and
+ reused.
+
+ The \l {painting/painterpaths}{Painter Paths} example shows how
+ painter paths can be used to build complex shapes for rendering.
+
+ \o \inlineimage qpainter-painterpaths.png
+ \endtable
+
+ QPainter also provides the fillPath() function which fills the
+ given QPainterPath with the given QBrush, and the strokePath()
+ function that draws the outline of the given path (i.e. strokes
+ the path).
+
+ See also the \l {demos/deform}{Vector Deformation} demo which
+ shows how to use advanced vector techniques to draw text using a
+ QPainterPath, the \l {demos/gradients}{Gradients} demo which shows
+ the different types of gradients that are available in Qt, and the \l
+ {demos/pathstroke}{Path Stroking} demo which shows Qt's built-in
+ dash patterns and shows how custom patterns can be used to extend
+ the range of available patterns.
+
+ \table
+ \header
+ \o \l {demos/deform}{Vector Deformation}
+ \o \l {demos/gradients}{Gradients}
+ \o \l {demos/pathstroke}{Path Stroking}
+ \row
+ \o \inlineimage qpainter-vectordeformation.png
+ \o \inlineimage qpainter-gradients.png
+ \o \inlineimage qpainter-pathstroking.png
+ \endtable
+
+
+ There are functions to draw pixmaps/images, namely drawPixmap(),
+ drawImage() and drawTiledPixmap(). Both drawPixmap() and drawImage()
+ produce the same result, except that drawPixmap() is faster
+ on-screen while drawImage() may be faster on a QPrinter or other
+ devices.
+
+ Text drawing is done using drawText(). When you need
+ fine-grained positioning, boundingRect() tells you where a given
+ drawText() command will draw.
+
+ There is a drawPicture() function that draws the contents of an
+ entire QPicture. The drawPicture() function is the only function
+ that disregards all the painter's settings as QPicture has its own
+ settings.
+
+ \section1 Rendering Quality
+
+ To get the optimal rendering result using QPainter, you should use
+ the platform independent QImage as paint device; i.e. using QImage
+ will ensure that the result has an identical pixel representation
+ on any platform.
+
+ The QPainter class also provides a means of controlling the
+ rendering quality through its RenderHint enum and the support for
+ floating point precision: All the functions for drawing primitives
+ has a floating point version. These are often used in combination
+ with the \l {RenderHint}{QPainter::Antialiasing} render hint.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainter-concentriccircles.png
+ \o
+ \bold {Concentric Circles Example}
+
+ The \l {painting/concentriccircles}{Concentric Circles} example
+ shows the improved rendering quality that can be obtained using
+ floating point precision and anti-aliasing when drawing custom
+ widgets.
+
+ The application's main window displays several widgets which are
+ drawn using the various combinations of precision and
+ anti-aliasing.
+
+ \endtable
+
+ The RenderHint enum specifies flags to QPainter that may or may
+ not be respected by any given engine. \l
+ {RenderHint}{QPainter::Antialiasing} indicates that the engine
+ should antialias edges of primitives if possible, \l
+ {RenderHint}{QPainter::TextAntialiasing} indicates that the engine
+ should antialias text if possible, and the \l
+ {RenderHint}{QPainter::SmoothPixmapTransform} indicates that the
+ engine should use a smooth pixmap transformation algorithm.
+ \l {RenderHint}{HighQualityAntialiasing} is an OpenGL-specific rendering hint
+ indicating that the engine should use fragment programs and offscreen
+ rendering for antialiasing.
+
+ The renderHints() function returns a flag that specifies the
+ rendering hints that are set for this painter. Use the
+ setRenderHint() function to set or clear the currently set
+ RenderHints.
+
+ \section1 Coordinate Transformations
+
+ Normally, the QPainter operates on the device's own coordinate
+ system (usually pixels), but QPainter has good support for
+ coordinate transformations.
+
+ \table
+ \header
+ \o nop \o rotate() \o scale() \o translate()
+ \row
+ \o \inlineimage qpainter-clock.png
+ \o \inlineimage qpainter-rotation.png
+ \o \inlineimage qpainter-scale.png
+ \o \inlineimage qpainter-translation.png
+ \endtable
+
+ The most commonly used transformations are scaling, rotation,
+ translation and shearing. Use the scale() function to scale the
+ coordinate system by a given offset, the rotate() function to
+ rotate it clockwise and translate() to translate it (i.e. adding a
+ given offset to the points). You can also twist the coordinate
+ system around the origin using the shear() function. See the \l
+ {demos/affine}{Affine Transformations} demo for a visualization of
+ a sheared coordinate system.
+
+ See also the \l {painting/transformations}{Transformations}
+ example which shows how transformations influence the way that
+ QPainter renders graphics primitives. In particular it shows how
+ the order of transformations affects the result.
+
+ \table 100%
+ \row
+ \o
+ \bold {Affine Transformations Demo}
+
+ The \l {demos/affine}{Affine Transformations} demo show Qt's
+ ability to perform affine transformations on painting
+ operations. The demo also allows the user to experiment with the
+ transformation operations and see the results immediately.
+
+ \o \inlineimage qpainter-affinetransformations.png
+ \endtable
+
+ All the tranformation operations operate on the transformation
+ worldTransform(). A matrix transforms a point in the plane to another
+ point. For more information about the transformation matrix, see
+ the \l {Coordinate System} and QTransform documentation.
+
+ The setWorldTransform() function can replace or add to the currently
+ set worldTransform(). The resetTransform() function resets any
+ transformations that were made using translate(), scale(),
+ shear(), rotate(), setWorldTransform(), setViewport() and setWindow()
+ functions. The deviceTransform() returns the matrix that transforms
+ from logical coordinates to device coordinates of the platform
+ dependent paint device. The latter function is only needed when
+ using platform painting commands on the platform dependent handle,
+ and the platform does not do transformations nativly.
+
+ When drawing with QPainter, we specify points using logical
+ coordinates which then are converted into the physical coordinates
+ of the paint device. The mapping of the logical coordinates to the
+ physical coordinates are handled by QPainter's combinedTransform(), a
+ combination of viewport() and window() and worldTransform(). The
+ viewport() represents the physical coordinates specifying an
+ arbitrary rectangle, the window() describes the same rectangle in
+ logical coordinates, and the worldTransform() is identical with the
+ transformation matrix.
+
+ See also \l {Coordinate System}
+
+ \section1 Clipping
+
+ QPainter can clip any drawing operation to a rectangle, a region,
+ or a vector path. The current clip is available using the
+ functions clipRegion() and clipPath(). Whether paths or regions are
+ preferred (faster) depends on the underlying paintEngine(). For
+ example, the QImage paint engine prefers paths while the X11 paint
+ engine prefers regions. Setting a clip is done in the painters
+ logical coordinates.
+
+ After QPainter's clipping, the paint device may also clip. For
+ example, most widgets clip away the pixels used by child widgets,
+ and most printers clip away an area near the edges of the paper.
+ This additional clipping is not reflected by the return value of
+ clipRegion() or hasClipping().
+
+ \section1 Composition Modes
+ \target Composition Modes
+
+ QPainter provides the CompositionMode enum which defines the
+ Porter-Duff rules for digital image compositing; it describes a
+ model for combining the pixels in one image, the source, with the
+ pixels in another image, the destination.
+
+ The two most common forms of composition are \l
+ {QPainter::CompositionMode}{Source} and \l
+ {QPainter::CompositionMode}{SourceOver}. \l
+ {QPainter::CompositionMode}{Source} is used to draw opaque objects
+ onto a paint device. In this mode, each pixel in the source
+ replaces the corresponding pixel in the destination. In \l
+ {QPainter::CompositionMode}{SourceOver} composition mode, the
+ source object is transparent and is drawn on top of the
+ destination.
+
+ Note that composition transformation operates pixelwise. For that
+ reason, there is a difference between using the graphic primitive
+ itself and its bounding rectangle: The bounding rect contains
+ pixels with alpha == 0 (i.e the pixels surrounding the
+ primitive). These pixels will overwrite the other image's pixels,
+ affectively clearing those, while the primitive only overwrites
+ its own area.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainter-compositiondemo.png
+
+ \o
+ \bold {Composition Modes Demo}
+
+ The \l {demos/composition}{Composition Modes} demo, available in
+ Qt's demo directory, allows you to experiment with the various
+ composition modes and see the results immediately.
+
+ \endtable
+
+ \section1 Limitations
+ \target Limitations
+
+ If you are using coordinates with Qt's raster-based paint engine, it is
+ important to note that, while coordinates greater than +/- 2\sup 15 can
+ be used, any painting performed with coordinates outside this range is not
+ guaranteed to be shown; the drawing may be clipped. This is due to the
+ use of \c{short int} in the implementation.
+
+ The outlines generated by Qt's stroker are only an approximation when dealing
+ with curved shapes. It is in most cases impossible to represent the outline of
+ a bezier curve segment using another bezier curve segment, and so Qt approximates
+ the curve outlines by using several smaller curves. For performance reasons there
+ is a limit to how many curves Qt uses for these outlines, and thus when using
+ large pen widths or scales the outline error increases. To generate outlines with
+ smaller errors it is possible to use the QPainterPathStroker class, which has the
+ setCurveThreshold member function which let's the user specify the error tolerance.
+ Another workaround is to convert the paths to polygons first and then draw the
+ polygons instead.
+
+ \section1 Performance
+
+ QPainter is a rich framework that allows developers to do a great
+ variety of graphical operations, such as gradients, composition
+ modes and vector graphics. And QPainter can do this across a
+ variety of different hardware and software stacks. Naturally the
+ underlying combination of hardware and software has some
+ implications for performance, and ensuring that every single
+ operation is fast in combination with all the various combinations
+ of composition modes, brushes, clipping, transformation, etc, is
+ close to an impossible task because of the number of
+ permutations. As a compromise we have selected a subset of the
+ QPainter API and backends, where performance is guaranteed to be as
+ good as we can sensibly get it for the given combination of
+ hardware and software.
+
+ The backends we focus on as high-performance engines are:
+
+ \list
+
+ \o Raster - This backend implements all rendering in pure software
+ and is always used to render into QImages. For optimal performance
+ only use the format types QImage::Format_ARGB32_Premultiplied,
+ QImage::Format_RGB32 or QImage::Format_RGB16. Any other format,
+ including QImage::Format_ARGB32, has significantly worse
+ performance. This engine is also used by default on Windows and on
+ QWS. It can be used as default graphics system on any
+ OS/hardware/software combination by passing \c {-graphicssystem
+ raster} on the command line
+
+ \o OpenGL 2.0 (ES) - This backend is the primary backend for
+ hardware accelerated graphics. It can be run on desktop machines
+ and embedded devices supporting the OpenGL 2.0 or OpenGL/ES 2.0
+ specification. This includes most graphics chips produced in the
+ last couple of years. The engine can be enabled by using QPainter
+ onto a QGLWidget or by passing \c {-graphicssystem opengl} on the
+ command line when the underlying system supports it.
+
+ \o OpenVG - This backend implements the Khronos standard for 2D
+ and Vector Graphics. It is primarily for embedded devices with
+ hardware support for OpenVG. The engine can be enabled by
+ passing \c {-graphicssystem openvg} on the command line when
+ the underlying system supports it.
+
+ \endlist
+
+ These operations are:
+
+ \list
+
+ \o Simple transformations, meaning translation and scaling, pluss
+ 0, 90, 180, 270 degree rotations.
+
+ \o \c drawPixmap() in combination with simple transformations and
+ opacity with non-smooth transformation mode
+ (\c QPainter::SmoothPixmapTransform not enabled as a render hint).
+
+ \o Rectangle fills with solid color, two-color linear gradients
+ and simple transforms.
+
+ \o Rectangular clipping with simple transformations and intersect
+ clip.
+
+ \o Composition Modes \c QPainter::CompositionMode_Source and
+ QPainter::CompositionMode_SourceOver
+
+ \o Rounded rectangle filling using solid color and two-color
+ linear gradients fills.
+
+ \o 3x3 patched pixmaps, via qDrawBorderPixmap.
+
+ \endlist
+
+ This list gives an indication of which features to safely use in
+ an application where performance is critical. For certain setups,
+ other operations may be fast too, but before making extensive use
+ of them, it is recommended to benchmark and verify them on the
+ system where the software will run in the end. There are also
+ cases where expensive operations are ok to use, for instance when
+ the result is cached in a QPixmap.
+
+ \sa QPaintDevice, QPaintEngine, {QtSvg Module}, {Basic Drawing Example},
+ {Drawing Utility Functions}
+*/
+
+/*!
+ \enum QPainter::RenderHint
+
+ Renderhints are used to specify flags to QPainter that may or
+ may not be respected by any given engine.
+
+ \value Antialiasing Indicates that the engine should antialias
+ edges of primitives if possible.
+
+ \value TextAntialiasing Indicates that the engine should antialias
+ text if possible. To forcibly disable antialiasing for text, do not
+ use this hint. Instead, set QFont::NoAntialias on your font's style
+ strategy.
+
+ \value SmoothPixmapTransform Indicates that the engine should use
+ a smooth pixmap transformation algorithm (such as bilinear) rather
+ than nearest neighbor.
+
+ \value HighQualityAntialiasing An OpenGL-specific rendering hint
+ indicating that the engine should use fragment programs and offscreen
+ rendering for antialiasing.
+
+ \value NonCosmeticDefaultPen The engine should interpret pens with a width
+ of 0 (which otherwise enables QPen::isCosmetic()) as being a non-cosmetic
+ pen with a width of 1.
+
+ \sa renderHints(), setRenderHint(), {QPainter#Rendering
+ Quality}{Rendering Quality}, {Concentric Circles Example}
+
+*/
+
+/*!
+ Constructs a painter.
+
+ \sa begin(), end()
+*/
+
+QPainter::QPainter()
+ : d_ptr(new QPainterPrivate(this))
+{
+}
+
+/*!
+ \fn QPainter::QPainter(QPaintDevice *device)
+
+ Constructs a painter that begins painting the paint \a device
+ immediately.
+
+ This constructor is convenient for short-lived painters, e.g. in a
+ QWidget::paintEvent() and should be used only once. The
+ constructor calls begin() for you and the QPainter destructor
+ automatically calls end().
+
+ Here's an example using begin() and end():
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 1
+
+ The same example using this constructor:
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 2
+
+ Since the constructor cannot provide feedback when the initialization
+ of the painter failed you should rather use begin() and end() to paint
+ on external devices, e.g. printers.
+
+ \sa begin(), end()
+*/
+
+QPainter::QPainter(QPaintDevice *pd)
+ : d_ptr(0)
+{
+ Q_ASSERT(pd != 0);
+ if (!QPainterPrivate::attachPainterPrivate(this, pd)) {
+ d_ptr.reset(new QPainterPrivate(this));
+ begin(pd);
+ }
+ Q_ASSERT(d_ptr);
+}
+
+/*!
+ Destroys the painter.
+*/
+QPainter::~QPainter()
+{
+ d_ptr->inDestructor = true;
+ QT_TRY {
+ if (isActive())
+ end();
+ else if (d_ptr->refcount > 1)
+ d_ptr->detachPainterPrivate(this);
+ } QT_CATCH(...) {
+ // don't throw anything in the destructor.
+ }
+ if (d_ptr) {
+ // Make sure we haven't messed things up.
+ Q_ASSERT(d_ptr->inDestructor);
+ d_ptr->inDestructor = false;
+ Q_ASSERT(d_ptr->refcount == 1);
+ if (d_ptr->d_ptrs)
+ free(d_ptr->d_ptrs);
+ }
+}
+
+/*!
+ Returns the paint device on which this painter is currently
+ painting, or 0 if the painter is not active.
+
+ \sa isActive()
+*/
+
+QPaintDevice *QPainter::device() const
+{
+ Q_D(const QPainter);
+ if (isActive() && d->engine->d_func()->currentClipWidget)
+ return d->engine->d_func()->currentClipWidget;
+ return d->original_device;
+}
+
+/*!
+ Returns true if begin() has been called and end() has not yet been
+ called; otherwise returns false.
+
+ \sa begin(), QPaintDevice::paintingActive()
+*/
+
+bool QPainter::isActive() const
+{
+ Q_D(const QPainter);
+ return d->engine;
+}
+
+/*!
+ Initializes the painters pen, background and font to the same as
+ the given \a widget. This function is called automatically when the
+ painter is opened on a QWidget.
+
+ \sa begin(), {QPainter#Settings}{Settings}
+*/
+void QPainter::initFrom(const QWidget *widget)
+{
+ Q_ASSERT_X(widget, "QPainter::initFrom(const QWidget *widget)", "Widget cannot be 0");
+ Q_D(QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::initFrom: Painter not active, aborted");
+ return;
+ }
+
+ const QPalette &pal = widget->palette();
+ d->state->pen = QPen(pal.brush(widget->foregroundRole()), 0);
+ d->state->bgBrush = pal.brush(widget->backgroundRole());
+ d->state->deviceFont = QFont(widget->font(), const_cast<QWidget*> (widget));
+ d->state->font = d->state->deviceFont;
+ if (d->extended) {
+ d->extended->penChanged();
+ } else if (d->engine) {
+ d->engine->setDirty(QPaintEngine::DirtyPen);
+ d->engine->setDirty(QPaintEngine::DirtyBrush);
+ d->engine->setDirty(QPaintEngine::DirtyFont);
+ }
+}
+
+
+/*!
+ Saves the current painter state (pushes the state onto a stack). A
+ save() must be followed by a corresponding restore(); the end()
+ function unwinds the stack.
+
+ \sa restore()
+*/
+
+void QPainter::save()
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::save()\n");
+#endif
+ Q_D(QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::save: Painter not active");
+ return;
+ }
+
+ if (d->extended) {
+ d->state = d->extended->createState(d->states.back());
+ d->extended->setState(d->state);
+ } else {
+ d->updateState(d->state);
+ d->state = new QPainterState(d->states.back());
+ d->engine->state = d->state;
+ }
+ d->states.push_back(d->state);
+}
+
+/*!
+ Restores the current painter state (pops a saved state off the
+ stack).
+
+ \sa save()
+*/
+
+void QPainter::restore()
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::restore()\n");
+#endif
+ Q_D(QPainter);
+ if (d->states.size()<=1) {
+ qWarning("QPainter::restore: Unbalanced save/restore");
+ return;
+ } else if (!d->engine) {
+ qWarning("QPainter::restore: Painter not active");
+ return;
+ }
+
+ QPainterState *tmp = d->state;
+ d->states.pop_back();
+ d->state = d->states.back();
+ d->txinv = false;
+
+ if (d->extended) {
+ d->checkEmulation();
+ d->extended->setState(d->state);
+ delete tmp;
+ return;
+ }
+
+ // trigger clip update if the clip path/region has changed since
+ // last save
+ if (!d->state->clipInfo.isEmpty()
+ && (tmp->changeFlags & (QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyClipPath))) {
+ // reuse the tmp state to avoid any extra allocs...
+ tmp->dirtyFlags = QPaintEngine::DirtyClipPath;
+ tmp->clipOperation = Qt::NoClip;
+ tmp->clipPath = QPainterPath();
+ d->engine->updateState(*tmp);
+ // replay the list of clip states,
+ for (int i=0; i<d->state->clipInfo.size(); ++i) {
+ const QPainterClipInfo &info = d->state->clipInfo.at(i);
+ tmp->matrix = info.matrix;
+ tmp->matrix *= d->state->redirectionMatrix;
+ tmp->clipOperation = info.operation;
+ if (info.clipType == QPainterClipInfo::RectClip) {
+ tmp->dirtyFlags = QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyTransform;
+ tmp->clipRegion = info.rect;
+ } else if (info.clipType == QPainterClipInfo::RegionClip) {
+ tmp->dirtyFlags = QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyTransform;
+ tmp->clipRegion = info.region;
+ } else { // clipType == QPainterClipInfo::PathClip
+ tmp->dirtyFlags = QPaintEngine::DirtyClipPath | QPaintEngine::DirtyTransform;
+ tmp->clipPath = info.path;
+ }
+ d->engine->updateState(*tmp);
+ }
+
+
+ //Since we've updated the clip region anyway, pretend that the clip path hasn't changed:
+ d->state->dirtyFlags &= ~(QPaintEngine::DirtyClipPath | QPaintEngine::DirtyClipRegion);
+ tmp->changeFlags &= ~(QPaintEngine::DirtyClipPath | QPaintEngine::DirtyClipRegion);
+ tmp->changeFlags |= QPaintEngine::DirtyTransform;
+ }
+
+ d->updateState(d->state);
+ delete tmp;
+}
+
+
+/*!
+
+ \fn bool QPainter::begin(QPaintDevice *device)
+
+ Begins painting the paint \a device and returns true if
+ successful; otherwise returns false.
+
+ Notice that all painter settings (setPen(), setBrush() etc.) are reset
+ to default values when begin() is called.
+
+ The errors that can occur are serious problems, such as these:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 3
+
+ Note that most of the time, you can use one of the constructors
+ instead of begin(), and that end() is automatically done at
+ destruction.
+
+ \warning A paint device can only be painted by one painter at a
+ time.
+
+ \warning Painting on a QImage with the format
+ QImage::Format_Indexed8 is not supported.
+
+ \sa end(), QPainter()
+*/
+
+static inline void qt_cleanup_painter_state(QPainterPrivate *d)
+{
+ d->states.clear();
+ delete d->state;
+ d->state = 0;
+ d->engine = 0;
+ d->device = 0;
+}
+
+bool QPainter::begin(QPaintDevice *pd)
+{
+ Q_ASSERT(pd);
+
+ if (pd->painters > 0) {
+ qWarning("QPainter::begin: A paint device can only be painted by one painter at a time.");
+ return false;
+ }
+
+ if (d_ptr->engine) {
+ qWarning("QPainter::begin: Painter already active");
+ return false;
+ }
+
+ if (QPainterPrivate::attachPainterPrivate(this, pd))
+ return true;
+
+ Q_D(QPainter);
+
+ d->helper_device = pd;
+ d->original_device = pd;
+ QPaintDevice *rpd = 0;
+
+ QPoint redirectionOffset;
+ // We know for sure that redirection is broken when the widget is inside
+ // its paint event, so it's safe to use our hard-coded redirection. However,
+ // there IS one particular case we still need to support, and that's
+ // when people call QPainter::setRedirected in the widget's paint event right
+ // before any painter is created (or QPainter::begin is called). In that
+ // particular case our hard-coded redirection is restored and the redirection
+ // is retrieved from QPainter::redirected (as before).
+ if (pd->devType() == QInternal::Widget)
+ rpd = static_cast<QWidget *>(pd)->d_func()->redirected(&redirectionOffset);
+
+ if (!rpd)
+ rpd = redirected(pd, &redirectionOffset);
+
+ if (rpd)
+ pd = rpd;
+
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::begin(), device=%p, type=%d\n", pd, pd->devType());
+#endif
+
+ if (pd->devType() == QInternal::Pixmap)
+ static_cast<QPixmap *>(pd)->detach();
+ else if (pd->devType() == QInternal::Image)
+ static_cast<QImage *>(pd)->detach();
+
+ d->engine = pd->paintEngine();
+
+ if (!d->engine) {
+ qWarning("QPainter::begin: Paint device returned engine == 0, type: %d", pd->devType());
+ return false;
+ }
+
+ d->device = pd;
+
+ d->extended = d->engine->isExtended() ? static_cast<QPaintEngineEx *>(d->engine) : 0;
+ if (d->emulationEngine)
+ d->emulationEngine->real_engine = d->extended;
+
+ // Setup new state...
+ Q_ASSERT(!d->state);
+ d->state = d->extended ? d->extended->createState(0) : new QPainterState;
+ d->state->painter = this;
+ d->states.push_back(d->state);
+
+ d->state->redirectionMatrix.translate(-redirectionOffset.x(), -redirectionOffset.y());
+ d->state->brushOrigin = QPointF();
+
+ // Slip a painter state into the engine before we do any other operations
+ if (d->extended)
+ d->extended->setState(d->state);
+ else
+ d->engine->state = d->state;
+
+ switch (pd->devType()) {
+ case QInternal::Widget:
+ {
+ const QWidget *widget = static_cast<const QWidget *>(pd);
+ Q_ASSERT(widget);
+
+ const bool paintOutsidePaintEvent = widget->testAttribute(Qt::WA_PaintOutsidePaintEvent);
+ const bool inPaintEvent = widget->testAttribute(Qt::WA_WState_InPaintEvent);
+ if(!d->engine->hasFeature(QPaintEngine::PaintOutsidePaintEvent)
+ && !paintOutsidePaintEvent && !inPaintEvent) {
+ qWarning("QPainter::begin: Widget painting can only begin as a "
+ "result of a paintEvent");
+ qt_cleanup_painter_state(d);
+ return false;
+ }
+
+ // Adjust offset for alien widgets painting outside the paint event.
+ if (!inPaintEvent && paintOutsidePaintEvent && !widget->internalWinId()
+ && widget->testAttribute(Qt::WA_WState_Created)) {
+ const QPoint offset = widget->mapTo(widget->nativeParentWidget(), QPoint());
+ d->state->redirectionMatrix.translate(offset.x(), offset.y());
+ }
+ break;
+ }
+ case QInternal::Pixmap:
+ {
+ QPixmap *pm = static_cast<QPixmap *>(pd);
+ Q_ASSERT(pm);
+ if (pm->isNull()) {
+ qWarning("QPainter::begin: Cannot paint on a null pixmap");
+ qt_cleanup_painter_state(d);
+ return false;
+ }
+
+ if (pm->depth() == 1) {
+ d->state->pen = QPen(Qt::color1);
+ d->state->brush = QBrush(Qt::color0);
+ }
+ break;
+ }
+ case QInternal::Image:
+ {
+ QImage *img = static_cast<QImage *>(pd);
+ Q_ASSERT(img);
+ if (img->isNull()) {
+ qWarning("QPainter::begin: Cannot paint on a null image");
+ qt_cleanup_painter_state(d);
+ return false;
+ } else if (img->format() == QImage::Format_Indexed8) {
+ // Painting on indexed8 images is not supported.
+ qWarning("QPainter::begin: Cannot paint on an image with the QImage::Format_Indexed8 format");
+ qt_cleanup_painter_state(d);
+ return false;
+ }
+ if (img->depth() == 1) {
+ d->state->pen = QPen(Qt::color1);
+ d->state->brush = QBrush(Qt::color0);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ if (d->state->ww == 0) // For compat with 3.x painter defaults
+ d->state->ww = d->state->wh = d->state->vw = d->state->vh = 1024;
+
+ d->engine->setPaintDevice(pd);
+
+ bool begun = d->engine->begin(pd);
+ if (!begun) {
+ qWarning("QPainter::begin(): Returned false");
+ if (d->engine->isActive()) {
+ end();
+ } else {
+ qt_cleanup_painter_state(d);
+ }
+ return false;
+ } else {
+ d->engine->setActive(begun);
+ }
+
+ // Copy painter properties from original paint device,
+ // required for QPixmap::grabWidget()
+ if (d->original_device->devType() == QInternal::Widget) {
+ QWidget *widget = static_cast<QWidget *>(d->original_device);
+ initFrom(widget);
+ } else {
+ d->state->layoutDirection = Qt::LayoutDirectionAuto;
+ // make sure we have a font compatible with the paintdevice
+ d->state->deviceFont = d->state->font = QFont(d->state->deviceFont, device());
+ }
+
+ QRect systemRect = d->engine->systemRect();
+ if (!systemRect.isEmpty()) {
+ d->state->ww = d->state->vw = systemRect.width();
+ d->state->wh = d->state->vh = systemRect.height();
+ } else {
+ d->state->ww = d->state->vw = pd->metric(QPaintDevice::PdmWidth);
+ d->state->wh = d->state->vh = pd->metric(QPaintDevice::PdmHeight);
+ }
+
+ const QPoint coordinateOffset = d->engine->coordinateOffset();
+ d->state->redirectionMatrix.translate(-coordinateOffset.x(), -coordinateOffset.y());
+
+ Q_ASSERT(d->engine->isActive());
+
+ if (!d->state->redirectionMatrix.isIdentity())
+ d->updateMatrix();
+
+ Q_ASSERT(d->engine->isActive());
+ d->state->renderHints = QPainter::TextAntialiasing;
+ ++d->device->painters;
+
+ d->state->emulationSpecifier = 0;
+
+ return true;
+}
+
+/*!
+ Ends painting. Any resources used while painting are released. You
+ don't normally need to call this since it is called by the
+ destructor.
+
+ Returns true if the painter is no longer active; otherwise returns false.
+
+ \sa begin(), isActive()
+*/
+
+bool QPainter::end()
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::end()\n");
+#endif
+ Q_D(QPainter);
+
+ if (!d->engine) {
+ qWarning("QPainter::end: Painter not active, aborted");
+ qt_cleanup_painter_state(d);
+ return false;
+ }
+
+ if (d->refcount > 1) {
+ d->detachPainterPrivate(this);
+ return true;
+ }
+
+ bool ended = true;
+
+ if (d->engine->isActive()) {
+ ended = d->engine->end();
+ d->updateState(0);
+
+ --d->device->painters;
+ if (d->device->painters == 0) {
+ d->engine->setPaintDevice(0);
+ d->engine->setActive(false);
+ }
+ }
+
+ if (d->states.size() > 1) {
+ qWarning("QPainter::end: Painter ended with %d saved states",
+ d->states.size());
+ }
+
+ if (d->engine->autoDestruct()) {
+ delete d->engine;
+ }
+
+ if (d->emulationEngine) {
+ delete d->emulationEngine;
+ d->emulationEngine = 0;
+ }
+
+ if (d->extended) {
+ d->extended = 0;
+ }
+
+ qt_cleanup_painter_state(d);
+
+ return ended;
+}
+
+
+/*!
+ Returns the paint engine that the painter is currently operating
+ on if the painter is active; otherwise 0.
+
+ \sa isActive()
+*/
+QPaintEngine *QPainter::paintEngine() const
+{
+ Q_D(const QPainter);
+ return d->engine;
+}
+
+/*!
+ \since 4.6
+
+ Flushes the painting pipeline and prepares for the user issuing commands
+ directly to the underlying graphics context. Must be followed by a call to
+ endNativePainting().
+
+ Note that only the states the underlying paint engine changes will be reset
+ to their respective default states. The states we reset may change from
+ release to release. The following states are currently reset in the OpenGL
+ 2 engine:
+
+ \list
+ \i blending is disabled
+ \i the depth, stencil and scissor tests are disabled
+ \i the active texture unit is reset to 0
+ \i the depth mask, depth function and the clear depth are reset to their
+ default values
+ \i the stencil mask, stencil operation and stencil function are reset to
+ their default values
+ \i the current color is reset to solid white
+ \endlist
+
+ If, for example, the OpenGL polygon mode is changed by the user inside a
+ beginNativePaint()/endNativePainting() block, it will not be reset to the
+ default state by endNativePainting(). Here is an example that shows
+ intermixing of painter commands and raw OpenGL commands:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 21
+
+ \sa endNativePainting()
+*/
+void QPainter::beginNativePainting()
+{
+ Q_D(QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::beginNativePainting: Painter not active");
+ return;
+ }
+
+ if (d->extended)
+ d->extended->beginNativePainting();
+}
+
+/*!
+ \since 4.6
+
+ Restores the painter after manually issuing native painting commands. Lets
+ the painter restore any native state that it relies on before calling any
+ other painter commands.
+
+ \sa beginNativePainting()
+*/
+void QPainter::endNativePainting()
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::beginNativePainting: Painter not active");
+ return;
+ }
+
+ if (d->extended)
+ d->extended->endNativePainting();
+ else
+ d->engine->syncState();
+}
+
+/*!
+ Returns the font metrics for the painter if the painter is
+ active. Otherwise, the return value is undefined.
+
+ \sa font(), isActive(), {QPainter#Settings}{Settings}
+*/
+
+QFontMetrics QPainter::fontMetrics() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::fontMetrics: Painter not active");
+ return QFontMetrics(QFont());
+ }
+ return QFontMetrics(d->state->font);
+}
+
+
+/*!
+ Returns the font info for the painter if the painter is
+ active. Otherwise, the return value is undefined.
+
+ \sa font(), isActive(), {QPainter#Settings}{Settings}
+*/
+
+QFontInfo QPainter::fontInfo() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::fontInfo: Painter not active");
+ return QFontInfo(QFont());
+ }
+ return QFontInfo(d->state->font);
+}
+
+/*!
+ \since 4.2
+
+ Returns the opacity of the painter. The default value is
+ 1.
+*/
+
+qreal QPainter::opacity() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::opacity: Painter not active");
+ return 1.0;
+ }
+ return d->state->opacity;
+}
+
+/*!
+ \since 4.2
+
+ Sets the opacity of the painter to \a opacity. The value should
+ be in the range 0.0 to 1.0, where 0.0 is fully transparent and
+ 1.0 is fully opaque.
+
+ Opacity set on the painter will apply to all drawing operations
+ individually.
+*/
+
+void QPainter::setOpacity(qreal opacity)
+{
+ Q_D(QPainter);
+
+ if (!d->engine) {
+ qWarning("QPainter::setOpacity: Painter not active");
+ return;
+ }
+
+ opacity = qMin(qreal(1), qMax(qreal(0), opacity));
+
+ if (opacity == d->state->opacity)
+ return;
+
+ d->state->opacity = opacity;
+
+ if (d->extended)
+ d->extended->opacityChanged();
+ else
+ d->state->dirtyFlags |= QPaintEngine::DirtyOpacity;
+}
+
+
+/*!
+ Returns the currently set brush origin.
+
+ \sa setBrushOrigin(), {QPainter#Settings}{Settings}
+*/
+
+QPoint QPainter::brushOrigin() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::brushOrigin: Painter not active");
+ return QPoint();
+ }
+ return QPointF(d->state->brushOrigin).toPoint();
+}
+
+/*!
+ \fn void QPainter::setBrushOrigin(const QPointF &position)
+
+ Sets the brush origin to \a position.
+
+ The brush origin specifies the (0, 0) coordinate of the painter's
+ brush.
+
+ Note that while the brushOrigin() was necessary to adopt the
+ parent's background for a widget in Qt 3, this is no longer the
+ case since the Qt 4 painter doesn't paint the background unless
+ you explicitly tell it to do so by setting the widget's \l
+ {QWidget::autoFillBackground}{autoFillBackground} property to
+ true.
+
+ \sa brushOrigin(), {QPainter#Settings}{Settings}
+*/
+
+void QPainter::setBrushOrigin(const QPointF &p)
+{
+ Q_D(QPainter);
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::setBrushOrigin(), (%.2f,%.2f)\n", p.x(), p.y());
+#endif
+
+ if (!d->engine) {
+ qWarning("QPainter::setBrushOrigin: Painter not active");
+ return;
+ }
+
+ d->state->brushOrigin = p;
+
+ if (d->extended) {
+ d->extended->brushOriginChanged();
+ return;
+ }
+
+ d->state->dirtyFlags |= QPaintEngine::DirtyBrushOrigin;
+}
+
+/*!
+ \fn void QPainter::setBrushOrigin(const QPoint &position)
+ \overload
+
+ Sets the brush's origin to the given \a position.
+*/
+
+/*!
+ \fn void QPainter::setBrushOrigin(int x, int y)
+
+ \overload
+
+ Sets the brush's origin to point (\a x, \a y).
+*/
+
+/*!
+ \enum QPainter::CompositionMode
+
+ Defines the modes supported for digital image compositing.
+ Composition modes are used to specify how the pixels in one image,
+ the source, are merged with the pixel in another image, the
+ destination.
+
+ Please note that the bitwise raster operation modes, denoted with
+ a RasterOp prefix, are only natively supported in the X11 and
+ raster paint engines. This means that the only way to utilize
+ these modes on the Mac is via a QImage. The RasterOp denoted blend
+ modes are \e not supported for pens and brushes with alpha
+ components. Also, turning on the QPainter::Antialiasing render
+ hint will effectively disable the RasterOp modes.
+
+
+ \image qpainter-compositionmode1.png
+ \image qpainter-compositionmode2.png
+
+ The most common type is SourceOver (often referred to as just
+ alpha blending) where the source pixel is blended on top of the
+ destination pixel in such a way that the alpha component of the
+ source defines the translucency of the pixel.
+
+ When the paint device is a QImage, the image format must be set to
+ \l {QImage::Format}{Format_ARGB32Premultiplied} or
+ \l {QImage::Format}{Format_ARGB32} for the composition modes to have
+ any effect. For performance the premultiplied version is the preferred
+ format.
+
+ When a composition mode is set it applies to all painting
+ operator, pens, brushes, gradients and pixmap/image drawing.
+
+ \value CompositionMode_SourceOver This is the default mode. The
+ alpha of the source is used to blend the pixel on top of the
+ destination.
+
+ \value CompositionMode_DestinationOver The alpha of the
+ destination is used to blend it on top of the source pixels. This
+ mode is the inverse of CompositionMode_SourceOver.
+
+ \value CompositionMode_Clear The pixels in the destination are
+ cleared (set to fully transparent) independent of the source.
+
+ \value CompositionMode_Source The output is the source
+ pixel. (This means a basic copy operation and is identical to
+ SourceOver when the source pixel is opaque).
+
+ \value CompositionMode_Destination The output is the destination
+ pixel. This means that the blending has no effect. This mode is
+ the inverse of CompositionMode_Source.
+
+ \value CompositionMode_SourceIn The output is the source, where
+ the alpha is reduced by that of the destination.
+
+ \value CompositionMode_DestinationIn The output is the
+ destination, where the alpha is reduced by that of the
+ source. This mode is the inverse of CompositionMode_SourceIn.
+
+ \value CompositionMode_SourceOut The output is the source, where
+ the alpha is reduced by the inverse of destination.
+
+ \value CompositionMode_DestinationOut The output is the
+ destination, where the alpha is reduced by the inverse of the
+ source. This mode is the inverse of CompositionMode_SourceOut.
+
+ \value CompositionMode_SourceAtop The source pixel is blended on
+ top of the destination, with the alpha of the source pixel reduced
+ by the alpha of the destination pixel.
+
+ \value CompositionMode_DestinationAtop The destination pixel is
+ blended on top of the source, with the alpha of the destination
+ pixel is reduced by the alpha of the destination pixel. This mode
+ is the inverse of CompositionMode_SourceAtop.
+
+ \value CompositionMode_Xor The source, whose alpha is reduced with
+ the inverse of the destination alpha, is merged with the
+ destination, whose alpha is reduced by the inverse of the source
+ alpha. CompositionMode_Xor is not the same as the bitwise Xor.
+
+ \value CompositionMode_Plus Both the alpha and color of the source
+ and destination pixels are added together.
+
+ \value CompositionMode_Multiply The output is the source color
+ multiplied by the destination. Multiplying a color with white
+ leaves the color unchanged, while multiplying a color
+ with black produces black.
+
+ \value CompositionMode_Screen The source and destination colors
+ are inverted and then multiplied. Screening a color with white
+ produces white, whereas screening a color with black leaves the
+ color unchanged.
+
+ \value CompositionMode_Overlay Multiplies or screens the colors
+ depending on the destination color. The destination color is mixed
+ with the source color to reflect the lightness or darkness of the
+ destination.
+
+ \value CompositionMode_Darken The darker of the source and
+ destination colors is selected.
+
+ \value CompositionMode_Lighten The lighter of the source and
+ destination colors is selected.
+
+ \value CompositionMode_ColorDodge The destination color is
+ brightened to reflect the source color. A black source color
+ leaves the destination color unchanged.
+
+ \value CompositionMode_ColorBurn The destination color is darkened
+ to reflect the source color. A white source color leaves the
+ destination color unchanged.
+
+ \value CompositionMode_HardLight Multiplies or screens the colors
+ depending on the source color. A light source color will lighten
+ the destination color, whereas a dark source color will darken the
+ destination color.
+
+ \value CompositionMode_SoftLight Darkens or lightens the colors
+ depending on the source color. Similar to
+ CompositionMode_HardLight.
+
+ \value CompositionMode_Difference Subtracts the darker of the
+ colors from the lighter. Painting with white inverts the
+ destination color, whereas painting with black leaves the
+ destination color unchanged.
+
+ \value CompositionMode_Exclusion Similar to
+ CompositionMode_Difference, but with a lower contrast. Painting
+ with white inverts the destination color, whereas painting with
+ black leaves the destination color unchanged.
+
+ \value RasterOp_SourceOrDestination Does a bitwise OR operation on
+ the source and destination pixels (src OR dst).
+
+ \value RasterOp_SourceAndDestination Does a bitwise AND operation
+ on the source and destination pixels (src AND dst).
+
+ \value RasterOp_SourceXorDestination Does a bitwise XOR operation
+ on the source and destination pixels (src XOR dst).
+
+ \value RasterOp_NotSourceAndNotDestination Does a bitwise NOR
+ operation on the source and destination pixels ((NOT src) AND (NOT
+ dst)).
+
+ \value RasterOp_NotSourceOrNotDestination Does a bitwise NAND
+ operation on the source and destination pixels ((NOT src) OR (NOT
+ dst)).
+
+ \value RasterOp_NotSourceXorDestination Does a bitwise operation
+ where the source pixels are inverted and then XOR'ed with the
+ destination ((NOT src) XOR dst).
+
+ \value RasterOp_NotSource Does a bitwise operation where the
+ source pixels are inverted (NOT src).
+
+ \value RasterOp_NotSourceAndDestination Does a bitwise operation
+ where the source is inverted and then AND'ed with the destination
+ ((NOT src) AND dst).
+
+ \value RasterOp_SourceAndNotDestination Does a bitwise operation
+ where the source is AND'ed with the inverted destination pixels
+ (src AND (NOT dst)).
+
+ \sa compositionMode(), setCompositionMode(), {QPainter#Composition
+ Modes}{Composition Modes}, {Image Composition Example}
+*/
+
+/*!
+ Sets the composition mode to the given \a mode.
+
+ \warning Only a QPainter operating on a QImage fully supports all
+ composition modes. The RasterOp modes are supported for X11 as
+ described in compositionMode().
+
+ \sa compositionMode()
+*/
+void QPainter::setCompositionMode(CompositionMode mode)
+{
+ Q_D(QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::setCompositionMode: Painter not active");
+ return;
+ }
+ if (d->state->composition_mode == mode)
+ return;
+ if (d->extended) {
+ d->state->composition_mode = mode;
+ d->extended->compositionModeChanged();
+ return;
+ }
+
+ if (mode >= QPainter::RasterOp_SourceOrDestination) {
+ if (!d->engine->hasFeature(QPaintEngine::RasterOpModes)) {
+ qWarning("QPainter::setCompositionMode: "
+ "Raster operation modes not supported on device");
+ return;
+ }
+ } else if (mode >= QPainter::CompositionMode_Plus) {
+ if (!d->engine->hasFeature(QPaintEngine::BlendModes)) {
+ qWarning("QPainter::setCompositionMode: "
+ "Blend modes not supported on device");
+ return;
+ }
+ } else if (!d->engine->hasFeature(QPaintEngine::PorterDuff)) {
+ if (mode != CompositionMode_Source && mode != CompositionMode_SourceOver) {
+ qWarning("QPainter::setCompositionMode: "
+ "PorterDuff modes not supported on device");
+ return;
+ }
+ }
+
+ d->state->composition_mode = mode;
+ d->state->dirtyFlags |= QPaintEngine::DirtyCompositionMode;
+}
+
+/*!
+ Returns the current composition mode.
+
+ \sa CompositionMode, setCompositionMode()
+*/
+QPainter::CompositionMode QPainter::compositionMode() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::compositionMode: Painter not active");
+ return QPainter::CompositionMode_SourceOver;
+ }
+ return d->state->composition_mode;
+}
+
+/*!
+ Returns the current background brush.
+
+ \sa setBackground(), {QPainter#Settings}{Settings}
+*/
+
+const QBrush &QPainter::background() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::background: Painter not active");
+ return d->fakeState()->brush;
+ }
+ return d->state->bgBrush;
+}
+
+
+/*!
+ Returns true if clipping has been set; otherwise returns false.
+
+ \sa setClipping(), {QPainter#Clipping}{Clipping}
+*/
+
+bool QPainter::hasClipping() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::hasClipping: Painter not active");
+ return false;
+ }
+ return d->state->clipEnabled && d->state->clipOperation != Qt::NoClip;
+}
+
+
+/*!
+ Enables clipping if \a enable is true, or disables clipping if \a
+ enable is false.
+
+ \sa hasClipping(), {QPainter#Clipping}{Clipping}
+*/
+
+void QPainter::setClipping(bool enable)
+{
+ Q_D(QPainter);
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::setClipping(), enable=%s, was=%s\n",
+ enable ? "on" : "off",
+ hasClipping() ? "on" : "off");
+#endif
+ if (!d->engine) {
+ qWarning("QPainter::setClipping: Painter not active, state will be reset by begin");
+ return;
+ }
+
+ if (hasClipping() == enable)
+ return;
+
+ // we can't enable clipping if we don't have a clip
+ if (enable
+ && (d->state->clipInfo.isEmpty() || d->state->clipInfo.last().operation == Qt::NoClip))
+ return;
+ d->state->clipEnabled = enable;
+
+ if (d->extended) {
+ d->extended->clipEnabledChanged();
+ return;
+ }
+
+ d->state->dirtyFlags |= QPaintEngine::DirtyClipEnabled;
+ d->updateState(d->state);
+}
+
+
+/*!
+ Returns the currently set clip region. Note that the clip region
+ is given in logical coordinates.
+
+ \warning QPainter does not store the combined clip explicitly as
+ this is handled by the underlying QPaintEngine, so the path is
+ recreated on demand and transformed to the current logical
+ coordinate system. This is potentially an expensive operation.
+
+ \sa setClipRegion(), clipPath(), setClipping()
+*/
+
+QRegion QPainter::clipRegion() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::clipRegion: Painter not active");
+ return QRegion();
+ }
+
+ QRegion region;
+ bool lastWasNothing = true;
+
+ if (!d->txinv)
+ const_cast<QPainter *>(this)->d_ptr->updateInvMatrix();
+
+ // ### Falcon: Use QPainterPath
+ for (int i=0; i<d->state->clipInfo.size(); ++i) {
+ const QPainterClipInfo &info = d->state->clipInfo.at(i);
+ switch (info.clipType) {
+
+ case QPainterClipInfo::RegionClip: {
+ QTransform matrix = (info.matrix * d->invMatrix);
+ if (lastWasNothing) {
+ region = info.region * matrix;
+ lastWasNothing = false;
+ continue;
+ }
+ if (info.operation == Qt::IntersectClip)
+ region &= info.region * matrix;
+ else if (info.operation == Qt::UniteClip)
+ region |= info.region * matrix;
+ else if (info.operation == Qt::NoClip) {
+ lastWasNothing = true;
+ region = QRegion();
+ } else
+ region = info.region * matrix;
+ break;
+ }
+
+ case QPainterClipInfo::PathClip: {
+ QTransform matrix = (info.matrix * d->invMatrix);
+ if (lastWasNothing) {
+ region = QRegion((info.path * matrix).toFillPolygon().toPolygon(),
+ info.path.fillRule());
+ lastWasNothing = false;
+ continue;
+ }
+ if (info.operation == Qt::IntersectClip) {
+ region &= QRegion((info.path * matrix).toFillPolygon().toPolygon(),
+ info.path.fillRule());
+ } else if (info.operation == Qt::UniteClip) {
+ region |= QRegion((info.path * matrix).toFillPolygon().toPolygon(),
+ info.path.fillRule());
+ } else if (info.operation == Qt::NoClip) {
+ lastWasNothing = true;
+ region = QRegion();
+ } else {
+ region = QRegion((info.path * matrix).toFillPolygon().toPolygon(),
+ info.path.fillRule());
+ }
+ break;
+ }
+
+ case QPainterClipInfo::RectClip: {
+ QTransform matrix = (info.matrix * d->invMatrix);
+ if (lastWasNothing) {
+ region = QRegion(info.rect) * matrix;
+ lastWasNothing = false;
+ continue;
+ }
+ if (info.operation == Qt::IntersectClip) {
+ // Use rect intersection if possible.
+ if (matrix.type() <= QTransform::TxScale)
+ region &= matrix.mapRect(info.rect);
+ else
+ region &= matrix.map(QRegion(info.rect));
+ } else if (info.operation == Qt::UniteClip) {
+ region |= QRegion(info.rect) * matrix;
+ } else if (info.operation == Qt::NoClip) {
+ lastWasNothing = true;
+ region = QRegion();
+ } else {
+ region = QRegion(info.rect) * matrix;
+ }
+ break;
+ }
+
+ case QPainterClipInfo::RectFClip: {
+ QTransform matrix = (info.matrix * d->invMatrix);
+ if (lastWasNothing) {
+ region = QRegion(info.rectf.toRect()) * matrix;
+ lastWasNothing = false;
+ continue;
+ }
+ if (info.operation == Qt::IntersectClip) {
+ // Use rect intersection if possible.
+ if (matrix.type() <= QTransform::TxScale)
+ region &= matrix.mapRect(info.rectf.toRect());
+ else
+ region &= matrix.map(QRegion(info.rectf.toRect()));
+ } else if (info.operation == Qt::UniteClip) {
+ region |= QRegion(info.rectf.toRect()) * matrix;
+ } else if (info.operation == Qt::NoClip) {
+ lastWasNothing = true;
+ region = QRegion();
+ } else {
+ region = QRegion(info.rectf.toRect()) * matrix;
+ }
+ break;
+ }
+ }
+ }
+
+ return region;
+}
+
+extern QPainterPath qt_regionToPath(const QRegion &region);
+
+/*!
+ Returns the currently clip as a path. Note that the clip path is
+ given in logical coordinates.
+
+ \warning QPainter does not store the combined clip explicitly as
+ this is handled by the underlying QPaintEngine, so the path is
+ recreated on demand and transformed to the current logical
+ coordinate system. This is potentially an expensive operation.
+
+ \sa setClipPath(), clipRegion(), setClipping()
+*/
+QPainterPath QPainter::clipPath() const
+{
+ Q_D(const QPainter);
+
+ // ### Since we do not support path intersections and path unions yet,
+ // we just use clipRegion() here...
+ if (!d->engine) {
+ qWarning("QPainter::clipPath: Painter not active");
+ return QPainterPath();
+ }
+
+ // No clip, return empty
+ if (d->state->clipInfo.size() == 0) {
+ return QPainterPath();
+ } else {
+
+ // Update inverse matrix, used below.
+ if (!d->txinv)
+ const_cast<QPainter *>(this)->d_ptr->updateInvMatrix();
+
+ // For the simple case avoid conversion.
+ if (d->state->clipInfo.size() == 1
+ && d->state->clipInfo.at(0).clipType == QPainterClipInfo::PathClip) {
+ QTransform matrix = (d->state->clipInfo.at(0).matrix * d->invMatrix);
+ return d->state->clipInfo.at(0).path * matrix;
+
+ } else if (d->state->clipInfo.size() == 1
+ && d->state->clipInfo.at(0).clipType == QPainterClipInfo::RectClip) {
+ QTransform matrix = (d->state->clipInfo.at(0).matrix * d->invMatrix);
+ QPainterPath path;
+ path.addRect(d->state->clipInfo.at(0).rect);
+ return path * matrix;
+ } else {
+ // Fallback to clipRegion() for now, since we don't have isect/unite for paths
+ return qt_regionToPath(clipRegion());
+ }
+ }
+}
+
+/*!
+ Returns the bounding rectangle of the current clip if there is a clip;
+ otherwise returns an empty rectangle. Note that the clip region is
+ given in logical coordinates.
+
+ The bounding rectangle is not guaranteed to be tight.
+
+ \sa setClipRect(), setClipPath(), setClipRegion()
+
+ \since 4.8
+ */
+
+QRectF QPainter::clipBoundingRect() const
+{
+ Q_D(const QPainter);
+
+ if (!d->engine) {
+ qWarning("QPainter::clipBoundingRect: Painter not active");
+ return QRectF();
+ }
+
+ // Accumulate the bounding box in device space. This is not 100%
+ // precise, but it fits within the guarantee and it is resonably
+ // fast.
+ QRectF bounds;
+ for (int i=0; i<d->state->clipInfo.size(); ++i) {
+ QRectF r;
+ const QPainterClipInfo &info = d->state->clipInfo.at(i);
+
+ if (info.clipType == QPainterClipInfo::RectClip)
+ r = info.rect;
+ else if (info.clipType == QPainterClipInfo::RectFClip)
+ r = info.rectf;
+ else if (info.clipType == QPainterClipInfo::RegionClip)
+ r = info.region.boundingRect();
+ else
+ r = info.path.boundingRect();
+
+ r = info.matrix.mapRect(r);
+
+ if (i == 0)
+ bounds = r;
+ else if (info.operation == Qt::IntersectClip)
+ bounds &= r;
+ else if (info.operation == Qt::UniteClip)
+ bounds |= r;
+ }
+
+
+ // Map the rectangle back into logical space using the inverse
+ // matrix.
+ if (!d->txinv)
+ const_cast<QPainter *>(this)->d_ptr->updateInvMatrix();
+
+ return d->invMatrix.mapRect(bounds);
+}
+
+/*!
+ \fn void QPainter::setClipRect(const QRectF &rectangle, Qt::ClipOperation operation)
+
+ Enables clipping, and sets the clip region to the given \a
+ rectangle using the given clip \a operation. The default operation
+ is to replace the current clip rectangle.
+
+ Note that the clip rectangle is specified in logical (painter)
+ coordinates.
+
+ \sa clipRegion(), setClipping(), {QPainter#Clipping}{Clipping}
+*/
+void QPainter::setClipRect(const QRectF &rect, Qt::ClipOperation op)
+{
+ Q_D(QPainter);
+
+ if (d->extended) {
+ if ((!d->state->clipEnabled && op != Qt::NoClip) || (d->state->clipOperation == Qt::NoClip && op == Qt::UniteClip))
+ op = Qt::ReplaceClip;
+
+ if (!d->engine) {
+ qWarning("QPainter::setClipRect: Painter not active");
+ return;
+ }
+ qreal right = rect.x() + rect.width();
+ qreal bottom = rect.y() + rect.height();
+ qreal pts[] = { rect.x(), rect.y(),
+ right, rect.y(),
+ right, bottom,
+ rect.x(), bottom };
+ QVectorPath vp(pts, 4, 0, QVectorPath::RectangleHint);
+ d->state->clipEnabled = true;
+ d->extended->clip(vp, op);
+ if (op == Qt::ReplaceClip || op == Qt::NoClip)
+ d->state->clipInfo.clear();
+ d->state->clipInfo << QPainterClipInfo(rect, op, d->state->matrix);
+ d->state->clipOperation = op;
+ return;
+ }
+
+ if (qreal(int(rect.top())) == rect.top()
+ && qreal(int(rect.bottom())) == rect.bottom()
+ && qreal(int(rect.left())) == rect.left()
+ && qreal(int(rect.right())) == rect.right())
+ {
+ setClipRect(rect.toRect(), op);
+ return;
+ }
+
+ if (rect.isEmpty()) {
+ setClipRegion(QRegion(), op);
+ return;
+ }
+
+ QPainterPath path;
+ path.addRect(rect);
+ setClipPath(path, op);
+}
+
+/*!
+ \fn void QPainter::setClipRect(const QRect &rectangle, Qt::ClipOperation operation)
+ \overload
+
+ Enables clipping, and sets the clip region to the given \a rectangle using the given
+ clip \a operation.
+*/
+void QPainter::setClipRect(const QRect &rect, Qt::ClipOperation op)
+{
+ Q_D(QPainter);
+
+ if (!d->engine) {
+ qWarning("QPainter::setClipRect: Painter not active");
+ return;
+ }
+
+ if ((!d->state->clipEnabled && op != Qt::NoClip) || (d->state->clipOperation == Qt::NoClip && op == Qt::UniteClip))
+ op = Qt::ReplaceClip;
+
+ if (d->extended) {
+ d->state->clipEnabled = true;
+ d->extended->clip(rect, op);
+ if (op == Qt::ReplaceClip || op == Qt::NoClip)
+ d->state->clipInfo.clear();
+ d->state->clipInfo << QPainterClipInfo(rect, op, d->state->matrix);
+ d->state->clipOperation = op;
+ return;
+ }
+
+ d->state->clipRegion = rect;
+ d->state->clipOperation = op;
+ if (op == Qt::NoClip || op == Qt::ReplaceClip)
+ d->state->clipInfo.clear();
+ d->state->clipInfo << QPainterClipInfo(rect, op, d->state->matrix);
+ d->state->clipEnabled = true;
+ d->state->dirtyFlags |= QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyClipEnabled;
+ d->updateState(d->state);
+}
+
+/*!
+ \fn void QPainter::setClipRect(int x, int y, int width, int height, Qt::ClipOperation operation)
+
+ Enables clipping, and sets the clip region to the rectangle beginning at (\a x, \a y)
+ with the given \a width and \a height.
+*/
+
+/*!
+ \fn void QPainter::setClipRegion(const QRegion &region, Qt::ClipOperation operation)
+
+ Sets the clip region to the given \a region using the specified clip
+ \a operation. The default clip operation is to replace the current
+ clip region.
+
+ Note that the clip region is given in logical coordinates.
+
+ \sa clipRegion(), setClipRect(), {QPainter#Clipping}{Clipping}
+*/
+void QPainter::setClipRegion(const QRegion &r, Qt::ClipOperation op)
+{
+ Q_D(QPainter);
+#ifdef QT_DEBUG_DRAW
+ QRect rect = r.boundingRect();
+ if (qt_show_painter_debug_output)
+ printf("QPainter::setClipRegion(), size=%d, [%d,%d,%d,%d]\n",
+ r.rects().size(), rect.x(), rect.y(), rect.width(), rect.height());
+#endif
+ if (!d->engine) {
+ qWarning("QPainter::setClipRegion: Painter not active");
+ return;
+ }
+
+ if ((!d->state->clipEnabled && op != Qt::NoClip) || (d->state->clipOperation == Qt::NoClip && op == Qt::UniteClip))
+ op = Qt::ReplaceClip;
+
+ if (d->extended) {
+ d->state->clipEnabled = true;
+ d->extended->clip(r, op);
+ if (op == Qt::NoClip || op == Qt::ReplaceClip)
+ d->state->clipInfo.clear();
+ d->state->clipInfo << QPainterClipInfo(r, op, d->state->matrix);
+ d->state->clipOperation = op;
+ return;
+ }
+
+ d->state->clipRegion = r;
+ d->state->clipOperation = op;
+ if (op == Qt::NoClip || op == Qt::ReplaceClip)
+ d->state->clipInfo.clear();
+ d->state->clipInfo << QPainterClipInfo(r, op, d->state->matrix);
+ d->state->clipEnabled = true;
+ d->state->dirtyFlags |= QPaintEngine::DirtyClipRegion | QPaintEngine::DirtyClipEnabled;
+ d->updateState(d->state);
+}
+
+/*!
+ \since 4.2
+ \obsolete
+
+ Sets the transformation matrix to \a matrix and enables transformations.
+
+ \note It is advisable to use setWorldTransform() instead of this function to
+ preserve the properties of perspective transformations.
+
+ If \a combine is true, then \a matrix is combined with the current
+ transformation matrix; otherwise \a matrix replaces the current
+ transformation matrix.
+
+ If \a matrix is the identity matrix and \a combine is false, this
+ function calls setWorldMatrixEnabled(false). (The identity matrix is the
+ matrix where QMatrix::m11() and QMatrix::m22() are 1.0 and the
+ rest are 0.0.)
+
+ The following functions can transform the coordinate system without using
+ a QMatrix:
+ \list
+ \i translate()
+ \i scale()
+ \i shear()
+ \i rotate()
+ \endlist
+
+ They operate on the painter's worldMatrix() and are implemented like this:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 4
+
+ Note that when using setWorldMatrix() function you should always have
+ \a combine be true when you are drawing into a QPicture. Otherwise
+ it may not be possible to replay the picture with additional
+ transformations; using the translate(), scale(), etc. convenience
+ functions is safe.
+
+ For more information about the coordinate system, transformations
+ and window-viewport conversion, see \l {Coordinate System}.
+
+ \sa setWorldTransform(), QTransform
+*/
+
+void QPainter::setWorldMatrix(const QMatrix &matrix, bool combine)
+{
+ setWorldTransform(QTransform(matrix), combine);
+}
+
+/*!
+ \since 4.2
+ \obsolete
+
+ Returns the world transformation matrix.
+
+ It is advisable to use worldTransform() because worldMatrix() does not
+ preserve the properties of perspective transformations.
+
+ \sa {QPainter#Coordinate Transformations}{Coordinate Transformations},
+ {Coordinate System}
+*/
+
+const QMatrix &QPainter::worldMatrix() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::worldMatrix: Painter not active");
+ return d->fakeState()->transform.toAffine();
+ }
+ return d->state->worldMatrix.toAffine();
+}
+
+/*!
+ \obsolete
+
+ Use setWorldTransform() instead.
+
+ \sa setWorldTransform()
+*/
+
+void QPainter::setMatrix(const QMatrix &matrix, bool combine)
+{
+ setWorldTransform(QTransform(matrix), combine);
+}
+
+/*!
+ \obsolete
+
+ Use worldTransform() instead.
+
+ \sa worldTransform()
+*/
+
+const QMatrix &QPainter::matrix() const
+{
+ return worldMatrix();
+}
+
+
+/*!
+ \since 4.2
+ \obsolete
+
+ Returns the transformation matrix combining the current
+ window/viewport and world transformation.
+
+ It is advisable to use combinedTransform() instead of this
+ function to preserve the properties of perspective transformations.
+
+ \sa setWorldTransform(), setWindow(), setViewport()
+*/
+QMatrix QPainter::combinedMatrix() const
+{
+ return combinedTransform().toAffine();
+}
+
+
+/*!
+ \obsolete
+
+ Returns the matrix that transforms from logical coordinates to
+ device coordinates of the platform dependent paint device.
+
+ \note It is advisable to use deviceTransform() instead of this
+ function to preserve the properties of perspective transformations.
+
+ This function is \e only needed when using platform painting
+ commands on the platform dependent handle (Qt::HANDLE), and the
+ platform does not do transformations nativly.
+
+ The QPaintEngine::PaintEngineFeature enum can be queried to
+ determine whether the platform performs the transformations or
+ not.
+
+ \sa worldMatrix(), QPaintEngine::hasFeature(),
+*/
+const QMatrix &QPainter::deviceMatrix() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::deviceMatrix: Painter not active");
+ return d->fakeState()->transform.toAffine();
+ }
+ return d->state->matrix.toAffine();
+}
+
+/*!
+ \obsolete
+
+ Resets any transformations that were made using translate(), scale(),
+ shear(), rotate(), setWorldMatrix(), setViewport() and
+ setWindow().
+
+ It is advisable to use resetTransform() instead of this function
+ to preserve the properties of perspective transformations.
+
+ \sa {QPainter#Coordinate Transformations}{Coordinate
+ Transformations}
+*/
+
+void QPainter::resetMatrix()
+{
+ resetTransform();
+}
+
+
+/*!
+ \since 4.2
+
+ Enables transformations if \a enable is true, or disables
+ transformations if \a enable is false. The world transformation
+ matrix is not changed.
+
+ \sa worldMatrixEnabled(), worldTransform(), {QPainter#Coordinate
+ Transformations}{Coordinate Transformations}
+*/
+
+void QPainter::setWorldMatrixEnabled(bool enable)
+{
+ Q_D(QPainter);
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::setMatrixEnabled(), enable=%d\n", enable);
+#endif
+
+ if (!d->engine) {
+ qWarning("QPainter::setMatrixEnabled: Painter not active");
+ return;
+ }
+ if (enable == d->state->WxF)
+ return;
+
+ d->state->WxF = enable;
+ d->updateMatrix();
+}
+
+/*!
+ \since 4.2
+
+ Returns true if world transformation is enabled; otherwise returns
+ false.
+
+ \sa setWorldMatrixEnabled(), worldTransform(), {Coordinate System}
+*/
+
+bool QPainter::worldMatrixEnabled() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::worldMatrixEnabled: Painter not active");
+ return false;
+ }
+ return d->state->WxF;
+}
+
+/*!
+ \obsolete
+
+ Use setWorldMatrixEnabled() instead.
+
+ \sa setWorldMatrixEnabled()
+*/
+
+void QPainter::setMatrixEnabled(bool enable)
+{
+ setWorldMatrixEnabled(enable);
+}
+
+/*!
+ \obsolete
+
+ Use worldMatrixEnabled() instead
+
+ \sa worldMatrixEnabled()
+*/
+
+bool QPainter::matrixEnabled() const
+{
+ return worldMatrixEnabled();
+}
+
+/*!
+ Scales the coordinate system by (\a{sx}, \a{sy}).
+
+ \sa setWorldTransform() {QPainter#Coordinate Transformations}{Coordinate
+ Transformations}
+*/
+
+void QPainter::scale(qreal sx, qreal sy)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::scale(), sx=%f, sy=%f\n", sx, sy);
+#endif
+ Q_D(QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::scale: Painter not active");
+ return;
+ }
+
+ d->state->worldMatrix.scale(sx,sy);
+ d->state->WxF = true;
+ d->updateMatrix();
+}
+
+/*!
+ Shears the coordinate system by (\a{sh}, \a{sv}).
+
+ \sa setWorldTransform(), {QPainter#Coordinate Transformations}{Coordinate
+ Transformations}
+*/
+
+void QPainter::shear(qreal sh, qreal sv)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::shear(), sh=%f, sv=%f\n", sh, sv);
+#endif
+ Q_D(QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::shear: Painter not active");
+ return;
+ }
+
+ d->state->worldMatrix.shear(sh, sv);
+ d->state->WxF = true;
+ d->updateMatrix();
+}
+
+/*!
+ \fn void QPainter::rotate(qreal angle)
+
+ Rotates the coordinate system the given \a angle clockwise.
+
+ \sa setWorldTransform(), {QPainter#Coordinate Transformations}{Coordinate
+ Transformations}
+*/
+
+void QPainter::rotate(qreal a)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::rotate(), angle=%f\n", a);
+#endif
+ Q_D(QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::rotate: Painter not active");
+ return;
+ }
+
+ d->state->worldMatrix.rotate(a);
+ d->state->WxF = true;
+ d->updateMatrix();
+}
+
+/*!
+ Translates the coordinate system by the given \a offset; i.e. the
+ given \a offset is added to points.
+
+ \sa setWorldTransform(), {QPainter#Coordinate Transformations}{Coordinate
+ Transformations}
+*/
+void QPainter::translate(const QPointF &offset)
+{
+ qreal dx = offset.x();
+ qreal dy = offset.y();
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::translate(), dx=%f, dy=%f\n", dx, dy);
+#endif
+ Q_D(QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::translate: Painter not active");
+ return;
+ }
+
+ d->state->worldMatrix.translate(dx, dy);
+ d->state->WxF = true;
+ d->updateMatrix();
+}
+
+/*!
+ \fn void QPainter::translate(const QPoint &offset)
+ \overload
+
+ Translates the coordinate system by the given \a offset.
+*/
+
+/*!
+ \fn void QPainter::translate(qreal dx, qreal dy)
+ \overload
+
+ Translates the coordinate system by the vector (\a dx, \a dy).
+*/
+
+/*!
+ \fn void QPainter::setClipPath(const QPainterPath &path, Qt::ClipOperation operation)
+
+ Enables clipping, and sets the clip path for the painter to the
+ given \a path, with the clip \a operation.
+
+ Note that the clip path is specified in logical (painter)
+ coordinates.
+
+ \sa clipPath(), clipRegion(), {QPainter#Clipping}{Clipping}
+
+*/
+void QPainter::setClipPath(const QPainterPath &path, Qt::ClipOperation op)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output) {
+ QRectF b = path.boundingRect();
+ printf("QPainter::setClipPath(), size=%d, op=%d, bounds=[%.2f,%.2f,%.2f,%.2f]\n",
+ path.elementCount(), op, b.x(), b.y(), b.width(), b.height());
+ }
+#endif
+ Q_D(QPainter);
+
+ if (!d->engine) {
+ qWarning("QPainter::setClipPath: Painter not active");
+ return;
+ }
+
+ if ((!d->state->clipEnabled && op != Qt::NoClip) || (d->state->clipOperation == Qt::NoClip && op == Qt::UniteClip))
+ op = Qt::ReplaceClip;
+
+ if (d->extended) {
+ d->state->clipEnabled = true;
+ d->extended->clip(path, op);
+ if (op == Qt::NoClip || op == Qt::ReplaceClip)
+ d->state->clipInfo.clear();
+ d->state->clipInfo << QPainterClipInfo(path, op, d->state->matrix);
+ d->state->clipOperation = op;
+ return;
+ }
+
+ d->state->clipPath = path;
+ d->state->clipOperation = op;
+ if (op == Qt::NoClip || op == Qt::ReplaceClip)
+ d->state->clipInfo.clear();
+ d->state->clipInfo << QPainterClipInfo(path, op, d->state->matrix);
+ d->state->clipEnabled = true;
+ d->state->dirtyFlags |= QPaintEngine::DirtyClipPath | QPaintEngine::DirtyClipEnabled;
+ d->updateState(d->state);
+}
+
+/*!
+ Draws the outline (strokes) the path \a path with the pen specified
+ by \a pen
+
+ \sa fillPath(), {QPainter#Drawing}{Drawing}
+*/
+void QPainter::strokePath(const QPainterPath &path, const QPen &pen)
+{
+ Q_D(QPainter);
+
+ if (!d->engine) {
+ qWarning("QPainter::strokePath: Painter not active");
+ return;
+ }
+
+ if (path.isEmpty())
+ return;
+
+ if (d->extended) {
+ const QGradient *g = qpen_brush(pen).gradient();
+ if (!g || g->coordinateMode() == QGradient::LogicalMode) {
+ d->extended->stroke(qtVectorPathForPath(path), pen);
+ return;
+ }
+ }
+
+ QBrush oldBrush = d->state->brush;
+ QPen oldPen = d->state->pen;
+
+ setPen(pen);
+ setBrush(Qt::NoBrush);
+
+ drawPath(path);
+
+ // Reset old state
+ setPen(oldPen);
+ setBrush(oldBrush);
+}
+
+/*!
+ Fills the given \a path using the given \a brush. The outline is
+ not drawn.
+
+ Alternatively, you can specify a QColor instead of a QBrush; the
+ QBrush constructor (taking a QColor argument) will automatically
+ create a solid pattern brush.
+
+ \sa drawPath()
+*/
+void QPainter::fillPath(const QPainterPath &path, const QBrush &brush)
+{
+ Q_D(QPainter);
+
+ if (!d->engine) {
+ qWarning("QPainter::fillPath: Painter not active");
+ return;
+ }
+
+ if (path.isEmpty())
+ return;
+
+ if (d->extended) {
+ const QGradient *g = brush.gradient();
+ if (!g || g->coordinateMode() == QGradient::LogicalMode) {
+ d->extended->fill(qtVectorPathForPath(path), brush);
+ return;
+ }
+ }
+
+ QBrush oldBrush = d->state->brush;
+ QPen oldPen = d->state->pen;
+
+ setPen(Qt::NoPen);
+ setBrush(brush);
+
+ drawPath(path);
+
+ // Reset old state
+ setPen(oldPen);
+ setBrush(oldBrush);
+}
+
+/*!
+ Draws the given painter \a path using the current pen for outline
+ and the current brush for filling.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainter-path.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 5
+ \endtable
+
+ \sa {painting/painterpaths}{the Painter Paths
+ example},{demos/deform}{the Vector Deformation demo}
+*/
+void QPainter::drawPath(const QPainterPath &path)
+{
+#ifdef QT_DEBUG_DRAW
+ QRectF pathBounds = path.boundingRect();
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawPath(), size=%d, [%.2f,%.2f,%.2f,%.2f]\n",
+ path.elementCount(),
+ pathBounds.x(), pathBounds.y(), pathBounds.width(), pathBounds.height());
+#endif
+
+ Q_D(QPainter);
+
+ if (!d->engine) {
+ qWarning("QPainter::drawPath: Painter not active");
+ return;
+ }
+
+ if (d->extended) {
+ d->extended->drawPath(path);
+ return;
+ }
+ d->updateState(d->state);
+
+ if (d->engine->hasFeature(QPaintEngine::PainterPaths) && d->state->emulationSpecifier == 0) {
+ d->engine->drawPath(path);
+ } else {
+ d->draw_helper(path);
+ }
+}
+
+/*!
+ \fn void QPainter::drawLine(const QLineF &line)
+
+ Draws a line defined by \a line.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainter-line.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 6
+ \endtable
+
+ \sa drawLines(), drawPolyline(), {Coordinate System}
+*/
+
+/*!
+ \fn void QPainter::drawLine(const QLine &line)
+ \overload
+
+ Draws a line defined by \a line.
+*/
+
+/*!
+ \fn void QPainter::drawLine(const QPoint &p1, const QPoint &p2)
+ \overload
+
+ Draws a line from \a p1 to \a p2.
+*/
+
+/*!
+ \fn void QPainter::drawLine(const QPointF &p1, const QPointF &p2)
+ \overload
+
+ Draws a line from \a p1 to \a p2.
+*/
+
+/*!
+ \fn void QPainter::drawLine(int x1, int y1, int x2, int y2)
+ \overload
+
+ Draws a line from (\a x1, \a y1) to (\a x2, \a y2) and sets the
+ current pen position to (\a x2, \a y2).
+*/
+
+/*!
+ \fn void QPainter::drawRect(const QRectF &rectangle)
+
+ Draws the current \a rectangle with the current pen and brush.
+
+ A filled rectangle has a size of \a{rectangle}.size(). A stroked
+ rectangle has a size of \a{rectangle}.size() plus the pen width.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainter-rectangle.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 7
+ \endtable
+
+ \sa drawRects(), drawPolygon(), {Coordinate System}
+*/
+
+/*!
+ \fn void QPainter::drawRect(const QRect &rectangle)
+
+ \overload
+
+ Draws the current \a rectangle with the current pen and brush.
+*/
+
+/*!
+ \fn void QPainter::drawRect(int x, int y, int width, int height)
+
+ \overload
+
+ Draws a rectangle with upper left corner at (\a{x}, \a{y}) and
+ with the given \a width and \a height.
+*/
+
+/*!
+ \fn void QPainter::drawRects(const QRectF *rectangles, int rectCount)
+
+ Draws the first \a rectCount of the given \a rectangles using the
+ current pen and brush.
+
+ \sa drawRect()
+*/
+void QPainter::drawRects(const QRectF *rects, int rectCount)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawRects(), count=%d\n", rectCount);
+#endif
+ Q_D(QPainter);
+
+ if (!d->engine) {
+ qWarning("QPainter::drawRects: Painter not active");
+ return;
+ }
+
+ if (rectCount <= 0)
+ return;
+
+ if (d->extended) {
+ d->extended->drawRects(rects, rectCount);
+ return;
+ }
+
+ d->updateState(d->state);
+
+ if (!d->state->emulationSpecifier) {
+ d->engine->drawRects(rects, rectCount);
+ return;
+ }
+
+ if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform
+ && d->state->matrix.type() == QTransform::TxTranslate) {
+ for (int i=0; i<rectCount; ++i) {
+ QRectF r(rects[i].x() + d->state->matrix.dx(),
+ rects[i].y() + d->state->matrix.dy(),
+ rects[i].width(),
+ rects[i].height());
+ d->engine->drawRects(&r, 1);
+ }
+ } else {
+ if (d->state->brushNeedsResolving() || d->state->penNeedsResolving()) {
+ for (int i=0; i<rectCount; ++i) {
+ QPainterPath rectPath;
+ rectPath.addRect(rects[i]);
+ d->draw_helper(rectPath, QPainterPrivate::StrokeAndFillDraw);
+ }
+ } else {
+ QPainterPath rectPath;
+ for (int i=0; i<rectCount; ++i)
+ rectPath.addRect(rects[i]);
+ d->draw_helper(rectPath, QPainterPrivate::StrokeAndFillDraw);
+ }
+ }
+}
+
+/*!
+ \fn void QPainter::drawRects(const QRect *rectangles, int rectCount)
+ \overload
+
+ Draws the first \a rectCount of the given \a rectangles using the
+ current pen and brush.
+*/
+void QPainter::drawRects(const QRect *rects, int rectCount)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawRects(), count=%d\n", rectCount);
+#endif
+ Q_D(QPainter);
+
+ if (!d->engine) {
+ qWarning("QPainter::drawRects: Painter not active");
+ return;
+ }
+
+ if (rectCount <= 0)
+ return;
+
+ if (d->extended) {
+ d->extended->drawRects(rects, rectCount);
+ return;
+ }
+
+ d->updateState(d->state);
+
+ if (!d->state->emulationSpecifier) {
+ d->engine->drawRects(rects, rectCount);
+ return;
+ }
+
+ if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform
+ && d->state->matrix.type() == QTransform::TxTranslate) {
+ for (int i=0; i<rectCount; ++i) {
+ QRectF r(rects[i].x() + d->state->matrix.dx(),
+ rects[i].y() + d->state->matrix.dy(),
+ rects[i].width(),
+ rects[i].height());
+
+ d->engine->drawRects(&r, 1);
+ }
+ } else {
+ if (d->state->brushNeedsResolving() || d->state->penNeedsResolving()) {
+ for (int i=0; i<rectCount; ++i) {
+ QPainterPath rectPath;
+ rectPath.addRect(rects[i]);
+ d->draw_helper(rectPath, QPainterPrivate::StrokeAndFillDraw);
+ }
+ } else {
+ QPainterPath rectPath;
+ for (int i=0; i<rectCount; ++i)
+ rectPath.addRect(rects[i]);
+
+ d->draw_helper(rectPath, QPainterPrivate::StrokeAndFillDraw);
+ }
+ }
+}
+
+/*!
+ \fn void QPainter::drawRects(const QVector<QRectF> &rectangles)
+ \overload
+
+ Draws the given \a rectangles using the current pen and brush.
+*/
+
+/*!
+ \fn void QPainter::drawRects(const QVector<QRect> &rectangles)
+
+ \overload
+
+ Draws the given \a rectangles using the current pen and brush.
+*/
+
+/*!
+ \fn void QPainter::drawPoint(const QPointF &position)
+
+ Draws a single point at the given \a position using the current
+ pen's color.
+
+ \sa {Coordinate System}
+*/
+
+/*!
+ \fn void QPainter::drawPoint(const QPoint &position)
+ \overload
+
+ Draws a single point at the given \a position using the current
+ pen's color.
+*/
+
+/*! \fn void QPainter::drawPoint(int x, int y)
+
+ \overload
+
+ Draws a single point at position (\a x, \a y).
+*/
+
+/*!
+ Draws the first \a pointCount points in the array \a points using
+ the current pen's color.
+
+ \sa {Coordinate System}
+*/
+void QPainter::drawPoints(const QPointF *points, int pointCount)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawPoints(), count=%d\n", pointCount);
+#endif
+ Q_D(QPainter);
+
+ if (!d->engine) {
+ qWarning("QPainter::drawPoints: Painter not active");
+ return;
+ }
+
+ if (pointCount <= 0)
+ return;
+
+ if (d->extended) {
+ d->extended->drawPoints(points, pointCount);
+ return;
+ }
+
+ d->updateState(d->state);
+
+ if (!d->state->emulationSpecifier) {
+ d->engine->drawPoints(points, pointCount);
+ return;
+ }
+
+ if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform
+ && d->state->matrix.type() == QTransform::TxTranslate) {
+ // ### use drawPoints function
+ for (int i=0; i<pointCount; ++i) {
+ QPointF pt(points[i].x() + d->state->matrix.dx(),
+ points[i].y() + d->state->matrix.dy());
+ d->engine->drawPoints(&pt, 1);
+ }
+ } else {
+ QPen pen = d->state->pen;
+ bool flat_pen = pen.capStyle() == Qt::FlatCap;
+ if (flat_pen) {
+ save();
+ pen.setCapStyle(Qt::SquareCap);
+ setPen(pen);
+ }
+ QPainterPath path;
+ for (int i=0; i<pointCount; ++i) {
+ path.moveTo(points[i].x(), points[i].y());
+ path.lineTo(points[i].x() + 0.0001, points[i].y());
+ }
+ d->draw_helper(path, QPainterPrivate::StrokeDraw);
+ if (flat_pen)
+ restore();
+ }
+}
+
+/*!
+ \overload
+
+ Draws the first \a pointCount points in the array \a points using
+ the current pen's color.
+*/
+
+void QPainter::drawPoints(const QPoint *points, int pointCount)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawPoints(), count=%d\n", pointCount);
+#endif
+ Q_D(QPainter);
+
+ if (!d->engine) {
+ qWarning("QPainter::drawPoints: Painter not active");
+ return;
+ }
+
+ if (pointCount <= 0)
+ return;
+
+ if (d->extended) {
+ d->extended->drawPoints(points, pointCount);
+ return;
+ }
+
+ d->updateState(d->state);
+
+ if (!d->state->emulationSpecifier) {
+ d->engine->drawPoints(points, pointCount);
+ return;
+ }
+
+ if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform
+ && d->state->matrix.type() == QTransform::TxTranslate) {
+ // ### use drawPoints function
+ for (int i=0; i<pointCount; ++i) {
+ QPointF pt(points[i].x() + d->state->matrix.dx(),
+ points[i].y() + d->state->matrix.dy());
+ d->engine->drawPoints(&pt, 1);
+ }
+ } else {
+ QPen pen = d->state->pen;
+ bool flat_pen = (pen.capStyle() == Qt::FlatCap);
+ if (flat_pen) {
+ save();
+ pen.setCapStyle(Qt::SquareCap);
+ setPen(pen);
+ }
+ QPainterPath path;
+ for (int i=0; i<pointCount; ++i) {
+ path.moveTo(points[i].x(), points[i].y());
+ path.lineTo(points[i].x() + 0.0001, points[i].y());
+ }
+ d->draw_helper(path, QPainterPrivate::StrokeDraw);
+ if (flat_pen)
+ restore();
+ }
+}
+
+/*!
+ \fn void QPainter::drawPoints(const QPolygonF &points)
+
+ \overload
+
+ Draws the points in the vector \a points.
+*/
+
+/*!
+ \fn void QPainter::drawPoints(const QPolygon &points)
+
+ \overload
+
+ Draws the points in the vector \a points.
+*/
+
+/*!
+ \fn void QPainter::drawPoints(const QPolygon &polygon, int index,
+ int count)
+
+ \overload
+ \compat
+
+ Draws \a count points in the vector \a polygon starting on \a index
+ using the current pen.
+
+ Use drawPoints() combined with QPolygon::constData() instead.
+
+ \oldcode
+ QPainter painter(this);
+ painter.drawPoints(polygon, index, count);
+ \newcode
+ int pointCount = (count == -1) ? polygon.size() - index : count;
+
+ QPainter painter(this);
+ painter.drawPoints(polygon.constData() + index, pointCount);
+ \endcode
+*/
+
+/*!
+ Sets the background mode of the painter to the given \a mode
+
+ Qt::TransparentMode (the default) draws stippled lines and text
+ without setting the background pixels. Qt::OpaqueMode fills these
+ space with the current background color.
+
+ Note that in order to draw a bitmap or pixmap transparently, you
+ must use QPixmap::setMask().
+
+ \sa backgroundMode(), setBackground(),
+ {QPainter#Settings}{Settings}
+*/
+
+void QPainter::setBackgroundMode(Qt::BGMode mode)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::setBackgroundMode(), mode=%d\n", mode);
+#endif
+
+ Q_D(QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::setBackgroundMode: Painter not active");
+ return;
+ }
+ if (d->state->bgMode == mode)
+ return;
+
+ d->state->bgMode = mode;
+ if (d->extended) {
+ d->checkEmulation();
+ } else {
+ d->state->dirtyFlags |= QPaintEngine::DirtyBackgroundMode;
+ }
+}
+
+/*!
+ Returns the current background mode.
+
+ \sa setBackgroundMode(), {QPainter#Settings}{Settings}
+*/
+Qt::BGMode QPainter::backgroundMode() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::backgroundMode: Painter not active");
+ return Qt::TransparentMode;
+ }
+ return d->state->bgMode;
+}
+
+
+/*!
+ \overload
+
+ Sets the painter's pen to have style Qt::SolidLine, width 0 and the
+ specified \a color.
+*/
+
+void QPainter::setPen(const QColor &color)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::setPen(), color=%04x\n", color.rgb());
+#endif
+ Q_D(QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::setPen: Painter not active");
+ return;
+ }
+
+ if (d->state->pen.style() == Qt::SolidLine
+ && d->state->pen.widthF() == 0
+ && d->state->pen.isSolid()
+ && d->state->pen.color() == color)
+ return;
+
+ QPen pen(color.isValid() ? color : QColor(Qt::black), 0, Qt::SolidLine);
+
+ d->state->pen = pen;
+ if (d->extended)
+ d->extended->penChanged();
+ else
+ d->state->dirtyFlags |= QPaintEngine::DirtyPen;
+}
+
+/*!
+ Sets the painter's pen to be the given \a pen.
+
+ The \a pen defines how to draw lines and outlines, and it also
+ defines the text color.
+
+ \sa pen(), {QPainter#Settings}{Settings}
+*/
+
+void QPainter::setPen(const QPen &pen)
+{
+
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::setPen(), color=%04x, (brushStyle=%d) style=%d, cap=%d, join=%d\n",
+ pen.color().rgb(), pen.brush().style(), pen.style(), pen.capStyle(), pen.joinStyle());
+#endif
+ Q_D(QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::setPen: Painter not active");
+ return;
+ }
+
+ if (d->state->pen == pen)
+ return;
+
+ d->state->pen = pen;
+
+ if (d->extended) {
+ d->checkEmulation();
+ d->extended->penChanged();
+ return;
+ }
+
+ d->state->dirtyFlags |= QPaintEngine::DirtyPen;
+}
+
+/*!
+ \overload
+
+ Sets the painter's pen to have the given \a style, width 0 and
+ black color.
+*/
+
+void QPainter::setPen(Qt::PenStyle style)
+{
+ Q_D(QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::setPen: Painter not active");
+ return;
+ }
+
+ if (d->state->pen.style() == style
+ && (style == Qt::NoPen || (d->state->pen.widthF() == 0
+ && d->state->pen.isSolid()
+ && d->state->pen.color() == QColor(Qt::black))))
+ return;
+
+ // QPen(Qt::NoPen) is to avoid creating QPenData, including its brush (from the color)
+ // Note that this works well as long as QPen(Qt::NoPen) returns a black, zero-width pen
+ d->state->pen = (style == Qt::NoPen) ? QPen(Qt::NoPen) : QPen(Qt::black, 0, style);
+
+ if (d->extended)
+ d->extended->penChanged();
+ else
+ d->state->dirtyFlags |= QPaintEngine::DirtyPen;
+
+}
+
+/*!
+ Returns the painter's current pen.
+
+ \sa setPen(), {QPainter#Settings}{Settings}
+*/
+
+const QPen &QPainter::pen() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::pen: Painter not active");
+ return d->fakeState()->pen;
+ }
+ return d->state->pen;
+}
+
+
+/*!
+ Sets the painter's brush to the given \a brush.
+
+ The painter's brush defines how shapes are filled.
+
+ \sa brush(), {QPainter#Settings}{Settings}
+*/
+
+void QPainter::setBrush(const QBrush &brush)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::setBrush(), color=%04x, style=%d\n", brush.color().rgb(), brush.style());
+#endif
+ Q_D(QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::setBrush: Painter not active");
+ return;
+ }
+
+ if (d->state->brush.d == brush.d)
+ return;
+
+ if (d->extended) {
+ d->state->brush = brush;
+ d->checkEmulation();
+ d->extended->brushChanged();
+ return;
+ }
+
+ d->state->brush = brush;
+ d->state->dirtyFlags |= QPaintEngine::DirtyBrush;
+}
+
+
+/*!
+ \overload
+
+ Sets the painter's brush to black color and the specified \a
+ style.
+*/
+
+void QPainter::setBrush(Qt::BrushStyle style)
+{
+ Q_D(QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::setBrush: Painter not active");
+ return;
+ }
+ if (d->state->brush.style() == style &&
+ (style == Qt::NoBrush
+ || (style == Qt::SolidPattern && d->state->brush.color() == QColor(0, 0, 0))))
+ return;
+ d->state->brush = QBrush(Qt::black, style);
+ if (d->extended)
+ d->extended->brushChanged();
+ else
+ d->state->dirtyFlags |= QPaintEngine::DirtyBrush;
+}
+
+/*!
+ Returns the painter's current brush.
+
+ \sa QPainter::setBrush(), {QPainter#Settings}{Settings}
+*/
+
+const QBrush &QPainter::brush() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::brush: Painter not active");
+ return d->fakeState()->brush;
+ }
+ return d->state->brush;
+}
+
+/*!
+ \fn void QPainter::setBackground(const QBrush &brush)
+
+ Sets the background brush of the painter to the given \a brush.
+
+ The background brush is the brush that is filled in when drawing
+ opaque text, stippled lines and bitmaps. The background brush has
+ no effect in transparent background mode (which is the default).
+
+ \sa background(), setBackgroundMode(),
+ {QPainter#Settings}{Settings}
+*/
+
+void QPainter::setBackground(const QBrush &bg)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::setBackground(), color=%04x, style=%d\n", bg.color().rgb(), bg.style());
+#endif
+
+ Q_D(QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::setBackground: Painter not active");
+ return;
+ }
+ d->state->bgBrush = bg;
+ if (!d->extended)
+ d->state->dirtyFlags |= QPaintEngine::DirtyBackground;
+}
+
+/*!
+ Sets the painter's font to the given \a font.
+
+ This font is used by subsequent drawText() functions. The text
+ color is the same as the pen color.
+
+ If you set a font that isn't available, Qt finds a close match.
+ font() will return what you set using setFont() and fontInfo() returns the
+ font actually being used (which may be the same).
+
+ \sa font(), drawText(), {QPainter#Settings}{Settings}
+*/
+
+void QPainter::setFont(const QFont &font)
+{
+ Q_D(QPainter);
+
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::setFont(), family=%s, pointSize=%d\n", font.family().toLatin1().constData(), font.pointSize());
+#endif
+
+ if (!d->engine) {
+ qWarning("QPainter::setFont: Painter not active");
+ return;
+ }
+
+ d->state->font = QFont(font.resolve(d->state->deviceFont), device());
+ if (!d->extended)
+ d->state->dirtyFlags |= QPaintEngine::DirtyFont;
+}
+
+/*!
+ Returns the currently set font used for drawing text.
+
+ \sa setFont(), drawText(), {QPainter#Settings}{Settings}
+*/
+const QFont &QPainter::font() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::font: Painter not active");
+ return d->fakeState()->font;
+ }
+ return d->state->font;
+}
+
+/*!
+ \since 4.4
+
+ Draws the given rectangle \a rect with rounded corners.
+
+ The \a xRadius and \a yRadius arguments specify the radii
+ of the ellipses defining the corners of the rounded rectangle.
+ When \a mode is Qt::RelativeSize, \a xRadius and
+ \a yRadius are specified in percentage of half the rectangle's
+ width and height respectively, and should be in the range
+ 0.0 to 100.0.
+
+ A filled rectangle has a size of rect.size(). A stroked rectangle
+ has a size of rect.size() plus the pen width.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainter-roundrect.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 8
+ \endtable
+
+ \sa drawRect(), QPen
+*/
+void QPainter::drawRoundedRect(const QRectF &rect, qreal xRadius, qreal yRadius, Qt::SizeMode mode)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawRoundedRect(), [%.2f,%.2f,%.2f,%.2f]\n", rect.x(), rect.y(), rect.width(), rect.height());
+#endif
+ Q_D(QPainter);
+
+ if (!d->engine)
+ return;
+
+ if (xRadius <= 0 || yRadius <= 0) { // draw normal rectangle
+ drawRect(rect);
+ return;
+ }
+
+ if (d->extended) {
+ d->extended->drawRoundedRect(rect, xRadius, yRadius, mode);
+ return;
+ }
+
+ QPainterPath path;
+ path.addRoundedRect(rect, xRadius, yRadius, mode);
+ drawPath(path);
+}
+
+/*!
+ \fn void QPainter::drawRoundedRect(const QRect &rect, qreal xRadius, qreal yRadius,
+ Qt::SizeMode mode = Qt::AbsoluteSize);
+ \since 4.4
+ \overload
+
+ Draws the given rectangle \a rect with rounded corners.
+*/
+
+/*!
+ \fn void QPainter::drawRoundedRect(int x, int y, int w, int h, qreal xRadius, qreal yRadius,
+ Qt::SizeMode mode = Qt::AbsoluteSize);
+ \since 4.4
+ \overload
+
+ Draws the given rectangle \a x, \a y, \a w, \a h with rounded corners.
+*/
+
+/*!
+ \obsolete
+
+ Draws a rectangle \a r with rounded corners.
+
+ The \a xRnd and \a yRnd arguments specify how rounded the corners
+ should be. 0 is angled corners, 99 is maximum roundedness.
+
+ A filled rectangle has a size of r.size(). A stroked rectangle
+ has a size of r.size() plus the pen width.
+
+ \sa drawRoundedRect()
+*/
+void QPainter::drawRoundRect(const QRectF &r, int xRnd, int yRnd)
+{
+ drawRoundedRect(r, xRnd, yRnd, Qt::RelativeSize);
+}
+
+
+/*!
+ \fn void QPainter::drawRoundRect(const QRect &r, int xRnd = 25, int yRnd = 25)
+
+ \overload
+ \obsolete
+
+ Draws the rectangle \a r with rounded corners.
+*/
+
+/*!
+ \obsolete
+
+ \fn QPainter::drawRoundRect(int x, int y, int w, int h, int xRnd, int yRnd)
+
+ \overload
+
+ Draws the rectangle \a x, \a y, \a w, \a h with rounded corners.
+*/
+
+/*!
+ \fn void QPainter::drawEllipse(const QRectF &rectangle)
+
+ Draws the ellipse defined by the given \a rectangle.
+
+ A filled ellipse has a size of \a{rectangle}.\l
+ {QRect::size()}{size()}. A stroked ellipse has a size of
+ \a{rectangle}.\l {QRect::size()}{size()} plus the pen width.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainter-ellipse.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 9
+ \endtable
+
+ \sa drawPie(), {Coordinate System}
+*/
+void QPainter::drawEllipse(const QRectF &r)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawEllipse(), [%.2f,%.2f,%.2f,%.2f]\n", r.x(), r.y(), r.width(), r.height());
+#endif
+ Q_D(QPainter);
+
+ if (!d->engine)
+ return;
+
+ QRectF rect(r.normalized());
+
+ if (d->extended) {
+ d->extended->drawEllipse(rect);
+ return;
+ }
+
+ d->updateState(d->state);
+ if (d->state->emulationSpecifier) {
+ if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform
+ && d->state->matrix.type() == QTransform::TxTranslate) {
+ rect.translate(QPointF(d->state->matrix.dx(), d->state->matrix.dy()));
+ } else {
+ QPainterPath path;
+ path.addEllipse(rect);
+ d->draw_helper(path, QPainterPrivate::StrokeAndFillDraw);
+ return;
+ }
+ }
+
+ d->engine->drawEllipse(rect);
+}
+
+/*!
+ \fn QPainter::drawEllipse(const QRect &rectangle)
+
+ \overload
+
+ Draws the ellipse defined by the given \a rectangle.
+*/
+void QPainter::drawEllipse(const QRect &r)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawEllipse(), [%d,%d,%d,%d]\n", r.x(), r.y(), r.width(), r.height());
+#endif
+ Q_D(QPainter);
+
+ if (!d->engine)
+ return;
+
+ QRect rect(r.normalized());
+
+ if (d->extended) {
+ d->extended->drawEllipse(rect);
+ return;
+ }
+
+ d->updateState(d->state);
+
+ if (d->state->emulationSpecifier) {
+ if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform
+ && d->state->matrix.type() == QTransform::TxTranslate) {
+ rect.translate(QPoint(qRound(d->state->matrix.dx()), qRound(d->state->matrix.dy())));
+ } else {
+ QPainterPath path;
+ path.addEllipse(rect);
+ d->draw_helper(path, QPainterPrivate::StrokeAndFillDraw);
+ return;
+ }
+ }
+
+ d->engine->drawEllipse(rect);
+}
+
+/*!
+ \fn QPainter::drawEllipse(int x, int y, int width, int height)
+
+ \overload
+
+ Draws the ellipse defined by the rectangle beginning at (\a{x},
+ \a{y}) with the given \a width and \a height.
+*/
+
+/*!
+ \since 4.4
+
+ \fn QPainter::drawEllipse(const QPointF &center, qreal rx, qreal ry)
+
+ \overload
+
+ Draws the ellipse positioned at \a{center} with radii \a{rx} and \a{ry}.
+*/
+
+/*!
+ \since 4.4
+
+ \fn QPainter::drawEllipse(const QPoint &center, int rx, int ry)
+
+ \overload
+
+ Draws the ellipse positioned at \a{center} with radii \a{rx} and \a{ry}.
+*/
+
+/*!
+ \fn void QPainter::drawArc(const QRectF &rectangle, int startAngle, int spanAngle)
+
+ Draws the arc defined by the given \a rectangle, \a startAngle and
+ \a spanAngle.
+
+ The \a startAngle and \a spanAngle must be specified in 1/16th of
+ a degree, i.e. a full circle equals 5760 (16 * 360). Positive
+ values for the angles mean counter-clockwise while negative values
+ mean the clockwise direction. Zero degrees is at the 3 o'clock
+ position.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainter-arc.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 10
+ \endtable
+
+ \sa drawPie(), drawChord(), {Coordinate System}
+*/
+
+void QPainter::drawArc(const QRectF &r, int a, int alen)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawArc(), [%.2f,%.2f,%.2f,%.2f], angle=%d, sweep=%d\n",
+ r.x(), r.y(), r.width(), r.height(), a/16, alen/16);
+#endif
+ Q_D(QPainter);
+
+ if (!d->engine)
+ return;
+
+ QRectF rect = r.normalized();
+
+ QPainterPath path;
+ path.arcMoveTo(rect, a/16.0);
+ path.arcTo(rect, a/16.0, alen/16.0);
+ strokePath(path, d->state->pen);
+}
+
+/*! \fn void QPainter::drawArc(const QRect &rectangle, int startAngle,
+ int spanAngle)
+
+ \overload
+
+ Draws the arc defined by the given \a rectangle, \a startAngle and
+ \a spanAngle.
+*/
+
+/*!
+ \fn void QPainter::drawArc(int x, int y, int width, int height,
+ int startAngle, int spanAngle)
+
+ \overload
+
+ Draws the arc defined by the rectangle beginning at (\a x, \a y)
+ with the specified \a width and \a height, and the given \a
+ startAngle and \a spanAngle.
+*/
+
+/*!
+ \fn void QPainter::drawPie(const QRectF &rectangle, int startAngle, int spanAngle)
+
+ Draws a pie defined by the given \a rectangle, \a startAngle and
+ and \a spanAngle.
+
+ The pie is filled with the current brush().
+
+ The startAngle and spanAngle must be specified in 1/16th of a
+ degree, i.e. a full circle equals 5760 (16 * 360). Positive values
+ for the angles mean counter-clockwise while negative values mean
+ the clockwise direction. Zero degrees is at the 3 o'clock
+ position.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainter-pie.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 11
+ \endtable
+
+ \sa drawEllipse(), drawChord(), {Coordinate System}
+*/
+void QPainter::drawPie(const QRectF &r, int a, int alen)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawPie(), [%.2f,%.2f,%.2f,%.2f], angle=%d, sweep=%d\n",
+ r.x(), r.y(), r.width(), r.height(), a/16, alen/16);
+#endif
+ Q_D(QPainter);
+
+ if (!d->engine)
+ return;
+
+ if (a > (360*16)) {
+ a = a % (360*16);
+ } else if (a < 0) {
+ a = a % (360*16);
+ if (a < 0) a += (360*16);
+ }
+
+ QRectF rect = r.normalized();
+
+ QPainterPath path;
+ path.moveTo(rect.center());
+ path.arcTo(rect.x(), rect.y(), rect.width(), rect.height(), a/16.0, alen/16.0);
+ path.closeSubpath();
+ drawPath(path);
+
+}
+
+/*!
+ \fn void QPainter::drawPie(const QRect &rectangle, int startAngle, int spanAngle)
+ \overload
+
+ Draws a pie defined by the given \a rectangle, \a startAngle and
+ and \a spanAngle.
+*/
+
+/*!
+ \fn void QPainter::drawPie(int x, int y, int width, int height, int
+ startAngle, int spanAngle)
+
+ \overload
+
+ Draws the pie defined by the rectangle beginning at (\a x, \a y) with
+ the specified \a width and \a height, and the given \a startAngle and
+ \a spanAngle.
+*/
+
+/*!
+ \fn void QPainter::drawChord(const QRectF &rectangle, int startAngle, int spanAngle)
+
+ Draws the chord defined by the given \a rectangle, \a startAngle and
+ \a spanAngle. The chord is filled with the current brush().
+
+ The startAngle and spanAngle must be specified in 1/16th of a
+ degree, i.e. a full circle equals 5760 (16 * 360). Positive values
+ for the angles mean counter-clockwise while negative values mean
+ the clockwise direction. Zero degrees is at the 3 o'clock
+ position.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainter-chord.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 12
+ \endtable
+
+ \sa drawArc(), drawPie(), {Coordinate System}
+*/
+void QPainter::drawChord(const QRectF &r, int a, int alen)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawChord(), [%.2f,%.2f,%.2f,%.2f], angle=%d, sweep=%d\n",
+ r.x(), r.y(), r.width(), r.height(), a/16, alen/16);
+#endif
+ Q_D(QPainter);
+
+ if (!d->engine)
+ return;
+
+ QRectF rect = r.normalized();
+
+ QPainterPath path;
+ path.arcMoveTo(rect, a/16.0);
+ path.arcTo(rect, a/16.0, alen/16.0);
+ path.closeSubpath();
+ drawPath(path);
+}
+/*!
+ \fn void QPainter::drawChord(const QRect &rectangle, int startAngle, int spanAngle)
+
+ \overload
+
+ Draws the chord defined by the given \a rectangle, \a startAngle and
+ \a spanAngle.
+*/
+
+/*!
+ \fn void QPainter::drawChord(int x, int y, int width, int height, int
+ startAngle, int spanAngle)
+
+ \overload
+
+ Draws the chord defined by the rectangle beginning at (\a x, \a y)
+ with the specified \a width and \a height, and the given \a
+ startAngle and \a spanAngle.
+*/
+
+#ifdef QT3_SUPPORT
+/*!
+ \fn void QPainter::drawLineSegments(const QPolygon &polygon, int
+ index, int count)
+
+ Draws \a count separate lines from points defined by the \a
+ polygon, starting at \a{polygon}\e{[index]} (\a index defaults to
+ 0). If \a count is -1 (the default) all points until the end of
+ the array are used.
+
+ Use drawLines() combined with QPolygon::constData() instead.
+
+ \oldcode
+ QPainter painter(this);
+ painter.drawLineSegments(polygon, index, count);
+ \newcode
+ int lineCount = (count == -1) ? (polygon.size() - index) / 2 : count;
+
+ QPainter painter(this);
+ painter.drawLines(polygon.constData() + index * 2, lineCount);
+ \endcode
+*/
+
+void QPainter::drawLineSegments(const QPolygon &a, int index, int nlines)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawLineSegments(), count=%d\n", a.size()/2);
+#endif
+ Q_D(QPainter);
+
+ if (!d->engine)
+ return;
+
+ if (nlines < 0)
+ nlines = a.size()/2 - index/2;
+ if (index + nlines*2 > (int)a.size())
+ nlines = (a.size() - index)/2;
+ if (nlines < 1 || index < 0)
+ return;
+
+ if (d->extended) {
+ // FALCON: Use QVectorPath
+ QVector<QLineF> lines;
+ for (int i=index; i<index + nlines*2; i+=2)
+ lines << QLineF(a.at(i), a.at(i+1));
+ d->extended->drawLines(lines.data(), lines.size());
+ return;
+ }
+
+ d->updateState(d->state);
+
+ QVector<QLineF> lines;
+ if (d->state->emulationSpecifier) {
+ if (d->state->emulationSpecifier == QPaintEngine::PrimitiveTransform
+ && d->state->matrix.type() == QTransform::TxTranslate) {
+ QPointF offset(d->state->matrix.dx(), d->state->matrix.dy());
+ for (int i=index; i<index + nlines*2; i+=2)
+ lines << QLineF(a.at(i) + offset, a.at(i+1) + offset);
+ } else {
+ QPainterPath linesPath;
+ for (int i=index; i<index + nlines*2; i+=2) {
+ linesPath.moveTo(a.at(i));
+ linesPath.lineTo(a.at(i+1));
+ }
+ d->draw_helper(linesPath, QPainterPrivate::StrokeDraw);
+ return;
+ }
+ } else {
+ for (int i=index; i<index + nlines*2; i+=2)
+ lines << QLineF(a.at(i), a.at(i+1));
+ }
+
+ d->engine->drawLines(lines.data(), lines.size());
+}
+#endif // QT3_SUPPORT
+
+/*!
+ Draws the first \a lineCount lines in the array \a lines
+ using the current pen.
+
+ \sa drawLine(), drawPolyline()
+*/
+void QPainter::drawLines(const QLineF *lines, int lineCount)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawLines(), line count=%d\n", lineCount);
+#endif
+
+ Q_D(QPainter);
+
+ if (!d->engine || lineCount < 1)
+ return;
+
+ if (d->extended) {
+ d->extended->drawLines(lines, lineCount);
+ return;
+ }
+
+ d->updateState(d->state);
+
+ uint lineEmulation = line_emulation(d->state->emulationSpecifier);
+
+ if (lineEmulation) {
+ if (lineEmulation == QPaintEngine::PrimitiveTransform
+ && d->state->matrix.type() == QTransform::TxTranslate) {
+ for (int i = 0; i < lineCount; ++i) {
+ QLineF line = lines[i];
+ line.translate(d->state->matrix.dx(), d->state->matrix.dy());
+ d->engine->drawLines(&line, 1);
+ }
+ } else {
+ QPainterPath linePath;
+ for (int i = 0; i < lineCount; ++i) {
+ linePath.moveTo(lines[i].p1());
+ linePath.lineTo(lines[i].p2());
+ }
+ d->draw_helper(linePath, QPainterPrivate::StrokeDraw);
+ }
+ return;
+ }
+ d->engine->drawLines(lines, lineCount);
+}
+
+/*!
+ \fn void QPainter::drawLines(const QLine *lines, int lineCount)
+ \overload
+
+ Draws the first \a lineCount lines in the array \a lines
+ using the current pen.
+*/
+void QPainter::drawLines(const QLine *lines, int lineCount)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawLine(), line count=%d\n", lineCount);
+#endif
+
+ Q_D(QPainter);
+
+ if (!d->engine || lineCount < 1)
+ return;
+
+ if (d->extended) {
+ d->extended->drawLines(lines, lineCount);
+ return;
+ }
+
+ d->updateState(d->state);
+
+ uint lineEmulation = line_emulation(d->state->emulationSpecifier);
+
+ if (lineEmulation) {
+ if (lineEmulation == QPaintEngine::PrimitiveTransform
+ && d->state->matrix.type() == QTransform::TxTranslate) {
+ for (int i = 0; i < lineCount; ++i) {
+ QLineF line = lines[i];
+ line.translate(d->state->matrix.dx(), d->state->matrix.dy());
+ d->engine->drawLines(&line, 1);
+ }
+ } else {
+ QPainterPath linePath;
+ for (int i = 0; i < lineCount; ++i) {
+ linePath.moveTo(lines[i].p1());
+ linePath.lineTo(lines[i].p2());
+ }
+ d->draw_helper(linePath, QPainterPrivate::StrokeDraw);
+ }
+ return;
+ }
+ d->engine->drawLines(lines, lineCount);
+}
+
+/*!
+ \overload
+
+ Draws the first \a lineCount lines in the array \a pointPairs
+ using the current pen. The lines are specified as pairs of points
+ so the number of entries in \a pointPairs must be at least \a
+ lineCount * 2.
+*/
+void QPainter::drawLines(const QPointF *pointPairs, int lineCount)
+{
+ Q_ASSERT(sizeof(QLineF) == 2*sizeof(QPointF));
+
+ drawLines((QLineF*)pointPairs, lineCount);
+}
+
+/*!
+ \overload
+
+ Draws the first \a lineCount lines in the array \a pointPairs
+ using the current pen.
+*/
+void QPainter::drawLines(const QPoint *pointPairs, int lineCount)
+{
+ Q_ASSERT(sizeof(QLine) == 2*sizeof(QPoint));
+
+ drawLines((QLine*)pointPairs, lineCount);
+}
+
+
+/*!
+ \fn void QPainter::drawLines(const QVector<QPointF> &pointPairs)
+ \overload
+
+ Draws a line for each pair of points in the vector \a pointPairs
+ using the current pen. If there is an odd number of points in the
+ array, the last point will be ignored.
+*/
+
+/*!
+ \fn void QPainter::drawLines(const QVector<QPoint> &pointPairs)
+ \overload
+
+ Draws a line for each pair of points in the vector \a pointPairs
+ using the current pen.
+*/
+
+/*!
+ \fn void QPainter::drawLines(const QVector<QLineF> &lines)
+ \overload
+
+ Draws the set of lines defined by the list \a lines using the
+ current pen and brush.
+*/
+
+/*!
+ \fn void QPainter::drawLines(const QVector<QLine> &lines)
+ \overload
+
+ Draws the set of lines defined by the list \a lines using the
+ current pen and brush.
+*/
+
+/*!
+ Draws the polyline defined by the first \a pointCount points in \a
+ points using the current pen.
+
+ Note that unlike the drawPolygon() function the last point is \e
+ not connected to the first, neither is the polyline filled.
+
+ \table 100%
+ \row
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 13
+ \endtable
+
+ \sa drawLines(), drawPolygon(), {Coordinate System}
+*/
+void QPainter::drawPolyline(const QPointF *points, int pointCount)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawPolyline(), count=%d\n", pointCount);
+#endif
+ Q_D(QPainter);
+
+ if (!d->engine || pointCount < 2)
+ return;
+
+ if (d->extended) {
+ d->extended->drawPolygon(points, pointCount, QPaintEngine::PolylineMode);
+ return;
+ }
+
+ d->updateState(d->state);
+
+ uint lineEmulation = line_emulation(d->state->emulationSpecifier);
+
+ if (lineEmulation) {
+ // ###
+// if (lineEmulation == QPaintEngine::PrimitiveTransform
+// && d->state->matrix.type() == QTransform::TxTranslate) {
+// } else {
+ QPainterPath polylinePath(points[0]);
+ for (int i=1; i<pointCount; ++i)
+ polylinePath.lineTo(points[i]);
+ d->draw_helper(polylinePath, QPainterPrivate::StrokeDraw);
+// }
+ } else {
+ d->engine->drawPolygon(points, pointCount, QPaintEngine::PolylineMode);
+ }
+}
+
+/*!
+ \overload
+
+ Draws the polyline defined by the first \a pointCount points in \a
+ points using the current pen.
+ */
+void QPainter::drawPolyline(const QPoint *points, int pointCount)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawPolyline(), count=%d\n", pointCount);
+#endif
+ Q_D(QPainter);
+
+ if (!d->engine || pointCount < 2)
+ return;
+
+ if (d->extended) {
+ d->extended->drawPolygon(points, pointCount, QPaintEngine::PolylineMode);
+ return;
+ }
+
+ d->updateState(d->state);
+
+ uint lineEmulation = line_emulation(d->state->emulationSpecifier);
+
+ if (lineEmulation) {
+ // ###
+// if (lineEmulation == QPaintEngine::PrimitiveTransform
+// && d->state->matrix.type() == QTransform::TxTranslate) {
+// } else {
+ QPainterPath polylinePath(points[0]);
+ for (int i=1; i<pointCount; ++i)
+ polylinePath.lineTo(points[i]);
+ d->draw_helper(polylinePath, QPainterPrivate::StrokeDraw);
+// }
+ } else {
+ d->engine->drawPolygon(points, pointCount, QPaintEngine::PolylineMode);
+ }
+}
+
+/*!
+ \fn void QPainter::drawPolyline(const QPolygon &polygon, int index, int
+ count)
+
+ \overload
+ \compat
+
+ Draws the polyline defined by the \a count lines of the given \a
+ polygon starting at \a index (\a index defaults to 0).
+
+ Use drawPolyline() combined with QPolygon::constData() instead.
+
+ \oldcode
+ QPainter painter(this);
+ painter.drawPolyline(polygon, index, count);
+ \newcode
+ int pointCount = (count == -1) ? polygon.size() - index : count;
+
+ QPainter painter(this);
+ painter.drawPolyline(polygon.constData() + index, pointCount);
+ \endcode
+*/
+
+/*!
+ \fn void QPainter::drawPolyline(const QPolygonF &points)
+
+ \overload
+
+ Draws the polyline defined by the given \a points using the
+ current pen.
+*/
+
+/*!
+ \fn void QPainter::drawPolyline(const QPolygon &points)
+
+ \overload
+
+ Draws the polyline defined by the given \a points using the
+ current pen.
+*/
+
+/*!
+ Draws the polygon defined by the first \a pointCount points in the
+ array \a points using the current pen and brush.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainter-polygon.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 14
+ \endtable
+
+ The first point is implicitly connected to the last point, and the
+ polygon is filled with the current brush().
+
+ If \a fillRule is Qt::WindingFill, the polygon is filled using the
+ winding fill algorithm. If \a fillRule is Qt::OddEvenFill, the
+ polygon is filled using the odd-even fill algorithm. See
+ \l{Qt::FillRule} for a more detailed description of these fill
+ rules.
+
+ \sa drawConvexPolygon(), drawPolyline(), {Coordinate System}
+*/
+void QPainter::drawPolygon(const QPointF *points, int pointCount, Qt::FillRule fillRule)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawPolygon(), count=%d\n", pointCount);
+#endif
+
+ Q_D(QPainter);
+
+ if (!d->engine || pointCount < 2)
+ return;
+
+ if (d->extended) {
+ d->extended->drawPolygon(points, pointCount, QPaintEngine::PolygonDrawMode(fillRule));
+ return;
+ }
+
+ d->updateState(d->state);
+
+ uint emulationSpecifier = d->state->emulationSpecifier;
+
+ if (emulationSpecifier) {
+ QPainterPath polygonPath(points[0]);
+ for (int i=1; i<pointCount; ++i)
+ polygonPath.lineTo(points[i]);
+ polygonPath.closeSubpath();
+ polygonPath.setFillRule(fillRule);
+ d->draw_helper(polygonPath);
+ return;
+ }
+
+ d->engine->drawPolygon(points, pointCount, QPaintEngine::PolygonDrawMode(fillRule));
+}
+
+/*! \overload
+
+ Draws the polygon defined by the first \a pointCount points in the
+ array \a points.
+*/
+void QPainter::drawPolygon(const QPoint *points, int pointCount, Qt::FillRule fillRule)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawPolygon(), count=%d\n", pointCount);
+#endif
+
+ Q_D(QPainter);
+
+ if (!d->engine || pointCount < 2)
+ return;
+
+ if (d->extended) {
+ d->extended->drawPolygon(points, pointCount, QPaintEngine::PolygonDrawMode(fillRule));
+ return;
+ }
+
+ d->updateState(d->state);
+
+ uint emulationSpecifier = d->state->emulationSpecifier;
+
+ if (emulationSpecifier) {
+ QPainterPath polygonPath(points[0]);
+ for (int i=1; i<pointCount; ++i)
+ polygonPath.lineTo(points[i]);
+ polygonPath.closeSubpath();
+ polygonPath.setFillRule(fillRule);
+ d->draw_helper(polygonPath);
+ return;
+ }
+
+ d->engine->drawPolygon(points, pointCount, QPaintEngine::PolygonDrawMode(fillRule));
+}
+
+/*! \fn void QPainter::drawPolygon(const QPolygonF &polygon, bool winding, int index = 0,
+ int count = -1)
+ \compat
+ \overload
+
+ Use drawPolygon() combined with QPolygonF::constData() instead.
+
+ \oldcode
+ QPainter painter(this);
+ painter.drawPolygon(polygon, winding, index, count);
+ \newcode
+ int pointCount = (count == -1) ? polygon.size() - index : count;
+ int fillRule = winding ? Qt::WindingFill : Qt::OddEvenFill;
+
+ QPainter painter(this);
+ painter.drawPolygon( polygon.constData() + index, pointCount, fillRule);
+ \endcode
+*/
+
+/*! \fn void QPainter::drawPolygon(const QPolygon &polygon, bool winding,
+ int index = 0, int count = -1)
+
+ \compat
+ \overload
+
+ Use drawPolygon() combined with QPolygon::constData() instead.
+
+ \oldcode
+ QPainter painter(this);
+ painter.drawPolygon(polygon, winding, index, count);
+ \newcode
+ int pointCount = (count == -1) ? polygon.size() - index : count;
+ int fillRule = winding ? Qt::WindingFill : Qt::OddEvenFill;
+
+ QPainter painter(this);
+ painter.drawPolygon( polygon.constData() + index, pointCount, fillRule);
+ \endcode
+*/
+
+/*! \fn void QPainter::drawPolygon(const QPolygonF &points, Qt::FillRule fillRule)
+
+ \overload
+
+ Draws the polygon defined by the given \a points using the fill
+ rule \a fillRule.
+*/
+
+/*! \fn void QPainter::drawPolygon(const QPolygon &points, Qt::FillRule fillRule)
+
+ \overload
+
+ Draws the polygon defined by the given \a points using the fill
+ rule \a fillRule.
+*/
+
+/*!
+ \fn void QPainter::drawConvexPolygon(const QPointF *points, int pointCount)
+
+ Draws the convex polygon defined by the first \a pointCount points
+ in the array \a points using the current pen.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainter-polygon.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 15
+ \endtable
+
+ The first point is implicitly connected to the last point, and the
+ polygon is filled with the current brush(). If the supplied
+ polygon is not convex, i.e. it contains at least one angle larger
+ than 180 degrees, the results are undefined.
+
+ On some platforms (e.g. X11), the drawConvexPolygon() function can
+ be faster than the drawPolygon() function.
+
+ \sa drawPolygon(), drawPolyline(), {Coordinate System}
+*/
+
+/*!
+ \fn void QPainter::drawConvexPolygon(const QPoint *points, int pointCount)
+ \overload
+
+ Draws the convex polygon defined by the first \a pointCount points
+ in the array \a points using the current pen.
+*/
+
+/*!
+ \fn void QPainter::drawConvexPolygon(const QPolygonF &polygon)
+
+ \overload
+
+ Draws the convex polygon defined by \a polygon using the current
+ pen and brush.
+*/
+
+/*!
+ \fn void QPainter::drawConvexPolygon(const QPolygon &polygon)
+ \overload
+
+ Draws the convex polygon defined by \a polygon using the current
+ pen and brush.
+*/
+
+/*!
+ \fn void QPainter::drawConvexPolygon(const QPolygonF &polygon, int
+ index, int count)
+
+ \compat
+ \overload
+
+ Use drawConvexPolygon() combined with QPolygonF::constData()
+ instead.
+
+ \oldcode
+ QPainter painter(this);
+ painter.drawConvexPolygon(polygon, index, count);
+ \newcode
+ int pointCount = (count == -1) ? polygon.size() - index : count;
+
+ QPainter painter(this);
+ painter.drawConvexPolygon(polygon.constData() + index, pointCount);
+ \endcode
+*/
+
+/*!
+ \fn void QPainter::drawConvexPolygon(const QPolygon &polygon, int
+ index, int count)
+
+ \compat
+ \overload
+
+ Use drawConvexPolygon() combined with QPolygon::constData()
+ instead.
+
+ \oldcode
+ QPainter painter(this);
+ painter.drawConvexPolygon(polygon, index, count);
+ \newcode
+ int pointCount = (count == -1) ? polygon.size() - index : count;
+
+ QPainter painter(this);
+ painter.drawConvexPolygon(polygon.constData() + index, pointCount);
+ \endcode
+*/
+
+void QPainter::drawConvexPolygon(const QPoint *points, int pointCount)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawConvexPolygon(), count=%d\n", pointCount);
+#endif
+
+ Q_D(QPainter);
+
+ if (!d->engine || pointCount < 2)
+ return;
+
+ if (d->extended) {
+ d->extended->drawPolygon(points, pointCount, QPaintEngine::ConvexMode);
+ return;
+ }
+
+ d->updateState(d->state);
+
+ uint emulationSpecifier = d->state->emulationSpecifier;
+
+ if (emulationSpecifier) {
+ QPainterPath polygonPath(points[0]);
+ for (int i=1; i<pointCount; ++i)
+ polygonPath.lineTo(points[i]);
+ polygonPath.closeSubpath();
+ polygonPath.setFillRule(Qt::WindingFill);
+ d->draw_helper(polygonPath);
+ return;
+ }
+
+ d->engine->drawPolygon(points, pointCount, QPaintEngine::ConvexMode);
+}
+
+void QPainter::drawConvexPolygon(const QPointF *points, int pointCount)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawConvexPolygon(), count=%d\n", pointCount);
+#endif
+
+ Q_D(QPainter);
+
+ if (!d->engine || pointCount < 2)
+ return;
+
+ if (d->extended) {
+ d->extended->drawPolygon(points, pointCount, QPaintEngine::ConvexMode);
+ return;
+ }
+
+ d->updateState(d->state);
+
+ uint emulationSpecifier = d->state->emulationSpecifier;
+
+ if (emulationSpecifier) {
+ QPainterPath polygonPath(points[0]);
+ for (int i=1; i<pointCount; ++i)
+ polygonPath.lineTo(points[i]);
+ polygonPath.closeSubpath();
+ polygonPath.setFillRule(Qt::WindingFill);
+ d->draw_helper(polygonPath);
+ return;
+ }
+
+ d->engine->drawPolygon(points, pointCount, QPaintEngine::ConvexMode);
+}
+
+static inline QPointF roundInDeviceCoordinates(const QPointF &p, const QTransform &m)
+{
+ return m.inverted().map(QPointF(m.map(p).toPoint()));
+}
+
+/*!
+ \fn void QPainter::drawPixmap(const QRectF &target, const QPixmap &pixmap, const QRectF &source)
+
+ Draws the rectangular portion \a source of the given \a pixmap
+ into the given \a target in the paint device.
+
+ \note The pixmap is scaled to fit the rectangle, if both the pixmap and rectangle size disagree.
+
+ \table 100%
+ \row
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 16
+ \endtable
+
+ If \a pixmap is a QBitmap it is drawn with the bits that are "set"
+ using the pens color. If backgroundMode is Qt::OpaqueMode, the
+ "unset" bits are drawn using the color of the background brush; if
+ backgroundMode is Qt::TransparentMode, the "unset" bits are
+ transparent. Drawing bitmaps with gradient or texture colors is
+ not supported.
+
+ \sa drawImage()
+*/
+void QPainter::drawPixmap(const QPointF &p, const QPixmap &pm)
+{
+#if defined QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawPixmap(), p=[%.2f,%.2f], pix=[%d,%d]\n",
+ p.x(), p.y(),
+ pm.width(), pm.height());
+#endif
+
+ Q_D(QPainter);
+
+ if (!d->engine || pm.isNull())
+ return;
+
+#ifndef QT_NO_DEBUG
+ qt_painter_thread_test(d->device->devType(), "drawPixmap()", true);
+#endif
+
+ if (d->extended) {
+ d->extended->drawPixmap(p, pm);
+ return;
+ }
+
+ qreal x = p.x();
+ qreal y = p.y();
+
+ int w = pm.width();
+ int h = pm.height();
+
+ if (w <= 0)
+ return;
+
+ // Emulate opaque background for bitmaps
+ if (d->state->bgMode == Qt::OpaqueMode && pm.isQBitmap()) {
+ fillRect(QRectF(x, y, w, h), d->state->bgBrush.color());
+ }
+
+ d->updateState(d->state);
+
+ if ((d->state->matrix.type() > QTransform::TxTranslate
+ && !d->engine->hasFeature(QPaintEngine::PixmapTransform))
+ || (!d->state->matrix.isAffine() && !d->engine->hasFeature(QPaintEngine::PerspectiveTransform))
+ || (d->state->opacity != 1.0 && !d->engine->hasFeature(QPaintEngine::ConstantOpacity)))
+ {
+ save();
+ // If there is no rotation involved we have to make sure we use the
+ // antialiased and not the aliased coordinate system by rounding the coordinates.
+ if (d->state->matrix.type() <= QTransform::TxScale) {
+ const QPointF p = roundInDeviceCoordinates(QPointF(x, y), d->state->matrix);
+ x = p.x();
+ y = p.y();
+ }
+ translate(x, y);
+ setBackgroundMode(Qt::TransparentMode);
+ setRenderHint(Antialiasing, renderHints() & SmoothPixmapTransform);
+ QBrush brush(d->state->pen.color(), pm);
+ setBrush(brush);
+ setPen(Qt::NoPen);
+ setBrushOrigin(QPointF(0, 0));
+
+ drawRect(pm.rect());
+ restore();
+ } else {
+ if (!d->engine->hasFeature(QPaintEngine::PixmapTransform)) {
+ x += d->state->matrix.dx();
+ y += d->state->matrix.dy();
+ }
+ d->engine->drawPixmap(QRectF(x, y, w, h), pm, QRectF(0, 0, w, h));
+ }
+}
+
+void QPainter::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
+{
+#if defined QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawPixmap(), target=[%.2f,%.2f,%.2f,%.2f], pix=[%d,%d], source=[%.2f,%.2f,%.2f,%.2f]\n",
+ r.x(), r.y(), r.width(), r.height(),
+ pm.width(), pm.height(),
+ sr.x(), sr.y(), sr.width(), sr.height());
+#endif
+
+ Q_D(QPainter);
+ if (!d->engine || pm.isNull())
+ return;
+#ifndef QT_NO_DEBUG
+ qt_painter_thread_test(d->device->devType(), "drawPixmap()", true);
+#endif
+
+ qreal x = r.x();
+ qreal y = r.y();
+ qreal w = r.width();
+ qreal h = r.height();
+ qreal sx = sr.x();
+ qreal sy = sr.y();
+ qreal sw = sr.width();
+ qreal sh = sr.height();
+
+ // Sanity-check clipping
+ if (sw <= 0)
+ sw = pm.width() - sx;
+
+ if (sh <= 0)
+ sh = pm.height() - sy;
+
+ if (w < 0)
+ w = sw;
+ if (h < 0)
+ h = sh;
+
+ if (sx < 0) {
+ qreal w_ratio = sx * w/sw;
+ x -= w_ratio;
+ w += w_ratio;
+ sw += sx;
+ sx = 0;
+ }
+
+ if (sy < 0) {
+ qreal h_ratio = sy * h/sh;
+ y -= h_ratio;
+ h += h_ratio;
+ sh += sy;
+ sy = 0;
+ }
+
+ if (sw + sx > pm.width()) {
+ qreal delta = sw - (pm.width() - sx);
+ qreal w_ratio = delta * w/sw;
+ sw -= delta;
+ w -= w_ratio;
+ }
+
+ if (sh + sy > pm.height()) {
+ qreal delta = sh - (pm.height() - sy);
+ qreal h_ratio = delta * h/sh;
+ sh -= delta;
+ h -= h_ratio;
+ }
+
+ if (w == 0 || h == 0 || sw <= 0 || sh <= 0)
+ return;
+
+ if (d->extended) {
+ d->extended->drawPixmap(QRectF(x, y, w, h), pm, QRectF(sx, sy, sw, sh));
+ return;
+ }
+
+ // Emulate opaque background for bitmaps
+ if (d->state->bgMode == Qt::OpaqueMode && pm.isQBitmap())
+ fillRect(QRectF(x, y, w, h), d->state->bgBrush.color());
+
+ d->updateState(d->state);
+
+ if ((d->state->matrix.type() > QTransform::TxTranslate
+ && !d->engine->hasFeature(QPaintEngine::PixmapTransform))
+ || (!d->state->matrix.isAffine() && !d->engine->hasFeature(QPaintEngine::PerspectiveTransform))
+ || (d->state->opacity != 1.0 && !d->engine->hasFeature(QPaintEngine::ConstantOpacity))
+ || ((sw != w || sh != h) && !d->engine->hasFeature(QPaintEngine::PixmapTransform)))
+ {
+ save();
+ // If there is no rotation involved we have to make sure we use the
+ // antialiased and not the aliased coordinate system by rounding the coordinates.
+ if (d->state->matrix.type() <= QTransform::TxScale) {
+ const QPointF p = roundInDeviceCoordinates(QPointF(x, y), d->state->matrix);
+ x = p.x();
+ y = p.y();
+ }
+
+ if (d->state->matrix.type() <= QTransform::TxTranslate && sw == w && sh == h) {
+ sx = qRound(sx);
+ sy = qRound(sy);
+ sw = qRound(sw);
+ sh = qRound(sh);
+ }
+
+ translate(x, y);
+ scale(w / sw, h / sh);
+ setBackgroundMode(Qt::TransparentMode);
+ setRenderHint(Antialiasing, renderHints() & SmoothPixmapTransform);
+ QBrush brush;
+
+ if (sw == pm.width() && sh == pm.height())
+ brush = QBrush(d->state->pen.color(), pm);
+ else
+ brush = QBrush(d->state->pen.color(), pm.copy(sx, sy, sw, sh));
+
+ setBrush(brush);
+ setPen(Qt::NoPen);
+
+ drawRect(QRectF(0, 0, sw, sh));
+ restore();
+ } else {
+ if (!d->engine->hasFeature(QPaintEngine::PixmapTransform)) {
+ x += d->state->matrix.dx();
+ y += d->state->matrix.dy();
+ }
+ d->engine->drawPixmap(QRectF(x, y, w, h), pm, QRectF(sx, sy, sw, sh));
+ }
+}
+
+
+/*!
+ \fn void QPainter::drawPixmap(const QRect &target, const QPixmap &pixmap,
+ const QRect &source)
+ \overload
+
+ Draws the rectangular portion \a source of the given \a pixmap
+ into the given \a target in the paint device.
+
+ \note The pixmap is scaled to fit the rectangle, if both the pixmap and rectangle size disagree.
+*/
+
+/*!
+ \fn void QPainter::drawPixmap(const QPointF &point, const QPixmap &pixmap,
+ const QRectF &source)
+ \overload
+
+ Draws the rectangular portion \a source of the given \a pixmap
+ with its origin at the given \a point.
+*/
+
+/*!
+ \fn void QPainter::drawPixmap(const QPoint &point, const QPixmap &pixmap,
+ const QRect &source)
+
+ \overload
+
+ Draws the rectangular portion \a source of the given \a pixmap
+ with its origin at the given \a point.
+*/
+
+/*!
+ \fn void QPainter::drawPixmap(const QPointF &point, const QPixmap &pixmap)
+ \overload
+
+ Draws the given \a pixmap with its origin at the given \a point.
+*/
+
+/*!
+ \fn void QPainter::drawPixmap(const QPoint &point, const QPixmap &pixmap)
+ \overload
+
+ Draws the given \a pixmap with its origin at the given \a point.
+*/
+
+/*!
+ \fn void QPainter::drawPixmap(int x, int y, const QPixmap &pixmap)
+
+ \overload
+
+ Draws the given \a pixmap at position (\a{x}, \a{y}).
+*/
+
+/*!
+ \fn void QPainter::drawPixmap(const QRect &rectangle, const QPixmap &pixmap)
+ \overload
+
+ Draws the given \a pixmap into the given \a rectangle.
+
+ \note The pixmap is scaled to fit the rectangle, if both the pixmap and rectangle size disagree.
+*/
+
+/*!
+ \fn void QPainter::drawPixmap(int x, int y, int width, int height,
+ const QPixmap &pixmap)
+
+ \overload
+
+ Draws the \a pixmap into the rectangle at position (\a{x}, \a{y})
+ with the given \a width and \a height.
+*/
+
+/*!
+ \fn void QPainter::drawPixmap(int x, int y, int w, int h, const QPixmap &pixmap,
+ int sx, int sy, int sw, int sh)
+
+ \overload
+
+ Draws the rectangular portion with the origin (\a{sx}, \a{sy}),
+ width \a sw and height \a sh, of the given \a pixmap , at the
+ point (\a{x}, \a{y}), with a width of \a w and a height of \a h.
+ If sw or sh are equal to zero the width/height of the pixmap
+ is used and adjusted by the offset sx/sy;
+*/
+
+/*!
+ \fn void QPainter::drawPixmap(int x, int y, const QPixmap &pixmap,
+ int sx, int sy, int sw, int sh)
+
+ \overload
+
+ Draws a pixmap at (\a{x}, \a{y}) by copying a part of the given \a
+ pixmap into the paint device.
+
+ (\a{x}, \a{y}) specifies the top-left point in the paint device that is
+ to be drawn onto. (\a{sx}, \a{sy}) specifies the top-left point in \a
+ pixmap that is to be drawn. The default is (0, 0).
+
+ (\a{sw}, \a{sh}) specifies the size of the pixmap that is to be drawn.
+ The default, (0, 0) (and negative) means all the way to the
+ bottom-right of the pixmap.
+*/
+
+void QPainter::drawImage(const QPointF &p, const QImage &image)
+{
+ Q_D(QPainter);
+
+ if (!d->engine || image.isNull())
+ return;
+
+ if (d->extended) {
+ d->extended->drawImage(p, image);
+ return;
+ }
+
+ qreal x = p.x();
+ qreal y = p.y();
+
+ int w = image.width();
+ int h = image.height();
+
+ d->updateState(d->state);
+
+ if (((d->state->matrix.type() > QTransform::TxTranslate)
+ && !d->engine->hasFeature(QPaintEngine::PixmapTransform))
+ || (!d->state->matrix.isAffine() && !d->engine->hasFeature(QPaintEngine::PerspectiveTransform))
+ || (d->state->opacity != 1.0 && !d->engine->hasFeature(QPaintEngine::ConstantOpacity)))
+ {
+ save();
+ // If there is no rotation involved we have to make sure we use the
+ // antialiased and not the aliased coordinate system by rounding the coordinates.
+ if (d->state->matrix.type() <= QTransform::TxScale) {
+ const QPointF p = roundInDeviceCoordinates(QPointF(x, y), d->state->matrix);
+ x = p.x();
+ y = p.y();
+ }
+ translate(x, y);
+ setBackgroundMode(Qt::TransparentMode);
+ setRenderHint(Antialiasing, renderHints() & SmoothPixmapTransform);
+ QBrush brush(image);
+ setBrush(brush);
+ setPen(Qt::NoPen);
+ setBrushOrigin(QPointF(0, 0));
+
+ drawRect(image.rect());
+ restore();
+ return;
+ }
+
+ if (d->state->matrix.type() == QTransform::TxTranslate
+ && !d->engine->hasFeature(QPaintEngine::PixmapTransform)) {
+ x += d->state->matrix.dx();
+ y += d->state->matrix.dy();
+ }
+
+ d->engine->drawImage(QRectF(x, y, w, h), image, QRectF(0, 0, w, h), Qt::AutoColor);
+}
+
+void QPainter::drawImage(const QRectF &targetRect, const QImage &image, const QRectF &sourceRect,
+ Qt::ImageConversionFlags flags)
+{
+ Q_D(QPainter);
+
+ if (!d->engine || image.isNull())
+ return;
+
+ qreal x = targetRect.x();
+ qreal y = targetRect.y();
+ qreal w = targetRect.width();
+ qreal h = targetRect.height();
+ qreal sx = sourceRect.x();
+ qreal sy = sourceRect.y();
+ qreal sw = sourceRect.width();
+ qreal sh = sourceRect.height();
+
+ // Sanity-check clipping
+ if (sw <= 0)
+ sw = image.width() - sx;
+
+ if (sh <= 0)
+ sh = image.height() - sy;
+
+ if (w < 0)
+ w = sw;
+ if (h < 0)
+ h = sh;
+
+ if (sx < 0) {
+ qreal w_ratio = sx * w/sw;
+ x -= w_ratio;
+ w += w_ratio;
+ sw += sx;
+ sx = 0;
+ }
+
+ if (sy < 0) {
+ qreal h_ratio = sy * h/sh;
+ y -= h_ratio;
+ h += h_ratio;
+ sh += sy;
+ sy = 0;
+ }
+
+ if (sw + sx > image.width()) {
+ qreal delta = sw - (image.width() - sx);
+ qreal w_ratio = delta * w/sw;
+ sw -= delta;
+ w -= w_ratio;
+ }
+
+ if (sh + sy > image.height()) {
+ qreal delta = sh - (image.height() - sy);
+ qreal h_ratio = delta * h/sh;
+ sh -= delta;
+ h -= h_ratio;
+ }
+
+ if (w == 0 || h == 0 || sw <= 0 || sh <= 0)
+ return;
+
+ if (d->extended) {
+ d->extended->drawImage(QRectF(x, y, w, h), image, QRectF(sx, sy, sw, sh), flags);
+ return;
+ }
+
+ d->updateState(d->state);
+
+ if (((d->state->matrix.type() > QTransform::TxTranslate || (sw != w || sh != h))
+ && !d->engine->hasFeature(QPaintEngine::PixmapTransform))
+ || (!d->state->matrix.isAffine() && !d->engine->hasFeature(QPaintEngine::PerspectiveTransform))
+ || (d->state->opacity != 1.0 && !d->engine->hasFeature(QPaintEngine::ConstantOpacity)))
+ {
+ save();
+ // If there is no rotation involved we have to make sure we use the
+ // antialiased and not the aliased coordinate system by rounding the coordinates.
+ if (d->state->matrix.type() <= QTransform::TxScale) {
+ const QPointF p = roundInDeviceCoordinates(QPointF(x, y), d->state->matrix);
+ x = p.x();
+ y = p.y();
+ }
+
+ if (d->state->matrix.type() <= QTransform::TxTranslate && sw == w && sh == h) {
+ sx = qRound(sx);
+ sy = qRound(sy);
+ sw = qRound(sw);
+ sh = qRound(sh);
+ }
+ translate(x, y);
+ scale(w / sw, h / sh);
+ setBackgroundMode(Qt::TransparentMode);
+ setRenderHint(Antialiasing, renderHints() & SmoothPixmapTransform);
+ QBrush brush(image);
+ setBrush(brush);
+ setPen(Qt::NoPen);
+ setBrushOrigin(QPointF(-sx, -sy));
+
+ drawRect(QRectF(0, 0, sw, sh));
+ restore();
+ return;
+ }
+
+ if (d->state->matrix.type() == QTransform::TxTranslate
+ && !d->engine->hasFeature(QPaintEngine::PixmapTransform)) {
+ x += d->state->matrix.dx();
+ y += d->state->matrix.dy();
+ }
+
+ d->engine->drawImage(QRectF(x, y, w, h), image, QRectF(sx, sy, sw, sh), flags);
+}
+
+/*!
+ Draws the glyphs represented by \a glyphs at \a position. The \a position gives the
+ edge of the baseline for the string of glyphs. The glyphs will be retrieved from the font
+ selected on \a glyphs and at offsets given by the positions in \a glyphs.
+
+ \since 4.8
+
+ \sa QGlyphs::setFont(), QGlyphs::setPositions(), QGlyphs::setGlyphIndexes()
+*/
+#if !defined(QT_NO_RAWFONT)
+void QPainter::drawGlyphs(const QPointF &position, const QGlyphs &glyphs)
+{
+ Q_D(QPainter);
+
+ QRawFont font = glyphs.font();
+ if (!font.isValid())
+ return;
+
+ QVector<quint32> glyphIndexes = glyphs.glyphIndexes();
+ QVector<QPointF> glyphPositions = glyphs.positions();
+
+ int count = qMin(glyphIndexes.size(), glyphPositions.size());
+ QVarLengthArray<QFixedPoint, 128> fixedPointPositions(count);
+
+ bool paintEngineSupportsTransformations =
+ d->extended != 0
+ ? qt_paintengine_supports_transformations(d->extended->type())
+ : false;
+ for (int i=0; i<count; ++i) {
+ QPointF processedPosition = position + glyphPositions.at(i);
+ if (!paintEngineSupportsTransformations)
+ processedPosition = d->state->transform().map(processedPosition);
+ fixedPointPositions[i] = QFixedPoint::fromPointF(processedPosition);
+ }
+
+ d->drawGlyphs(glyphIndexes.data(), fixedPointPositions.data(), count, font, glyphs.overline(),
+ glyphs.underline(), glyphs.strikeOut());
+}
+
+void QPainterPrivate::drawGlyphs(quint32 *glyphArray, QFixedPoint *positions, int glyphCount,
+ const QRawFont &font, bool overline, bool underline,
+ bool strikeOut)
+{
+ Q_Q(QPainter);
+
+ updateState(state);
+
+ QRawFontPrivate *fontD = QRawFontPrivate::get(font);
+ QFontEngine *fontEngine = fontD->fontEngine;
+
+ QFixed leftMost;
+ QFixed rightMost;
+ QFixed baseLine;
+ for (int i=0; i<glyphCount; ++i) {
+ glyph_metrics_t gm = fontEngine->boundingBox(glyphArray[i]);
+ if (i == 0 || leftMost > positions[i].x)
+ leftMost = positions[i].x;
+
+ // We don't support glyphs that do not share a common baseline. If this turns out to
+ // be a relevant use case, then we need to find clusters of glyphs that share a baseline
+ // and do a drawTextItemDecorations call per cluster.
+ if (i == 0 || baseLine < positions[i].y)
+ baseLine = positions[i].y;
+
+ // We use the advance rather than the actual bounds to match the algorithm in drawText()
+ if (i == 0 || rightMost < positions[i].x + gm.xoff)
+ rightMost = positions[i].x + gm.xoff;
+ }
+
+ QFixed width = rightMost - leftMost;
+
+ if (extended != 0) {
+ QStaticTextItem staticTextItem;
+ staticTextItem.color = state->pen.color();
+ staticTextItem.font = state->font;
+ staticTextItem.setFontEngine(fontEngine);
+ staticTextItem.numGlyphs = glyphCount;
+ staticTextItem.glyphs = reinterpret_cast<glyph_t *>(const_cast<glyph_t *>(glyphArray));
+ staticTextItem.glyphPositions = positions;
+
+ extended->drawStaticTextItem(&staticTextItem);
+ } else {
+ QTextItemInt textItem;
+ textItem.fontEngine = fontEngine;
+
+ QVarLengthArray<QFixed, 128> advances(glyphCount);
+ QVarLengthArray<QGlyphJustification, 128> glyphJustifications(glyphCount);
+ QVarLengthArray<HB_GlyphAttributes, 128> glyphAttributes(glyphCount);
+ qMemSet(glyphAttributes.data(), 0, glyphAttributes.size() * sizeof(HB_GlyphAttributes));
+ qMemSet(advances.data(), 0, advances.size() * sizeof(QFixed));
+ qMemSet(glyphJustifications.data(), 0, glyphJustifications.size() * sizeof(QGlyphJustification));
+
+ textItem.glyphs.numGlyphs = glyphCount;
+ textItem.glyphs.glyphs = reinterpret_cast<HB_Glyph *>(const_cast<quint32 *>(glyphArray));
+ textItem.glyphs.offsets = positions;
+ textItem.glyphs.advances_x = advances.data();
+ textItem.glyphs.advances_y = advances.data();
+ textItem.glyphs.justifications = glyphJustifications.data();
+ textItem.glyphs.attributes = glyphAttributes.data();
+
+ engine->drawTextItem(QPointF(0, 0), textItem);
+ }
+
+ QTextItemInt::RenderFlags flags;
+ if (underline)
+ flags |= QTextItemInt::Underline;
+ if (overline)
+ flags |= QTextItemInt::Overline;
+ if (strikeOut)
+ flags |= QTextItemInt::StrikeOut;
+
+ drawTextItemDecoration(q, QPointF(leftMost.toReal(), baseLine.toReal()),
+ fontEngine,
+ (underline
+ ? QTextCharFormat::SingleUnderline
+ : QTextCharFormat::NoUnderline),
+ flags, width.toReal(), QTextCharFormat());
+}
+#endif // QT_NO_RAWFONT
+
+/*!
+
+ \fn void QPainter::drawStaticText(const QPoint &topLeftPosition, const QStaticText &staticText)
+ \since 4.7
+ \overload
+
+ Draws the \a staticText at the \a topLeftPosition.
+
+ \note The y-position is used as the top of the font.
+
+*/
+
+/*!
+ \fn void QPainter::drawStaticText(int left, int top, const QStaticText &staticText)
+ \since 4.7
+ \overload
+
+ Draws the \a staticText at coordinates \a left and \a top.
+
+ \note The y-position is used as the top of the font.
+*/
+
+/*!
+ \fn void QPainter::drawText(const QPointF &position, const QString &text)
+
+ Draws the given \a text with the currently defined text direction,
+ beginning at the given \a position.
+
+ This function does not handle the newline character (\n), as it cannot
+ break text into multiple lines, and it cannot display the newline character.
+ Use the QPainter::drawText() overload that takes a rectangle instead
+ if you want to draw multiple lines of text with the newline character, or
+ if you want the text to be wrapped.
+
+ By default, QPainter draws text anti-aliased.
+
+ \note The y-position is used as the baseline of the font.
+*/
+
+void QPainter::drawText(const QPointF &p, const QString &str)
+{
+ drawText(p, str, 0, 0);
+}
+
+/*!
+ \since 4.7
+
+ Draws the given \a staticText at the given \a topLeftPosition.
+
+ The text will be drawn using the font and the transformation set on the painter. If the
+ font and/or transformation set on the painter are different from the ones used to initialize
+ the layout of the QStaticText, then the layout will have to be recalculated. Use
+ QStaticText::prepare() to initialize \a staticText with the font and transformation with which
+ it will later be drawn.
+
+ If \a topLeftPosition is not the same as when \a staticText was initialized, or when it was
+ last drawn, then there will be a slight overhead when translating the text to its new position.
+
+ \note If the painter's transformation is not affine, then \a staticText will be drawn using
+ regular calls to drawText(), losing any potential for performance improvement.
+
+ \note The y-position is used as the top of the font.
+
+ \sa QStaticText
+*/
+void QPainter::drawStaticText(const QPointF &topLeftPosition, const QStaticText &staticText)
+{
+ Q_D(QPainter);
+ if (!d->engine || staticText.text().isEmpty() || pen().style() == Qt::NoPen)
+ return;
+
+ QStaticTextPrivate *staticText_d =
+ const_cast<QStaticTextPrivate *>(QStaticTextPrivate::get(&staticText));
+
+ if (font() != staticText_d->font) {
+ staticText_d->font = font();
+ staticText_d->needsRelayout = true;
+ }
+
+ // If we don't have an extended paint engine, or if the painter is projected,
+ // we go through standard code path
+ if (d->extended == 0 || !d->state->matrix.isAffine()) {
+ staticText_d->paintText(topLeftPosition, this);
+ return;
+ }
+
+ bool paintEngineSupportsTransformations = qt_paintengine_supports_transformations(d->extended->type());
+ if (paintEngineSupportsTransformations && !staticText_d->untransformedCoordinates) {
+ staticText_d->untransformedCoordinates = true;
+ staticText_d->needsRelayout = true;
+ } else if (!paintEngineSupportsTransformations && staticText_d->untransformedCoordinates) {
+ staticText_d->untransformedCoordinates = false;
+ staticText_d->needsRelayout = true;
+ }
+
+ // Don't recalculate entire layout because of translation, rather add the dx and dy
+ // into the position to move each text item the correct distance.
+ QPointF transformedPosition = topLeftPosition;
+ if (!staticText_d->untransformedCoordinates)
+ transformedPosition = transformedPosition * d->state->matrix;
+ QTransform oldMatrix;
+
+ // The translation has been applied to transformedPosition. Remove translation
+ // component from matrix.
+ if (d->state->matrix.isTranslating() && !staticText_d->untransformedCoordinates) {
+ qreal m11 = d->state->matrix.m11();
+ qreal m12 = d->state->matrix.m12();
+ qreal m13 = d->state->matrix.m13();
+ qreal m21 = d->state->matrix.m21();
+ qreal m22 = d->state->matrix.m22();
+ qreal m23 = d->state->matrix.m23();
+ qreal m33 = d->state->matrix.m33();
+
+ oldMatrix = d->state->matrix;
+ d->state->matrix.setMatrix(m11, m12, m13,
+ m21, m22, m23,
+ 0.0, 0.0, m33);
+ }
+
+ // If the transform is not identical to the text transform,
+ // we have to relayout the text (for other transformations than plain translation)
+ bool staticTextNeedsReinit = staticText_d->needsRelayout;
+ if (!staticText_d->untransformedCoordinates && staticText_d->matrix != d->state->matrix) {
+ staticText_d->matrix = d->state->matrix;
+ staticTextNeedsReinit = true;
+ }
+
+ // Recreate the layout of the static text because the matrix or font has changed
+ if (staticTextNeedsReinit)
+ staticText_d->init();
+
+ if (transformedPosition != staticText_d->position) { // Translate to actual position
+ QFixed fx = QFixed::fromReal(transformedPosition.x());
+ QFixed fy = QFixed::fromReal(transformedPosition.y());
+ QFixed oldX = QFixed::fromReal(staticText_d->position.x());
+ QFixed oldY = QFixed::fromReal(staticText_d->position.y());
+ for (int item=0; item<staticText_d->itemCount;++item) {
+ QStaticTextItem *textItem = staticText_d->items + item;
+ for (int i=0; i<textItem->numGlyphs; ++i) {
+ textItem->glyphPositions[i].x += fx - oldX;
+ textItem->glyphPositions[i].y += fy - oldY;
+ }
+ textItem->userDataNeedsUpdate = true;
+ }
+
+ staticText_d->position = transformedPosition;
+ }
+
+ QPen oldPen = d->state->pen;
+ QColor currentColor = oldPen.color();
+ for (int i=0; i<staticText_d->itemCount; ++i) {
+ QStaticTextItem *item = staticText_d->items + i;
+ if (item->color.isValid() && currentColor != item->color) {
+ setPen(item->color);
+ currentColor = item->color;
+ }
+ d->extended->drawStaticTextItem(item);
+
+ qt_draw_decoration_for_glyphs(this, item->glyphs, item->glyphPositions,
+ item->numGlyphs, item->fontEngine(), staticText_d->font,
+ QTextCharFormat());
+ }
+ if (currentColor != oldPen.color())
+ setPen(oldPen);
+
+ if (!staticText_d->untransformedCoordinates && oldMatrix.isTranslating())
+ d->state->matrix = oldMatrix;
+}
+
+/*!
+ \internal
+*/
+void QPainter::drawText(const QPointF &p, const QString &str, int tf, int justificationPadding)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawText(), pos=[%.2f,%.2f], str='%s'\n", p.x(), p.y(), str.toLatin1().constData());
+#endif
+
+ Q_D(QPainter);
+
+ if (!d->engine || str.isEmpty() || pen().style() == Qt::NoPen)
+ return;
+
+ if (tf & Qt::TextBypassShaping) {
+ // Skip harfbuzz complex shaping, shape using glyph advances only
+ int len = str.length();
+ int numGlyphs = len;
+ QVarLengthGlyphLayoutArray glyphs(len);
+ QFontEngine *fontEngine = d->state->font.d->engineForScript(QUnicodeTables::Common);
+ if (!fontEngine->stringToCMap(str.data(), len, &glyphs, &numGlyphs, 0)) {
+ glyphs.resize(numGlyphs);
+ if (!fontEngine->stringToCMap(str.data(), len, &glyphs, &numGlyphs, 0))
+ Q_ASSERT_X(false, Q_FUNC_INFO, "stringToCMap shouldn't fail twice");
+ }
+
+ QTextItemInt gf(glyphs, &d->state->font, str.data(), len, fontEngine);
+ drawTextItem(p, gf);
+ return;
+ }
+
+ QStackTextEngine engine(str, d->state->font);
+ engine.option.setTextDirection(d->state->layoutDirection);
+ if (tf & (Qt::TextForceLeftToRight|Qt::TextForceRightToLeft)) {
+ engine.ignoreBidi = true;
+ engine.option.setTextDirection((tf & Qt::TextForceLeftToRight) ? Qt::LeftToRight : Qt::RightToLeft);
+ }
+ engine.itemize();
+ QScriptLine line;
+ line.length = str.length();
+ engine.shapeLine(line);
+
+ int nItems = engine.layoutData->items.size();
+ QVarLengthArray<int> visualOrder(nItems);
+ QVarLengthArray<uchar> levels(nItems);
+ for (int i = 0; i < nItems; ++i)
+ levels[i] = engine.layoutData->items[i].analysis.bidiLevel;
+ QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
+
+ if (justificationPadding > 0) {
+ engine.option.setAlignment(Qt::AlignJustify);
+ engine.forceJustification = true;
+ // this works because justify() is only interested in the difference between width and textWidth
+ line.width = justificationPadding;
+ engine.justify(line);
+ }
+ QFixed x = QFixed::fromReal(p.x());
+
+ for (int i = 0; i < nItems; ++i) {
+ int item = visualOrder[i];
+ const QScriptItem &si = engine.layoutData->items.at(item);
+ if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
+ x += si.width;
+ continue;
+ }
+ QFont f = engine.font(si);
+ QTextItemInt gf(si, &f);
+ gf.glyphs = engine.shapedGlyphs(&si);
+ gf.chars = engine.layoutData->string.unicode() + si.position;
+ gf.num_chars = engine.length(item);
+ if (engine.forceJustification) {
+ for (int j=0; j<gf.glyphs.numGlyphs; ++j)
+ gf.width += gf.glyphs.effectiveAdvance(j);
+ } else {
+ gf.width = si.width;
+ }
+ gf.logClusters = engine.logClusters(&si);
+
+ drawTextItem(QPointF(x.toReal(), p.y()), gf);
+
+ x += gf.width;
+ }
+}
+
+void QPainter::drawText(const QRect &r, int flags, const QString &str, QRect *br)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawText(), r=[%d,%d,%d,%d], flags=%d, str='%s'\n",
+ r.x(), r.y(), r.width(), r.height(), flags, str.toLatin1().constData());
+#endif
+
+ Q_D(QPainter);
+
+ if (!d->engine || str.length() == 0 || pen().style() == Qt::NoPen)
+ return;
+
+ if (!d->extended)
+ d->updateState(d->state);
+
+ QRectF bounds;
+ qt_format_text(d->state->font, r, flags, 0, str, br ? &bounds : 0, 0, 0, 0, this);
+ if (br)
+ *br = bounds.toAlignedRect();
+}
+
+/*!
+ \fn void QPainter::drawText(const QPoint &position, const QString &text)
+
+ \overload
+
+ Draws the given \a text with the currently defined text direction,
+ beginning at the given \a position.
+
+ By default, QPainter draws text anti-aliased.
+
+ \note The y-position is used as the baseline of the font.
+
+*/
+
+/*!
+ \fn void QPainter::drawText(const QRectF &rectangle, int flags, const QString &text, QRectF *boundingRect)
+ \overload
+
+ Draws the given \a text within the provided \a rectangle.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainter-text.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 17
+ \endtable
+
+ The \a boundingRect (if not null) is set to the what the bounding rectangle
+ should be in order to enclose the whole text. The \a flags argument is a bitwise
+ OR of the following flags:
+
+ \list
+ \o Qt::AlignLeft
+ \o Qt::AlignRight
+ \o Qt::AlignHCenter
+ \o Qt::AlignJustify
+ \o Qt::AlignTop
+ \o Qt::AlignBottom
+ \o Qt::AlignVCenter
+ \o Qt::AlignCenter
+ \o Qt::TextDontClip
+ \o Qt::TextSingleLine
+ \o Qt::TextExpandTabs
+ \o Qt::TextShowMnemonic
+ \o Qt::TextWordWrap
+ \o Qt::TextIncludeTrailingSpaces
+ \endlist
+
+ \sa Qt::AlignmentFlag, Qt::TextFlag, boundingRect(), layoutDirection()
+
+ By default, QPainter draws text anti-aliased.
+
+ \note The y-coordinate of \a rectangle is used as the top of the font.
+*/
+void QPainter::drawText(const QRectF &r, int flags, const QString &str, QRectF *br)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawText(), r=[%.2f,%.2f,%.2f,%.2f], flags=%d, str='%s'\n",
+ r.x(), r.y(), r.width(), r.height(), flags, str.toLatin1().constData());
+#endif
+
+ Q_D(QPainter);
+
+ if (!d->engine || str.length() == 0 || pen().style() == Qt::NoPen)
+ return;
+
+ if (!d->extended)
+ d->updateState(d->state);
+
+ qt_format_text(d->state->font, r, flags, 0, str, br, 0, 0, 0, this);
+}
+
+/*!
+ \fn void QPainter::drawText(const QRect &rectangle, int flags, const QString &text, QRect *boundingRect)
+ \overload
+
+ Draws the given \a text within the provided \a rectangle according
+ to the specified \a flags. The \a boundingRect (if not null) is set to
+ the what the bounding rectangle should be in order to enclose the whole text.
+
+ By default, QPainter draws text anti-aliased.
+
+ \note The y-coordinate of \a rectangle is used as the top of the font.
+*/
+
+/*!
+ \fn void QPainter::drawText(int x, int y, const QString &text)
+
+ \overload
+
+ Draws the given \a text at position (\a{x}, \a{y}), using the painter's
+ currently defined text direction.
+
+ By default, QPainter draws text anti-aliased.
+
+ \note The y-position is used as the baseline of the font.
+
+*/
+
+/*!
+ \fn void QPainter::drawText(int x, int y, int width, int height, int flags,
+ const QString &text, QRect *boundingRect)
+
+ \overload
+
+ Draws the given \a text within the rectangle with origin (\a{x},
+ \a{y}), \a width and \a height.
+
+ The \a boundingRect (if not null) is set to the actual bounding
+ rectangle of the output. The \a flags argument is a bitwise OR of
+ the following flags:
+
+ \list
+ \o Qt::AlignLeft
+ \o Qt::AlignRight
+ \o Qt::AlignHCenter
+ \o Qt::AlignJustify
+ \o Qt::AlignTop
+ \o Qt::AlignBottom
+ \o Qt::AlignVCenter
+ \o Qt::AlignCenter
+ \o Qt::TextSingleLine
+ \o Qt::TextExpandTabs
+ \o Qt::TextShowMnemonic
+ \o Qt::TextWordWrap
+ \endlist
+
+ By default, QPainter draws text anti-aliased.
+
+ \note The y-position is used as the top of the font.
+
+ \sa Qt::AlignmentFlag, Qt::TextFlag
+*/
+
+/*!
+ \fn void QPainter::drawText(const QRectF &rectangle, const QString &text,
+ const QTextOption &option)
+ \overload
+
+ Draws the given \a text in the \a rectangle specified using the \a option
+ to control its positioning and orientation.
+
+ By default, QPainter draws text anti-aliased.
+
+ \note The y-coordinate of \a rectangle is used as the top of the font.
+*/
+void QPainter::drawText(const QRectF &r, const QString &text, const QTextOption &o)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawText(), r=[%.2f,%.2f,%.2f,%.2f], str='%s'\n",
+ r.x(), r.y(), r.width(), r.height(), text.toLatin1().constData());
+#endif
+
+ Q_D(QPainter);
+
+ if (!d->engine || text.length() == 0 || pen().style() == Qt::NoPen)
+ return;
+
+ if (!d->extended)
+ d->updateState(d->state);
+
+ qt_format_text(d->state->font, r, 0, &o, text, 0, 0, 0, 0, this);
+}
+
+/*!
+ \fn void QPainter::drawTextItem(int x, int y, const QTextItem &ti)
+
+ \internal
+ \overload
+*/
+
+/*!
+ \fn void QPainter::drawTextItem(const QPoint &p, const QTextItem &ti)
+
+ \internal
+ \overload
+
+ Draws the text item \a ti at position \a p.
+*/
+
+/*!
+ \fn void QPainter::drawTextItem(const QPointF &p, const QTextItem &ti)
+
+ \internal
+ \since 4.1
+
+ Draws the text item \a ti at position \a p.
+
+ This method ignores the painters background mode and
+ color. drawText and qt_format_text have to do it themselves, as
+ only they know the extents of the complete string.
+
+ It ignores the font set on the painter as the text item has one of its own.
+
+ The underline and strikeout parameters of the text items font are
+ ignored aswell. You'll need to pass in the correct flags to get
+ underlining and strikeout.
+*/
+
+static QPixmap generateWavyPixmap(qreal maxRadius, const QPen &pen)
+{
+ const qreal radiusBase = qMax(qreal(1), maxRadius);
+
+ QString key = QLatin1Literal("WaveUnderline-")
+ % pen.color().name()
+ % HexString<qreal>(radiusBase);
+
+ QPixmap pixmap;
+ if (QPixmapCache::find(key, pixmap))
+ return pixmap;
+
+ const qreal halfPeriod = qMax(qreal(2), qreal(radiusBase * 1.61803399)); // the golden ratio
+ const int width = qCeil(100 / (2 * halfPeriod)) * (2 * halfPeriod);
+ const int radius = qFloor(radiusBase);
+
+ QPainterPath path;
+
+ qreal xs = 0;
+ qreal ys = radius;
+
+ while (xs < width) {
+ xs += halfPeriod;
+ ys = -ys;
+ path.quadTo(xs - halfPeriod / 2, ys, xs, 0);
+ }
+
+ pixmap = QPixmap(width, radius * 2);
+ pixmap.fill(Qt::transparent);
+ {
+ QPen wavePen = pen;
+ wavePen.setCapStyle(Qt::SquareCap);
+
+ // This is to protect against making the line too fat, as happens on Mac OS X
+ // due to it having a rather thick width for the regular underline.
+ const qreal maxPenWidth = .8 * radius;
+ if (wavePen.widthF() > maxPenWidth)
+ wavePen.setWidth(maxPenWidth);
+
+ QPainter imgPainter(&pixmap);
+ imgPainter.setPen(wavePen);
+ imgPainter.setRenderHint(QPainter::Antialiasing);
+ imgPainter.translate(0, radius);
+ imgPainter.drawPath(path);
+ }
+
+ QPixmapCache::insert(key, pixmap);
+
+ return pixmap;
+}
+
+static void drawTextItemDecoration(QPainter *painter, const QPointF &pos, const QFontEngine *fe,
+ QTextCharFormat::UnderlineStyle underlineStyle,
+ QTextItem::RenderFlags flags, qreal width,
+ const QTextCharFormat &charFormat)
+{
+ if (underlineStyle == QTextCharFormat::NoUnderline
+ && !(flags & (QTextItem::StrikeOut | QTextItem::Overline)))
+ return;
+
+ const QPen oldPen = painter->pen();
+ const QBrush oldBrush = painter->brush();
+ painter->setBrush(Qt::NoBrush);
+ QPen pen = oldPen;
+ pen.setStyle(Qt::SolidLine);
+ pen.setWidthF(fe->lineThickness().toReal());
+ pen.setCapStyle(Qt::FlatCap);
+
+ QLineF line(pos.x(), pos.y(), pos.x() + qFloor(width), pos.y());
+
+ const qreal underlineOffset = fe->underlinePosition().toReal();
+ // deliberately ceil the offset to avoid the underline coming too close to
+ // the text above it.
+ const qreal aliasedCoordinateDelta = 0.5 - 0.015625;
+ const qreal underlinePos = pos.y() + qCeil(underlineOffset) - aliasedCoordinateDelta;
+
+ if (underlineStyle == QTextCharFormat::SpellCheckUnderline) {
+ underlineStyle = QTextCharFormat::UnderlineStyle(QApplication::style()->styleHint(QStyle::SH_SpellCheckUnderlineStyle));
+ }
+
+ if (underlineStyle == QTextCharFormat::WaveUnderline) {
+ painter->save();
+ painter->translate(0, pos.y() + 1);
+
+ QColor uc = charFormat.underlineColor();
+ if (uc.isValid())
+ pen.setColor(uc);
+
+ // Adapt wave to underlineOffset or pen width, whatever is larger, to make it work on all platforms
+ const QPixmap wave = generateWavyPixmap(qMax(underlineOffset, pen.widthF()), pen);
+ const int descent = (int) fe->descent().toReal();
+
+ painter->setBrushOrigin(painter->brushOrigin().x(), 0);
+ painter->fillRect(pos.x(), 0, qCeil(width), qMin(wave.height(), descent), wave);
+ painter->restore();
+ } else if (underlineStyle != QTextCharFormat::NoUnderline) {
+ QLineF underLine(line.x1(), underlinePos, line.x2(), underlinePos);
+
+ QColor uc = charFormat.underlineColor();
+ if (uc.isValid())
+ pen.setColor(uc);
+
+ pen.setStyle((Qt::PenStyle)(underlineStyle));
+ painter->setPen(pen);
+ painter->drawLine(underLine);
+ }
+
+ pen.setStyle(Qt::SolidLine);
+ pen.setColor(oldPen.color());
+
+ if (flags & QTextItem::StrikeOut) {
+ QLineF strikeOutLine = line;
+ strikeOutLine.translate(0., - fe->ascent().toReal() / 3.);
+ painter->setPen(pen);
+ painter->drawLine(strikeOutLine);
+ }
+
+ if (flags & QTextItem::Overline) {
+ QLineF overLine = line;
+ overLine.translate(0., - fe->ascent().toReal());
+ painter->setPen(pen);
+ painter->drawLine(overLine);
+ }
+
+ painter->setPen(oldPen);
+ painter->setBrush(oldBrush);
+}
+
+Q_GUI_EXPORT void qt_draw_decoration_for_glyphs(QPainter *painter, const glyph_t *glyphArray,
+ const QFixedPoint *positions, int glyphCount,
+ QFontEngine *fontEngine, const QFont &font,
+ const QTextCharFormat &charFormat)
+{
+ if (!(font.underline() || font.strikeOut() || font.overline()))
+ return;
+
+ QFixed leftMost;
+ QFixed rightMost;
+ QFixed baseLine;
+ for (int i=0; i<glyphCount; ++i) {
+ glyph_metrics_t gm = fontEngine->boundingBox(glyphArray[i]);
+ if (i == 0 || leftMost > positions[i].x)
+ leftMost = positions[i].x;
+
+ // We don't support glyphs that do not share a common baseline. If this turns out to
+ // be a relevant use case, then we need to find clusters of glyphs that share a baseline
+ // and do a drawTextItemDecorations call per cluster.
+ if (i == 0 || baseLine < positions[i].y)
+ baseLine = positions[i].y;
+
+ // We use the advance rather than the actual bounds to match the algorithm in drawText()
+ if (i == 0 || rightMost < positions[i].x + gm.xoff)
+ rightMost = positions[i].x + gm.xoff;
+ }
+
+ QFixed width = rightMost - leftMost;
+ QTextItem::RenderFlags flags = 0;
+
+ if (font.underline())
+ flags |= QTextItem::Underline;
+ if (font.overline())
+ flags |= QTextItem::Overline;
+ if (font.strikeOut())
+ flags |= QTextItem::StrikeOut;
+
+ drawTextItemDecoration(painter, QPointF(leftMost.toReal(), baseLine.toReal()),
+ fontEngine,
+ font.underline() ? QTextCharFormat::SingleUnderline
+ : QTextCharFormat::NoUnderline, flags,
+ width.toReal(), charFormat);
+}
+
+void QPainter::drawTextItem(const QPointF &p, const QTextItem &_ti)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawTextItem(), pos=[%.f,%.f], str='%s'\n",
+ p.x(), p.y(), qPrintable(_ti.text()));
+#endif
+
+ Q_D(QPainter);
+
+ if (!d->engine)
+ return;
+
+#ifndef QT_NO_DEBUG
+ qt_painter_thread_test(d->device->devType(),
+ "text and fonts",
+ QFontDatabase::supportsThreadedFontRendering());
+#endif
+
+ QTextItemInt &ti = const_cast<QTextItemInt &>(static_cast<const QTextItemInt &>(_ti));
+
+ if (!d->extended && d->state->bgMode == Qt::OpaqueMode) {
+ QRectF rect(p.x(), p.y() - ti.ascent.toReal(), ti.width.toReal(), (ti.ascent + ti.descent + 1).toReal());
+ fillRect(rect, d->state->bgBrush);
+ }
+
+ if (pen().style() == Qt::NoPen)
+ return;
+
+ const RenderHints oldRenderHints = d->state->renderHints;
+ if (!d->state->renderHints & QPainter::Antialiasing && d->state->matrix.type() >= QTransform::TxScale) {
+ // draw antialias decoration (underline/overline/strikeout) with
+ // transformed text
+
+ bool aa = true;
+ const QTransform &m = d->state->matrix;
+ if (d->state->matrix.type() < QTransform::TxShear) {
+ bool isPlain90DegreeRotation =
+ (qFuzzyIsNull(m.m11())
+ && qFuzzyIsNull(m.m12() - qreal(1))
+ && qFuzzyIsNull(m.m21() + qreal(1))
+ && qFuzzyIsNull(m.m22())
+ )
+ ||
+ (qFuzzyIsNull(m.m11() + qreal(1))
+ && qFuzzyIsNull(m.m12())
+ && qFuzzyIsNull(m.m21())
+ && qFuzzyIsNull(m.m22() + qreal(1))
+ )
+ ||
+ (qFuzzyIsNull(m.m11())
+ && qFuzzyIsNull(m.m12() + qreal(1))
+ && qFuzzyIsNull(m.m21() - qreal(1))
+ && qFuzzyIsNull(m.m22())
+ )
+ ;
+ aa = !isPlain90DegreeRotation;
+ }
+ if (aa)
+ setRenderHint(QPainter::Antialiasing, true);
+ }
+
+ if (!d->extended)
+ d->updateState(d->state);
+
+ if (!ti.glyphs.numGlyphs) {
+ // nothing to do
+ } else if (ti.fontEngine->type() == QFontEngine::Multi) {
+ QFontEngineMulti *multi = static_cast<QFontEngineMulti *>(ti.fontEngine);
+
+ const QGlyphLayout &glyphs = ti.glyphs;
+ int which = glyphs.glyphs[0] >> 24;
+
+ qreal x = p.x();
+ qreal y = p.y();
+
+ int start = 0;
+ int end, i;
+ for (end = 0; end < ti.glyphs.numGlyphs; ++end) {
+ const int e = glyphs.glyphs[end] >> 24;
+ if (e == which)
+ continue;
+
+
+ QTextItemInt ti2 = ti.midItem(multi->engine(which), start, end - start);
+ ti2.width = 0;
+ // set the high byte to zero and calc the width
+ for (i = start; i < end; ++i) {
+ glyphs.glyphs[i] = glyphs.glyphs[i] & 0xffffff;
+ ti2.width += ti.glyphs.effectiveAdvance(i);
+ }
+
+ d->engine->drawTextItem(QPointF(x, y), ti2);
+
+ // reset the high byte for all glyphs and advance to the next sub-string
+ const int hi = which << 24;
+ for (i = start; i < end; ++i) {
+ glyphs.glyphs[i] = hi | glyphs.glyphs[i];
+ }
+ x += ti2.width.toReal();
+
+ // change engine
+ start = end;
+ which = e;
+ }
+
+ QTextItemInt ti2 = ti.midItem(multi->engine(which), start, end - start);
+ ti2.width = 0;
+ // set the high byte to zero and calc the width
+ for (i = start; i < end; ++i) {
+ glyphs.glyphs[i] = glyphs.glyphs[i] & 0xffffff;
+ ti2.width += ti.glyphs.effectiveAdvance(i);
+ }
+
+ if (d->extended)
+ d->extended->drawTextItem(QPointF(x, y), ti2);
+ else
+ d->engine->drawTextItem(QPointF(x,y), ti2);
+
+ // reset the high byte for all glyphs
+ const int hi = which << 24;
+ for (i = start; i < end; ++i)
+ glyphs.glyphs[i] = hi | glyphs.glyphs[i];
+
+ } else {
+ if (d->extended)
+ d->extended->drawTextItem(p, ti);
+ else
+ d->engine->drawTextItem(p, ti);
+ }
+ drawTextItemDecoration(this, p, ti.fontEngine, ti.underlineStyle, ti.flags, ti.width.toReal(),
+ ti.charFormat);
+
+ if (d->state->renderHints != oldRenderHints) {
+ d->state->renderHints = oldRenderHints;
+ if (d->extended)
+ d->extended->renderHintsChanged();
+ else
+ d->state->dirtyFlags |= QPaintEngine::DirtyHints;
+ }
+}
+
+/*!
+ \fn QRectF QPainter::boundingRect(const QRectF &rectangle, int flags, const QString &text)
+
+ Returns the bounding rectangle of the \a text as it will appear
+ when drawn inside the given \a rectangle with the specified \a
+ flags using the currently set font(); i.e the function tells you
+ where the drawText() function will draw when given the same
+ arguments.
+
+ If the \a text does not fit within the given \a rectangle using
+ the specified \a flags, the function returns the required
+ rectangle.
+
+ The \a flags argument is a bitwise OR of the following flags:
+ \list
+ \o Qt::AlignLeft
+ \o Qt::AlignRight
+ \o Qt::AlignHCenter
+ \o Qt::AlignTop
+ \o Qt::AlignBottom
+ \o Qt::AlignVCenter
+ \o Qt::AlignCenter
+ \o Qt::TextSingleLine
+ \o Qt::TextExpandTabs
+ \o Qt::TextShowMnemonic
+ \o Qt::TextWordWrap
+ \o Qt::TextIncludeTrailingSpaces
+ \endlist
+ If several of the horizontal or several of the vertical alignment
+ flags are set, the resulting alignment is undefined.
+
+ \sa drawText(), Qt::Alignment, Qt::TextFlag
+*/
+
+/*!
+ \fn QRect QPainter::boundingRect(const QRect &rectangle, int flags,
+ const QString &text)
+
+ \overload
+
+ Returns the bounding rectangle of the \a text as it will appear
+ when drawn inside the given \a rectangle with the specified \a
+ flags using the currently set font().
+*/
+
+/*!
+ \fn QRect QPainter::boundingRect(int x, int y, int w, int h, int flags,
+ const QString &text);
+
+ \overload
+
+ Returns the bounding rectangle of the given \a text as it will
+ appear when drawn inside the rectangle beginning at the point
+ (\a{x}, \a{y}) with width \a w and height \a h.
+*/
+QRect QPainter::boundingRect(const QRect &rect, int flags, const QString &str)
+{
+ if (str.isEmpty())
+ return QRect(rect.x(),rect.y(), 0,0);
+ QRect brect;
+ drawText(rect, flags | Qt::TextDontPrint, str, &brect);
+ return brect;
+}
+
+
+
+QRectF QPainter::boundingRect(const QRectF &rect, int flags, const QString &str)
+{
+ if (str.isEmpty())
+ return QRectF(rect.x(),rect.y(), 0,0);
+ QRectF brect;
+ drawText(rect, flags | Qt::TextDontPrint, str, &brect);
+ return brect;
+}
+
+/*!
+ \fn QRectF QPainter::boundingRect(const QRectF &rectangle,
+ const QString &text, const QTextOption &option)
+
+ \overload
+
+ Instead of specifying flags as a bitwise OR of the
+ Qt::AlignmentFlag and Qt::TextFlag, this overloaded function takes
+ an \a option argument. The QTextOption class provides a
+ description of general rich text properties.
+
+ \sa QTextOption
+*/
+QRectF QPainter::boundingRect(const QRectF &r, const QString &text, const QTextOption &o)
+{
+ Q_D(QPainter);
+
+ if (!d->engine || text.length() == 0)
+ return QRectF(r.x(),r.y(), 0,0);
+
+ QRectF br;
+ qt_format_text(d->state->font, r, Qt::TextDontPrint, &o, text, &br, 0, 0, 0, this);
+ return br;
+}
+
+/*!
+ \fn void QPainter::drawTiledPixmap(const QRectF &rectangle, const QPixmap &pixmap, const QPointF &position)
+
+ Draws a tiled \a pixmap, inside the given \a rectangle with its
+ origin at the given \a position.
+
+ Calling drawTiledPixmap() is similar to calling drawPixmap()
+ several times to fill (tile) an area with a pixmap, but is
+ potentially much more efficient depending on the underlying window
+ system.
+
+ \sa drawPixmap()
+*/
+void QPainter::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &sp)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::drawTiledPixmap(), target=[%.2f,%.2f,%.2f,%.2f], pix=[%d,%d], offset=[%.2f,%.2f]\n",
+ r.x(), r.y(), r.width(), r.height(),
+ pixmap.width(), pixmap.height(),
+ sp.x(), sp.y());
+#endif
+
+ Q_D(QPainter);
+ if (!d->engine || pixmap.isNull() || r.isEmpty())
+ return;
+
+#ifndef QT_NO_DEBUG
+ qt_painter_thread_test(d->device->devType(), "drawTiledPixmap()", true);
+#endif
+
+ qreal sw = pixmap.width();
+ qreal sh = pixmap.height();
+ qreal sx = sp.x();
+ qreal sy = sp.y();
+ if (sx < 0)
+ sx = qRound(sw) - qRound(-sx) % qRound(sw);
+ else
+ sx = qRound(sx) % qRound(sw);
+ if (sy < 0)
+ sy = qRound(sh) - -qRound(sy) % qRound(sh);
+ else
+ sy = qRound(sy) % qRound(sh);
+
+
+ if (d->extended) {
+ d->extended->drawTiledPixmap(r, pixmap, QPointF(sx, sy));
+ return;
+ }
+
+ if (d->state->bgMode == Qt::OpaqueMode && pixmap.isQBitmap())
+ fillRect(r, d->state->bgBrush);
+
+ d->updateState(d->state);
+ if ((d->state->matrix.type() > QTransform::TxTranslate
+ && !d->engine->hasFeature(QPaintEngine::PixmapTransform))
+ || (d->state->opacity != 1.0 && !d->engine->hasFeature(QPaintEngine::ConstantOpacity)))
+ {
+ save();
+ setBackgroundMode(Qt::TransparentMode);
+ setRenderHint(Antialiasing, renderHints() & SmoothPixmapTransform);
+ setBrush(QBrush(d->state->pen.color(), pixmap));
+ setPen(Qt::NoPen);
+
+ // If there is no rotation involved we have to make sure we use the
+ // antialiased and not the aliased coordinate system by rounding the coordinates.
+ if (d->state->matrix.type() <= QTransform::TxScale) {
+ const QPointF p = roundInDeviceCoordinates(r.topLeft(), d->state->matrix);
+
+ if (d->state->matrix.type() <= QTransform::TxTranslate) {
+ sx = qRound(sx);
+ sy = qRound(sy);
+ }
+
+ setBrushOrigin(QPointF(r.x()-sx, r.y()-sy));
+ drawRect(QRectF(p, r.size()));
+ } else {
+ setBrushOrigin(QPointF(r.x()-sx, r.y()-sy));
+ drawRect(r);
+ }
+ restore();
+ return;
+ }
+
+ qreal x = r.x();
+ qreal y = r.y();
+ if (d->state->matrix.type() == QTransform::TxTranslate
+ && !d->engine->hasFeature(QPaintEngine::PixmapTransform)) {
+ x += d->state->matrix.dx();
+ y += d->state->matrix.dy();
+ }
+
+ d->engine->drawTiledPixmap(QRectF(x, y, r.width(), r.height()), pixmap, QPointF(sx, sy));
+}
+
+/*!
+ \fn QPainter::drawTiledPixmap(const QRect &rectangle, const QPixmap &pixmap,
+ const QPoint &position = QPoint())
+ \overload
+
+ Draws a tiled \a pixmap, inside the given \a rectangle with its
+ origin at the given \a position.
+*/
+
+/*!
+ \fn void QPainter::drawTiledPixmap(int x, int y, int width, int height, const
+ QPixmap &pixmap, int sx, int sy);
+ \overload
+
+ Draws a tiled \a pixmap in the specified rectangle.
+
+ (\a{x}, \a{y}) specifies the top-left point in the paint device
+ that is to be drawn onto; with the given \a width and \a
+ height. (\a{sx}, \a{sy}) specifies the top-left point in the \a
+ pixmap that is to be drawn; this defaults to (0, 0).
+*/
+
+#ifndef QT_NO_PICTURE
+
+/*!
+ \fn void QPainter::drawPicture(const QPointF &point, const QPicture &picture)
+
+ Replays the given \a picture at the given \a point.
+
+ The QPicture class is a paint device that records and replays
+ QPainter commands. A picture serializes the painter commands to an
+ IO device in a platform-independent format. Everything that can be
+ painted on a widget or pixmap can also be stored in a picture.
+
+ This function does exactly the same as QPicture::play() when
+ called with \a point = QPoint(0, 0).
+
+ \table 100%
+ \row
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 18
+ \endtable
+
+ \sa QPicture::play()
+*/
+
+void QPainter::drawPicture(const QPointF &p, const QPicture &picture)
+{
+ Q_D(QPainter);
+
+ if (!d->engine)
+ return;
+
+ if (!d->extended)
+ d->updateState(d->state);
+
+ save();
+ translate(p);
+ const_cast<QPicture *>(&picture)->play(this);
+ restore();
+}
+
+/*!
+ \fn void QPainter::drawPicture(const QPoint &point, const QPicture &picture)
+ \overload
+
+ Replays the given \a picture at the given \a point.
+*/
+
+/*!
+ \fn void QPainter::drawPicture(int x, int y, const QPicture &picture)
+ \overload
+
+ Draws the given \a picture at point (\a x, \a y).
+*/
+
+#endif // QT_NO_PICTURE
+
+/*!
+ \fn void QPainter::eraseRect(const QRectF &rectangle)
+
+ Erases the area inside the given \a rectangle. Equivalent to
+ calling
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 19
+
+ \sa fillRect()
+*/
+void QPainter::eraseRect(const QRectF &r)
+{
+ Q_D(QPainter);
+
+ fillRect(r, d->state->bgBrush);
+}
+
+static inline bool needsResolving(const QBrush &brush)
+{
+ Qt::BrushStyle s = brush.style();
+ return ((s == Qt::LinearGradientPattern || s == Qt::RadialGradientPattern ||
+ s == Qt::ConicalGradientPattern) &&
+ brush.gradient()->coordinateMode() == QGradient::ObjectBoundingMode);
+}
+
+/*!
+ \fn void QPainter::eraseRect(const QRect &rectangle)
+ \overload
+
+ Erases the area inside the given \a rectangle.
+*/
+
+/*!
+ \fn void QPainter::eraseRect(int x, int y, int width, int height)
+ \overload
+
+ Erases the area inside the rectangle beginning at (\a x, \a y)
+ with the given \a width and \a height.
+*/
+
+
+/*!
+ \fn void QPainter::fillRect(int x, int y, int width, int height, Qt::BrushStyle style)
+ \overload
+
+ Fills the rectangle beginning at (\a{x}, \a{y}) with the given \a
+ width and \a height, using the brush \a style specified.
+
+ \since 4.5
+*/
+
+/*!
+ \fn void QPainter::fillRect(const QRect &rectangle, Qt::BrushStyle style)
+ \overload
+
+ Fills the given \a rectangle with the brush \a style specified.
+
+ \since 4.5
+*/
+
+/*!
+ \fn void QPainter::fillRect(const QRectF &rectangle, Qt::BrushStyle style)
+ \overload
+
+ Fills the given \a rectangle with the brush \a style specified.
+
+ \since 4.5
+*/
+
+/*!
+ \fn void QPainter::fillRect(const QRectF &rectangle, const QBrush &brush)
+
+ Fills the given \a rectangle with the \a brush specified.
+
+ Alternatively, you can specify a QColor instead of a QBrush; the
+ QBrush constructor (taking a QColor argument) will automatically
+ create a solid pattern brush.
+
+ \sa drawRect()
+*/
+void QPainter::fillRect(const QRectF &r, const QBrush &brush)
+{
+ Q_D(QPainter);
+
+ if (!d->engine)
+ return;
+
+ if (d->extended) {
+ const QGradient *g = brush.gradient();
+ if (!g || g->coordinateMode() == QGradient::LogicalMode) {
+ d->extended->fillRect(r, brush);
+ return;
+ }
+ }
+
+ QPen oldPen = pen();
+ QBrush oldBrush = this->brush();
+ setPen(Qt::NoPen);
+ if (brush.style() == Qt::SolidPattern) {
+ d->colorBrush.setStyle(Qt::SolidPattern);
+ d->colorBrush.setColor(brush.color());
+ setBrush(d->colorBrush);
+ } else {
+ setBrush(brush);
+ }
+
+ drawRect(r);
+ setBrush(oldBrush);
+ setPen(oldPen);
+}
+
+/*!
+ \fn void QPainter::fillRect(const QRect &rectangle, const QBrush &brush)
+ \overload
+
+ Fills the given \a rectangle with the specified \a brush.
+*/
+
+void QPainter::fillRect(const QRect &r, const QBrush &brush)
+{
+ Q_D(QPainter);
+
+ if (!d->engine)
+ return;
+
+ if (d->extended) {
+ const QGradient *g = brush.gradient();
+ if (!g || g->coordinateMode() == QGradient::LogicalMode) {
+ d->extended->fillRect(r, brush);
+ return;
+ }
+ }
+
+ QPen oldPen = pen();
+ QBrush oldBrush = this->brush();
+ setPen(Qt::NoPen);
+ if (brush.style() == Qt::SolidPattern) {
+ d->colorBrush.setStyle(Qt::SolidPattern);
+ d->colorBrush.setColor(brush.color());
+ setBrush(d->colorBrush);
+ } else {
+ setBrush(brush);
+ }
+
+ drawRect(r);
+ setBrush(oldBrush);
+ setPen(oldPen);
+}
+
+
+
+/*!
+ \fn void QPainter::fillRect(const QRect &rectangle, const QColor &color)
+ \overload
+
+ Fills the given \a rectangle with the \a color specified.
+
+ \since 4.5
+*/
+void QPainter::fillRect(const QRect &r, const QColor &color)
+{
+ Q_D(QPainter);
+
+ if (!d->engine)
+ return;
+
+ if (d->extended) {
+ d->extended->fillRect(r, color);
+ return;
+ }
+
+ fillRect(r, QBrush(color));
+}
+
+
+/*!
+ \fn void QPainter::fillRect(const QRectF &rectangle, const QColor &color)
+ \overload
+
+ Fills the given \a rectangle with the \a color specified.
+
+ \since 4.5
+*/
+void QPainter::fillRect(const QRectF &r, const QColor &color)
+{
+ Q_D(QPainter);
+
+ if (!d->engine)
+ return;
+
+ if (d->extended) {
+ d->extended->fillRect(r, color);
+ return;
+ }
+
+ fillRect(r, QBrush(color));
+}
+
+/*!
+ \fn void QPainter::fillRect(int x, int y, int width, int height, const QBrush &brush)
+
+ \overload
+
+ Fills the rectangle beginning at (\a{x}, \a{y}) with the given \a
+ width and \a height, using the given \a brush.
+*/
+
+/*!
+ \fn void QPainter::fillRect(int x, int y, int width, int height, const QColor &color)
+
+ \overload
+
+ Fills the rectangle beginning at (\a{x}, \a{y}) with the given \a
+ width and \a height, using the given \a color.
+
+ \since 4.5
+*/
+
+/*!
+ \fn void QPainter::fillRect(int x, int y, int width, int height, Qt::GlobalColor color)
+
+ \overload
+
+ Fills the rectangle beginning at (\a{x}, \a{y}) with the given \a
+ width and \a height, using the given \a color.
+
+ \since 4.5
+*/
+
+/*!
+ \fn void QPainter::fillRect(const QRect &rectangle, Qt::GlobalColor color);
+
+ \overload
+
+ Fills the given \a rectangle with the specified \a color.
+
+ \since 4.5
+*/
+
+/*!
+ \fn void QPainter::fillRect(const QRectF &rectangle, Qt::GlobalColor color);
+
+ \overload
+
+ Fills the given \a rectangle with the specified \a color.
+
+ \since 4.5
+*/
+
+/*!
+ Sets the given render \a hint on the painter if \a on is true;
+ otherwise clears the render hint.
+
+ \sa setRenderHints(), renderHints(), {QPainter#Rendering
+ Quality}{Rendering Quality}
+*/
+void QPainter::setRenderHint(RenderHint hint, bool on)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::setRenderHint: hint=%x, %s\n", hint, on ? "on" : "off");
+#endif
+
+#ifndef QT_NO_DEBUG
+ static const bool antialiasingDisabled = qgetenv("QT_NO_ANTIALIASING").toInt();
+ if (hint == QPainter::Antialiasing && antialiasingDisabled)
+ return;
+#endif
+
+ setRenderHints(hint, on);
+}
+
+/*!
+ \since 4.2
+
+ Sets the given render \a hints on the painter if \a on is true;
+ otherwise clears the render hints.
+
+ \sa setRenderHint(), renderHints(), {QPainter#Rendering
+ Quality}{Rendering Quality}
+*/
+
+void QPainter::setRenderHints(RenderHints hints, bool on)
+{
+ Q_D(QPainter);
+
+ if (!d->engine) {
+ qWarning("QPainter::setRenderHint: Painter must be active to set rendering hints");
+ return;
+ }
+
+ if (on)
+ d->state->renderHints |= hints;
+ else
+ d->state->renderHints &= ~hints;
+
+ if (d->extended)
+ d->extended->renderHintsChanged();
+ else
+ d->state->dirtyFlags |= QPaintEngine::DirtyHints;
+}
+
+/*!
+ Returns a flag that specifies the rendering hints that are set for
+ this painter.
+
+ \sa testRenderHint(), {QPainter#Rendering Quality}{Rendering Quality}
+*/
+QPainter::RenderHints QPainter::renderHints() const
+{
+ Q_D(const QPainter);
+
+ if (!d->engine)
+ return 0;
+
+ return d->state->renderHints;
+}
+
+/*!
+ \fn bool QPainter::testRenderHint(RenderHint hint) const
+ \since 4.3
+
+ Returns true if \a hint is set; otherwise returns false.
+
+ \sa renderHints(), setRenderHint()
+*/
+
+/*!
+ Returns true if view transformation is enabled; otherwise returns
+ false.
+
+ \sa setViewTransformEnabled(), worldTransform()
+*/
+
+bool QPainter::viewTransformEnabled() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::viewTransformEnabled: Painter not active");
+ return false;
+ }
+ return d->state->VxF;
+}
+
+/*!
+ \fn void QPainter::setWindow(const QRect &rectangle)
+
+ Sets the painter's window to the given \a rectangle, and enables
+ view transformations.
+
+ The window rectangle is part of the view transformation. The
+ window specifies the logical coordinate system. Its sister, the
+ viewport(), specifies the device coordinate system.
+
+ The default window rectangle is the same as the device's
+ rectangle.
+
+ \sa window(), viewTransformEnabled(), {Coordinate
+ System#Window-Viewport Conversion}{Window-Viewport Conversion}
+*/
+
+/*!
+ \fn void QPainter::setWindow(int x, int y, int width, int height)
+ \overload
+
+ Sets the painter's window to the rectangle beginning at (\a x, \a
+ y) and the given \a width and \a height.
+*/
+
+void QPainter::setWindow(const QRect &r)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::setWindow(), [%d,%d,%d,%d]\n", r.x(), r.y(), r.width(), r.height());
+#endif
+
+ Q_D(QPainter);
+
+ if (!d->engine) {
+ qWarning("QPainter::setWindow: Painter not active");
+ return;
+ }
+
+ d->state->wx = r.x();
+ d->state->wy = r.y();
+ d->state->ww = r.width();
+ d->state->wh = r.height();
+
+ d->state->VxF = true;
+ d->updateMatrix();
+}
+
+/*!
+ Returns the window rectangle.
+
+ \sa setWindow(), setViewTransformEnabled()
+*/
+
+QRect QPainter::window() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::window: Painter not active");
+ return QRect();
+ }
+ return QRect(d->state->wx, d->state->wy, d->state->ww, d->state->wh);
+}
+
+/*!
+ \fn void QPainter::setViewport(const QRect &rectangle)
+
+ Sets the painter's viewport rectangle to the given \a rectangle,
+ and enables view transformations.
+
+ The viewport rectangle is part of the view transformation. The
+ viewport specifies the device coordinate system. Its sister, the
+ window(), specifies the logical coordinate system.
+
+ The default viewport rectangle is the same as the device's
+ rectangle.
+
+ \sa viewport(), viewTransformEnabled() {Coordinate
+ System#Window-Viewport Conversion}{Window-Viewport Conversion}
+*/
+
+/*!
+ \fn void QPainter::setViewport(int x, int y, int width, int height)
+ \overload
+
+ Sets the painter's viewport rectangle to be the rectangle
+ beginning at (\a x, \a y) with the given \a width and \a height.
+*/
+
+void QPainter::setViewport(const QRect &r)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::setViewport(), [%d,%d,%d,%d]\n", r.x(), r.y(), r.width(), r.height());
+#endif
+
+ Q_D(QPainter);
+
+ if (!d->engine) {
+ qWarning("QPainter::setViewport: Painter not active");
+ return;
+ }
+
+ d->state->vx = r.x();
+ d->state->vy = r.y();
+ d->state->vw = r.width();
+ d->state->vh = r.height();
+
+ d->state->VxF = true;
+ d->updateMatrix();
+}
+
+/*!
+ Returns the viewport rectangle.
+
+ \sa setViewport(), setViewTransformEnabled()
+*/
+
+QRect QPainter::viewport() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::viewport: Painter not active");
+ return QRect();
+ }
+ return QRect(d->state->vx, d->state->vy, d->state->vw, d->state->vh);
+}
+
+/*! \fn bool QPainter::hasViewXForm() const
+ \compat
+
+ Use viewTransformEnabled() instead.
+*/
+
+/*! \fn bool QPainter::hasWorldXForm() const
+ \compat
+
+ Use worldMatrixEnabled() instead.
+*/
+
+/*! \fn void QPainter::resetXForm()
+ \compat
+
+ Use resetTransform() instead.
+*/
+
+/*! \fn void QPainter::setViewXForm(bool enabled)
+ \compat
+
+ Use setViewTransformEnabled() instead.
+*/
+
+/*! \fn void QPainter::setWorldXForm(bool enabled)
+ \compat
+
+ Use setWorldMatrixEnabled() instead.
+*/
+/*!
+ Enables view transformations if \a enable is true, or disables
+ view transformations if \a enable is false.
+
+ \sa viewTransformEnabled(), {Coordinate System#Window-Viewport
+ Conversion}{Window-Viewport Conversion}
+*/
+
+void QPainter::setViewTransformEnabled(bool enable)
+{
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::setViewTransformEnabled(), enable=%d\n", enable);
+#endif
+
+ Q_D(QPainter);
+
+ if (!d->engine) {
+ qWarning("QPainter::setViewTransformEnabled: Painter not active");
+ return;
+ }
+
+ if (enable == d->state->VxF)
+ return;
+
+ d->state->VxF = enable;
+ d->updateMatrix();
+}
+
+#ifdef QT3_SUPPORT
+
+/*!
+ \obsolete
+
+ Use the worldTransform() combined with QTransform::dx() instead.
+
+ \oldcode
+ QPainter painter(this);
+ qreal x = painter.translationX();
+ \newcode
+ QPainter painter(this);
+ qreal x = painter.worldTransform().dx();
+ \endcode
+*/
+qreal QPainter::translationX() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::translationX: Painter not active");
+ return 0.0;
+ }
+ return d->state->worldMatrix.dx();
+}
+
+/*!
+ \obsolete
+
+ Use the worldTransform() combined with QTransform::dy() instead.
+
+ \oldcode
+ QPainter painter(this);
+ qreal y = painter.translationY();
+ \newcode
+ QPainter painter(this);
+ qreal y = painter.worldTransform().dy();
+ \endcode
+*/
+qreal QPainter::translationY() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::translationY: Painter not active");
+ return 0.0;
+ }
+ return d->state->worldMatrix.dy();
+}
+
+/*!
+ \fn void QPainter::map(int x, int y, int *rx, int *ry) const
+
+ \internal
+
+ Sets (\a{rx}, \a{ry}) to the point that results from applying the
+ painter's current transformation on the point (\a{x}, \a{y}).
+*/
+void QPainter::map(int x, int y, int *rx, int *ry) const
+{
+ QPoint p(x, y);
+ p = p * combinedMatrix();
+ *rx = p.x();
+ *ry = p.y();
+}
+
+/*!
+ \fn QPoint QPainter::xForm(const QPoint &point) const
+
+ Use combinedTransform() instead.
+*/
+
+QPoint QPainter::xForm(const QPoint &p) const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::xForm: Painter not active");
+ return QPoint();
+ }
+ if (d->state->matrix.type() == QTransform::TxNone)
+ return p;
+ return p * combinedMatrix();
+}
+
+
+/*!
+ \fn QRect QPainter::xForm(const QRect &rectangle) const
+ \overload
+
+ Use combinedTransform() instead of this function and call
+ mapRect() on the result to obtain a QRect.
+*/
+
+QRect QPainter::xForm(const QRect &r) const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::xForm: Painter not active");
+ return QRect();
+ }
+ if (d->state->matrix.type() == QTransform::TxNone)
+ return r;
+ return combinedMatrix().mapRect(r);
+}
+
+/*!
+ \fn QPolygon QPainter::xForm(const QPolygon &polygon) const
+ \overload
+
+ Use combinedTransform() instead.
+*/
+
+QPolygon QPainter::xForm(const QPolygon &a) const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::xForm: Painter not active");
+ return QPolygon();
+ }
+ if (d->state->matrix.type() == QTransform::TxNone)
+ return a;
+ return a * combinedMatrix();
+}
+
+/*!
+ \fn QPolygon QPainter::xForm(const QPolygon &polygon, int index, int count) const
+ \overload
+
+ Use combinedTransform() combined with QPolygon::mid() instead.
+
+ \oldcode
+ QPainter painter(this);
+ QPolygon transformed = painter.xForm(polygon, index, count)
+ \newcode
+ QPainter painter(this);
+ QPolygon transformed = polygon.mid(index, count) * painter.combinedTransform();
+ \endcode
+*/
+
+QPolygon QPainter::xForm(const QPolygon &av, int index, int npoints) const
+{
+ int lastPoint = npoints < 0 ? av.size() : index+npoints;
+ QPolygon a(lastPoint-index);
+ memcpy(a.data(), av.data()+index, (lastPoint-index)*sizeof(QPoint));
+ return a * combinedMatrix();
+}
+
+/*!
+ \fn QPoint QPainter::xFormDev(const QPoint &point) const
+ \overload
+ \obsolete
+
+ Use combinedTransform() combined with QTransform::inverted() instead.
+
+ \oldcode
+ QPainter painter(this);
+ QPoint transformed = painter.xFormDev(point);
+ \newcode
+ QPainter painter(this);
+ QPoint transformed = point * painter.combinedTransform().inverted();
+ \endcode
+*/
+
+QPoint QPainter::xFormDev(const QPoint &p) const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::xFormDev: Painter not active");
+ return QPoint();
+ }
+ if(d->state->matrix.type() == QTransform::TxNone)
+ return p;
+ return p * combinedMatrix().inverted();
+}
+
+/*!
+ \fn QRect QPainter::xFormDev(const QRect &rectangle) const
+ \overload
+ \obsolete
+
+ Use combinedTransform() combined with QTransform::inverted() instead.
+
+ \oldcode
+ QPainter painter(this);
+ QRect transformed = painter.xFormDev(rectangle);
+ \newcode
+ QPainter painter(this);
+ QRegion region = QRegion(rectangle) * painter.combinedTransform().inverted();
+ QRect transformed = region.boundingRect();
+ \endcode
+*/
+
+QRect QPainter::xFormDev(const QRect &r) const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::xFormDev: Painter not active");
+ return QRect();
+ }
+ if (d->state->matrix.type() == QTransform::TxNone)
+ return r;
+ return combinedMatrix().inverted().mapRect(r);
+}
+
+/*!
+ \overload
+
+ \fn QPoint QPainter::xFormDev(const QPolygon &polygon) const
+ \obsolete
+
+ Use combinedTransform() combined with QTransform::inverted() instead.
+
+ \oldcode
+ QPainter painter(this);
+ QPolygon transformed = painter.xFormDev(rectangle);
+ \newcode
+ QPainter painter(this);
+ QPolygon transformed = polygon * painter.combinedTransform().inverted();
+ \endcode
+*/
+
+QPolygon QPainter::xFormDev(const QPolygon &a) const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::xFormDev: Painter not active");
+ return QPolygon();
+ }
+ if (d->state->matrix.type() == QTransform::TxNone)
+ return a;
+ return a * combinedMatrix().inverted();
+}
+
+/*!
+ \fn QPolygon QPainter::xFormDev(const QPolygon &polygon, int index, int count) const
+ \overload
+ \obsolete
+
+ Use combinedTransform() combined with QPolygon::mid() and QTransform::inverted() instead.
+
+ \oldcode
+ QPainter painter(this);
+ QPolygon transformed = painter.xFormDev(polygon, index, count);
+ \newcode
+ QPainter painter(this);
+ QPolygon transformed = polygon.mid(index, count) * painter.combinedTransform().inverted();
+ \endcode
+*/
+
+QPolygon QPainter::xFormDev(const QPolygon &ad, int index, int npoints) const
+{
+ Q_D(const QPainter);
+ int lastPoint = npoints < 0 ? ad.size() : index+npoints;
+ QPolygon a(lastPoint-index);
+ memcpy(a.data(), ad.data()+index, (lastPoint-index)*sizeof(QPoint));
+ if (d->state->matrix.type() == QTransform::TxNone)
+ return a;
+ return a * combinedMatrix().inverted();
+}
+
+/*!
+ \fn void QPainter::drawCubicBezier(const QPolygon &controlPoints, int index)
+
+ Draws a cubic Bezier curve defined by the \a controlPoints,
+ starting at \a{controlPoints}\e{[index]} (\a index defaults to 0).
+ Points after \a{controlPoints}\e{[index + 3]} are ignored. Nothing
+ happens if there aren't enough control points.
+
+ Use strokePath() instead.
+
+ \oldcode
+ QPainter painter(this);
+ painter.drawCubicBezier(controlPoints, index)
+ \newcode
+ QPainterPath path;
+ path.moveTo(controlPoints.at(index));
+ path.cubicTo(controlPoints.at(index+1),
+ controlPoints.at(index+2),
+ controlPoints.at(index+3));
+
+ QPainter painter(this);
+ painter.strokePath(path, painter.pen());
+ \endcode
+*/
+void QPainter::drawCubicBezier(const QPolygon &a, int index)
+{
+ Q_D(QPainter);
+
+ if (!d->engine)
+ return;
+
+ if ((int)a.size() - index < 4) {
+ qWarning("QPainter::drawCubicBezier: Cubic Bezier needs 4 control "
+ "points");
+ return;
+ }
+
+ QPainterPath path;
+ path.moveTo(a.at(index));
+ path.cubicTo(a.at(index+1), a.at(index+2), a.at(index+3));
+ strokePath(path, d->state->pen);
+}
+#endif
+
+struct QPaintDeviceRedirection
+{
+ QPaintDeviceRedirection() : device(0), replacement(0), internalWidgetRedirectionIndex(-1) {}
+ QPaintDeviceRedirection(const QPaintDevice *device, QPaintDevice *replacement,
+ const QPoint& offset, int internalWidgetRedirectionIndex)
+ : device(device), replacement(replacement), offset(offset),
+ internalWidgetRedirectionIndex(internalWidgetRedirectionIndex) { }
+ const QPaintDevice *device;
+ QPaintDevice *replacement;
+ QPoint offset;
+ int internalWidgetRedirectionIndex;
+ bool operator==(const QPaintDevice *pdev) const { return device == pdev; }
+ Q_DUMMY_COMPARISON_OPERATOR(QPaintDeviceRedirection)
+};
+
+typedef QList<QPaintDeviceRedirection> QPaintDeviceRedirectionList;
+Q_GLOBAL_STATIC(QPaintDeviceRedirectionList, globalRedirections)
+Q_GLOBAL_STATIC(QMutex, globalRedirectionsMutex)
+Q_GLOBAL_STATIC(QAtomicInt, globalRedirectionAtomic)
+
+/*!
+ \threadsafe
+
+ \obsolete
+
+ Please use QWidget::render() instead.
+
+ Redirects all paint commands for the given paint \a device, to the
+ \a replacement device. The optional point \a offset defines an
+ offset within the source device.
+
+ The redirection will not be effective until the begin() function
+ has been called; make sure to call end() for the given \a
+ device's painter (if any) before redirecting. Call
+ restoreRedirected() to restore the previous redirection.
+
+ \warning Making use of redirections in the QPainter API implies
+ that QPainter::begin() and QPaintDevice destructors need to hold
+ a mutex for a short period. This can impact performance. Use of
+ QWidget::render is strongly encouraged.
+
+ \sa redirected(), restoreRedirected()
+*/
+void QPainter::setRedirected(const QPaintDevice *device,
+ QPaintDevice *replacement,
+ const QPoint &offset)
+{
+ Q_ASSERT(device != 0);
+
+ bool hadInternalWidgetRedirection = false;
+ if (device->devType() == QInternal::Widget) {
+ const QWidgetPrivate *widgetPrivate = static_cast<const QWidget *>(device)->d_func();
+ // This is the case when the widget is in a paint event.
+ if (widgetPrivate->redirectDev) {
+ // Remove internal redirection and put it back into the global redirection list.
+ QPoint oldOffset;
+ QPaintDevice *oldReplacement = widgetPrivate->redirected(&oldOffset);
+ const_cast<QWidgetPrivate *>(widgetPrivate)->restoreRedirected();
+ setRedirected(device, oldReplacement, oldOffset);
+ hadInternalWidgetRedirection = true;
+ }
+ }
+
+ QPoint roffset;
+ QPaintDevice *rdev = redirected(replacement, &roffset);
+
+ QMutexLocker locker(globalRedirectionsMutex());
+ QPaintDeviceRedirectionList *redirections = globalRedirections();
+ Q_ASSERT(redirections != 0);
+ *redirections += QPaintDeviceRedirection(device, rdev ? rdev : replacement, offset + roffset,
+ hadInternalWidgetRedirection ? redirections->size() - 1 : -1);
+ globalRedirectionAtomic()->ref();
+}
+
+/*!
+ \threadsafe
+
+ \obsolete
+
+ Using QWidget::render() obsoletes the use of this function.
+
+ Restores the previous redirection for the given \a device after a
+ call to setRedirected().
+
+ \warning Making use of redirections in the QPainter API implies
+ that QPainter::begin() and QPaintDevice destructors need to hold
+ a mutex for a short period. This can impact performance. Use of
+ QWidget::render is strongly encouraged.
+
+ \sa redirected()
+ */
+void QPainter::restoreRedirected(const QPaintDevice *device)
+{
+ Q_ASSERT(device != 0);
+ QMutexLocker locker(globalRedirectionsMutex());
+ QPaintDeviceRedirectionList *redirections = globalRedirections();
+ Q_ASSERT(redirections != 0);
+ for (int i = redirections->size()-1; i >= 0; --i) {
+ if (redirections->at(i) == device) {
+ globalRedirectionAtomic()->deref();
+ const int internalWidgetRedirectionIndex = redirections->at(i).internalWidgetRedirectionIndex;
+ redirections->removeAt(i);
+ // Restore the internal widget redirection, i.e. remove it from the global
+ // redirection list and put it back into QWidgetPrivate. The index is only set when
+ // someone call QPainter::setRedirected in a widget's paint event and we internally
+ // have a redirection set (typically set in QWidgetPrivate::drawWidget).
+ if (internalWidgetRedirectionIndex >= 0) {
+ Q_ASSERT(internalWidgetRedirectionIndex < redirections->size());
+ const QPaintDeviceRedirection &redirectionDevice = redirections->at(internalWidgetRedirectionIndex);
+ QWidget *widget = static_cast<QWidget *>(const_cast<QPaintDevice *>(device));
+ widget->d_func()->setRedirected(redirectionDevice.replacement, redirectionDevice.offset);
+ redirections->removeAt(internalWidgetRedirectionIndex);
+ }
+ return;
+ }
+ }
+}
+
+/*!
+ \threadsafe
+
+ \obsolete
+
+ Using QWidget::render() obsoletes the use of this function.
+
+ Returns the replacement for given \a device. The optional out
+ parameter \a offset returns the offset within the replaced device.
+
+ \warning Making use of redirections in the QPainter API implies
+ that QPainter::begin() and QPaintDevice destructors need to hold
+ a mutex for a short period. This can impact performance. Use of
+ QWidget::render is strongly encouraged.
+
+ \sa setRedirected(), restoreRedirected()
+*/
+QPaintDevice *QPainter::redirected(const QPaintDevice *device, QPoint *offset)
+{
+ Q_ASSERT(device != 0);
+
+ if (device->devType() == QInternal::Widget) {
+ const QWidgetPrivate *widgetPrivate = static_cast<const QWidget *>(device)->d_func();
+ if (widgetPrivate->redirectDev)
+ return widgetPrivate->redirected(offset);
+ }
+
+ if (!globalRedirectionAtomic() || *globalRedirectionAtomic() == 0)
+ return 0;
+
+ QMutexLocker locker(globalRedirectionsMutex());
+ QPaintDeviceRedirectionList *redirections = globalRedirections();
+ Q_ASSERT(redirections != 0);
+ for (int i = redirections->size()-1; i >= 0; --i)
+ if (redirections->at(i) == device) {
+ if (offset)
+ *offset = redirections->at(i).offset;
+ return redirections->at(i).replacement;
+ }
+ if (offset)
+ *offset = QPoint(0, 0);
+ return 0;
+}
+
+
+void qt_painter_removePaintDevice(QPaintDevice *dev)
+{
+ if (!globalRedirectionAtomic() || *globalRedirectionAtomic() == 0)
+ return;
+
+ QMutex *mutex = 0;
+ QT_TRY {
+ mutex = globalRedirectionsMutex();
+ } QT_CATCH(...) {
+ // ignore the missing mutex, since we could be called from
+ // a destructor, and destructors shall not throw
+ }
+ QMutexLocker locker(mutex);
+ QPaintDeviceRedirectionList *redirections = 0;
+ QT_TRY {
+ redirections = globalRedirections();
+ } QT_CATCH(...) {
+ // do nothing - code below is safe with redirections being 0.
+ }
+ if (redirections) {
+ for (int i = 0; i < redirections->size(); ) {
+ if(redirections->at(i) == dev || redirections->at(i).replacement == dev)
+ redirections->removeAt(i);
+ else
+ ++i;
+ }
+ }
+}
+
+void qt_format_text(const QFont &fnt, const QRectF &_r,
+ int tf, const QString& str, QRectF *brect,
+ int tabstops, int *ta, int tabarraylen,
+ QPainter *painter)
+{
+ qt_format_text(fnt, _r,
+ tf, 0, str, brect,
+ tabstops, ta, tabarraylen,
+ painter);
+}
+void qt_format_text(const QFont &fnt, const QRectF &_r,
+ int tf, const QTextOption *option, const QString& str, QRectF *brect,
+ int tabstops, int *ta, int tabarraylen,
+ QPainter *painter)
+{
+
+ Q_ASSERT( !((tf & ~Qt::TextDontPrint)!=0 && option!=0) ); // we either have an option or flags
+
+ if (option) {
+ tf |= option->alignment();
+ if (option->wrapMode() != QTextOption::NoWrap)
+ tf |= Qt::TextWordWrap;
+
+ if (option->flags() & QTextOption::IncludeTrailingSpaces)
+ tf |= Qt::TextIncludeTrailingSpaces;
+
+ if (option->tabStop() >= 0 || !option->tabArray().isEmpty())
+ tf |= Qt::TextExpandTabs;
+ }
+
+ // we need to copy r here to protect against the case (&r == brect).
+ QRectF r(_r);
+
+ bool dontclip = (tf & Qt::TextDontClip);
+ bool wordwrap = (tf & Qt::TextWordWrap) || (tf & Qt::TextWrapAnywhere);
+ bool singleline = (tf & Qt::TextSingleLine);
+ bool showmnemonic = (tf & Qt::TextShowMnemonic);
+ bool hidemnmemonic = (tf & Qt::TextHideMnemonic);
+
+ Qt::LayoutDirection layout_direction;
+ if (tf & Qt::TextForceLeftToRight)
+ layout_direction = Qt::LeftToRight;
+ else if (tf & Qt::TextForceRightToLeft)
+ layout_direction = Qt::RightToLeft;
+ else if (option)
+ layout_direction = option->textDirection();
+ else if (painter)
+ layout_direction = painter->layoutDirection();
+ else
+ layout_direction = Qt::LeftToRight;
+
+ tf = QStyle::visualAlignment(layout_direction, QFlag(tf));
+
+ bool isRightToLeft = layout_direction == Qt::RightToLeft;
+ bool expandtabs = ((tf & Qt::TextExpandTabs) &&
+ (((tf & Qt::AlignLeft) && !isRightToLeft) ||
+ ((tf & Qt::AlignRight) && isRightToLeft)));
+
+ if (!painter)
+ tf |= Qt::TextDontPrint;
+
+ uint maxUnderlines = 0;
+ int numUnderlines = 0;
+ QVarLengthArray<int, 32> underlinePositions(1);
+
+ QFontMetricsF fm(fnt);
+ QString text = str;
+ int offset = 0;
+start_lengthVariant:
+ bool hasMoreLengthVariants = false;
+ // compatible behaviour to the old implementation. Replace
+ // tabs by spaces
+ int old_offset = offset;
+ for (; offset < text.length(); offset++) {
+ QChar chr = text.at(offset);
+ if (chr == QLatin1Char('\r') || (singleline && chr == QLatin1Char('\n'))) {
+ text[offset] = QLatin1Char(' ');
+ } else if (chr == QLatin1Char('\n')) {
+ text[offset] = QChar::LineSeparator;
+ } else if (chr == QLatin1Char('&')) {
+ ++maxUnderlines;
+ } else if (chr == QLatin1Char('\t')) {
+ if (!expandtabs) {
+ text[offset] = QLatin1Char(' ');
+ } else if (!tabarraylen && !tabstops) {
+ tabstops = qRound(fm.width(QLatin1Char('x'))*8);
+ }
+ } else if (chr == QChar(ushort(0x9c))) {
+ // string with multiple length variants
+ hasMoreLengthVariants = true;
+ break;
+ }
+ }
+
+ int length = offset - old_offset;
+ if ((hidemnmemonic || showmnemonic) && maxUnderlines > 0) {
+ underlinePositions.resize(maxUnderlines + 1);
+
+ QChar *cout = text.data() + old_offset;
+ QChar *cin = cout;
+ int l = length;
+ while (l) {
+ if (*cin == QLatin1Char('&')) {
+ ++cin;
+ --length;
+ --l;
+ if (!l)
+ break;
+ if (*cin != QLatin1Char('&') && !hidemnmemonic)
+ underlinePositions[numUnderlines++] = cout - text.data() - old_offset;
+ }
+ *cout = *cin;
+ ++cout;
+ ++cin;
+ --l;
+ }
+ }
+
+ // no need to do extra work for underlines if we don't paint
+ if (tf & Qt::TextDontPrint)
+ numUnderlines = 0;
+
+ underlinePositions[numUnderlines] = -1;
+ qreal height = 0;
+ qreal width = 0;
+
+ QString finalText = text.mid(old_offset, length);
+ QStackTextEngine engine(finalText, fnt);
+ if (option) {
+ engine.option = *option;
+ }
+
+ if (engine.option.tabStop() < 0 && tabstops > 0)
+ engine.option.setTabStop(tabstops);
+
+ if (engine.option.tabs().isEmpty() && ta) {
+ QList<qreal> tabs;
+ for (int i = 0; i < tabarraylen; i++)
+ tabs.append(qreal(ta[i]));
+ engine.option.setTabArray(tabs);
+ }
+
+ engine.option.setTextDirection(layout_direction);
+ if (tf & Qt::AlignJustify)
+ engine.option.setAlignment(Qt::AlignJustify);
+ else
+ engine.option.setAlignment(Qt::AlignLeft); // do not do alignment twice
+
+ if (!option && (tf & Qt::TextWrapAnywhere))
+ engine.option.setWrapMode(QTextOption::WrapAnywhere);
+
+ if (tf & Qt::TextJustificationForced)
+ engine.forceJustification = true;
+ QTextLayout textLayout(&engine);
+ textLayout.setCacheEnabled(true);
+ textLayout.engine()->underlinePositions = underlinePositions.data();
+
+ if (finalText.isEmpty()) {
+ height = fm.height();
+ width = 0;
+ tf |= Qt::TextDontPrint;
+ } else {
+ qreal lineWidth = 0x01000000;
+ if (wordwrap || (tf & Qt::TextJustificationForced))
+ lineWidth = qMax<qreal>(0, r.width());
+ if(!wordwrap)
+ tf |= Qt::TextIncludeTrailingSpaces;
+ textLayout.engine()->ignoreBidi = bool(tf & Qt::TextDontPrint);
+ textLayout.beginLayout();
+
+ qreal leading = fm.leading();
+ height = -leading;
+
+ while (1) {
+ QTextLine l = textLayout.createLine();
+ if (!l.isValid())
+ break;
+
+ l.setLineWidth(lineWidth);
+ height += leading;
+ l.setPosition(QPointF(0., height));
+ height += l.height();
+ width = qMax(width, l.naturalTextWidth());
+ if (!dontclip && !brect && height >= r.height())
+ break;
+ }
+ textLayout.endLayout();
+ }
+
+ qreal yoff = 0;
+ qreal xoff = 0;
+ if (tf & Qt::AlignBottom) {
+ yoff = r.height() - height;
+ } else if (tf & Qt::AlignVCenter) {
+ yoff = (r.height() - height)/2;
+ if (painter) {
+ QTransform::TransformationType type = painter->transform().type();
+ if (type <= QTransform::TxScale) {
+ // do the rounding manually to work around inconsistencies
+ // in the paint engines when drawing on floating point offsets
+ const qreal scale = painter->transform().m22();
+ if (scale != 0)
+ yoff = -qRound(-yoff * scale) / scale;
+ }
+ }
+ }
+ if (tf & Qt::AlignRight) {
+ xoff = r.width() - width;
+ } else if (tf & Qt::AlignHCenter) {
+ xoff = (r.width() - width)/2;
+ if (painter) {
+ QTransform::TransformationType type = painter->transform().type();
+ if (type <= QTransform::TxScale) {
+ // do the rounding manually to work around inconsistencies
+ // in the paint engines when drawing on floating point offsets
+ const qreal scale = painter->transform().m11();
+ if (scale != 0)
+ xoff = qRound(xoff * scale) / scale;
+ }
+ }
+ }
+ QRectF bounds = QRectF(r.x() + xoff, r.y() + yoff, width, height);
+
+ if (hasMoreLengthVariants && !(tf & Qt::TextLongestVariant) && !r.contains(bounds)) {
+ offset++;
+ goto start_lengthVariant;
+ }
+ if (brect)
+ *brect = bounds;
+
+ if (!(tf & Qt::TextDontPrint)) {
+ bool restore = false;
+ if (!dontclip && !r.contains(bounds)) {
+ restore = true;
+ painter->save();
+ painter->setClipRect(r, Qt::IntersectClip);
+ }
+
+ for (int i = 0; i < textLayout.lineCount(); i++) {
+ QTextLine line = textLayout.lineAt(i);
+
+ qreal advance = line.horizontalAdvance();
+ xoff = 0;
+ if (tf & Qt::AlignRight) {
+ QTextEngine *eng = textLayout.engine();
+ xoff = r.width() - advance -
+ eng->leadingSpaceWidth(eng->lines[line.lineNumber()]).toReal();
+ }
+ else if (tf & Qt::AlignHCenter)
+ xoff = (r.width() - advance) / 2;
+
+ line.draw(painter, QPointF(r.x() + xoff, r.y() + yoff));
+ }
+
+ if (restore) {
+ painter->restore();
+ }
+ }
+}
+
+/*!
+ Sets the layout direction used by the painter when drawing text,
+ to the specified \a direction.
+
+ The default is Qt::LayoutDirectionAuto, which will implicitly determine the
+ direction from the text drawn.
+
+ \sa QTextOption::setTextDirection(), layoutDirection(), drawText(), {QPainter#Settings}{Settings}
+*/
+void QPainter::setLayoutDirection(Qt::LayoutDirection direction)
+{
+ Q_D(QPainter);
+ if (d->state)
+ d->state->layoutDirection = direction;
+}
+
+/*!
+ Returns the layout direction used by the painter when drawing text.
+
+ \sa QTextOption::textDirection(), setLayoutDirection(), drawText(), {QPainter#Settings}{Settings}
+*/
+Qt::LayoutDirection QPainter::layoutDirection() const
+{
+ Q_D(const QPainter);
+ return d->state ? d->state->layoutDirection : Qt::LayoutDirectionAuto;
+}
+
+QPainterState::QPainterState(const QPainterState *s)
+ : brushOrigin(s->brushOrigin), font(s->font), deviceFont(s->deviceFont),
+ pen(s->pen), brush(s->brush), bgBrush(s->bgBrush),
+ clipRegion(s->clipRegion), clipPath(s->clipPath),
+ clipOperation(s->clipOperation),
+ renderHints(s->renderHints), clipInfo(s->clipInfo),
+ worldMatrix(s->worldMatrix), matrix(s->matrix), redirectionMatrix(s->redirectionMatrix),
+ wx(s->wx), wy(s->wy), ww(s->ww), wh(s->wh),
+ vx(s->vx), vy(s->vy), vw(s->vw), vh(s->vh),
+ opacity(s->opacity), WxF(s->WxF), VxF(s->VxF),
+ clipEnabled(s->clipEnabled), bgMode(s->bgMode), painter(s->painter),
+ layoutDirection(s->layoutDirection),
+ composition_mode(s->composition_mode),
+ emulationSpecifier(s->emulationSpecifier), changeFlags(0)
+{
+ dirtyFlags = s->dirtyFlags;
+}
+
+QPainterState::QPainterState()
+ : brushOrigin(0, 0), bgBrush(Qt::white), clipOperation(Qt::NoClip),
+ renderHints(0),
+ wx(0), wy(0), ww(0), wh(0), vx(0), vy(0), vw(0), vh(0),
+ opacity(1), WxF(false), VxF(false), clipEnabled(true),
+ bgMode(Qt::TransparentMode), painter(0),
+ layoutDirection(QApplication::layoutDirection()),
+ composition_mode(QPainter::CompositionMode_SourceOver),
+ emulationSpecifier(0), changeFlags(0)
+{
+ dirtyFlags = 0;
+}
+
+QPainterState::~QPainterState()
+{
+}
+
+void QPainterState::init(QPainter *p) {
+ bgBrush = Qt::white;
+ bgMode = Qt::TransparentMode;
+ WxF = false;
+ VxF = false;
+ clipEnabled = true;
+ wx = wy = ww = wh = 0;
+ vx = vy = vw = vh = 0;
+ painter = p;
+ pen = QPen();
+ brushOrigin = QPointF(0, 0);
+ brush = QBrush();
+ font = deviceFont = QFont();
+ clipRegion = QRegion();
+ clipPath = QPainterPath();
+ clipOperation = Qt::NoClip;
+ clipInfo.clear();
+ worldMatrix.reset();
+ matrix.reset();
+ layoutDirection = QApplication::layoutDirection();
+ composition_mode = QPainter::CompositionMode_SourceOver;
+ emulationSpecifier = 0;
+ dirtyFlags = 0;
+ changeFlags = 0;
+ renderHints = 0;
+ opacity = 1;
+}
+
+#ifdef QT3_SUPPORT
+static void bitBlt_helper(QPaintDevice *dst, const QPoint &dp,
+ const QPaintDevice *src, const QRect &sr, bool)
+{
+ Q_ASSERT(dst);
+ Q_ASSERT(src);
+
+ if (src->devType() == QInternal::Pixmap) {
+ const QPixmap *pixmap = static_cast<const QPixmap *>(src);
+ QPainter pt(dst);
+ pt.drawPixmap(dp, *pixmap, sr);
+
+ } else {
+ qWarning("QPainter: bitBlt only works when source is of type pixmap");
+ }
+}
+
+void bitBlt(QPaintDevice *dst, int dx, int dy,
+ const QPaintDevice *src, int sx, int sy, int sw, int sh,
+ bool ignoreMask )
+{
+ bitBlt_helper(dst, QPoint(dx, dy), src, QRect(sx, sy, sw, sh), ignoreMask);
+}
+
+void bitBlt(QPaintDevice *dst, const QPoint &dp, const QPaintDevice *src, const QRect &sr, bool ignoreMask)
+{
+ bitBlt_helper(dst, dp, src, sr, ignoreMask);
+}
+
+void bitBlt(QPaintDevice *dst, int dx, int dy,
+ const QImage *src, int sx, int sy, int sw, int sh, int fl)
+{
+ Qt::ImageConversionFlags flags(fl);
+ QPixmap srcPixmap = QPixmap::fromImage(*src, flags);
+ bitBlt_helper(dst, QPoint(dx, dy), &srcPixmap, QRect(sx, sy, sw, sh), false);
+}
+
+#endif // QT3_SUPPORT
+
+/*!
+ \fn void QPainter::setBackgroundColor(const QColor &color)
+
+ Use setBackground() instead.
+*/
+
+/*!
+ \fn const QColor &QPainter::backgroundColor() const
+
+ Use background() and QBrush::color() instead.
+
+ \oldcode
+ QColor myColor = backgroundColor();
+ \newcode
+ QColor myColor = background().color();
+ \endcode
+
+ Note that the background can be a complex brush such as a texture
+ or a gradient.
+*/
+
+/*!
+ \fn void QPainter::drawText(int x, int y, const QString &text, int pos, int length)
+ \compat
+
+ Use drawText() combined with QString::mid() instead.
+
+ \oldcode
+ QPainter painter(this);
+ painter.drawText(x, y, text, pos, length);
+ \newcode
+ QPainter painter(this);
+ painter.drawText(x, y, text.mid(pos, length));
+ \endcode
+*/
+
+/*!
+ \fn void QPainter::drawText(const QPoint &point, const QString &text, int pos, int length)
+ \compat
+
+ Use drawText() combined with QString::mid() instead.
+
+ \oldcode
+ QPainter painter(this);
+ painter.drawText(point, text, pos, length);
+ \newcode
+ QPainter painter(this);
+ painter.drawText(point, text.mid(pos, length));
+ \endcode
+*/
+
+/*!
+ \fn void QPainter::drawText(int x, int y, const QString &text, int length)
+ \compat
+
+ Use drawText() combined with QString::left() instead.
+
+ \oldcode
+ QPainter painter(this);
+ painter.drawText(x, y, text, length);
+ \newcode
+ QPainter painter(this);
+ painter.drawText(x, y, text.left(length));
+ \endcode
+*/
+
+/*!
+ \fn void QPainter::drawText(const QPoint &point, const QString &text, int length)
+ \compat
+
+ Use drawText() combined with QString::left() instead.
+
+ \oldcode
+ QPainter painter(this);
+ painter.drawText(point, text, length);
+ \newcode
+ QPainter painter(this);
+ painter.drawText(point, text.left(length));
+ \endcode
+*/
+
+/*!
+ \fn bool QPainter::begin(QPaintDevice *device, const QWidget *init)
+ \compat
+
+ Use begin() instead.
+
+ If the paint \a device is a QWidget, QPainter is initialized after
+ the widget's settings automatically. Otherwise, you must call the
+ initFrom() function to initialize the painters pen, background and
+ font to the same as any given widget.
+
+ \oldcode
+ QPainter painter(this);
+ painter.begin(device, init);
+ \newcode
+ QPainter painter(this);
+ painter.begin(device);
+ painter.initFrom(init);
+ \endcode
+*/
+
+/*!
+ \fn void QPainter::drawImage(const QRectF &target, const QImage &image, const QRectF &source,
+ Qt::ImageConversionFlags flags)
+
+ Draws the rectangular portion \a source of the given \a image
+ into the \a target rectangle in the paint device.
+
+ \note The image is scaled to fit the rectangle, if both the image and rectangle size disagree.
+
+ If the image needs to be modified to fit in a lower-resolution
+ result (e.g. converting from 32-bit to 8-bit), use the \a flags to
+ specify how you would prefer this to happen.
+
+ \table 100%
+ \row
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainter.cpp 20
+ \endtable
+
+ \sa drawPixmap()
+*/
+
+/*!
+ \fn void QPainter::drawImage(const QRect &target, const QImage &image, const QRect &source,
+ Qt::ImageConversionFlags flags)
+ \overload
+
+ Draws the rectangular portion \a source of the given \a image
+ into the \a target rectangle in the paint device.
+
+ \note The image is scaled to fit the rectangle, if both the image and rectangle size disagree.
+*/
+
+/*!
+ \fn void QPainter::drawImage(const QPointF &point, const QImage &image)
+
+ \overload
+
+ Draws the given \a image at the given \a point.
+*/
+
+/*!
+ \fn void QPainter::drawImage(const QPoint &point, const QImage &image)
+
+ \overload
+
+ Draws the given \a image at the given \a point.
+*/
+
+/*!
+ \fn void QPainter::drawImage(const QPointF &point, const QImage &image, const QRectF &source,
+ Qt::ImageConversionFlags flags = 0)
+
+ \overload
+
+ Draws the rectangular portion \a source of the given \a image with
+ its origin at the given \a point.
+*/
+
+/*!
+ \fn void QPainter::drawImage(const QPoint &point, const QImage &image, const QRect &source,
+ Qt::ImageConversionFlags flags = 0)
+ \overload
+
+ Draws the rectangular portion \a source of the given \a image with
+ its origin at the given \a point.
+*/
+
+/*!
+ \fn void QPainter::drawImage(const QRectF &rectangle, const QImage &image)
+
+ \overload
+
+ Draws the given \a image into the given \a rectangle.
+
+ \note The image is scaled to fit the rectangle, if both the image and rectangle size disagree.
+*/
+
+/*!
+ \fn void QPainter::drawImage(const QRect &rectangle, const QImage &image)
+
+ \overload
+
+ Draws the given \a image into the given \a rectangle.
+
+ \note The image is scaled to fit the rectangle, if both the image and rectangle size disagree.
+*/
+
+/*!
+ \fn void QPainter::drawImage(int x, int y, const QImage &image,
+ int sx, int sy, int sw, int sh,
+ Qt::ImageConversionFlags flags)
+ \overload
+
+ Draws an image at (\a{x}, \a{y}) by copying a part of \a image into
+ the paint device.
+
+ (\a{x}, \a{y}) specifies the top-left point in the paint device that is
+ to be drawn onto. (\a{sx}, \a{sy}) specifies the top-left point in \a
+ image that is to be drawn. The default is (0, 0).
+
+ (\a{sw}, \a{sh}) specifies the size of the image that is to be drawn.
+ The default, (0, 0) (and negative) means all the way to the
+ bottom-right of the image.
+*/
+
+/*!
+ \fn void QPainter::redirect(QPaintDevice *pdev, QPaintDevice *replacement)
+
+ Use setRedirected() instead.
+*/
+
+/*!
+ \fn QPaintDevice *QPainter::redirect(QPaintDevice *pdev)
+
+ Use redirected() instead.
+*/
+
+/*!
+ \fn QRect QPainter::boundingRect(const QRect &rectangle, int flags,
+ const QString &text, int length)
+ \compat
+
+ Returns the bounding rectangle for the given \a length of the \a
+ text constrained by the provided \a rectangle.
+
+ Use boundingRect() combined with QString::left() instead.
+
+ \oldcode
+ QRect rectangle = boundingRect(rect, flags, text, length);
+ \newcode
+ QRect rectangle = boundingRect(rect, flags, text.left(length));
+ \endcode
+*/
+
+/*!
+ \fn void QPainter::drawText(const QRect &rectangle, int flags, const QString &text,
+ int length, QRect *br)
+ \compat
+
+ Use drawText() combined with QString::left() instead.
+
+ \oldcode
+ QPainter painter(this);
+ painter.drawText(rectangle, flags, text, length, br );
+ \newcode
+ QPainter painter(this);
+ painter.drawText(rectangle, flags, text.left(length), br );
+ \endcode
+*/
+
+/*!
+ \fn QRect QPainter::boundingRect(int x, int y, int width, int height, int flags,
+ const QString &text, int length);
+
+ \compat
+
+ Returns the bounding rectangle for the given \a length of the \a
+ text constrained by the rectangle that begins at point (\a{x},
+ \a{y}) with the given \a width and \a height.
+
+ Use boundingRect() combined with QString::left() instead.
+
+ \oldcode
+ QRect rectangle = boundingRect(x, y, width, height, flags, text, length);
+ \newcode
+ QRect rectangle = boundingRect(x, y, width, height, flags, text.left(length));
+ \endcode
+*/
+
+/*!
+ \fn void QPainter::drawText(int x, int y, int width, int height, int flags,
+ const QString &text, int length, QRect *br)
+
+ \compat
+
+ Use drawText() combined with QString::left() instead.
+
+ \oldcode
+ QPainter painter(this);
+ painter.drawText(x, y, width, height, flags, text, length, br );
+ \newcode
+ QPainter painter(this);
+ painter.drawText(x, y, width, height, flags, text.left(length), br );
+ \endcode
+*/
+
+
+/*!
+ \class QPaintEngineState
+ \since 4.1
+
+ \brief The QPaintEngineState class provides information about the
+ active paint engine's current state.
+ \reentrant
+
+ QPaintEngineState records which properties that have changed since
+ the last time the paint engine was updated, as well as their
+ current value.
+
+ Which properties that have changed can at any time be retrieved
+ using the state() function. This function returns an instance of
+ the QPaintEngine::DirtyFlags type which stores an OR combination
+ of QPaintEngine::DirtyFlag values. The QPaintEngine::DirtyFlag
+ enum defines whether a property has changed since the last update
+ or not.
+
+ If a property is marked with a dirty flag, its current value can
+ be retrieved using the corresponding get function:
+
+ \target GetFunction
+
+ \table
+ \header \o Property Flag \o Current Property Value
+ \row \o QPaintEngine::DirtyBackground \o backgroundBrush()
+ \row \o QPaintEngine::DirtyBackgroundMode \o backgroundMode()
+ \row \o QPaintEngine::DirtyBrush \o brush()
+ \row \o QPaintEngine::DirtyBrushOrigin \o brushOrigin()
+ \row \o QPaintEngine::DirtyClipRegion \e or QPaintEngine::DirtyClipPath
+ \o clipOperation()
+ \row \o QPaintEngine::DirtyClipPath \o clipPath()
+ \row \o QPaintEngine::DirtyClipRegion \o clipRegion()
+ \row \o QPaintEngine::DirtyCompositionMode \o compositionMode()
+ \row \o QPaintEngine::DirtyFont \o font()
+ \row \o QPaintEngine::DirtyTransform \o transform()
+ \row \o QPaintEngine::DirtyClipEnabled \o isClipEnabled()
+ \row \o QPaintEngine::DirtyPen \o pen()
+ \row \o QPaintEngine::DirtyHints \o renderHints()
+ \endtable
+
+ The QPaintEngineState class also provide the painter() function
+ which returns a pointer to the painter that is currently updating
+ the paint engine.
+
+ An instance of this class, representing the current state of the
+ active paint engine, is passed as argument to the
+ QPaintEngine::updateState() function. The only situation in which
+ you will have to use this class directly is when implementing your
+ own paint engine.
+
+ \sa QPaintEngine
+*/
+
+
+/*!
+ \fn QPaintEngine::DirtyFlags QPaintEngineState::state() const
+
+ Returns a combination of flags identifying the set of properties
+ that need to be updated when updating the paint engine's state
+ (i.e. during a call to the QPaintEngine::updateState() function).
+
+ \sa QPaintEngine::updateState()
+*/
+
+
+/*!
+ Returns the pen in the current paint engine state.
+
+ This variable should only be used when the state() returns a
+ combination which includes the QPaintEngine::DirtyPen flag.
+
+ \sa state(), QPaintEngine::updateState()
+*/
+
+QPen QPaintEngineState::pen() const
+{
+ return static_cast<const QPainterState *>(this)->pen;
+}
+
+/*!
+ Returns the brush in the current paint engine state.
+
+ This variable should only be used when the state() returns a
+ combination which includes the QPaintEngine::DirtyBrush flag.
+
+ \sa state(), QPaintEngine::updateState()
+*/
+
+QBrush QPaintEngineState::brush() const
+{
+ return static_cast<const QPainterState *>(this)->brush;
+}
+
+/*!
+ Returns the brush origin in the current paint engine state.
+
+ This variable should only be used when the state() returns a
+ combination which includes the QPaintEngine::DirtyBrushOrigin flag.
+
+ \sa state(), QPaintEngine::updateState()
+*/
+
+QPointF QPaintEngineState::brushOrigin() const
+{
+ return static_cast<const QPainterState *>(this)->brushOrigin;
+}
+
+/*!
+ Returns the background brush in the current paint engine state.
+
+ This variable should only be used when the state() returns a
+ combination which includes the QPaintEngine::DirtyBackground flag.
+
+ \sa state(), QPaintEngine::updateState()
+*/
+
+QBrush QPaintEngineState::backgroundBrush() const
+{
+ return static_cast<const QPainterState *>(this)->bgBrush;
+}
+
+/*!
+ Returns the background mode in the current paint engine
+ state.
+
+ This variable should only be used when the state() returns a
+ combination which includes the QPaintEngine::DirtyBackgroundMode flag.
+
+ \sa state(), QPaintEngine::updateState()
+*/
+
+Qt::BGMode QPaintEngineState::backgroundMode() const
+{
+ return static_cast<const QPainterState *>(this)->bgMode;
+}
+
+/*!
+ Returns the font in the current paint engine
+ state.
+
+ This variable should only be used when the state() returns a
+ combination which includes the QPaintEngine::DirtyFont flag.
+
+ \sa state(), QPaintEngine::updateState()
+*/
+
+QFont QPaintEngineState::font() const
+{
+ return static_cast<const QPainterState *>(this)->font;
+}
+
+/*!
+ \since 4.2
+ \obsolete
+
+ Returns the matrix in the current paint engine
+ state.
+
+ \note It is advisable to use transform() instead of this function to
+ preserve the properties of perspective transformations.
+
+ This variable should only be used when the state() returns a
+ combination which includes the QPaintEngine::DirtyTransform flag.
+
+ \sa state(), QPaintEngine::updateState()
+*/
+
+QMatrix QPaintEngineState::matrix() const
+{
+ const QPainterState *st = static_cast<const QPainterState *>(this);
+
+ return st->matrix.toAffine();
+}
+
+/*!
+ \since 4.3
+
+ Returns the matrix in the current paint engine state.
+
+ This variable should only be used when the state() returns a
+ combination which includes the QPaintEngine::DirtyTransform flag.
+
+ \sa state(), QPaintEngine::updateState()
+*/
+
+
+QTransform QPaintEngineState::transform() const
+{
+ const QPainterState *st = static_cast<const QPainterState *>(this);
+
+ return st->matrix;
+}
+
+
+/*!
+ Returns the clip operation in the current paint engine
+ state.
+
+ This variable should only be used when the state() returns a
+ combination which includes either the QPaintEngine::DirtyClipPath
+ or the QPaintEngine::DirtyClipRegion flag.
+
+ \sa state(), QPaintEngine::updateState()
+*/
+
+Qt::ClipOperation QPaintEngineState::clipOperation() const
+{
+ return static_cast<const QPainterState *>(this)->clipOperation;
+}
+
+/*!
+ \since 4.3
+
+ Returns whether the coordinate of the fill have been specified
+ as bounded by the current rendering operation and have to be
+ resolved (about the currently rendered primitive).
+*/
+bool QPaintEngineState::brushNeedsResolving() const
+{
+ const QBrush &brush = static_cast<const QPainterState *>(this)->brush;
+ return needsResolving(brush);
+}
+
+
+/*!
+ \since 4.3
+
+ Returns whether the coordinate of the stroke have been specified
+ as bounded by the current rendering operation and have to be
+ resolved (about the currently rendered primitive).
+*/
+bool QPaintEngineState::penNeedsResolving() const
+{
+ const QPen &pen = static_cast<const QPainterState *>(this)->pen;
+ return needsResolving(pen.brush());
+}
+
+/*!
+ Returns the clip region in the current paint engine state.
+
+ This variable should only be used when the state() returns a
+ combination which includes the QPaintEngine::DirtyClipRegion flag.
+
+ \sa state(), QPaintEngine::updateState()
+*/
+
+QRegion QPaintEngineState::clipRegion() const
+{
+ return static_cast<const QPainterState *>(this)->clipRegion;
+}
+
+/*!
+ Returns the clip path in the current paint engine state.
+
+ This variable should only be used when the state() returns a
+ combination which includes the QPaintEngine::DirtyClipPath flag.
+
+ \sa state(), QPaintEngine::updateState()
+*/
+
+QPainterPath QPaintEngineState::clipPath() const
+{
+ return static_cast<const QPainterState *>(this)->clipPath;
+}
+
+/*!
+ Returns whether clipping is enabled or not in the current paint
+ engine state.
+
+ This variable should only be used when the state() returns a
+ combination which includes the QPaintEngine::DirtyClipEnabled
+ flag.
+
+ \sa state(), QPaintEngine::updateState()
+*/
+
+bool QPaintEngineState::isClipEnabled() const
+{
+ return static_cast<const QPainterState *>(this)->clipEnabled;
+}
+
+/*!
+ Returns the render hints in the current paint engine state.
+
+ This variable should only be used when the state() returns a
+ combination which includes the QPaintEngine::DirtyHints
+ flag.
+
+ \sa state(), QPaintEngine::updateState()
+*/
+
+QPainter::RenderHints QPaintEngineState::renderHints() const
+{
+ return static_cast<const QPainterState *>(this)->renderHints;
+}
+
+/*!
+ Returns the composition mode in the current paint engine state.
+
+ This variable should only be used when the state() returns a
+ combination which includes the QPaintEngine::DirtyCompositionMode
+ flag.
+
+ \sa state(), QPaintEngine::updateState()
+*/
+
+QPainter::CompositionMode QPaintEngineState::compositionMode() const
+{
+ return static_cast<const QPainterState *>(this)->composition_mode;
+}
+
+
+/*!
+ Returns a pointer to the painter currently updating the paint
+ engine.
+*/
+
+QPainter *QPaintEngineState::painter() const
+{
+ return static_cast<const QPainterState *>(this)->painter;
+}
+
+
+/*!
+ \since 4.2
+
+ Returns the opacity in the current paint engine state.
+*/
+
+qreal QPaintEngineState::opacity() const
+{
+ return static_cast<const QPainterState *>(this)->opacity;
+}
+
+/*!
+ \since 4.3
+
+ Sets the world transformation matrix.
+ If \a combine is true, the specified \a transform is combined with
+ the current matrix; otherwise it replaces the current matrix.
+
+ \sa transform() setWorldTransform()
+*/
+
+void QPainter::setTransform(const QTransform &transform, bool combine )
+{
+ setWorldTransform(transform, combine);
+}
+
+/*!
+ Returns the world transformation matrix.
+
+ \sa worldTransform()
+*/
+
+const QTransform & QPainter::transform() const
+{
+ return worldTransform();
+}
+
+
+/*!
+ Returns the matrix that transforms from logical coordinates to
+ device coordinates of the platform dependent paint device.
+
+ This function is \e only needed when using platform painting
+ commands on the platform dependent handle (Qt::HANDLE), and the
+ platform does not do transformations nativly.
+
+ The QPaintEngine::PaintEngineFeature enum can be queried to
+ determine whether the platform performs the transformations or
+ not.
+
+ \sa worldTransform(), QPaintEngine::hasFeature(),
+*/
+
+const QTransform & QPainter::deviceTransform() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::deviceTransform: Painter not active");
+ return d->fakeState()->transform;
+ }
+ return d->state->matrix;
+}
+
+
+/*!
+ Resets any transformations that were made using translate(),
+ scale(), shear(), rotate(), setWorldTransform(), setViewport()
+ and setWindow().
+
+ \sa {Coordinate Transformations}
+*/
+
+void QPainter::resetTransform()
+{
+ Q_D(QPainter);
+#ifdef QT_DEBUG_DRAW
+ if (qt_show_painter_debug_output)
+ printf("QPainter::resetMatrix()\n");
+#endif
+ if (!d->engine) {
+ qWarning("QPainter::resetMatrix: Painter not active");
+ return;
+ }
+
+ d->state->wx = d->state->wy = d->state->vx = d->state->vy = 0; // default view origins
+ d->state->ww = d->state->vw = d->device->metric(QPaintDevice::PdmWidth);
+ d->state->wh = d->state->vh = d->device->metric(QPaintDevice::PdmHeight);
+ d->state->worldMatrix = QTransform();
+ setMatrixEnabled(false);
+ setViewTransformEnabled(false);
+ if (d->extended)
+ d->extended->transformChanged();
+ else
+ d->state->dirtyFlags |= QPaintEngine::DirtyTransform;
+}
+
+/*!
+ Sets the world transformation matrix.
+ If \a combine is true, the specified \a matrix is combined with the current matrix;
+ otherwise it replaces the current matrix.
+
+ \sa transform(), setTransform()
+*/
+
+void QPainter::setWorldTransform(const QTransform &matrix, bool combine )
+{
+ Q_D(QPainter);
+
+ if (!d->engine) {
+ qWarning("QPainter::setWorldTransform: Painter not active");
+ return;
+ }
+
+ if (combine)
+ d->state->worldMatrix = matrix * d->state->worldMatrix; // combines
+ else
+ d->state->worldMatrix = matrix; // set new matrix
+
+ d->state->WxF = true;
+ d->updateMatrix();
+}
+
+/*!
+ Returns the world transformation matrix.
+*/
+
+const QTransform & QPainter::worldTransform() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::worldTransform: Painter not active");
+ return d->fakeState()->transform;
+ }
+ return d->state->worldMatrix;
+}
+
+/*!
+ Returns the transformation matrix combining the current
+ window/viewport and world transformation.
+
+ \sa setWorldTransform(), setWindow(), setViewport()
+*/
+
+QTransform QPainter::combinedTransform() const
+{
+ Q_D(const QPainter);
+ if (!d->engine) {
+ qWarning("QPainter::combinedTransform: Painter not active");
+ return QTransform();
+ }
+ return d->state->worldMatrix * d->viewTransform();
+}
+
+/*!
+ \since 4.7
+
+ This function is used to draw \a pixmap, or a sub-rectangle of \a pixmap,
+ at multiple positions with different scale, rotation and opacity. \a
+ fragments is an array of \a fragmentCount elements specifying the
+ parameters used to draw each pixmap fragment. The \a hints
+ parameter can be used to pass in drawing hints.
+
+ This function is potentially faster than multiple calls to drawPixmap(),
+ since the backend can optimize state changes.
+
+ \sa QPainter::PixmapFragment, QPainter::PixmapFragmentHint
+*/
+
+void QPainter::drawPixmapFragments(const PixmapFragment *fragments, int fragmentCount,
+ const QPixmap &pixmap, PixmapFragmentHints hints)
+{
+ Q_D(QPainter);
+
+ if (!d->engine || pixmap.isNull())
+ return;
+
+#ifndef QT_NO_DEBUG
+ for (int i = 0; i < fragmentCount; ++i) {
+ QRectF sourceRect(fragments[i].sourceLeft, fragments[i].sourceTop,
+ fragments[i].width, fragments[i].height);
+ if (!(QRectF(pixmap.rect()).contains(sourceRect)))
+ qWarning("QPainter::drawPixmapFragments - the source rect is not contained by the pixmap's rectangle");
+ }
+#endif
+
+ if (d->engine->isExtended()) {
+ d->extended->drawPixmapFragments(fragments, fragmentCount, pixmap, hints);
+ } else {
+ qreal oldOpacity = opacity();
+ QTransform oldTransform = transform();
+
+ for (int i = 0; i < fragmentCount; ++i) {
+ QTransform transform = oldTransform;
+ qreal xOffset = 0;
+ qreal yOffset = 0;
+ if (fragments[i].rotation == 0) {
+ xOffset = fragments[i].x;
+ yOffset = fragments[i].y;
+ } else {
+ transform.translate(fragments[i].x, fragments[i].y);
+ transform.rotate(fragments[i].rotation);
+ }
+ setOpacity(oldOpacity * fragments[i].opacity);
+ setTransform(transform);
+
+ qreal w = fragments[i].scaleX * fragments[i].width;
+ qreal h = fragments[i].scaleY * fragments[i].height;
+ QRectF sourceRect(fragments[i].sourceLeft, fragments[i].sourceTop,
+ fragments[i].width, fragments[i].height);
+ drawPixmap(QRectF(-0.5 * w + xOffset, -0.5 * h + yOffset, w, h), pixmap, sourceRect);
+ }
+
+ setOpacity(oldOpacity);
+ setTransform(oldTransform);
+ }
+}
+
+/*!
+ \since 4.7
+ \class QPainter::PixmapFragment
+
+ \brief This class is used in conjunction with the
+ QPainter::drawPixmapFragments() function to specify how a pixmap, or
+ sub-rect of a pixmap, is drawn.
+
+ The \a sourceLeft, \a sourceTop, \a width and \a height variables are used
+ as a source rectangle within the pixmap passed into the
+ QPainter::drawPixmapFragments() function. The variables \a x, \a y, \a
+ width and \a height are used to calculate the target rectangle that is
+ drawn. \a x and \a y denotes the center of the target rectangle. The \a
+ width and \a height in the target rectangle is scaled by the \a scaleX and
+ \a scaleY values. The resulting target rectangle is then rotated \a
+ rotation degrees around the \a x, \a y center point.
+
+ \sa QPainter::drawPixmapFragments()
+*/
+
+/*!
+ \since 4.7
+
+ This is a convenience function that returns a QPainter::PixmapFragment that is
+ initialized with the \a pos, \a sourceRect, \a scaleX, \a scaleY, \a
+ rotation, \a opacity parameters.
+*/
+
+QPainter::PixmapFragment QPainter::PixmapFragment::create(const QPointF &pos, const QRectF &sourceRect,
+ qreal scaleX, qreal scaleY, qreal rotation,
+ qreal opacity)
+{
+ PixmapFragment fragment = {pos.x(), pos.y(), sourceRect.x(), sourceRect.y(), sourceRect.width(),
+ sourceRect.height(), scaleX, scaleY, rotation, opacity};
+ return fragment;
+}
+
+/*!
+ \variable QPainter::PixmapFragment::x
+ \brief the x coordinate of center point in the target rectangle.
+*/
+
+/*!
+ \variable QPainter::PixmapFragment::y
+ \brief the y coordinate of the center point in the target rectangle.
+*/
+
+/*!
+ \variable QPainter::PixmapFragment::sourceLeft
+ \brief the left coordinate of the source rectangle.
+*/
+
+/*!
+ \variable QPainter::PixmapFragment::sourceTop
+ \brief the top coordinate of the source rectangle.
+*/
+
+/*!
+ \variable QPainter::PixmapFragment::width
+
+ \brief the width of the source rectangle and is used to calculate the width
+ of the target rectangle.
+*/
+
+/*!
+ \variable QPainter::PixmapFragment::height
+
+ \brief the height of the source rectangle and is used to calculate the
+ height of the target rectangle.
+*/
+
+/*!
+ \variable QPainter::PixmapFragment::scaleX
+ \brief the horizontal scale of the target rectangle.
+*/
+
+/*!
+ \variable QPainter::PixmapFragment::scaleY
+ \brief the vertical scale of the target rectangle.
+*/
+
+/*!
+ \variable QPainter::PixmapFragment::rotation
+
+ \brief the rotation of the target rectangle in degrees. The target
+ rectangle is rotated after it has been scaled.
+*/
+
+/*!
+ \variable QPainter::PixmapFragment::opacity
+
+ \brief the opacity of the target rectangle, where 0.0 is fully transparent
+ and 1.0 is fully opaque.
+*/
+
+/*!
+ \since 4.7
+
+ \enum QPainter::PixmapFragmentHint
+
+ \value OpaqueHint Indicates that the pixmap fragments to be drawn are
+ opaque. Opaque fragments are potentially faster to draw.
+
+ \sa QPainter::drawPixmapFragments(), QPainter::PixmapFragment
+*/
+
+void qt_draw_helper(QPainterPrivate *p, const QPainterPath &path, QPainterPrivate::DrawOperation operation)
+{
+ p->draw_helper(path, operation);
+}
+
+/*! \fn Display *QPaintDevice::x11Display() const
+ Use QX11Info::display() instead.
+
+ \oldcode
+ Display *display = widget->x11Display();
+ \newcode
+ Display *display = QX11Info::display();
+ \endcode
+
+ \sa QWidget::x11Info(), QX11Info::display()
+*/
+
+/*! \fn int QPaintDevice::x11Screen() const
+ Use QX11Info::screen() instead.
+
+ \oldcode
+ int screen = widget->x11Screen();
+ \newcode
+ int screen = widget->x11Info().screen();
+ \endcode
+
+ \sa QWidget::x11Info(), QPixmap::x11Info()
+*/
+
+/*! \fn void *QPaintDevice::x11Visual() const
+ Use QX11Info::visual() instead.
+
+ \oldcode
+ void *visual = widget->x11Visual();
+ \newcode
+ void *visual = widget->x11Info().visual();
+ \endcode
+
+ \sa QWidget::x11Info(), QPixmap::x11Info()
+*/
+
+/*! \fn int QPaintDevice::x11Depth() const
+ Use QX11Info::depth() instead.
+
+ \oldcode
+ int depth = widget->x11Depth();
+ \newcode
+ int depth = widget->x11Info().depth();
+ \endcode
+
+ \sa QWidget::x11Info(), QPixmap::x11Info()
+*/
+
+/*! \fn int QPaintDevice::x11Cells() const
+ Use QX11Info::cells() instead.
+
+ \oldcode
+ int cells = widget->x11Cells();
+ \newcode
+ int cells = widget->x11Info().cells();
+ \endcode
+
+ \sa QWidget::x11Info(), QPixmap::x11Info()
+*/
+
+/*! \fn Qt::HANDLE QPaintDevice::x11Colormap() const
+ Use QX11Info::colormap() instead.
+
+ \oldcode
+ unsigned long screen = widget->x11Colormap();
+ \newcode
+ unsigned long screen = widget->x11Info().colormap();
+ \endcode
+
+ \sa QWidget::x11Info(), QPixmap::x11Info()
+*/
+
+/*! \fn bool QPaintDevice::x11DefaultColormap() const
+ Use QX11Info::defaultColormap() instead.
+
+ \oldcode
+ bool isDefault = widget->x11DefaultColormap();
+ \newcode
+ bool isDefault = widget->x11Info().defaultColormap();
+ \endcode
+
+ \sa QWidget::x11Info(), QPixmap::x11Info()
+*/
+
+/*! \fn bool QPaintDevice::x11DefaultVisual() const
+ Use QX11Info::defaultVisual() instead.
+
+ \oldcode
+ bool isDefault = widget->x11DefaultVisual();
+ \newcode
+ bool isDefault = widget->x11Info().defaultVisual();
+ \endcode
+
+ \sa QWidget::x11Info(), QPixmap::x11Info()
+*/
+
+/*! \fn void *QPaintDevice::x11AppVisual(int screen)
+ Use QX11Info::visual() instead.
+
+ \oldcode
+ void *visual = QPaintDevice::x11AppVisual(screen);
+ \newcode
+ void *visual = qApp->x11Info(screen).visual();
+ \endcode
+
+ \sa QWidget::x11Info(), QPixmap::x11Info()
+*/
+
+/*! \fn Qt::HANDLE QPaintDevice::x11AppColormap(int screen)
+ Use QX11Info::colormap() instead.
+
+ \oldcode
+ unsigned long colormap = QPaintDevice::x11AppColormap(screen);
+ \newcode
+ unsigned long colormap = qApp->x11Info(screen).colormap();
+ \endcode
+
+ \sa QWidget::x11Info(), QPixmap::x11Info()
+*/
+
+/*! \fn Display *QPaintDevice::x11AppDisplay()
+ Use QX11Info::display() instead.
+
+ \oldcode
+ Display *display = QPaintDevice::x11AppDisplay();
+ \newcode
+ Display *display = qApp->x11Info().display();
+ \endcode
+
+ \sa QWidget::x11Info(), QPixmap::x11Info()
+*/
+
+/*! \fn int QPaintDevice::x11AppScreen()
+ Use QX11Info::screen() instead.
+
+ \oldcode
+ int screen = QPaintDevice::x11AppScreen();
+ \newcode
+ int screen = qApp->x11Info().screen();
+ \endcode
+
+ \sa QWidget::x11Info(), QPixmap::x11Info()
+*/
+
+/*! \fn int QPaintDevice::x11AppDepth(int screen)
+ Use QX11Info::depth() instead.
+
+ \oldcode
+ int depth = QPaintDevice::x11AppDepth(screen);
+ \newcode
+ int depth = qApp->x11Info(screen).depth();
+ \endcode
+
+ \sa QWidget::x11Info(), QPixmap::x11Info()
+*/
+
+/*! \fn int QPaintDevice::x11AppCells(int screen)
+ Use QX11Info::cells() instead.
+
+ \oldcode
+ int cells = QPaintDevice::x11AppCells(screen);
+ \newcode
+ int cells = qApp->x11Info(screen).cells();
+ \endcode
+
+ \sa QWidget::x11Info(), QPixmap::x11Info()
+*/
+
+/*! \fn Qt::HANDLE QPaintDevice::x11AppRootWindow(int screen)
+ Use QX11Info::appRootWindow() instead.
+
+ \oldcode
+ unsigned long window = QPaintDevice::x11AppRootWindow(screen);
+ \newcode
+ unsigned long window = qApp->x11Info(screen).appRootWindow();
+ \endcode
+
+ \sa QWidget::x11Info(), QPixmap::x11Info()
+*/
+
+/*! \fn bool QPaintDevice::x11AppDefaultColormap(int screen)
+ Use QX11Info::defaultColormap() instead.
+
+ \oldcode
+ bool isDefault = QPaintDevice::x11AppDefaultColormap(screen);
+ \newcode
+ bool isDefault = qApp->x11Info(screen).defaultColormap();
+ \endcode
+
+ \sa QWidget::x11Info(), QPixmap::x11Info()
+*/
+
+/*! \fn bool QPaintDevice::x11AppDefaultVisual(int screen)
+ Use QX11Info::defaultVisual() instead.
+
+ \oldcode
+ bool isDefault = QPaintDevice::x11AppDefaultVisual(screen);
+ \newcode
+ bool isDefault = qApp->x11Info(screen).defaultVisual();
+ \endcode
+
+ \sa QWidget::x11Info(), QPixmap::x11Info()
+*/
+
+/*! \fn void QPaintDevice::x11SetAppDpiX(int dpi, int screen)
+ Use QX11Info::setAppDpiX() instead.
+*/
+
+/*! \fn void QPaintDevice::x11SetAppDpiY(int dpi, int screen)
+ Use QX11Info::setAppDpiY() instead.
+*/
+
+/*! \fn int QPaintDevice::x11AppDpiX(int screen)
+ Use QX11Info::appDpiX() instead.
+
+ \oldcode
+ bool isDefault = QPaintDevice::x11AppDpiX(screen);
+ \newcode
+ bool isDefault = qApp->x11Info(screen).appDpiX();
+ \endcode
+
+ \sa QWidget::x11Info(), QPixmap::x11Info()
+*/
+
+/*! \fn int QPaintDevice::x11AppDpiY(int screen)
+ Use QX11Info::appDpiY() instead.
+
+ \oldcode
+ bool isDefault = QPaintDevice::x11AppDpiY(screen);
+ \newcode
+ bool isDefault = qApp->x11Info(screen).appDpiY();
+ \endcode
+
+ \sa QWidget::x11Info(), QPixmap::x11Info()
+*/
+
+/*! \fn HDC QPaintDevice::getDC() const
+ \internal
+*/
+
+/*! \fn void QPaintDevice::releaseDC(HDC) const
+ \internal
+*/
+
+/*! \fn QWSDisplay *QPaintDevice::qwsDisplay()
+ \internal
+*/
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qpainter.h b/src/gui/painting/qpainter.h
new file mode 100644
index 0000000000..4b2c447cca
--- /dev/null
+++ b/src/gui/painting/qpainter.h
@@ -0,0 +1,1008 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTER_H
+#define QPAINTER_H
+
+#include <QtCore/qnamespace.h>
+#include <QtCore/qrect.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qscopedpointer.h>
+#include <QtGui/qpixmap.h>
+#include <QtGui/qimage.h>
+#include <QtGui/qtextoption.h>
+#include <QtGui/qdrawutil.h>
+
+#ifndef QT_INCLUDE_COMPAT
+#include <QtGui/qpolygon.h>
+#include <QtGui/qpen.h>
+#include <QtGui/qbrush.h>
+#include <QtGui/qmatrix.h>
+#include <QtGui/qtransform.h>
+#include <QtGui/qfontinfo.h>
+#include <QtGui/qfontmetrics.h>
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QBrush;
+class QFontInfo;
+class QFontMetrics;
+class QPaintDevice;
+class QPainterPath;
+class QPainterPrivate;
+class QPen;
+class QPolygon;
+class QTextItem;
+class QMatrix;
+class QTransform;
+class QStaticText;
+class QGlyphs;
+
+class QPainterPrivateDeleter;
+
+class Q_GUI_EXPORT QPainter
+{
+ Q_DECLARE_PRIVATE(QPainter)
+ Q_GADGET
+ Q_FLAGS(RenderHint RenderHints)
+
+public:
+ enum RenderHint {
+ Antialiasing = 0x01,
+ TextAntialiasing = 0x02,
+ SmoothPixmapTransform = 0x04,
+ HighQualityAntialiasing = 0x08,
+ NonCosmeticDefaultPen = 0x10
+ };
+
+ Q_DECLARE_FLAGS(RenderHints, RenderHint)
+
+ class PixmapFragment {
+ public:
+ qreal x;
+ qreal y;
+ qreal sourceLeft;
+ qreal sourceTop;
+ qreal width;
+ qreal height;
+ qreal scaleX;
+ qreal scaleY;
+ qreal rotation;
+ qreal opacity;
+ static PixmapFragment Q_GUI_EXPORT create(const QPointF &pos, const QRectF &sourceRect,
+ qreal scaleX = 1, qreal scaleY = 1,
+ qreal rotation = 0, qreal opacity = 1);
+ };
+
+ enum PixmapFragmentHint {
+ OpaqueHint = 0x01
+ };
+
+ Q_DECLARE_FLAGS(PixmapFragmentHints, PixmapFragmentHint)
+
+ QPainter();
+ explicit QPainter(QPaintDevice *);
+ ~QPainter();
+
+ QPaintDevice *device() const;
+
+ bool begin(QPaintDevice *);
+ bool end();
+ bool isActive() const;
+
+ void initFrom(const QWidget *widget);
+
+ enum CompositionMode {
+ CompositionMode_SourceOver,
+ CompositionMode_DestinationOver,
+ CompositionMode_Clear,
+ CompositionMode_Source,
+ CompositionMode_Destination,
+ CompositionMode_SourceIn,
+ CompositionMode_DestinationIn,
+ CompositionMode_SourceOut,
+ CompositionMode_DestinationOut,
+ CompositionMode_SourceAtop,
+ CompositionMode_DestinationAtop,
+ CompositionMode_Xor,
+
+ //svg 1.2 blend modes
+ CompositionMode_Plus,
+ CompositionMode_Multiply,
+ CompositionMode_Screen,
+ CompositionMode_Overlay,
+ CompositionMode_Darken,
+ CompositionMode_Lighten,
+ CompositionMode_ColorDodge,
+ CompositionMode_ColorBurn,
+ CompositionMode_HardLight,
+ CompositionMode_SoftLight,
+ CompositionMode_Difference,
+ CompositionMode_Exclusion,
+
+ // ROPs
+ RasterOp_SourceOrDestination,
+ RasterOp_SourceAndDestination,
+ RasterOp_SourceXorDestination,
+ RasterOp_NotSourceAndNotDestination,
+ RasterOp_NotSourceOrNotDestination,
+ RasterOp_NotSourceXorDestination,
+ RasterOp_NotSource,
+ RasterOp_NotSourceAndDestination,
+ RasterOp_SourceAndNotDestination
+ };
+ void setCompositionMode(CompositionMode mode);
+ CompositionMode compositionMode() const;
+
+ const QFont &font() const;
+ void setFont(const QFont &f);
+
+ QFontMetrics fontMetrics() const;
+ QFontInfo fontInfo() const;
+
+ void setPen(const QColor &color);
+ void setPen(const QPen &pen);
+ void setPen(Qt::PenStyle style);
+ const QPen &pen() const;
+
+ void setBrush(const QBrush &brush);
+ void setBrush(Qt::BrushStyle style);
+ const QBrush &brush() const;
+
+ // attributes/modes
+ void setBackgroundMode(Qt::BGMode mode);
+ Qt::BGMode backgroundMode() const;
+
+ QPoint brushOrigin() const;
+ inline void setBrushOrigin(int x, int y);
+ inline void setBrushOrigin(const QPoint &);
+ void setBrushOrigin(const QPointF &);
+
+ void setBackground(const QBrush &bg);
+ const QBrush &background() const;
+
+ qreal opacity() const;
+ void setOpacity(qreal opacity);
+
+ // Clip functions
+ QRegion clipRegion() const;
+ QPainterPath clipPath() const;
+
+ void setClipRect(const QRectF &, Qt::ClipOperation op = Qt::ReplaceClip);
+ void setClipRect(const QRect &, Qt::ClipOperation op = Qt::ReplaceClip);
+ inline void setClipRect(int x, int y, int w, int h, Qt::ClipOperation op = Qt::ReplaceClip);
+
+ void setClipRegion(const QRegion &, Qt::ClipOperation op = Qt::ReplaceClip);
+
+ void setClipPath(const QPainterPath &path, Qt::ClipOperation op = Qt::ReplaceClip);
+
+ void setClipping(bool enable);
+ bool hasClipping() const;
+
+ QRectF clipBoundingRect() const;
+
+ void save();
+ void restore();
+
+ // XForm functions
+ void setMatrix(const QMatrix &matrix, bool combine = false);
+ const QMatrix &matrix() const;
+ const QMatrix &deviceMatrix() const;
+ void resetMatrix();
+
+ void setTransform(const QTransform &transform, bool combine = false);
+ const QTransform &transform() const;
+ const QTransform &deviceTransform() const;
+ void resetTransform();
+
+ void setWorldMatrix(const QMatrix &matrix, bool combine = false);
+ const QMatrix &worldMatrix() const;
+
+ void setWorldTransform(const QTransform &matrix, bool combine = false);
+ const QTransform &worldTransform() const;
+
+ QMatrix combinedMatrix() const;
+ QTransform combinedTransform() const;
+
+ void setMatrixEnabled(bool enabled);
+ bool matrixEnabled() const;
+
+ void setWorldMatrixEnabled(bool enabled);
+ bool worldMatrixEnabled() const;
+
+ void scale(qreal sx, qreal sy);
+ void shear(qreal sh, qreal sv);
+ void rotate(qreal a);
+
+ void translate(const QPointF &offset);
+ inline void translate(const QPoint &offset);
+ inline void translate(qreal dx, qreal dy);
+
+ QRect window() const;
+ void setWindow(const QRect &window);
+ inline void setWindow(int x, int y, int w, int h);
+
+ QRect viewport() const;
+ void setViewport(const QRect &viewport);
+ inline void setViewport(int x, int y, int w, int h);
+
+ void setViewTransformEnabled(bool enable);
+ bool viewTransformEnabled() const;
+
+ // drawing functions
+ void strokePath(const QPainterPath &path, const QPen &pen);
+ void fillPath(const QPainterPath &path, const QBrush &brush);
+ void drawPath(const QPainterPath &path);
+
+ inline void drawPoint(const QPointF &pt);
+ inline void drawPoint(const QPoint &p);
+ inline void drawPoint(int x, int y);
+
+ void drawPoints(const QPointF *points, int pointCount);
+ inline void drawPoints(const QPolygonF &points);
+ void drawPoints(const QPoint *points, int pointCount);
+ inline void drawPoints(const QPolygon &points);
+
+ inline void drawLine(const QLineF &line);
+ inline void drawLine(const QLine &line);
+ inline void drawLine(int x1, int y1, int x2, int y2);
+ inline void drawLine(const QPoint &p1, const QPoint &p2);
+ inline void drawLine(const QPointF &p1, const QPointF &p2);
+
+ void drawLines(const QLineF *lines, int lineCount);
+ inline void drawLines(const QVector<QLineF> &lines);
+ void drawLines(const QPointF *pointPairs, int lineCount);
+ inline void drawLines(const QVector<QPointF> &pointPairs);
+ void drawLines(const QLine *lines, int lineCount);
+ inline void drawLines(const QVector<QLine> &lines);
+ void drawLines(const QPoint *pointPairs, int lineCount);
+ inline void drawLines(const QVector<QPoint> &pointPairs);
+
+ inline void drawRect(const QRectF &rect);
+ inline void drawRect(int x1, int y1, int w, int h);
+ inline void drawRect(const QRect &rect);
+
+ void drawRects(const QRectF *rects, int rectCount);
+ inline void drawRects(const QVector<QRectF> &rectangles);
+ void drawRects(const QRect *rects, int rectCount);
+ inline void drawRects(const QVector<QRect> &rectangles);
+
+ void drawEllipse(const QRectF &r);
+ void drawEllipse(const QRect &r);
+ inline void drawEllipse(int x, int y, int w, int h);
+
+ inline void drawEllipse(const QPointF &center, qreal rx, qreal ry);
+ inline void drawEllipse(const QPoint &center, int rx, int ry);
+
+ void drawPolyline(const QPointF *points, int pointCount);
+ inline void drawPolyline(const QPolygonF &polyline);
+ void drawPolyline(const QPoint *points, int pointCount);
+ inline void drawPolyline(const QPolygon &polygon);
+
+ void drawPolygon(const QPointF *points, int pointCount, Qt::FillRule fillRule = Qt::OddEvenFill);
+ inline void drawPolygon(const QPolygonF &polygon, Qt::FillRule fillRule = Qt::OddEvenFill);
+ void drawPolygon(const QPoint *points, int pointCount, Qt::FillRule fillRule = Qt::OddEvenFill);
+ inline void drawPolygon(const QPolygon &polygon, Qt::FillRule fillRule = Qt::OddEvenFill);
+
+ void drawConvexPolygon(const QPointF *points, int pointCount);
+ inline void drawConvexPolygon(const QPolygonF &polygon);
+ void drawConvexPolygon(const QPoint *points, int pointCount);
+ inline void drawConvexPolygon(const QPolygon &polygon);
+
+ void drawArc(const QRectF &rect, int a, int alen);
+ inline void drawArc(const QRect &, int a, int alen);
+ inline void drawArc(int x, int y, int w, int h, int a, int alen);
+
+ void drawPie(const QRectF &rect, int a, int alen);
+ inline void drawPie(int x, int y, int w, int h, int a, int alen);
+ inline void drawPie(const QRect &, int a, int alen);
+
+ void drawChord(const QRectF &rect, int a, int alen);
+ inline void drawChord(int x, int y, int w, int h, int a, int alen);
+ inline void drawChord(const QRect &, int a, int alen);
+
+ void drawRoundedRect(const QRectF &rect, qreal xRadius, qreal yRadius,
+ Qt::SizeMode mode = Qt::AbsoluteSize);
+ inline void drawRoundedRect(int x, int y, int w, int h, qreal xRadius, qreal yRadius,
+ Qt::SizeMode mode = Qt::AbsoluteSize);
+ inline void drawRoundedRect(const QRect &rect, qreal xRadius, qreal yRadius,
+ Qt::SizeMode mode = Qt::AbsoluteSize);
+
+ void drawRoundRect(const QRectF &r, int xround = 25, int yround = 25);
+ inline void drawRoundRect(int x, int y, int w, int h, int = 25, int = 25);
+ inline void drawRoundRect(const QRect &r, int xround = 25, int yround = 25);
+
+ void drawTiledPixmap(const QRectF &rect, const QPixmap &pm, const QPointF &offset = QPointF());
+ inline void drawTiledPixmap(int x, int y, int w, int h, const QPixmap &, int sx=0, int sy=0);
+ inline void drawTiledPixmap(const QRect &, const QPixmap &, const QPoint & = QPoint());
+#ifndef QT_NO_PICTURE
+ void drawPicture(const QPointF &p, const QPicture &picture);
+ inline void drawPicture(int x, int y, const QPicture &picture);
+ inline void drawPicture(const QPoint &p, const QPicture &picture);
+#endif
+
+ void drawPixmap(const QRectF &targetRect, const QPixmap &pixmap, const QRectF &sourceRect);
+ inline void drawPixmap(const QRect &targetRect, const QPixmap &pixmap, const QRect &sourceRect);
+ inline void drawPixmap(int x, int y, int w, int h, const QPixmap &pm,
+ int sx, int sy, int sw, int sh);
+ inline void drawPixmap(int x, int y, const QPixmap &pm,
+ int sx, int sy, int sw, int sh);
+ inline void drawPixmap(const QPointF &p, const QPixmap &pm, const QRectF &sr);
+ inline void drawPixmap(const QPoint &p, const QPixmap &pm, const QRect &sr);
+ void drawPixmap(const QPointF &p, const QPixmap &pm);
+ inline void drawPixmap(const QPoint &p, const QPixmap &pm);
+ inline void drawPixmap(int x, int y, const QPixmap &pm);
+ inline void drawPixmap(const QRect &r, const QPixmap &pm);
+ inline void drawPixmap(int x, int y, int w, int h, const QPixmap &pm);
+
+ void drawPixmapFragments(const PixmapFragment *fragments, int fragmentCount,
+ const QPixmap &pixmap, PixmapFragmentHints hints = 0);
+
+ void drawImage(const QRectF &targetRect, const QImage &image, const QRectF &sourceRect,
+ Qt::ImageConversionFlags flags = Qt::AutoColor);
+ inline void drawImage(const QRect &targetRect, const QImage &image, const QRect &sourceRect,
+ Qt::ImageConversionFlags flags = Qt::AutoColor);
+ inline void drawImage(const QPointF &p, const QImage &image, const QRectF &sr,
+ Qt::ImageConversionFlags flags = Qt::AutoColor);
+ inline void drawImage(const QPoint &p, const QImage &image, const QRect &sr,
+ Qt::ImageConversionFlags flags = Qt::AutoColor);
+ inline void drawImage(const QRectF &r, const QImage &image);
+ inline void drawImage(const QRect &r, const QImage &image);
+ void drawImage(const QPointF &p, const QImage &image);
+ inline void drawImage(const QPoint &p, const QImage &image);
+ inline void drawImage(int x, int y, const QImage &image, int sx = 0, int sy = 0,
+ int sw = -1, int sh = -1, Qt::ImageConversionFlags flags = Qt::AutoColor);
+
+ void setLayoutDirection(Qt::LayoutDirection direction);
+ Qt::LayoutDirection layoutDirection() const;
+
+#if !defined(QT_NO_RAWFONT)
+ void drawGlyphs(const QPointF &position, const QGlyphs &glyphs);
+#endif
+
+ void drawStaticText(const QPointF &topLeftPosition, const QStaticText &staticText);
+ inline void drawStaticText(const QPoint &topLeftPosition, const QStaticText &staticText);
+ inline void drawStaticText(int left, int top, const QStaticText &staticText);
+
+ void drawText(const QPointF &p, const QString &s);
+ inline void drawText(const QPoint &p, const QString &s);
+ inline void drawText(int x, int y, const QString &s);
+
+ void drawText(const QPointF &p, const QString &str, int tf, int justificationPadding);
+
+ void drawText(const QRectF &r, int flags, const QString &text, QRectF *br=0);
+ void drawText(const QRect &r, int flags, const QString &text, QRect *br=0);
+ inline void drawText(int x, int y, int w, int h, int flags, const QString &text, QRect *br=0);
+
+ void drawText(const QRectF &r, const QString &text, const QTextOption &o = QTextOption());
+
+ QRectF boundingRect(const QRectF &rect, int flags, const QString &text);
+ QRect boundingRect(const QRect &rect, int flags, const QString &text);
+ inline QRect boundingRect(int x, int y, int w, int h, int flags, const QString &text);
+
+ QRectF boundingRect(const QRectF &rect, const QString &text, const QTextOption &o = QTextOption());
+
+ void drawTextItem(const QPointF &p, const QTextItem &ti);
+ inline void drawTextItem(int x, int y, const QTextItem &ti);
+ inline void drawTextItem(const QPoint &p, const QTextItem &ti);
+
+ void fillRect(const QRectF &, const QBrush &);
+ inline void fillRect(int x, int y, int w, int h, const QBrush &);
+ void fillRect(const QRect &, const QBrush &);
+
+ void fillRect(const QRectF &, const QColor &color);
+ inline void fillRect(int x, int y, int w, int h, const QColor &color);
+ void fillRect(const QRect &, const QColor &color);
+
+ inline void fillRect(int x, int y, int w, int h, Qt::GlobalColor c);
+ inline void fillRect(const QRect &r, Qt::GlobalColor c);
+ inline void fillRect(const QRectF &r, Qt::GlobalColor c);
+
+ inline void fillRect(int x, int y, int w, int h, Qt::BrushStyle style);
+ inline void fillRect(const QRect &r, Qt::BrushStyle style);
+ inline void fillRect(const QRectF &r, Qt::BrushStyle style);
+
+ void eraseRect(const QRectF &);
+ inline void eraseRect(int x, int y, int w, int h);
+ inline void eraseRect(const QRect &);
+
+ void setRenderHint(RenderHint hint, bool on = true);
+ void setRenderHints(RenderHints hints, bool on = true);
+ RenderHints renderHints() const;
+ inline bool testRenderHint(RenderHint hint) const { return renderHints() & hint; }
+
+ QPaintEngine *paintEngine() const;
+
+ static void setRedirected(const QPaintDevice *device, QPaintDevice *replacement,
+ const QPoint& offset = QPoint());
+ static QPaintDevice *redirected(const QPaintDevice *device, QPoint *offset = 0);
+ static void restoreRedirected(const QPaintDevice *device);
+
+ void beginNativePainting();
+ void endNativePainting();
+
+#ifdef QT3_SUPPORT
+
+ inline QT3_SUPPORT void setBackgroundColor(const QColor &color) { setBackground(color); }
+ inline QT3_SUPPORT const QColor &backgroundColor() const { return background().color(); }
+
+ inline QT3_SUPPORT void drawText(int x, int y, const QString &s, int pos, int len)
+ { drawText(x, y, s.mid(pos, len)); }
+ inline QT3_SUPPORT void drawText(const QPoint &p, const QString &s, int pos, int len)
+ { drawText(p, s.mid(pos, len)); }
+ inline QT3_SUPPORT void drawText(int x, int y, const QString &s, int len)
+ { drawText(x, y, s.left(len)); }
+ inline QT3_SUPPORT void drawText(const QPoint &p, const QString &s, int len)
+ { drawText(p, s.left(len)); }
+ inline QT3_SUPPORT void drawText(const QRect &r, int flags, const QString &str, int len, QRect *br=0)
+ { drawText(r, flags, str.left(len), br); }
+ inline QT3_SUPPORT void drawText(int x, int y, int w, int h, int flags, const QString &text, int len, QRect *br=0)
+ { drawText(QRect(x, y, w, h), flags, text.left(len), br); }
+ inline QT3_SUPPORT QRect boundingRect(const QRect &rect, int flags, const QString &text, int len)
+ { return boundingRect(rect, flags, text.left(len)); }
+ inline QT3_SUPPORT QRect boundingRect(int x, int y, int w, int h, int flags, const QString &text, int len)
+ { return boundingRect(QRect(x, y, w, h), flags, text.left(len)); }
+
+ inline QT3_SUPPORT bool begin(QPaintDevice *pdev, const QWidget *init)
+ { bool ret = begin(pdev); initFrom(init); return ret; }
+ QT3_SUPPORT void drawPoints(const QPolygon &pa, int index, int npoints = -1)
+ { drawPoints(pa.constData() + index, npoints == -1 ? pa.size() - index : npoints); }
+
+ QT3_SUPPORT void drawCubicBezier(const QPolygon &pa, int index = 0);
+
+ QT3_SUPPORT void drawLineSegments(const QPolygon &points, int index = 0, int nlines = -1);
+
+ inline QT3_SUPPORT void drawPolyline(const QPolygon &pa, int index, int npoints = -1)
+ { drawPolyline(pa.constData() + index, npoints == -1 ? pa.size() - index : npoints); }
+
+ inline QT3_SUPPORT void drawPolygon(const QPolygon &pa, bool winding, int index = 0, int npoints = -1)
+ { drawPolygon(pa.constData() + index, npoints == -1 ? pa.size() - index : npoints,
+ winding ? Qt::WindingFill : Qt::OddEvenFill); }
+
+ inline QT3_SUPPORT void drawPolygon(const QPolygonF &polygon, bool winding, int index = 0,
+ int npoints = -1)
+ { drawPolygon(polygon.constData() + index, npoints == -1 ? polygon.size() - index : npoints,
+ winding ? Qt::WindingFill : Qt::OddEvenFill); }
+
+ inline QT3_SUPPORT void drawConvexPolygon(const QPolygonF &polygon, int index, int npoints = -1)
+ { drawConvexPolygon(polygon.constData() + index, npoints == -1 ? polygon.size() - index : npoints); }
+ inline QT3_SUPPORT void drawConvexPolygon(const QPolygon &pa, int index, int npoints = -1)
+ { drawConvexPolygon(pa.constData() + index, npoints == -1 ? pa.size() - index : npoints); }
+
+ static inline QT3_SUPPORT void redirect(QPaintDevice *pdev, QPaintDevice *replacement)
+ { setRedirected(pdev, replacement); }
+ static inline QT3_SUPPORT QPaintDevice *redirect(QPaintDevice *pdev)
+ { return const_cast<QPaintDevice*>(redirected(pdev)); }
+
+ inline QT3_SUPPORT void setWorldXForm(bool enabled) { setMatrixEnabled(enabled); }
+ inline QT3_SUPPORT bool hasWorldXForm() const { return matrixEnabled(); }
+ inline QT3_SUPPORT void resetXForm() { resetTransform(); }
+
+ inline QT3_SUPPORT void setViewXForm(bool enabled) { setViewTransformEnabled(enabled); }
+ inline QT3_SUPPORT bool hasViewXForm() const { return viewTransformEnabled(); }
+
+ QT3_SUPPORT void map(int x, int y, int *rx, int *ry) const;
+ QT3_SUPPORT QPoint xForm(const QPoint &) const; // map virtual -> deviceb
+ QT3_SUPPORT QRect xForm(const QRect &) const;
+ QT3_SUPPORT QPolygon xForm(const QPolygon &) const;
+ QT3_SUPPORT QPolygon xForm(const QPolygon &, int index, int npoints) const;
+ QT3_SUPPORT QPoint xFormDev(const QPoint &) const; // map device -> virtual
+ QT3_SUPPORT QRect xFormDev(const QRect &) const;
+ QT3_SUPPORT QPolygon xFormDev(const QPolygon &) const;
+ QT3_SUPPORT QPolygon xFormDev(const QPolygon &, int index, int npoints) const;
+ QT3_SUPPORT qreal translationX() const;
+ QT3_SUPPORT qreal translationY() const;
+#endif
+
+private:
+ Q_DISABLE_COPY(QPainter)
+ friend class Q3Painter;
+
+ QScopedPointer<QPainterPrivate> d_ptr;
+
+ friend class QFontEngine;
+ friend class QFontEngineBox;
+ friend class QFontEngineFT;
+ friend class QFontEngineMac;
+ friend class QFontEngineWin;
+ friend class QFontEngineXLFD;
+ friend class QWSManager;
+ friend class QPaintEngine;
+ friend class QPaintEngineExPrivate;
+ friend class QOpenGLPaintEngine;
+ friend class QX11PaintEngine;
+ friend class QX11PaintEnginePrivate;
+ friend class QWin32PaintEngine;
+ friend class QWin32PaintEnginePrivate;
+ friend class QRasterPaintEngine;
+ friend class QAlphaPaintEngine;
+ friend class QPreviewPaintEngine;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QPainter::RenderHints)
+
+//
+// functions
+//
+inline void QPainter::drawLine(const QLineF &l)
+{
+ drawLines(&l, 1);
+}
+
+inline void QPainter::drawLine(const QLine &line)
+{
+ drawLines(&line, 1);
+}
+
+inline void QPainter::drawLine(int x1, int y1, int x2, int y2)
+{
+ QLine l(x1, y1, x2, y2);
+ drawLines(&l, 1);
+}
+
+inline void QPainter::drawLine(const QPoint &p1, const QPoint &p2)
+{
+ QLine l(p1, p2);
+ drawLines(&l, 1);
+}
+
+inline void QPainter::drawLine(const QPointF &p1, const QPointF &p2)
+{
+ drawLine(QLineF(p1, p2));
+}
+
+inline void QPainter::drawLines(const QVector<QLineF> &lines)
+{
+ drawLines(lines.constData(), lines.size());
+}
+
+inline void QPainter::drawLines(const QVector<QLine> &lines)
+{
+ drawLines(lines.constData(), lines.size());
+}
+
+inline void QPainter::drawLines(const QVector<QPointF> &pointPairs)
+{
+ drawLines(pointPairs.constData(), pointPairs.size() / 2);
+}
+
+inline void QPainter::drawLines(const QVector<QPoint> &pointPairs)
+{
+ drawLines(pointPairs.constData(), pointPairs.size() / 2);
+}
+
+inline void QPainter::drawPolyline(const QPolygonF &polyline)
+{
+ drawPolyline(polyline.constData(), polyline.size());
+}
+
+inline void QPainter::drawPolyline(const QPolygon &polyline)
+{
+ drawPolyline(polyline.constData(), polyline.size());
+}
+
+inline void QPainter::drawPolygon(const QPolygonF &polygon, Qt::FillRule fillRule)
+{
+ drawPolygon(polygon.constData(), polygon.size(), fillRule);
+}
+
+inline void QPainter::drawPolygon(const QPolygon &polygon, Qt::FillRule fillRule)
+{
+ drawPolygon(polygon.constData(), polygon.size(), fillRule);
+}
+
+inline void QPainter::drawConvexPolygon(const QPolygonF &poly)
+{
+ drawConvexPolygon(poly.constData(), poly.size());
+}
+
+inline void QPainter::drawConvexPolygon(const QPolygon &poly)
+{
+ drawConvexPolygon(poly.constData(), poly.size());
+}
+
+inline void QPainter::drawRect(const QRectF &rect)
+{
+ drawRects(&rect, 1);
+}
+
+inline void QPainter::drawRect(int x, int y, int w, int h)
+{
+ QRect r(x, y, w, h);
+ drawRects(&r, 1);
+}
+
+inline void QPainter::drawRect(const QRect &r)
+{
+ drawRects(&r, 1);
+}
+
+inline void QPainter::drawRects(const QVector<QRectF> &rects)
+{
+ drawRects(rects.constData(), rects.size());
+}
+
+inline void QPainter::drawRects(const QVector<QRect> &rects)
+{
+ drawRects(rects.constData(), rects.size());
+}
+
+inline void QPainter::drawPoint(const QPointF &p)
+{
+ drawPoints(&p, 1);
+}
+
+inline void QPainter::drawPoint(int x, int y)
+{
+ QPoint p(x, y);
+ drawPoints(&p, 1);
+}
+
+inline void QPainter::drawPoint(const QPoint &p)
+{
+ drawPoints(&p, 1);
+}
+
+inline void QPainter::drawPoints(const QPolygonF &points)
+{
+ drawPoints(points.constData(), points.size());
+}
+
+inline void QPainter::drawPoints(const QPolygon &points)
+{
+ drawPoints(points.constData(), points.size());
+}
+
+inline void QPainter::drawRoundRect(int x, int y, int w, int h, int xRnd, int yRnd)
+{
+ drawRoundRect(QRectF(x, y, w, h), xRnd, yRnd);
+}
+
+inline void QPainter::drawRoundRect(const QRect &rect, int xRnd, int yRnd)
+{
+ drawRoundRect(QRectF(rect), xRnd, yRnd);
+}
+
+inline void QPainter::drawRoundedRect(int x, int y, int w, int h, qreal xRadius, qreal yRadius,
+ Qt::SizeMode mode)
+{
+ drawRoundedRect(QRectF(x, y, w, h), xRadius, yRadius, mode);
+}
+
+inline void QPainter::drawRoundedRect(const QRect &rect, qreal xRadius, qreal yRadius,
+ Qt::SizeMode mode)
+{
+ drawRoundedRect(QRectF(rect), xRadius, yRadius, mode);
+}
+
+inline void QPainter::drawEllipse(int x, int y, int w, int h)
+{
+ drawEllipse(QRect(x, y, w, h));
+}
+
+inline void QPainter::drawEllipse(const QPointF &center, qreal rx, qreal ry)
+{
+ drawEllipse(QRectF(center.x() - rx, center.y() - ry, 2 * rx, 2 * ry));
+}
+
+inline void QPainter::drawEllipse(const QPoint &center, int rx, int ry)
+{
+ drawEllipse(QRect(center.x() - rx, center.y() - ry, 2 * rx, 2 * ry));
+}
+
+inline void QPainter::drawArc(const QRect &r, int a, int alen)
+{
+ drawArc(QRectF(r), a, alen);
+}
+
+inline void QPainter::drawArc(int x, int y, int w, int h, int a, int alen)
+{
+ drawArc(QRectF(x, y, w, h), a, alen);
+}
+
+inline void QPainter::drawPie(const QRect &rect, int a, int alen)
+{
+ drawPie(QRectF(rect), a, alen);
+}
+
+inline void QPainter::drawPie(int x, int y, int w, int h, int a, int alen)
+{
+ drawPie(QRectF(x, y, w, h), a, alen);
+}
+
+inline void QPainter::drawChord(const QRect &rect, int a, int alen)
+{
+ drawChord(QRectF(rect), a, alen);
+}
+
+inline void QPainter::drawChord(int x, int y, int w, int h, int a, int alen)
+{
+ drawChord(QRectF(x, y, w, h), a, alen);
+}
+
+inline void QPainter::setClipRect(int x, int y, int w, int h, Qt::ClipOperation op)
+{
+ setClipRect(QRect(x, y, w, h), op);
+}
+
+inline void QPainter::eraseRect(const QRect &rect)
+{
+ eraseRect(QRectF(rect));
+}
+
+inline void QPainter::eraseRect(int x, int y, int w, int h)
+{
+ eraseRect(QRectF(x, y, w, h));
+}
+
+inline void QPainter::fillRect(int x, int y, int w, int h, const QBrush &b)
+{
+ fillRect(QRect(x, y, w, h), b);
+}
+
+inline void QPainter::fillRect(int x, int y, int w, int h, const QColor &b)
+{
+ fillRect(QRect(x, y, w, h), b);
+}
+
+inline void QPainter::fillRect(int x, int y, int w, int h, Qt::GlobalColor c)
+{
+ fillRect(QRect(x, y, w, h), QColor(c));
+}
+
+inline void QPainter::fillRect(const QRect &r, Qt::GlobalColor c)
+{
+ fillRect(r, QColor(c));
+}
+
+inline void QPainter::fillRect(const QRectF &r, Qt::GlobalColor c)
+{
+ fillRect(r, QColor(c));
+}
+
+inline void QPainter::fillRect(int x, int y, int w, int h, Qt::BrushStyle style)
+{
+ fillRect(QRectF(x, y, w, h), QBrush(style));
+}
+
+inline void QPainter::fillRect(const QRect &r, Qt::BrushStyle style)
+{
+ fillRect(QRectF(r), QBrush(style));
+}
+
+inline void QPainter::fillRect(const QRectF &r, Qt::BrushStyle style)
+{
+ fillRect(r, QBrush(style));
+}
+
+
+inline void QPainter::setBrushOrigin(int x, int y)
+{
+ setBrushOrigin(QPoint(x, y));
+}
+
+inline void QPainter::setBrushOrigin(const QPoint &p)
+{
+ setBrushOrigin(QPointF(p));
+}
+
+inline void QPainter::drawTiledPixmap(const QRect &rect, const QPixmap &pm, const QPoint &offset)
+{
+ drawTiledPixmap(QRectF(rect), pm, QPointF(offset));
+}
+
+inline void QPainter::drawTiledPixmap(int x, int y, int w, int h, const QPixmap &pm, int sx, int sy)
+{
+ drawTiledPixmap(QRectF(x, y, w, h), pm, QPointF(sx, sy));
+}
+
+inline void QPainter::drawPixmap(const QRect &targetRect, const QPixmap &pixmap, const QRect &sourceRect)
+{
+ drawPixmap(QRectF(targetRect), pixmap, QRectF(sourceRect));
+}
+
+inline void QPainter::drawPixmap(const QPoint &p, const QPixmap &pm)
+{
+ drawPixmap(QPointF(p), pm);
+}
+
+inline void QPainter::drawPixmap(const QRect &r, const QPixmap &pm)
+{
+ drawPixmap(QRectF(r), pm, QRectF());
+}
+
+inline void QPainter::drawPixmap(int x, int y, const QPixmap &pm)
+{
+ drawPixmap(QPointF(x, y), pm);
+}
+
+inline void QPainter::drawPixmap(int x, int y, int w, int h, const QPixmap &pm)
+{
+ drawPixmap(QRectF(x, y, w, h), pm, QRectF());
+}
+
+inline void QPainter::drawPixmap(int x, int y, int w, int h, const QPixmap &pm,
+ int sx, int sy, int sw, int sh)
+{
+ drawPixmap(QRectF(x, y, w, h), pm, QRectF(sx, sy, sw, sh));
+}
+
+inline void QPainter::drawPixmap(int x, int y, const QPixmap &pm,
+ int sx, int sy, int sw, int sh)
+{
+ drawPixmap(QRectF(x, y, -1, -1), pm, QRectF(sx, sy, sw, sh));
+}
+
+inline void QPainter::drawPixmap(const QPointF &p, const QPixmap &pm, const QRectF &sr)
+{
+ drawPixmap(QRectF(p.x(), p.y(), -1, -1), pm, sr);
+}
+
+inline void QPainter::drawPixmap(const QPoint &p, const QPixmap &pm, const QRect &sr)
+{
+ drawPixmap(QRectF(p.x(), p.y(), -1, -1), pm, sr);
+}
+
+inline void QPainter::drawTextItem(int x, int y, const QTextItem &ti)
+{
+ drawTextItem(QPointF(x, y), ti);
+}
+
+inline void QPainter::drawImage(const QRect &targetRect, const QImage &image, const QRect &sourceRect,
+ Qt::ImageConversionFlags flags)
+{
+ drawImage(QRectF(targetRect), image, QRectF(sourceRect), flags);
+}
+
+inline void QPainter::drawImage(const QPointF &p, const QImage &image, const QRectF &sr,
+ Qt::ImageConversionFlags flags)
+{
+ drawImage(QRectF(p.x(), p.y(), -1, -1), image, sr, flags);
+}
+
+inline void QPainter::drawImage(const QPoint &p, const QImage &image, const QRect &sr,
+ Qt::ImageConversionFlags flags)
+{
+ drawImage(QRect(p.x(), p.y(), -1, -1), image, sr, flags);
+}
+
+
+inline void QPainter::drawImage(const QRectF &r, const QImage &image)
+{
+ drawImage(r, image, QRect(0, 0, image.width(), image.height()));
+}
+
+inline void QPainter::drawImage(const QRect &r, const QImage &image)
+{
+ drawImage(r, image, QRectF(0, 0, image.width(), image.height()));
+}
+
+inline void QPainter::drawImage(const QPoint &p, const QImage &image)
+{
+ drawImage(QPointF(p), image);
+}
+
+inline void QPainter::drawImage(int x, int y, const QImage &image, int sx, int sy, int sw, int sh,
+ Qt::ImageConversionFlags flags)
+{
+ if (sx == 0 && sy == 0 && sw == -1 && sh == -1 && flags == Qt::AutoColor)
+ drawImage(QPointF(x, y), image);
+ else
+ drawImage(QRectF(x, y, -1, -1), image, QRectF(sx, sy, sw, sh), flags);
+}
+
+inline void QPainter::drawStaticText(const QPoint &p, const QStaticText &staticText)
+{
+ drawStaticText(QPointF(p), staticText);
+}
+
+inline void QPainter::drawStaticText(int x, int y, const QStaticText &staticText)
+{
+ drawStaticText(QPointF(x, y), staticText);
+}
+
+inline void QPainter::drawTextItem(const QPoint &p, const QTextItem &ti)
+{
+ drawTextItem(QPointF(p), ti);
+}
+
+inline void QPainter::drawText(const QPoint &p, const QString &s)
+{
+ drawText(QPointF(p), s);
+}
+
+inline void QPainter::drawText(int x, int y, int w, int h, int flags, const QString &str, QRect *br)
+{
+ drawText(QRect(x, y, w, h), flags, str, br);
+}
+
+inline void QPainter::drawText(int x, int y, const QString &s)
+{
+ drawText(QPointF(x, y), s);
+}
+
+inline QRect QPainter::boundingRect(int x, int y, int w, int h, int flags, const QString &text)
+{
+ return boundingRect(QRect(x, y, w, h), flags, text);
+}
+
+inline void QPainter::translate(qreal dx, qreal dy)
+{
+ translate(QPointF(dx, dy));
+}
+
+inline void QPainter::translate(const QPoint &offset)
+{
+ translate(offset.x(), offset.y());
+}
+
+inline void QPainter::setViewport(int x, int y, int w, int h)
+{
+ setViewport(QRect(x, y, w, h));
+}
+
+inline void QPainter::setWindow(int x, int y, int w, int h)
+{
+ setWindow(QRect(x, y, w, h));
+}
+
+#ifndef QT_NO_PICTURE
+inline void QPainter::drawPicture(int x, int y, const QPicture &p)
+{
+ drawPicture(QPoint(x, y), p);
+}
+
+inline void QPainter::drawPicture(const QPoint &pt, const QPicture &p)
+{
+ drawPicture(QPointF(pt), p);
+}
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPAINTER_H
diff --git a/src/gui/painting/qpainter_p.h b/src/gui/painting/qpainter_p.h
new file mode 100644
index 0000000000..205c10a09f
--- /dev/null
+++ b/src/gui/painting/qpainter_p.h
@@ -0,0 +1,273 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTER_P_H
+#define QPAINTER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtGui/qbrush.h"
+#include "QtGui/qfont.h"
+#include "QtGui/qpen.h"
+#include "QtGui/qregion.h"
+#include "QtGui/qmatrix.h"
+#include "QtGui/qpainter.h"
+#include "QtGui/qpainterpath.h"
+#include "QtGui/qpaintengine.h"
+#include <QtCore/qhash.h>
+
+#include <private/qpen_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPaintEngine;
+class QEmulationPaintEngine;
+class QPaintEngineEx;
+struct QFixedPoint;
+
+struct QTLWExtra;
+
+struct DataPtrContainer {
+ void *ptr;
+};
+
+inline void *data_ptr(const QTransform &t) { return (DataPtrContainer *) &t; }
+inline bool qtransform_fast_equals(const QTransform &a, const QTransform &b) { return data_ptr(a) == data_ptr(b); }
+
+// QPen inline functions...
+inline QPen::DataPtr &data_ptr(const QPen &p) { return const_cast<QPen &>(p).data_ptr(); }
+inline bool qpen_fast_equals(const QPen &a, const QPen &b) { return data_ptr(a) == data_ptr(b); }
+inline QBrush qpen_brush(const QPen &p) { return data_ptr(p)->brush; }
+inline qreal qpen_widthf(const QPen &p) { return data_ptr(p)->width; }
+inline Qt::PenStyle qpen_style(const QPen &p) { return data_ptr(p)->style; }
+inline Qt::PenCapStyle qpen_capStyle(const QPen &p) { return data_ptr(p)->capStyle; }
+inline Qt::PenJoinStyle qpen_joinStyle(const QPen &p) { return data_ptr(p)->joinStyle; }
+
+// QBrush inline functions...
+inline QBrush::DataPtr &data_ptr(const QBrush &p) { return const_cast<QBrush &>(p).data_ptr(); }
+inline bool qbrush_fast_equals(const QBrush &a, const QBrush &b) { return data_ptr(a) == data_ptr(b); }
+inline Qt::BrushStyle qbrush_style(const QBrush &b) { return data_ptr(b)->style; }
+inline const QColor &qbrush_color(const QBrush &b) { return data_ptr(b)->color; }
+inline bool qbrush_has_transform(const QBrush &b) { return data_ptr(b)->transform.type() > QTransform::TxNone; }
+
+class QPainterClipInfo
+{
+public:
+ enum ClipType { RegionClip, PathClip, RectClip, RectFClip };
+
+ QPainterClipInfo(const QPainterPath &p, Qt::ClipOperation op, const QTransform &m) :
+ clipType(PathClip), matrix(m), operation(op), path(p) { }
+
+ QPainterClipInfo(const QRegion &r, Qt::ClipOperation op, const QTransform &m) :
+ clipType(RegionClip), matrix(m), operation(op), region(r) { }
+
+ QPainterClipInfo(const QRect &r, Qt::ClipOperation op, const QTransform &m) :
+ clipType(RectClip), matrix(m), operation(op), rect(r) { }
+
+ QPainterClipInfo(const QRectF &r, Qt::ClipOperation op, const QTransform &m) :
+ clipType(RectFClip), matrix(m), operation(op), rectf(r) { }
+
+ ClipType clipType;
+ QTransform matrix;
+ Qt::ClipOperation operation;
+ QPainterPath path;
+ QRegion region;
+ QRect rect;
+ QRectF rectf;
+
+ // ###
+// union {
+// QRegionData *d;
+// QPainterPathPrivate *pathData;
+
+// struct {
+// int x, y, w, h;
+// } rectData;
+// struct {
+// qreal x, y, w, h;
+// } rectFData;
+// };
+
+};
+
+
+class Q_GUI_EXPORT QPainterState : public QPaintEngineState
+{
+public:
+ QPainterState();
+ QPainterState(const QPainterState *s);
+ virtual ~QPainterState();
+ void init(QPainter *p);
+
+ QPointF brushOrigin;
+ QFont font;
+ QFont deviceFont;
+ QPen pen;
+ QBrush brush;
+ QBrush bgBrush; // background brush
+ QRegion clipRegion;
+ QPainterPath clipPath;
+ Qt::ClipOperation clipOperation;
+ QPainter::RenderHints renderHints;
+ QList<QPainterClipInfo> clipInfo; // ### Make me smaller and faster to copy around...
+ QTransform worldMatrix; // World transformation matrix, not window and viewport
+ QTransform matrix; // Complete transformation matrix,
+ QTransform redirectionMatrix;
+ int wx, wy, ww, wh; // window rectangle
+ int vx, vy, vw, vh; // viewport rectangle
+ qreal opacity;
+
+ uint WxF:1; // World transformation
+ uint VxF:1; // View transformation
+ uint clipEnabled:1;
+
+ Qt::BGMode bgMode;
+ QPainter *painter;
+ Qt::LayoutDirection layoutDirection;
+ QPainter::CompositionMode composition_mode;
+ uint emulationSpecifier;
+ uint changeFlags;
+};
+
+struct QPainterDummyState
+{
+ QFont font;
+ QPen pen;
+ QBrush brush;
+ QTransform transform;
+};
+
+class QRawFont;
+class QPainterPrivate
+{
+ Q_DECLARE_PUBLIC(QPainter)
+public:
+ QPainterPrivate(QPainter *painter)
+ : q_ptr(painter), d_ptrs(0), state(0), dummyState(0), txinv(0), inDestructor(false), d_ptrs_size(0),
+ refcount(1), device(0), original_device(0), helper_device(0), engine(0), emulationEngine(0),
+ extended(0)
+ {
+ }
+
+ ~QPainterPrivate();
+
+ QPainter *q_ptr;
+ QPainterPrivate **d_ptrs;
+
+ QPainterState *state;
+ QVector<QPainterState*> states;
+
+ mutable QPainterDummyState *dummyState;
+
+ QTransform invMatrix;
+ uint txinv:1;
+ uint inDestructor : 1;
+ uint d_ptrs_size;
+ uint refcount;
+
+ enum DrawOperation { StrokeDraw = 0x1,
+ FillDraw = 0x2,
+ StrokeAndFillDraw = 0x3
+ };
+
+ QPainterDummyState *fakeState() const {
+ if (!dummyState)
+ dummyState = new QPainterDummyState();
+ return dummyState;
+ }
+
+ void updateEmulationSpecifier(QPainterState *s);
+ void updateStateImpl(QPainterState *state);
+ void updateState(QPainterState *state);
+
+ void draw_helper(const QPainterPath &path, DrawOperation operation = StrokeAndFillDraw);
+ void drawStretchedGradient(const QPainterPath &path, DrawOperation operation);
+ void drawOpaqueBackground(const QPainterPath &path, DrawOperation operation);
+
+#if !defined(QT_NO_RAWFONT)
+ void drawGlyphs(quint32 *glyphArray, QFixedPoint *positionArray, int glyphCount,
+ const QRawFont &font, bool overline = false, bool underline = false,
+ bool strikeOut = false);
+#endif
+
+ void updateMatrix();
+ void updateInvMatrix();
+
+ int rectSubtraction() const {
+ return state->pen.style() != Qt::NoPen && state->pen.width() == 0 ? 1 : 0;
+ }
+
+ void checkEmulation();
+
+ static QPainterPrivate *get(QPainter *painter)
+ {
+ return painter->d_ptr.data();
+ }
+
+ QTransform viewTransform() const;
+ static bool attachPainterPrivate(QPainter *q, QPaintDevice *pdev);
+ void detachPainterPrivate(QPainter *q);
+
+ QPaintDevice *device;
+ QPaintDevice *original_device;
+ QPaintDevice *helper_device;
+ QPaintEngine *engine;
+ QEmulationPaintEngine *emulationEngine;
+ QPaintEngineEx *extended;
+ QBrush colorBrush; // for fill with solid color
+};
+
+Q_GUI_EXPORT void qt_draw_helper(QPainterPrivate *p, const QPainterPath &path, QPainterPrivate::DrawOperation operation);
+
+QString qt_generate_brush_key(const QBrush &brush);
+
+QT_END_NAMESPACE
+
+#endif // QPAINTER_P_H
diff --git a/src/gui/painting/qpainterpath.cpp b/src/gui/painting/qpainterpath.cpp
new file mode 100644
index 0000000000..27aed3226f
--- /dev/null
+++ b/src/gui/painting/qpainterpath.cpp
@@ -0,0 +1,3420 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpainterpath.h"
+#include "qpainterpath_p.h"
+
+#include <qbitmap.h>
+#include <qdebug.h>
+#include <qiodevice.h>
+#include <qlist.h>
+#include <qmatrix.h>
+#include <qpen.h>
+#include <qpolygon.h>
+#include <qtextlayout.h>
+#include <qvarlengtharray.h>
+#include <qmath.h>
+
+#include <private/qbezier_p.h>
+#include <private/qfontengine_p.h>
+#include <private/qnumeric_p.h>
+#include <private/qobject_p.h>
+#include <private/qpathclipper_p.h>
+#include <private/qstroker_p.h>
+#include <private/qtextengine_p.h>
+
+#include <limits.h>
+
+#if 0
+#include <performance.h>
+#else
+#define PM_INIT
+#define PM_MEASURE(x)
+#define PM_DISPLAY
+#endif
+
+QT_BEGIN_NAMESPACE
+
+struct QPainterPathPrivateDeleter
+{
+ static inline void cleanup(QPainterPathPrivate *d)
+ {
+ // note - we must up-cast to QPainterPathData since QPainterPathPrivate
+ // has a non-virtual destructor!
+ if (d && !d->ref.deref())
+ delete static_cast<QPainterPathData *>(d);
+ }
+};
+
+// This value is used to determine the length of control point vectors
+// when approximating arc segments as curves. The factor is multiplied
+// with the radius of the circle.
+
+// #define QPP_DEBUG
+// #define QPP_STROKE_DEBUG
+//#define QPP_FILLPOLYGONS_DEBUG
+
+QPainterPath qt_stroke_dash(const QPainterPath &path, qreal *dashes, int dashCount);
+
+void qt_find_ellipse_coords(const QRectF &r, qreal angle, qreal length,
+ QPointF* startPoint, QPointF *endPoint)
+{
+ if (r.isNull()) {
+ if (startPoint)
+ *startPoint = QPointF();
+ if (endPoint)
+ *endPoint = QPointF();
+ return;
+ }
+
+ qreal w2 = r.width() / 2;
+ qreal h2 = r.height() / 2;
+
+ qreal angles[2] = { angle, angle + length };
+ QPointF *points[2] = { startPoint, endPoint };
+
+ for (int i = 0; i < 2; ++i) {
+ if (!points[i])
+ continue;
+
+ qreal theta = angles[i] - 360 * qFloor(angles[i] / 360);
+ qreal t = theta / 90;
+ // truncate
+ int quadrant = int(t);
+ t -= quadrant;
+
+ t = qt_t_for_arc_angle(90 * t);
+
+ // swap x and y?
+ if (quadrant & 1)
+ t = 1 - t;
+
+ qreal a, b, c, d;
+ QBezier::coefficients(t, a, b, c, d);
+ QPointF p(a + b + c*QT_PATH_KAPPA, d + c + b*QT_PATH_KAPPA);
+
+ // left quadrants
+ if (quadrant == 1 || quadrant == 2)
+ p.rx() = -p.x();
+
+ // top quadrants
+ if (quadrant == 0 || quadrant == 1)
+ p.ry() = -p.y();
+
+ *points[i] = r.center() + QPointF(w2 * p.x(), h2 * p.y());
+ }
+}
+
+#ifdef QPP_DEBUG
+static void qt_debug_path(const QPainterPath &path)
+{
+ const char *names[] = {
+ "MoveTo ",
+ "LineTo ",
+ "CurveTo ",
+ "CurveToData"
+ };
+
+ printf("\nQPainterPath: elementCount=%d\n", path.elementCount());
+ for (int i=0; i<path.elementCount(); ++i) {
+ const QPainterPath::Element &e = path.elementAt(i);
+ Q_ASSERT(e.type >= 0 && e.type <= QPainterPath::CurveToDataElement);
+ printf(" - %3d:: %s, (%.2f, %.2f)\n", i, names[e.type], e.x, e.y);
+ }
+}
+#endif
+
+/*!
+ \class QPainterPath
+ \ingroup painting
+ \ingroup shared
+
+ \brief The QPainterPath class provides a container for painting operations,
+ enabling graphical shapes to be constructed and reused.
+
+ A painter path is an object composed of a number of graphical
+ building blocks, such as rectangles, ellipses, lines, and curves.
+ Building blocks can be joined in closed subpaths, for example as a
+ rectangle or an ellipse. A closed path has coinciding start and
+ end points. Or they can exist independently as unclosed subpaths,
+ such as lines and curves.
+
+ A QPainterPath object can be used for filling, outlining, and
+ clipping. To generate fillable outlines for a given painter path,
+ use the QPainterPathStroker class. The main advantage of painter
+ paths over normal drawing operations is that complex shapes only
+ need to be created once; then they can be drawn many times using
+ only calls to the QPainter::drawPath() function.
+
+ QPainterPath provides a collection of functions that can be used
+ to obtain information about the path and its elements. In addition
+ it is possible to reverse the order of the elements using the
+ toReversed() function. There are also several functions to convert
+ this painter path object into a polygon representation.
+
+ \tableofcontents
+
+ \section1 Composing a QPainterPath
+
+ A QPainterPath object can be constructed as an empty path, with a
+ given start point, or as a copy of another QPainterPath object.
+ Once created, lines and curves can be added to the path using the
+ lineTo(), arcTo(), cubicTo() and quadTo() functions. The lines and
+ curves stretch from the currentPosition() to the position passed
+ as argument.
+
+ The currentPosition() of the QPainterPath object is always the end
+ position of the last subpath that was added (or the initial start
+ point). Use the moveTo() function to move the currentPosition()
+ without adding a component. The moveTo() function implicitly
+ starts a new subpath, and closes the previous one. Another way of
+ starting a new subpath is to call the closeSubpath() function
+ which closes the current path by adding a line from the
+ currentPosition() back to the path's start position. Note that the
+ new path will have (0, 0) as its initial currentPosition().
+
+ QPainterPath class also provides several convenience functions to
+ add closed subpaths to a painter path: addEllipse(), addPath(),
+ addRect(), addRegion() and addText(). The addPolygon() function
+ adds an \e unclosed subpath. In fact, these functions are all
+ collections of moveTo(), lineTo() and cubicTo() operations.
+
+ In addition, a path can be added to the current path using the
+ connectPath() function. But note that this function will connect
+ the last element of the current path to the first element of given
+ one by adding a line.
+
+ Below is a code snippet that shows how a QPainterPath object can
+ be used:
+
+ \table 100%
+ \row
+ \o \inlineimage qpainterpath-construction.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainterpath.cpp 0
+ \endtable
+
+ The painter path is initially empty when constructed. We first add
+ a rectangle, which is a closed subpath. Then we add two bezier
+ curves which together form a closed subpath even though they are
+ not closed individually. Finally we draw the entire path. The path
+ is filled using the default fill rule, Qt::OddEvenFill. Qt
+ provides two methods for filling paths:
+
+ \table
+ \header
+ \o Qt::OddEvenFill
+ \o Qt::WindingFill
+ \row
+ \o \inlineimage qt-fillrule-oddeven.png
+ \o \inlineimage qt-fillrule-winding.png
+ \endtable
+
+ See the Qt::FillRule documentation for the definition of the
+ rules. A painter path's currently set fill rule can be retrieved
+ using the fillRule() function, and altered using the setFillRule()
+ function.
+
+ \section1 QPainterPath Information
+
+ The QPainterPath class provides a collection of functions that
+ returns information about the path and its elements.
+
+ The currentPosition() function returns the end point of the last
+ subpath that was added (or the initial start point). The
+ elementAt() function can be used to retrieve the various subpath
+ elements, the \e number of elements can be retrieved using the
+ elementCount() function, and the isEmpty() function tells whether
+ this QPainterPath object contains any elements at all.
+
+ The controlPointRect() function returns the rectangle containing
+ all the points and control points in this path. This function is
+ significantly faster to compute than the exact boundingRect()
+ which returns the bounding rectangle of this painter path with
+ floating point precision.
+
+ Finally, QPainterPath provides the contains() function which can
+ be used to determine whether a given point or rectangle is inside
+ the path, and the intersects() function which determines if any of
+ the points inside a given rectangle also are inside this path.
+
+ \section1 QPainterPath Conversion
+
+ For compatibility reasons, it might be required to simplify the
+ representation of a painter path: QPainterPath provides the
+ toFillPolygon(), toFillPolygons() and toSubpathPolygons()
+ functions which convert the painter path into a polygon. The
+ toFillPolygon() returns the painter path as one single polygon,
+ while the two latter functions return a list of polygons.
+
+ The toFillPolygons() and toSubpathPolygons() functions are
+ provided because it is usually faster to draw several small
+ polygons than to draw one large polygon, even though the total
+ number of points drawn is the same. The difference between the two
+ is the \e number of polygons they return: The toSubpathPolygons()
+ creates one polygon for each subpath regardless of intersecting
+ subpaths (i.e. overlapping bounding rectangles), while the
+ toFillPolygons() functions creates only one polygon for
+ overlapping subpaths.
+
+ The toFillPolygon() and toFillPolygons() functions first convert
+ all the subpaths to polygons, then uses a rewinding technique to
+ make sure that overlapping subpaths can be filled using the
+ correct fill rule. Note that rewinding inserts additional lines in
+ the polygon so the outline of the fill polygon does not match the
+ outline of the path.
+
+ \section1 Examples
+
+ Qt provides the \l {painting/painterpaths}{Painter Paths Example}
+ and the \l {demos/deform}{Vector Deformation Demo} which are
+ located in Qt's example and demo directories respectively.
+
+ The \l {painting/painterpaths}{Painter Paths Example} shows how
+ painter paths can be used to build complex shapes for rendering
+ and lets the user experiment with the filling and stroking. The
+ \l {demos/deform}{Vector Deformation Demo} shows how to use
+ QPainterPath to draw text.
+
+ \table
+ \header
+ \o \l {painting/painterpaths}{Painter Paths Example}
+ \o \l {demos/deform}{Vector Deformation Demo}
+ \row
+ \o \inlineimage qpainterpath-example.png
+ \o \inlineimage qpainterpath-demo.png
+ \endtable
+
+ \sa QPainterPathStroker, QPainter, QRegion, {Painter Paths Example}
+*/
+
+/*!
+ \enum QPainterPath::ElementType
+
+ This enum describes the types of elements used to connect vertices
+ in subpaths.
+
+ Note that elements added as closed subpaths using the
+ addEllipse(), addPath(), addPolygon(), addRect(), addRegion() and
+ addText() convenience functions, is actually added to the path as
+ a collection of separate elements using the moveTo(), lineTo() and
+ cubicTo() functions.
+
+ \value MoveToElement A new subpath. See also moveTo().
+ \value LineToElement A line. See also lineTo().
+ \value CurveToElement A curve. See also cubicTo() and quadTo().
+ \value CurveToDataElement The extra data required to describe a curve in
+ a CurveToElement element.
+
+ \sa elementAt(), elementCount()
+*/
+
+/*!
+ \class QPainterPath::Element
+
+ \brief The QPainterPath::Element class specifies the position and
+ type of a subpath.
+
+ Once a QPainterPath object is constructed, subpaths like lines and
+ curves can be added to the path (creating
+ QPainterPath::LineToElement and QPainterPath::CurveToElement
+ components).
+
+ The lines and curves stretch from the currentPosition() to the
+ position passed as argument. The currentPosition() of the
+ QPainterPath object is always the end position of the last subpath
+ that was added (or the initial start point). The moveTo() function
+ can be used to move the currentPosition() without adding a line or
+ curve, creating a QPainterPath::MoveToElement component.
+
+ \sa QPainterPath
+*/
+
+/*!
+ \variable QPainterPath::Element::x
+ \brief the x coordinate of the element's position.
+
+ \sa {operator QPointF()}
+*/
+
+/*!
+ \variable QPainterPath::Element::y
+ \brief the y coordinate of the element's position.
+
+ \sa {operator QPointF()}
+*/
+
+/*!
+ \variable QPainterPath::Element::type
+ \brief the type of element
+
+ \sa isCurveTo(), isLineTo(), isMoveTo()
+*/
+
+/*!
+ \fn bool QPainterPath::Element::operator==(const Element &other) const
+ \since 4.2
+
+ Returns true if this element is equal to \a other;
+ otherwise returns false.
+
+ \sa operator!=()
+*/
+
+/*!
+ \fn bool QPainterPath::Element::operator!=(const Element &other) const
+ \since 4.2
+
+ Returns true if this element is not equal to \a other;
+ otherwise returns false.
+
+ \sa operator==()
+*/
+
+/*!
+ \fn bool QPainterPath::Element::isCurveTo () const
+
+ Returns true if the element is a curve, otherwise returns false.
+
+ \sa type, QPainterPath::CurveToElement
+*/
+
+/*!
+ \fn bool QPainterPath::Element::isLineTo () const
+
+ Returns true if the element is a line, otherwise returns false.
+
+ \sa type, QPainterPath::LineToElement
+*/
+
+/*!
+ \fn bool QPainterPath::Element::isMoveTo () const
+
+ Returns true if the element is moving the current position,
+ otherwise returns false.
+
+ \sa type, QPainterPath::MoveToElement
+*/
+
+/*!
+ \fn QPainterPath::Element::operator QPointF () const
+
+ Returns the element's position.
+
+ \sa x, y
+*/
+
+/*!
+ \fn void QPainterPath::addEllipse(qreal x, qreal y, qreal width, qreal height)
+ \overload
+
+ Creates an ellipse within the bounding rectangle defined by its top-left
+ corner at (\a x, \a y), \a width and \a height, and adds it to the
+ painter path as a closed subpath.
+*/
+
+/*!
+ \since 4.4
+
+ \fn void QPainterPath::addEllipse(const QPointF &center, qreal rx, qreal ry)
+ \overload
+
+ Creates an ellipse positioned at \a{center} with radii \a{rx} and \a{ry},
+ and adds it to the painter path as a closed subpath.
+*/
+
+/*!
+ \fn void QPainterPath::addText(qreal x, qreal y, const QFont &font, const QString &text)
+ \overload
+
+ Adds the given \a text to this path as a set of closed subpaths created
+ from the \a font supplied. The subpaths are positioned so that the left
+ end of the text's baseline lies at the point specified by (\a x, \a y).
+*/
+
+/*!
+ \fn int QPainterPath::elementCount() const
+
+ Returns the number of path elements in the painter path.
+
+ \sa ElementType, elementAt(), isEmpty()
+*/
+
+/*!
+ \fn const QPainterPath::Element &QPainterPath::elementAt(int index) const
+
+ Returns the element at the given \a index in the painter path.
+
+ \sa ElementType, elementCount(), isEmpty()
+*/
+
+/*!
+ \fn void QPainterPath::setElementPositionAt(int index, qreal x, qreal y)
+ \since 4.2
+
+ Sets the x and y coordinate of the element at index \a index to \a
+ x and \a y.
+*/
+
+/*###
+ \fn QPainterPath &QPainterPath::operator +=(const QPainterPath &other)
+
+ Appends the \a other painter path to this painter path and returns a
+ reference to the result.
+*/
+
+/*!
+ Constructs an empty QPainterPath object.
+*/
+QPainterPath::QPainterPath()
+ : d_ptr(0)
+{
+}
+
+/*!
+ \fn QPainterPath::QPainterPath(const QPainterPath &path)
+
+ Creates a QPainterPath object that is a copy of the given \a path.
+
+ \sa operator=()
+*/
+QPainterPath::QPainterPath(const QPainterPath &other)
+ : d_ptr(other.d_ptr.data())
+{
+ if (d_ptr)
+ d_ptr->ref.ref();
+}
+
+/*!
+ Creates a QPainterPath object with the given \a startPoint as its
+ current position.
+*/
+
+QPainterPath::QPainterPath(const QPointF &startPoint)
+ : d_ptr(new QPainterPathData)
+{
+ Element e = { startPoint.x(), startPoint.y(), MoveToElement };
+ d_func()->elements << e;
+}
+
+/*!
+ \internal
+*/
+void QPainterPath::detach_helper()
+{
+ QPainterPathPrivate *data = new QPainterPathData(*d_func());
+ d_ptr.reset(data);
+}
+
+/*!
+ \internal
+*/
+void QPainterPath::ensureData_helper()
+{
+ QPainterPathPrivate *data = new QPainterPathData;
+ data->elements.reserve(16);
+ QPainterPath::Element e = { 0, 0, QPainterPath::MoveToElement };
+ data->elements << e;
+ d_ptr.reset(data);
+ Q_ASSERT(d_ptr != 0);
+}
+
+/*!
+ \fn QPainterPath &QPainterPath::operator=(const QPainterPath &path)
+
+ Assigns the given \a path to this painter path.
+
+ \sa QPainterPath()
+*/
+QPainterPath &QPainterPath::operator=(const QPainterPath &other)
+{
+ if (other.d_func() != d_func()) {
+ QPainterPathPrivate *data = other.d_func();
+ if (data)
+ data->ref.ref();
+ d_ptr.reset(data);
+ }
+ return *this;
+}
+
+/*!
+ \fn void QPainterPath::swap(QPainterPath &other)
+ \since 4.8
+
+ Swaps painter path \a other with this painter path. This operation is very
+ fast and never fails.
+*/
+
+/*!
+ Destroys this QPainterPath object.
+*/
+QPainterPath::~QPainterPath()
+{
+}
+
+/*!
+ Closes the current subpath by drawing a line to the beginning of
+ the subpath, automatically starting a new path. The current point
+ of the new path is (0, 0).
+
+ If the subpath does not contain any elements, this function does
+ nothing.
+
+ \sa moveTo(), {QPainterPath#Composing a QPainterPath}{Composing
+ a QPainterPath}
+ */
+void QPainterPath::closeSubpath()
+{
+#ifdef QPP_DEBUG
+ printf("QPainterPath::closeSubpath()\n");
+#endif
+ if (isEmpty())
+ return;
+ detach();
+
+ d_func()->close();
+}
+
+/*!
+ \fn void QPainterPath::moveTo(qreal x, qreal y)
+
+ \overload
+
+ Moves the current position to (\a{x}, \a{y}) and starts a new
+ subpath, implicitly closing the previous path.
+*/
+
+/*!
+ \fn void QPainterPath::moveTo(const QPointF &point)
+
+ Moves the current point to the given \a point, implicitly starting
+ a new subpath and closing the previous one.
+
+ \sa closeSubpath(), {QPainterPath#Composing a
+ QPainterPath}{Composing a QPainterPath}
+*/
+void QPainterPath::moveTo(const QPointF &p)
+{
+#ifdef QPP_DEBUG
+ printf("QPainterPath::moveTo() (%.2f,%.2f)\n", p.x(), p.y());
+#endif
+
+ if (!qt_is_finite(p.x()) || !qt_is_finite(p.y())) {
+#ifndef QT_NO_DEBUG
+ qWarning("QPainterPath::moveTo: Adding point where x or y is NaN or Inf, ignoring call");
+#endif
+ return;
+ }
+
+ ensureData();
+ detach();
+
+ QPainterPathData *d = d_func();
+ Q_ASSERT(!d->elements.isEmpty());
+
+ d->require_moveTo = false;
+
+ if (d->elements.last().type == MoveToElement) {
+ d->elements.last().x = p.x();
+ d->elements.last().y = p.y();
+ } else {
+ Element elm = { p.x(), p.y(), MoveToElement };
+ d->elements.append(elm);
+ }
+ d->cStart = d->elements.size() - 1;
+}
+
+/*!
+ \fn void QPainterPath::lineTo(qreal x, qreal y)
+
+ \overload
+
+ Draws a line from the current position to the point (\a{x},
+ \a{y}).
+*/
+
+/*!
+ \fn void QPainterPath::lineTo(const QPointF &endPoint)
+
+ Adds a straight line from the current position to the given \a
+ endPoint. After the line is drawn, the current position is updated
+ to be at the end point of the line.
+
+ \sa addPolygon(), addRect(), {QPainterPath#Composing a
+ QPainterPath}{Composing a QPainterPath}
+ */
+void QPainterPath::lineTo(const QPointF &p)
+{
+#ifdef QPP_DEBUG
+ printf("QPainterPath::lineTo() (%.2f,%.2f)\n", p.x(), p.y());
+#endif
+
+ if (!qt_is_finite(p.x()) || !qt_is_finite(p.y())) {
+#ifndef QT_NO_DEBUG
+ qWarning("QPainterPath::lineTo: Adding point where x or y is NaN or Inf, ignoring call");
+#endif
+ return;
+ }
+
+ ensureData();
+ detach();
+
+ QPainterPathData *d = d_func();
+ Q_ASSERT(!d->elements.isEmpty());
+ d->maybeMoveTo();
+ if (p == QPointF(d->elements.last()))
+ return;
+ Element elm = { p.x(), p.y(), LineToElement };
+ d->elements.append(elm);
+
+ d->convex = d->elements.size() == 3 || (d->elements.size() == 4 && d->isClosed());
+}
+
+/*!
+ \fn void QPainterPath::cubicTo(qreal c1X, qreal c1Y, qreal c2X,
+ qreal c2Y, qreal endPointX, qreal endPointY);
+
+ \overload
+
+ Adds a cubic Bezier curve between the current position and the end
+ point (\a{endPointX}, \a{endPointY}) with control points specified
+ by (\a{c1X}, \a{c1Y}) and (\a{c2X}, \a{c2Y}).
+*/
+
+/*!
+ \fn void QPainterPath::cubicTo(const QPointF &c1, const QPointF &c2, const QPointF &endPoint)
+
+ Adds a cubic Bezier curve between the current position and the
+ given \a endPoint using the control points specified by \a c1, and
+ \a c2.
+
+ After the curve is added, the current position is updated to be at
+ the end point of the curve.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainterpath-cubicto.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainterpath.cpp 1
+ \endtable
+
+ \sa quadTo(), {QPainterPath#Composing a QPainterPath}{Composing
+ a QPainterPath}
+*/
+void QPainterPath::cubicTo(const QPointF &c1, const QPointF &c2, const QPointF &e)
+{
+#ifdef QPP_DEBUG
+ printf("QPainterPath::cubicTo() (%.2f,%.2f), (%.2f,%.2f), (%.2f,%.2f)\n",
+ c1.x(), c1.y(), c2.x(), c2.y(), e.x(), e.y());
+#endif
+
+ if (!qt_is_finite(c1.x()) || !qt_is_finite(c1.y()) || !qt_is_finite(c2.x()) || !qt_is_finite(c2.y())
+ || !qt_is_finite(e.x()) || !qt_is_finite(e.y())) {
+#ifndef QT_NO_DEBUG
+ qWarning("QPainterPath::cubicTo: Adding point where x or y is NaN or Inf, ignoring call");
+#endif
+ return;
+ }
+
+ ensureData();
+ detach();
+
+ QPainterPathData *d = d_func();
+ Q_ASSERT(!d->elements.isEmpty());
+
+
+ // Abort on empty curve as a stroker cannot handle this and the
+ // curve is irrelevant anyway.
+ if (d->elements.last() == c1 && c1 == c2 && c2 == e)
+ return;
+
+ d->maybeMoveTo();
+
+ Element ce1 = { c1.x(), c1.y(), CurveToElement };
+ Element ce2 = { c2.x(), c2.y(), CurveToDataElement };
+ Element ee = { e.x(), e.y(), CurveToDataElement };
+ d->elements << ce1 << ce2 << ee;
+}
+
+/*!
+ \fn void QPainterPath::quadTo(qreal cx, qreal cy, qreal endPointX, qreal endPointY);
+
+ \overload
+
+ Adds a quadratic Bezier curve between the current point and the endpoint
+ (\a{endPointX}, \a{endPointY}) with the control point specified by
+ (\a{cx}, \a{cy}).
+*/
+
+/*!
+ \fn void QPainterPath::quadTo(const QPointF &c, const QPointF &endPoint)
+
+ Adds a quadratic Bezier curve between the current position and the
+ given \a endPoint with the control point specified by \a c.
+
+ After the curve is added, the current point is updated to be at
+ the end point of the curve.
+
+ \sa cubicTo(), {QPainterPath#Composing a QPainterPath}{Composing a
+ QPainterPath}
+*/
+void QPainterPath::quadTo(const QPointF &c, const QPointF &e)
+{
+#ifdef QPP_DEBUG
+ printf("QPainterPath::quadTo() (%.2f,%.2f), (%.2f,%.2f)\n",
+ c.x(), c.y(), e.x(), e.y());
+#endif
+
+ if (!qt_is_finite(c.x()) || !qt_is_finite(c.y()) || !qt_is_finite(e.x()) || !qt_is_finite(e.y())) {
+#ifndef QT_NO_DEBUG
+ qWarning("QPainterPath::quadTo: Adding point where x or y is NaN or Inf, ignoring call");
+#endif
+ return;
+ }
+
+ ensureData();
+ detach();
+
+ Q_D(QPainterPath);
+ Q_ASSERT(!d->elements.isEmpty());
+ const QPainterPath::Element &elm = d->elements.at(elementCount()-1);
+ QPointF prev(elm.x, elm.y);
+
+ // Abort on empty curve as a stroker cannot handle this and the
+ // curve is irrelevant anyway.
+ if (prev == c && c == e)
+ return;
+
+ QPointF c1((prev.x() + 2*c.x()) / 3, (prev.y() + 2*c.y()) / 3);
+ QPointF c2((e.x() + 2*c.x()) / 3, (e.y() + 2*c.y()) / 3);
+ cubicTo(c1, c2, e);
+}
+
+/*!
+ \fn void QPainterPath::arcTo(qreal x, qreal y, qreal width, qreal
+ height, qreal startAngle, qreal sweepLength)
+
+ \overload
+
+ Creates an arc that occupies the rectangle QRectF(\a x, \a y, \a
+ width, \a height), beginning at the specified \a startAngle and
+ extending \a sweepLength degrees counter-clockwise.
+
+*/
+
+/*!
+ \fn void QPainterPath::arcTo(const QRectF &rectangle, qreal startAngle, qreal sweepLength)
+
+ Creates an arc that occupies the given \a rectangle, beginning at
+ the specified \a startAngle and extending \a sweepLength degrees
+ counter-clockwise.
+
+ Angles are specified in degrees. Clockwise arcs can be specified
+ using negative angles.
+
+ Note that this function connects the starting point of the arc to
+ the current position if they are not already connected. After the
+ arc has been added, the current position is the last point in
+ arc. To draw a line back to the first point, use the
+ closeSubpath() function.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainterpath-arcto.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainterpath.cpp 2
+ \endtable
+
+ \sa arcMoveTo(), addEllipse(), QPainter::drawArc(), QPainter::drawPie(),
+ {QPainterPath#Composing a QPainterPath}{Composing a
+ QPainterPath}
+*/
+void QPainterPath::arcTo(const QRectF &rect, qreal startAngle, qreal sweepLength)
+{
+#ifdef QPP_DEBUG
+ printf("QPainterPath::arcTo() (%.2f, %.2f, %.2f, %.2f, angle=%.2f, sweep=%.2f\n",
+ rect.x(), rect.y(), rect.width(), rect.height(), startAngle, sweepLength);
+#endif
+
+ if ((!qt_is_finite(rect.x()) && !qt_is_finite(rect.y())) || !qt_is_finite(rect.width()) || !qt_is_finite(rect.height())
+ || !qt_is_finite(startAngle) || !qt_is_finite(sweepLength)) {
+#ifndef QT_NO_DEBUG
+ qWarning("QPainterPath::arcTo: Adding arc where a parameter is NaN or Inf, ignoring call");
+#endif
+ return;
+ }
+
+ if (rect.isNull())
+ return;
+
+ ensureData();
+ detach();
+
+ int point_count;
+ QPointF pts[15];
+ QPointF curve_start = qt_curves_for_arc(rect, startAngle, sweepLength, pts, &point_count);
+
+ lineTo(curve_start);
+ for (int i=0; i<point_count; i+=3) {
+ cubicTo(pts[i].x(), pts[i].y(),
+ pts[i+1].x(), pts[i+1].y(),
+ pts[i+2].x(), pts[i+2].y());
+ }
+
+}
+
+
+/*!
+ \fn void QPainterPath::arcMoveTo(qreal x, qreal y, qreal width, qreal height, qreal angle)
+ \overload
+ \since 4.2
+
+ Creates a move to that lies on the arc that occupies the
+ QRectF(\a x, \a y, \a width, \a height) at \a angle.
+*/
+
+
+/*!
+ \fn void QPainterPath::arcMoveTo(const QRectF &rectangle, qreal angle)
+ \since 4.2
+
+ Creates a move to that lies on the arc that occupies the given \a
+ rectangle at \a angle.
+
+ Angles are specified in degrees. Clockwise arcs can be specified
+ using negative angles.
+
+ \sa moveTo(), arcTo()
+*/
+
+void QPainterPath::arcMoveTo(const QRectF &rect, qreal angle)
+{
+ if (rect.isNull())
+ return;
+
+ QPointF pt;
+ qt_find_ellipse_coords(rect, angle, 0, &pt, 0);
+ moveTo(pt);
+}
+
+
+
+/*!
+ \fn QPointF QPainterPath::currentPosition() const
+
+ Returns the current position of the path.
+*/
+QPointF QPainterPath::currentPosition() const
+{
+ return !d_ptr || d_func()->elements.isEmpty()
+ ? QPointF()
+ : QPointF(d_func()->elements.last().x, d_func()->elements.last().y);
+}
+
+
+/*!
+ \fn void QPainterPath::addRect(qreal x, qreal y, qreal width, qreal height)
+
+ \overload
+
+ Adds a rectangle at position (\a{x}, \a{y}), with the given \a
+ width and \a height, as a closed subpath.
+*/
+
+/*!
+ \fn void QPainterPath::addRect(const QRectF &rectangle)
+
+ Adds the given \a rectangle to this path as a closed subpath.
+
+ The \a rectangle is added as a clockwise set of lines. The painter
+ path's current position after the \a rectangle has been added is
+ at the top-left corner of the rectangle.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainterpath-addrectangle.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainterpath.cpp 3
+ \endtable
+
+ \sa addRegion(), lineTo(), {QPainterPath#Composing a
+ QPainterPath}{Composing a QPainterPath}
+*/
+void QPainterPath::addRect(const QRectF &r)
+{
+ if (!qt_is_finite(r.x()) || !qt_is_finite(r.y()) || !qt_is_finite(r.width()) || !qt_is_finite(r.height())) {
+#ifndef QT_NO_DEBUG
+ qWarning("QPainterPath::addRect: Adding rect where a parameter is NaN or Inf, ignoring call");
+#endif
+ return;
+ }
+
+ if (r.isNull())
+ return;
+
+ ensureData();
+ detach();
+
+ bool first = d_func()->elements.size() < 2;
+
+ d_func()->elements.reserve(d_func()->elements.size() + 5);
+ moveTo(r.x(), r.y());
+
+ Element l1 = { r.x() + r.width(), r.y(), LineToElement };
+ Element l2 = { r.x() + r.width(), r.y() + r.height(), LineToElement };
+ Element l3 = { r.x(), r.y() + r.height(), LineToElement };
+ Element l4 = { r.x(), r.y(), LineToElement };
+
+ d_func()->elements << l1 << l2 << l3 << l4;
+ d_func()->require_moveTo = true;
+ d_func()->convex = first;
+}
+
+/*!
+ Adds the given \a polygon to the path as an (unclosed) subpath.
+
+ Note that the current position after the polygon has been added,
+ is the last point in \a polygon. To draw a line back to the first
+ point, use the closeSubpath() function.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainterpath-addpolygon.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainterpath.cpp 4
+ \endtable
+
+ \sa lineTo(), {QPainterPath#Composing a QPainterPath}{Composing
+ a QPainterPath}
+*/
+void QPainterPath::addPolygon(const QPolygonF &polygon)
+{
+ if (polygon.isEmpty())
+ return;
+
+ ensureData();
+ detach();
+
+ d_func()->elements.reserve(d_func()->elements.size() + polygon.size());
+
+ moveTo(polygon.first());
+ for (int i=1; i<polygon.size(); ++i) {
+ Element elm = { polygon.at(i).x(), polygon.at(i).y(), LineToElement };
+ d_func()->elements << elm;
+ }
+}
+
+/*!
+ \fn void QPainterPath::addEllipse(const QRectF &boundingRectangle)
+
+ Creates an ellipse within the specified \a boundingRectangle
+ and adds it to the painter path as a closed subpath.
+
+ The ellipse is composed of a clockwise curve, starting and
+ finishing at zero degrees (the 3 o'clock position).
+
+ \table 100%
+ \row
+ \o \inlineimage qpainterpath-addellipse.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainterpath.cpp 5
+ \endtable
+
+ \sa arcTo(), QPainter::drawEllipse(), {QPainterPath#Composing a
+ QPainterPath}{Composing a QPainterPath}
+*/
+void QPainterPath::addEllipse(const QRectF &boundingRect)
+{
+ if (!qt_is_finite(boundingRect.x()) || !qt_is_finite(boundingRect.y())
+ || !qt_is_finite(boundingRect.width()) || !qt_is_finite(boundingRect.height())) {
+#ifndef QT_NO_DEBUG
+ qWarning("QPainterPath::addEllipse: Adding ellipse where a parameter is NaN or Inf, ignoring call");
+#endif
+ return;
+ }
+
+ if (boundingRect.isNull())
+ return;
+
+ ensureData();
+ detach();
+
+ Q_D(QPainterPath);
+ bool first = d_func()->elements.size() < 2;
+ d->elements.reserve(d->elements.size() + 13);
+
+ QPointF pts[12];
+ int point_count;
+ QPointF start = qt_curves_for_arc(boundingRect, 0, -360, pts, &point_count);
+
+ moveTo(start);
+ cubicTo(pts[0], pts[1], pts[2]); // 0 -> 270
+ cubicTo(pts[3], pts[4], pts[5]); // 270 -> 180
+ cubicTo(pts[6], pts[7], pts[8]); // 180 -> 90
+ cubicTo(pts[9], pts[10], pts[11]); // 90 - >0
+ d_func()->require_moveTo = true;
+
+ d_func()->convex = first;
+}
+
+/*!
+ \fn void QPainterPath::addText(const QPointF &point, const QFont &font, const QString &text)
+
+ Adds the given \a text to this path as a set of closed subpaths
+ created from the \a font supplied. The subpaths are positioned so
+ that the left end of the text's baseline lies at the specified \a
+ point.
+
+ \table 100%
+ \row
+ \o \inlineimage qpainterpath-addtext.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpainterpath.cpp 6
+ \endtable
+
+ \sa QPainter::drawText(), {QPainterPath#Composing a
+ QPainterPath}{Composing a QPainterPath}
+*/
+void QPainterPath::addText(const QPointF &point, const QFont &f, const QString &text)
+{
+ if (text.isEmpty())
+ return;
+
+ ensureData();
+ detach();
+
+ QTextLayout layout(text, f);
+ layout.setCacheEnabled(true);
+ QTextEngine *eng = layout.engine();
+ layout.beginLayout();
+ QTextLine line = layout.createLine();
+ layout.endLayout();
+ const QScriptLine &sl = eng->lines[0];
+ if (!sl.length || !eng->layoutData)
+ return;
+
+ int nItems = eng->layoutData->items.size();
+
+ qreal x(point.x());
+ qreal y(point.y());
+
+ QVarLengthArray<int> visualOrder(nItems);
+ QVarLengthArray<uchar> levels(nItems);
+ for (int i = 0; i < nItems; ++i)
+ levels[i] = eng->layoutData->items[i].analysis.bidiLevel;
+ QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
+
+ for (int i = 0; i < nItems; ++i) {
+ int item = visualOrder[i];
+ QScriptItem &si = eng->layoutData->items[item];
+
+ if (si.analysis.flags < QScriptAnalysis::TabOrObject) {
+ QGlyphLayout glyphs = eng->shapedGlyphs(&si);
+ QFontEngine *fe = f.d->engineForScript(si.analysis.script);
+ Q_ASSERT(fe);
+ fe->addOutlineToPath(x, y, glyphs, this,
+ si.analysis.bidiLevel % 2
+ ? QTextItem::RenderFlags(QTextItem::RightToLeft)
+ : QTextItem::RenderFlags(0));
+
+ const qreal lw = fe->lineThickness().toReal();
+ if (f.d->underline) {
+ qreal pos = fe->underlinePosition().toReal();
+ addRect(x, y + pos, si.width.toReal(), lw);
+ }
+ if (f.d->overline) {
+ qreal pos = fe->ascent().toReal() + 1;
+ addRect(x, y - pos, si.width.toReal(), lw);
+ }
+ if (f.d->strikeOut) {
+ qreal pos = fe->ascent().toReal() / 3;
+ addRect(x, y - pos, si.width.toReal(), lw);
+ }
+ }
+ x += si.width.toReal();
+ }
+}
+
+/*!
+ \fn void QPainterPath::addPath(const QPainterPath &path)
+
+ Adds the given \a path to \e this path as a closed subpath.
+
+ \sa connectPath(), {QPainterPath#Composing a
+ QPainterPath}{Composing a QPainterPath}
+*/
+void QPainterPath::addPath(const QPainterPath &other)
+{
+ if (other.isEmpty())
+ return;
+
+ ensureData();
+ detach();
+
+ QPainterPathData *d = reinterpret_cast<QPainterPathData *>(d_func());
+ // Remove last moveto so we don't get multiple moveto's
+ if (d->elements.last().type == MoveToElement)
+ d->elements.remove(d->elements.size()-1);
+
+ // Locate where our own current subpath will start after the other path is added.
+ int cStart = d->elements.size() + other.d_func()->cStart;
+ d->elements += other.d_func()->elements;
+ d->cStart = cStart;
+
+ d->require_moveTo = other.d_func()->isClosed();
+}
+
+
+/*!
+ \fn void QPainterPath::connectPath(const QPainterPath &path)
+
+ Connects the given \a path to \e this path by adding a line from the
+ last element of this path to the first element of the given path.
+
+ \sa addPath(), {QPainterPath#Composing a QPainterPath}{Composing
+ a QPainterPath}
+*/
+void QPainterPath::connectPath(const QPainterPath &other)
+{
+ if (other.isEmpty())
+ return;
+
+ ensureData();
+ detach();
+
+ QPainterPathData *d = reinterpret_cast<QPainterPathData *>(d_func());
+ // Remove last moveto so we don't get multiple moveto's
+ if (d->elements.last().type == MoveToElement)
+ d->elements.remove(d->elements.size()-1);
+
+ // Locate where our own current subpath will start after the other path is added.
+ int cStart = d->elements.size() + other.d_func()->cStart;
+ int first = d->elements.size();
+ d->elements += other.d_func()->elements;
+
+ if (first != 0)
+ d->elements[first].type = LineToElement;
+
+ // avoid duplicate points
+ if (first > 0 && QPointF(d->elements[first]) == QPointF(d->elements[first - 1])) {
+ d->elements.remove(first--);
+ --cStart;
+ }
+
+ if (cStart != first)
+ d->cStart = cStart;
+}
+
+/*!
+ Adds the given \a region to the path by adding each rectangle in
+ the region as a separate closed subpath.
+
+ \sa addRect(), {QPainterPath#Composing a QPainterPath}{Composing
+ a QPainterPath}
+*/
+void QPainterPath::addRegion(const QRegion &region)
+{
+ ensureData();
+ detach();
+
+ QVector<QRect> rects = region.rects();
+ d_func()->elements.reserve(rects.size() * 5);
+ for (int i=0; i<rects.size(); ++i)
+ addRect(rects.at(i));
+}
+
+
+/*!
+ Returns the painter path's currently set fill rule.
+
+ \sa setFillRule()
+*/
+Qt::FillRule QPainterPath::fillRule() const
+{
+ return isEmpty() ? Qt::OddEvenFill : d_func()->fillRule;
+}
+
+/*!
+ \fn void QPainterPath::setFillRule(Qt::FillRule fillRule)
+
+ Sets the fill rule of the painter path to the given \a
+ fillRule. Qt provides two methods for filling paths:
+
+ \table
+ \header
+ \o Qt::OddEvenFill (default)
+ \o Qt::WindingFill
+ \row
+ \o \inlineimage qt-fillrule-oddeven.png
+ \o \inlineimage qt-fillrule-winding.png
+ \endtable
+
+ \sa fillRule()
+*/
+void QPainterPath::setFillRule(Qt::FillRule fillRule)
+{
+ ensureData();
+ if (d_func()->fillRule == fillRule)
+ return;
+ detach();
+
+ d_func()->fillRule = fillRule;
+}
+
+#define QT_BEZIER_A(bezier, coord) 3 * (-bezier.coord##1 \
+ + 3*bezier.coord##2 \
+ - 3*bezier.coord##3 \
+ +bezier.coord##4)
+
+#define QT_BEZIER_B(bezier, coord) 6 * (bezier.coord##1 \
+ - 2*bezier.coord##2 \
+ + bezier.coord##3)
+
+#define QT_BEZIER_C(bezier, coord) 3 * (- bezier.coord##1 \
+ + bezier.coord##2)
+
+#define QT_BEZIER_CHECK_T(bezier, t) \
+ if (t >= 0 && t <= 1) { \
+ QPointF p(b.pointAt(t)); \
+ if (p.x() < minx) minx = p.x(); \
+ else if (p.x() > maxx) maxx = p.x(); \
+ if (p.y() < miny) miny = p.y(); \
+ else if (p.y() > maxy) maxy = p.y(); \
+ }
+
+
+static QRectF qt_painterpath_bezier_extrema(const QBezier &b)
+{
+ qreal minx, miny, maxx, maxy;
+
+ // initialize with end points
+ if (b.x1 < b.x4) {
+ minx = b.x1;
+ maxx = b.x4;
+ } else {
+ minx = b.x4;
+ maxx = b.x1;
+ }
+ if (b.y1 < b.y4) {
+ miny = b.y1;
+ maxy = b.y4;
+ } else {
+ miny = b.y4;
+ maxy = b.y1;
+ }
+
+ // Update for the X extrema
+ {
+ qreal ax = QT_BEZIER_A(b, x);
+ qreal bx = QT_BEZIER_B(b, x);
+ qreal cx = QT_BEZIER_C(b, x);
+ // specialcase quadratic curves to avoid div by zero
+ if (qFuzzyIsNull(ax)) {
+
+ // linear curves are covered by initialization.
+ if (!qFuzzyIsNull(bx)) {
+ qreal t = -cx / bx;
+ QT_BEZIER_CHECK_T(b, t);
+ }
+
+ } else {
+ const qreal tx = bx * bx - 4 * ax * cx;
+
+ if (tx >= 0) {
+ qreal temp = qSqrt(tx);
+ qreal rcp = 1 / (2 * ax);
+ qreal t1 = (-bx + temp) * rcp;
+ QT_BEZIER_CHECK_T(b, t1);
+
+ qreal t2 = (-bx - temp) * rcp;
+ QT_BEZIER_CHECK_T(b, t2);
+ }
+ }
+ }
+
+ // Update for the Y extrema
+ {
+ qreal ay = QT_BEZIER_A(b, y);
+ qreal by = QT_BEZIER_B(b, y);
+ qreal cy = QT_BEZIER_C(b, y);
+
+ // specialcase quadratic curves to avoid div by zero
+ if (qFuzzyIsNull(ay)) {
+
+ // linear curves are covered by initialization.
+ if (!qFuzzyIsNull(by)) {
+ qreal t = -cy / by;
+ QT_BEZIER_CHECK_T(b, t);
+ }
+
+ } else {
+ const qreal ty = by * by - 4 * ay * cy;
+
+ if (ty > 0) {
+ qreal temp = qSqrt(ty);
+ qreal rcp = 1 / (2 * ay);
+ qreal t1 = (-by + temp) * rcp;
+ QT_BEZIER_CHECK_T(b, t1);
+
+ qreal t2 = (-by - temp) * rcp;
+ QT_BEZIER_CHECK_T(b, t2);
+ }
+ }
+ }
+ return QRectF(minx, miny, maxx - minx, maxy - miny);
+}
+
+/*!
+ Returns the bounding rectangle of this painter path as a rectangle with
+ floating point precision.
+
+ \sa controlPointRect()
+*/
+QRectF QPainterPath::boundingRect() const
+{
+ if (!d_ptr)
+ return QRectF();
+ QPainterPathData *d = d_func();
+
+ if (d->dirtyBounds)
+ computeBoundingRect();
+ return d->bounds;
+}
+
+/*!
+ Returns the rectangle containing all the points and control points
+ in this path.
+
+ This function is significantly faster to compute than the exact
+ boundingRect(), and the returned rectangle is always a superset of
+ the rectangle returned by boundingRect().
+
+ \sa boundingRect()
+*/
+QRectF QPainterPath::controlPointRect() const
+{
+ if (!d_ptr)
+ return QRectF();
+ QPainterPathData *d = d_func();
+
+ if (d->dirtyControlBounds)
+ computeControlPointRect();
+ return d->controlBounds;
+}
+
+
+/*!
+ \fn bool QPainterPath::isEmpty() const
+
+ Returns true if either there are no elements in this path, or if the only
+ element is a MoveToElement; otherwise returns false.
+
+ \sa elementCount()
+*/
+
+/*!
+ Creates and returns a reversed copy of the path.
+
+ It is the order of the elements that is reversed: If a
+ QPainterPath is composed by calling the moveTo(), lineTo() and
+ cubicTo() functions in the specified order, the reversed copy is
+ composed by calling cubicTo(), lineTo() and moveTo().
+*/
+QPainterPath QPainterPath::toReversed() const
+{
+ Q_D(const QPainterPath);
+ QPainterPath rev;
+
+ if (isEmpty()) {
+ rev = *this;
+ return rev;
+ }
+
+ rev.moveTo(d->elements.at(d->elements.size()-1).x, d->elements.at(d->elements.size()-1).y);
+
+ for (int i=d->elements.size()-1; i>=1; --i) {
+ const QPainterPath::Element &elm = d->elements.at(i);
+ const QPainterPath::Element &prev = d->elements.at(i-1);
+ switch (elm.type) {
+ case LineToElement:
+ rev.lineTo(prev.x, prev.y);
+ break;
+ case MoveToElement:
+ rev.moveTo(prev.x, prev.y);
+ break;
+ case CurveToDataElement:
+ {
+ Q_ASSERT(i>=3);
+ const QPainterPath::Element &cp1 = d->elements.at(i-2);
+ const QPainterPath::Element &sp = d->elements.at(i-3);
+ Q_ASSERT(prev.type == CurveToDataElement);
+ Q_ASSERT(cp1.type == CurveToElement);
+ rev.cubicTo(prev.x, prev.y, cp1.x, cp1.y, sp.x, sp.y);
+ i -= 2;
+ break;
+ }
+ default:
+ Q_ASSERT(!"qt_reversed_path");
+ break;
+ }
+ }
+ //qt_debug_path(rev);
+ return rev;
+}
+
+/*!
+ Converts the path into a list of polygons using the QTransform
+ \a matrix, and returns the list.
+
+ This function creates one polygon for each subpath regardless of
+ intersecting subpaths (i.e. overlapping bounding rectangles). To
+ make sure that such overlapping subpaths are filled correctly, use
+ the toFillPolygons() function instead.
+
+ \sa toFillPolygons(), toFillPolygon(), {QPainterPath#QPainterPath
+ Conversion}{QPainterPath Conversion}
+*/
+QList<QPolygonF> QPainterPath::toSubpathPolygons(const QTransform &matrix) const
+{
+
+ Q_D(const QPainterPath);
+ QList<QPolygonF> flatCurves;
+ if (isEmpty())
+ return flatCurves;
+
+ QPolygonF current;
+ for (int i=0; i<elementCount(); ++i) {
+ const QPainterPath::Element &e = d->elements.at(i);
+ switch (e.type) {
+ case QPainterPath::MoveToElement:
+ if (current.size() > 1)
+ flatCurves += current;
+ current.clear();
+ current.reserve(16);
+ current += QPointF(e.x, e.y) * matrix;
+ break;
+ case QPainterPath::LineToElement:
+ current += QPointF(e.x, e.y) * matrix;
+ break;
+ case QPainterPath::CurveToElement: {
+ Q_ASSERT(d->elements.at(i+1).type == QPainterPath::CurveToDataElement);
+ Q_ASSERT(d->elements.at(i+2).type == QPainterPath::CurveToDataElement);
+ QBezier bezier = QBezier::fromPoints(QPointF(d->elements.at(i-1).x, d->elements.at(i-1).y) * matrix,
+ QPointF(e.x, e.y) * matrix,
+ QPointF(d->elements.at(i+1).x, d->elements.at(i+1).y) * matrix,
+ QPointF(d->elements.at(i+2).x, d->elements.at(i+2).y) * matrix);
+ bezier.addToPolygon(&current);
+ i+=2;
+ break;
+ }
+ case QPainterPath::CurveToDataElement:
+ Q_ASSERT(!"QPainterPath::toSubpathPolygons(), bad element type");
+ break;
+ }
+ }
+
+ if (current.size()>1)
+ flatCurves += current;
+
+ return flatCurves;
+}
+
+/*!
+ \overload
+ */
+QList<QPolygonF> QPainterPath::toSubpathPolygons(const QMatrix &matrix) const
+{
+ return toSubpathPolygons(QTransform(matrix));
+}
+
+/*!
+ Converts the path into a list of polygons using the
+ QTransform \a matrix, and returns the list.
+
+ The function differs from the toFillPolygon() function in that it
+ creates several polygons. It is provided because it is usually
+ faster to draw several small polygons than to draw one large
+ polygon, even though the total number of points drawn is the same.
+
+ The toFillPolygons() function differs from the toSubpathPolygons()
+ function in that it create only polygon for subpaths that have
+ overlapping bounding rectangles.
+
+ Like the toFillPolygon() function, this function uses a rewinding
+ technique to make sure that overlapping subpaths can be filled
+ using the correct fill rule. Note that rewinding inserts addition
+ lines in the polygons so the outline of the fill polygon does not
+ match the outline of the path.
+
+ \sa toSubpathPolygons(), toFillPolygon(),
+ {QPainterPath#QPainterPath Conversion}{QPainterPath Conversion}
+*/
+QList<QPolygonF> QPainterPath::toFillPolygons(const QTransform &matrix) const
+{
+
+ QList<QPolygonF> polys;
+
+ QList<QPolygonF> subpaths = toSubpathPolygons(matrix);
+ int count = subpaths.size();
+
+ if (count == 0)
+ return polys;
+
+ QList<QRectF> bounds;
+ for (int i=0; i<count; ++i)
+ bounds += subpaths.at(i).boundingRect();
+
+#ifdef QPP_FILLPOLYGONS_DEBUG
+ printf("QPainterPath::toFillPolygons, subpathCount=%d\n", count);
+ for (int i=0; i<bounds.size(); ++i)
+ qDebug() << " bounds" << i << bounds.at(i);
+#endif
+
+ QVector< QList<int> > isects;
+ isects.resize(count);
+
+ // find all intersections
+ for (int j=0; j<count; ++j) {
+ if (subpaths.at(j).size() <= 2)
+ continue;
+ QRectF cbounds = bounds.at(j);
+ for (int i=0; i<count; ++i) {
+ if (cbounds.intersects(bounds.at(i))) {
+ isects[j] << i;
+ }
+ }
+ }
+
+#ifdef QPP_FILLPOLYGONS_DEBUG
+ printf("Intersections before flattening:\n");
+ for (int i = 0; i < count; ++i) {
+ printf("%d: ", i);
+ for (int j = 0; j < isects[i].size(); ++j) {
+ printf("%d ", isects[i][j]);
+ }
+ printf("\n");
+ }
+#endif
+
+ // flatten the sets of intersections
+ for (int i=0; i<count; ++i) {
+ const QList<int> &current_isects = isects.at(i);
+ for (int j=0; j<current_isects.size(); ++j) {
+ int isect_j = current_isects.at(j);
+ if (isect_j == i)
+ continue;
+ for (int k=0; k<isects[isect_j].size(); ++k) {
+ int isect_k = isects[isect_j][k];
+ if (isect_k != i && !isects.at(i).contains(isect_k)) {
+ isects[i] += isect_k;
+ }
+ }
+ isects[isect_j].clear();
+ }
+ }
+
+#ifdef QPP_FILLPOLYGONS_DEBUG
+ printf("Intersections after flattening:\n");
+ for (int i = 0; i < count; ++i) {
+ printf("%d: ", i);
+ for (int j = 0; j < isects[i].size(); ++j) {
+ printf("%d ", isects[i][j]);
+ }
+ printf("\n");
+ }
+#endif
+
+ // Join the intersected subpaths as rewinded polygons
+ for (int i=0; i<count; ++i) {
+ const QList<int> &subpath_list = isects[i];
+ if (!subpath_list.isEmpty()) {
+ QPolygonF buildUp;
+ for (int j=0; j<subpath_list.size(); ++j) {
+ const QPolygonF &subpath = subpaths.at(subpath_list.at(j));
+ buildUp += subpath;
+ if (!subpath.isClosed())
+ buildUp += subpath.first();
+ if (!buildUp.isClosed())
+ buildUp += buildUp.first();
+ }
+ polys += buildUp;
+ }
+ }
+
+ return polys;
+}
+
+/*!
+ \overload
+ */
+QList<QPolygonF> QPainterPath::toFillPolygons(const QMatrix &matrix) const
+{
+ return toFillPolygons(QTransform(matrix));
+}
+
+//same as qt_polygon_isect_line in qpolygon.cpp
+static void qt_painterpath_isect_line(const QPointF &p1,
+ const QPointF &p2,
+ const QPointF &pos,
+ int *winding)
+{
+ qreal x1 = p1.x();
+ qreal y1 = p1.y();
+ qreal x2 = p2.x();
+ qreal y2 = p2.y();
+ qreal y = pos.y();
+
+ int dir = 1;
+
+ if (qFuzzyCompare(y1, y2)) {
+ // ignore horizontal lines according to scan conversion rule
+ return;
+ } else if (y2 < y1) {
+ qreal x_tmp = x2; x2 = x1; x1 = x_tmp;
+ qreal y_tmp = y2; y2 = y1; y1 = y_tmp;
+ dir = -1;
+ }
+
+ if (y >= y1 && y < y2) {
+ qreal x = x1 + ((x2 - x1) / (y2 - y1)) * (y - y1);
+
+ // count up the winding number if we're
+ if (x<=pos.x()) {
+ (*winding) += dir;
+ }
+ }
+}
+
+static void qt_painterpath_isect_curve(const QBezier &bezier, const QPointF &pt,
+ int *winding, int depth = 0)
+{
+ qreal y = pt.y();
+ qreal x = pt.x();
+ QRectF bounds = bezier.bounds();
+
+ // potential intersection, divide and try again...
+ // Please note that a sideeffect of the bottom exclusion is that
+ // horizontal lines are dropped, but this is correct according to
+ // scan conversion rules.
+ if (y >= bounds.y() && y < bounds.y() + bounds.height()) {
+
+ // hit lower limit... This is a rough threshold, but its a
+ // tradeoff between speed and precision.
+ const qreal lower_bound = qreal(.001);
+ if (depth == 32 || (bounds.width() < lower_bound && bounds.height() < lower_bound)) {
+ // We make the assumption here that the curve starts to
+ // approximate a line after while (i.e. that it doesn't
+ // change direction drastically during its slope)
+ if (bezier.pt1().x() <= x) {
+ (*winding) += (bezier.pt4().y() > bezier.pt1().y() ? 1 : -1);
+ }
+ return;
+ }
+
+ // split curve and try again...
+ QBezier first_half, second_half;
+ bezier.split(&first_half, &second_half);
+ qt_painterpath_isect_curve(first_half, pt, winding, depth + 1);
+ qt_painterpath_isect_curve(second_half, pt, winding, depth + 1);
+ }
+}
+
+/*!
+ \fn bool QPainterPath::contains(const QPointF &point) const
+
+ Returns true if the given \a point is inside the path, otherwise
+ returns false.
+
+ \sa intersects()
+*/
+bool QPainterPath::contains(const QPointF &pt) const
+{
+ if (isEmpty() || !controlPointRect().contains(pt))
+ return false;
+
+ QPainterPathData *d = d_func();
+
+ int winding_number = 0;
+
+ QPointF last_pt;
+ QPointF last_start;
+ for (int i=0; i<d->elements.size(); ++i) {
+ const Element &e = d->elements.at(i);
+
+ switch (e.type) {
+
+ case MoveToElement:
+ if (i > 0) // implicitly close all paths.
+ qt_painterpath_isect_line(last_pt, last_start, pt, &winding_number);
+ last_start = last_pt = e;
+ break;
+
+ case LineToElement:
+ qt_painterpath_isect_line(last_pt, e, pt, &winding_number);
+ last_pt = e;
+ break;
+
+ case CurveToElement:
+ {
+ const QPainterPath::Element &cp2 = d->elements.at(++i);
+ const QPainterPath::Element &ep = d->elements.at(++i);
+ qt_painterpath_isect_curve(QBezier::fromPoints(last_pt, e, cp2, ep),
+ pt, &winding_number);
+ last_pt = ep;
+
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // implicitly close last subpath
+ if (last_pt != last_start)
+ qt_painterpath_isect_line(last_pt, last_start, pt, &winding_number);
+
+ return (d->fillRule == Qt::WindingFill
+ ? (winding_number != 0)
+ : ((winding_number % 2) != 0));
+}
+
+static bool qt_painterpath_isect_line_rect(qreal x1, qreal y1, qreal x2, qreal y2,
+ const QRectF &rect)
+{
+ qreal left = rect.left();
+ qreal right = rect.right();
+ qreal top = rect.top();
+ qreal bottom = rect.bottom();
+
+ enum { Left, Right, Top, Bottom };
+ // clip the lines, after cohen-sutherland, see e.g. http://www.nondot.org/~sabre/graphpro/line6.html
+ int p1 = ((x1 < left) << Left)
+ | ((x1 > right) << Right)
+ | ((y1 < top) << Top)
+ | ((y1 > bottom) << Bottom);
+ int p2 = ((x2 < left) << Left)
+ | ((x2 > right) << Right)
+ | ((y2 < top) << Top)
+ | ((y2 > bottom) << Bottom);
+
+ if (p1 & p2)
+ // completely inside
+ return false;
+
+ if (p1 | p2) {
+ qreal dx = x2 - x1;
+ qreal dy = y2 - y1;
+
+ // clip x coordinates
+ if (x1 < left) {
+ y1 += dy/dx * (left - x1);
+ x1 = left;
+ } else if (x1 > right) {
+ y1 -= dy/dx * (x1 - right);
+ x1 = right;
+ }
+ if (x2 < left) {
+ y2 += dy/dx * (left - x2);
+ x2 = left;
+ } else if (x2 > right) {
+ y2 -= dy/dx * (x2 - right);
+ x2 = right;
+ }
+
+ p1 = ((y1 < top) << Top)
+ | ((y1 > bottom) << Bottom);
+ p2 = ((y2 < top) << Top)
+ | ((y2 > bottom) << Bottom);
+
+ if (p1 & p2)
+ return false;
+
+ // clip y coordinates
+ if (y1 < top) {
+ x1 += dx/dy * (top - y1);
+ y1 = top;
+ } else if (y1 > bottom) {
+ x1 -= dx/dy * (y1 - bottom);
+ y1 = bottom;
+ }
+ if (y2 < top) {
+ x2 += dx/dy * (top - y2);
+ y2 = top;
+ } else if (y2 > bottom) {
+ x2 -= dx/dy * (y2 - bottom);
+ y2 = bottom;
+ }
+
+ p1 = ((x1 < left) << Left)
+ | ((x1 > right) << Right);
+ p2 = ((x2 < left) << Left)
+ | ((x2 > right) << Right);
+
+ if (p1 & p2)
+ return false;
+
+ return true;
+ }
+ return false;
+}
+
+static bool qt_isect_curve_horizontal(const QBezier &bezier, qreal y, qreal x1, qreal x2, int depth = 0)
+{
+ QRectF bounds = bezier.bounds();
+
+ if (y >= bounds.top() && y < bounds.bottom()
+ && bounds.right() >= x1 && bounds.left() < x2) {
+ const qreal lower_bound = qreal(.01);
+ if (depth == 32 || (bounds.width() < lower_bound && bounds.height() < lower_bound))
+ return true;
+
+ QBezier first_half, second_half;
+ bezier.split(&first_half, &second_half);
+ if (qt_isect_curve_horizontal(first_half, y, x1, x2, depth + 1)
+ || qt_isect_curve_horizontal(second_half, y, x1, x2, depth + 1))
+ return true;
+ }
+ return false;
+}
+
+static bool qt_isect_curve_vertical(const QBezier &bezier, qreal x, qreal y1, qreal y2, int depth = 0)
+{
+ QRectF bounds = bezier.bounds();
+
+ if (x >= bounds.left() && x < bounds.right()
+ && bounds.bottom() >= y1 && bounds.top() < y2) {
+ const qreal lower_bound = qreal(.01);
+ if (depth == 32 || (bounds.width() < lower_bound && bounds.height() < lower_bound))
+ return true;
+
+ QBezier first_half, second_half;
+ bezier.split(&first_half, &second_half);
+ if (qt_isect_curve_vertical(first_half, x, y1, y2, depth + 1)
+ || qt_isect_curve_vertical(second_half, x, y1, y2, depth + 1))
+ return true;
+ }
+ return false;
+}
+
+/*
+ Returns true if any lines or curves cross the four edges in of rect
+*/
+static bool qt_painterpath_check_crossing(const QPainterPath *path, const QRectF &rect)
+{
+ QPointF last_pt;
+ QPointF last_start;
+ for (int i=0; i<path->elementCount(); ++i) {
+ const QPainterPath::Element &e = path->elementAt(i);
+
+ switch (e.type) {
+
+ case QPainterPath::MoveToElement:
+ if (i > 0
+ && qFuzzyCompare(last_pt.x(), last_start.x())
+ && qFuzzyCompare(last_pt.y(), last_start.y())
+ && qt_painterpath_isect_line_rect(last_pt.x(), last_pt.y(),
+ last_start.x(), last_start.y(), rect))
+ return true;
+ last_start = last_pt = e;
+ break;
+
+ case QPainterPath::LineToElement:
+ if (qt_painterpath_isect_line_rect(last_pt.x(), last_pt.y(), e.x, e.y, rect))
+ return true;
+ last_pt = e;
+ break;
+
+ case QPainterPath::CurveToElement:
+ {
+ QPointF cp2 = path->elementAt(++i);
+ QPointF ep = path->elementAt(++i);
+ QBezier bezier = QBezier::fromPoints(last_pt, e, cp2, ep);
+ if (qt_isect_curve_horizontal(bezier, rect.top(), rect.left(), rect.right())
+ || qt_isect_curve_horizontal(bezier, rect.bottom(), rect.left(), rect.right())
+ || qt_isect_curve_vertical(bezier, rect.left(), rect.top(), rect.bottom())
+ || qt_isect_curve_vertical(bezier, rect.right(), rect.top(), rect.bottom()))
+ return true;
+ last_pt = ep;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // implicitly close last subpath
+ if (last_pt != last_start
+ && qt_painterpath_isect_line_rect(last_pt.x(), last_pt.y(),
+ last_start.x(), last_start.y(), rect))
+ return true;
+
+ return false;
+}
+
+/*!
+ \fn bool QPainterPath::intersects(const QRectF &rectangle) const
+
+ Returns true if any point in the given \a rectangle intersects the
+ path; otherwise returns false.
+
+ There is an intersection if any of the lines making up the
+ rectangle crosses a part of the path or if any part of the
+ rectangle overlaps with any area enclosed by the path. This
+ function respects the current fillRule to determine what is
+ considered inside the path.
+
+ \sa contains()
+*/
+bool QPainterPath::intersects(const QRectF &rect) const
+{
+ if (elementCount() == 1 && rect.contains(elementAt(0)))
+ return true;
+
+ if (isEmpty())
+ return false;
+
+ QRectF cp = controlPointRect();
+ QRectF rn = rect.normalized();
+
+ // QRectF::intersects returns false if one of the rects is a null rect
+ // which would happen for a painter path consisting of a vertical or
+ // horizontal line
+ if (qMax(rn.left(), cp.left()) > qMin(rn.right(), cp.right())
+ || qMax(rn.top(), cp.top()) > qMin(rn.bottom(), cp.bottom()))
+ return false;
+
+ // If any path element cross the rect its bound to be an intersection
+ if (qt_painterpath_check_crossing(this, rect))
+ return true;
+
+ if (contains(rect.center()))
+ return true;
+
+ Q_D(QPainterPath);
+
+ // Check if the rectangle surounds any subpath...
+ for (int i=0; i<d->elements.size(); ++i) {
+ const Element &e = d->elements.at(i);
+ if (e.type == QPainterPath::MoveToElement && rect.contains(e))
+ return true;
+ }
+
+ return false;
+}
+
+/*!
+ Translates all elements in the path by (\a{dx}, \a{dy}).
+
+ \since 4.6
+ \sa translated()
+*/
+void QPainterPath::translate(qreal dx, qreal dy)
+{
+ if (!d_ptr || (dx == 0 && dy == 0))
+ return;
+
+ int elementsLeft = d_ptr->elements.size();
+ if (elementsLeft <= 0)
+ return;
+
+ detach();
+ QPainterPath::Element *element = d_func()->elements.data();
+ Q_ASSERT(element);
+ while (elementsLeft--) {
+ element->x += dx;
+ element->y += dy;
+ ++element;
+ }
+}
+
+/*!
+ \fn void QPainterPath::translate(const QPointF &offset)
+ \overload
+ \since 4.6
+
+ Translates all elements in the path by the given \a offset.
+
+ \sa translated()
+*/
+
+/*!
+ Returns a copy of the path that is translated by (\a{dx}, \a{dy}).
+
+ \since 4.6
+ \sa translate()
+*/
+QPainterPath QPainterPath::translated(qreal dx, qreal dy) const
+{
+ QPainterPath copy(*this);
+ copy.translate(dx, dy);
+ return copy;
+}
+
+/*!
+ \fn QPainterPath QPainterPath::translated(const QPointF &offset) const;
+ \overload
+ \since 4.6
+
+ Returns a copy of the path that is translated by the given \a offset.
+
+ \sa translate()
+*/
+
+/*!
+ \fn bool QPainterPath::contains(const QRectF &rectangle) const
+
+ Returns true if the given \a rectangle is inside the path,
+ otherwise returns false.
+*/
+bool QPainterPath::contains(const QRectF &rect) const
+{
+ Q_D(QPainterPath);
+
+ // the path is empty or the control point rect doesn't completely
+ // cover the rectangle we abort stratight away.
+ if (isEmpty() || !controlPointRect().contains(rect))
+ return false;
+
+ // if there are intersections, chances are that the rect is not
+ // contained, except if we have winding rule, in which case it
+ // still might.
+ if (qt_painterpath_check_crossing(this, rect)) {
+ if (fillRule() == Qt::OddEvenFill) {
+ return false;
+ } else {
+ // Do some wague sampling in the winding case. This is not
+ // precise but it should mostly be good enough.
+ if (!contains(rect.topLeft()) ||
+ !contains(rect.topRight()) ||
+ !contains(rect.bottomRight()) ||
+ !contains(rect.bottomLeft()))
+ return false;
+ }
+ }
+
+ // If there exists a point inside that is not part of the path its
+ // because: rectangle lies completely outside path or a subpath
+ // excludes parts of the rectangle. Both cases mean that the rect
+ // is not contained
+ if (!contains(rect.center()))
+ return false;
+
+ // If there are any subpaths inside this rectangle we need to
+ // check if they are still contained as a result of the fill
+ // rule. This can only be the case for WindingFill though. For
+ // OddEvenFill the rect will never be contained if it surrounds a
+ // subpath. (the case where two subpaths are completely identical
+ // can be argued but we choose to neglect it).
+ for (int i=0; i<d->elements.size(); ++i) {
+ const Element &e = d->elements.at(i);
+ if (e.type == QPainterPath::MoveToElement && rect.contains(e)) {
+ if (fillRule() == Qt::OddEvenFill)
+ return false;
+
+ bool stop = false;
+ for (; !stop && i<d->elements.size(); ++i) {
+ const Element &el = d->elements.at(i);
+ switch (el.type) {
+ case MoveToElement:
+ stop = true;
+ break;
+ case LineToElement:
+ if (!contains(el))
+ return false;
+ break;
+ case CurveToElement:
+ if (!contains(d->elements.at(i+2)))
+ return false;
+ i += 2;
+ break;
+ default:
+ break;
+ }
+ }
+
+ // compensate for the last ++i in the inner for
+ --i;
+ }
+ }
+
+ return true;
+}
+
+static inline bool epsilonCompare(const QPointF &a, const QPointF &b, const QSizeF &epsilon)
+{
+ return qAbs(a.x() - b.x()) <= epsilon.width()
+ && qAbs(a.y() - b.y()) <= epsilon.height();
+}
+
+/*!
+ Returns true if this painterpath is equal to the given \a path.
+
+ Note that comparing paths may involve a per element comparison
+ which can be slow for complex paths.
+
+ \sa operator!=()
+*/
+
+bool QPainterPath::operator==(const QPainterPath &path) const
+{
+ QPainterPathData *d = reinterpret_cast<QPainterPathData *>(d_func());
+ if (path.d_func() == d)
+ return true;
+ else if (!d || !path.d_func())
+ return false;
+ else if (d->fillRule != path.d_func()->fillRule)
+ return false;
+ else if (d->elements.size() != path.d_func()->elements.size())
+ return false;
+
+ const qreal qt_epsilon = sizeof(qreal) == sizeof(double) ? 1e-12 : qreal(1e-5);
+
+ QSizeF epsilon = boundingRect().size();
+ epsilon.rwidth() *= qt_epsilon;
+ epsilon.rheight() *= qt_epsilon;
+
+ for (int i = 0; i < d->elements.size(); ++i)
+ if (d->elements.at(i).type != path.d_func()->elements.at(i).type
+ || !epsilonCompare(d->elements.at(i), path.d_func()->elements.at(i), epsilon))
+ return false;
+
+ return true;
+}
+
+/*!
+ Returns true if this painter path differs from the given \a path.
+
+ Note that comparing paths may involve a per element comparison
+ which can be slow for complex paths.
+
+ \sa operator==()
+*/
+
+bool QPainterPath::operator!=(const QPainterPath &path) const
+{
+ return !(*this==path);
+}
+
+/*!
+ \since 4.5
+
+ Returns the intersection of this path and the \a other path.
+
+ \sa intersected(), operator&=(), united(), operator|()
+*/
+QPainterPath QPainterPath::operator&(const QPainterPath &other) const
+{
+ return intersected(other);
+}
+
+/*!
+ \since 4.5
+
+ Returns the union of this path and the \a other path.
+
+ \sa united(), operator|=(), intersected(), operator&()
+*/
+QPainterPath QPainterPath::operator|(const QPainterPath &other) const
+{
+ return united(other);
+}
+
+/*!
+ \since 4.5
+
+ Returns the union of this path and the \a other path. This function is equivalent
+ to operator|().
+
+ \sa united(), operator+=(), operator-()
+*/
+QPainterPath QPainterPath::operator+(const QPainterPath &other) const
+{
+ return united(other);
+}
+
+/*!
+ \since 4.5
+
+ Subtracts the \a other path from a copy of this path, and returns the copy.
+
+ \sa subtracted(), operator-=(), operator+()
+*/
+QPainterPath QPainterPath::operator-(const QPainterPath &other) const
+{
+ return subtracted(other);
+}
+
+/*!
+ \since 4.5
+
+ Intersects this path with \a other and returns a reference to this path.
+
+ \sa intersected(), operator&(), operator|=()
+*/
+QPainterPath &QPainterPath::operator&=(const QPainterPath &other)
+{
+ return *this = (*this & other);
+}
+
+/*!
+ \since 4.5
+
+ Unites this path with \a other and returns a reference to this path.
+
+ \sa united(), operator|(), operator&=()
+*/
+QPainterPath &QPainterPath::operator|=(const QPainterPath &other)
+{
+ return *this = (*this | other);
+}
+
+/*!
+ \since 4.5
+
+ Unites this path with \a other, and returns a reference to this path. This
+ is equivalent to operator|=().
+
+ \sa united(), operator+(), operator-=()
+*/
+QPainterPath &QPainterPath::operator+=(const QPainterPath &other)
+{
+ return *this = (*this + other);
+}
+
+/*!
+ \since 4.5
+
+ Subtracts \a other from this path, and returns a reference to this
+ path.
+
+ \sa subtracted(), operator-(), operator+=()
+*/
+QPainterPath &QPainterPath::operator-=(const QPainterPath &other)
+{
+ return *this = (*this - other);
+}
+
+#ifndef QT_NO_DATASTREAM
+/*!
+ \fn QDataStream &operator<<(QDataStream &stream, const QPainterPath &path)
+ \relates QPainterPath
+
+ Writes the given painter \a path to the given \a stream, and
+ returns a reference to the \a stream.
+
+ \sa {Serializing Qt Data Types}
+*/
+QDataStream &operator<<(QDataStream &s, const QPainterPath &p)
+{
+ if (p.isEmpty()) {
+ s << 0;
+ return s;
+ }
+
+ s << p.elementCount();
+ for (int i=0; i < p.d_func()->elements.size(); ++i) {
+ const QPainterPath::Element &e = p.d_func()->elements.at(i);
+ s << int(e.type);
+ s << double(e.x) << double(e.y);
+ }
+ s << p.d_func()->cStart;
+ s << int(p.d_func()->fillRule);
+ return s;
+}
+
+/*!
+ \fn QDataStream &operator>>(QDataStream &stream, QPainterPath &path)
+ \relates QPainterPath
+
+ Reads a painter path from the given \a stream into the specified \a path,
+ and returns a reference to the \a stream.
+
+ \sa {Serializing Qt Data Types}
+*/
+QDataStream &operator>>(QDataStream &s, QPainterPath &p)
+{
+ int size;
+ s >> size;
+
+ if (size == 0)
+ return s;
+
+ p.ensureData(); // in case if p.d_func() == 0
+ if (p.d_func()->elements.size() == 1) {
+ Q_ASSERT(p.d_func()->elements.at(0).type == QPainterPath::MoveToElement);
+ p.d_func()->elements.clear();
+ }
+ p.d_func()->elements.reserve(p.d_func()->elements.size() + size);
+ for (int i=0; i<size; ++i) {
+ int type;
+ double x, y;
+ s >> type;
+ s >> x;
+ s >> y;
+ Q_ASSERT(type >= 0 && type <= 3);
+ if (!qt_is_finite(x) || !qt_is_finite(y)) {
+#ifndef QT_NO_DEBUG
+ qWarning("QDataStream::operator>>: NaN or Inf element found in path, skipping it");
+#endif
+ continue;
+ }
+ QPainterPath::Element elm = { x, y, QPainterPath::ElementType(type) };
+ p.d_func()->elements.append(elm);
+ }
+ s >> p.d_func()->cStart;
+ int fillRule;
+ s >> fillRule;
+ Q_ASSERT(fillRule == Qt::OddEvenFill || Qt::WindingFill);
+ p.d_func()->fillRule = Qt::FillRule(fillRule);
+ p.d_func()->dirtyBounds = true;
+ p.d_func()->dirtyControlBounds = true;
+ return s;
+}
+#endif // QT_NO_DATASTREAM
+
+
+/*******************************************************************************
+ * class QPainterPathStroker
+ */
+
+void qt_path_stroke_move_to(qfixed x, qfixed y, void *data)
+{
+ ((QPainterPath *) data)->moveTo(qt_fixed_to_real(x), qt_fixed_to_real(y));
+}
+
+void qt_path_stroke_line_to(qfixed x, qfixed y, void *data)
+{
+ ((QPainterPath *) data)->lineTo(qt_fixed_to_real(x), qt_fixed_to_real(y));
+}
+
+void qt_path_stroke_cubic_to(qfixed c1x, qfixed c1y,
+ qfixed c2x, qfixed c2y,
+ qfixed ex, qfixed ey,
+ void *data)
+{
+ ((QPainterPath *) data)->cubicTo(qt_fixed_to_real(c1x), qt_fixed_to_real(c1y),
+ qt_fixed_to_real(c2x), qt_fixed_to_real(c2y),
+ qt_fixed_to_real(ex), qt_fixed_to_real(ey));
+}
+
+/*!
+ \since 4.1
+ \class QPainterPathStroker
+ \ingroup painting
+
+ \brief The QPainterPathStroker class is used to generate fillable
+ outlines for a given painter path.
+
+ By calling the createStroke() function, passing a given
+ QPainterPath as argument, a new painter path representing the
+ outline of the given path is created. The newly created painter
+ path can then be filled to draw the original painter path's
+ outline.
+
+ You can control the various design aspects (width, cap styles,
+ join styles and dash pattern) of the outlining using the following
+ functions:
+
+ \list
+ \o setWidth()
+ \o setCapStyle()
+ \o setJoinStyle()
+ \o setDashPattern()
+ \endlist
+
+ The setDashPattern() function accepts both a Qt::PenStyle object
+ and a vector representation of the pattern as argument.
+
+ In addition you can specify a curve's threshold, controlling the
+ granularity with which a curve is drawn, using the
+ setCurveThreshold() function. The default threshold is a well
+ adjusted value (0.25), and normally you should not need to modify
+ it. However, you can make the curve's appearance smoother by
+ decreasing its value.
+
+ You can also control the miter limit for the generated outline
+ using the setMiterLimit() function. The miter limit describes how
+ far from each join the miter join can extend. The limit is
+ specified in the units of width so the pixelwise miter limit will
+ be \c {miterlimit * width}. This value is only used if the join
+ style is Qt::MiterJoin.
+
+ The painter path generated by the createStroke() function should
+ only be used for outlining the given painter path. Otherwise it
+ may cause unexpected behavior. Generated outlines also require the
+ Qt::WindingFill rule which is set by default.
+
+ \sa QPen, QBrush
+*/
+
+QPainterPathStrokerPrivate::QPainterPathStrokerPrivate()
+ : dashOffset(0)
+{
+ stroker.setMoveToHook(qt_path_stroke_move_to);
+ stroker.setLineToHook(qt_path_stroke_line_to);
+ stroker.setCubicToHook(qt_path_stroke_cubic_to);
+}
+
+/*!
+ Creates a new stroker.
+ */
+QPainterPathStroker::QPainterPathStroker()
+ : d_ptr(new QPainterPathStrokerPrivate)
+{
+}
+
+/*!
+ Destroys the stroker.
+*/
+QPainterPathStroker::~QPainterPathStroker()
+{
+}
+
+
+/*!
+ Generates a new path that is a fillable area representing the
+ outline of the given \a path.
+
+ The various design aspects of the outline are based on the
+ stroker's properties: width(), capStyle(), joinStyle(),
+ dashPattern(), curveThreshold() and miterLimit().
+
+ The generated path should only be used for outlining the given
+ painter path. Otherwise it may cause unexpected
+ behavior. Generated outlines also require the Qt::WindingFill rule
+ which is set by default.
+*/
+QPainterPath QPainterPathStroker::createStroke(const QPainterPath &path) const
+{
+ QPainterPathStrokerPrivate *d = const_cast<QPainterPathStrokerPrivate *>(d_func());
+ QPainterPath stroke;
+ if (path.isEmpty())
+ return path;
+ if (d->dashPattern.isEmpty()) {
+ d->stroker.strokePath(path, &stroke, QTransform());
+ } else {
+ QDashStroker dashStroker(&d->stroker);
+ dashStroker.setDashPattern(d->dashPattern);
+ dashStroker.setDashOffset(d->dashOffset);
+ dashStroker.setClipRect(d->stroker.clipRect());
+ dashStroker.strokePath(path, &stroke, QTransform());
+ }
+ stroke.setFillRule(Qt::WindingFill);
+ return stroke;
+}
+
+/*!
+ Sets the width of the generated outline painter path to \a width.
+
+ The generated outlines will extend approximately 50% of \a width
+ to each side of the given input path's original outline.
+*/
+void QPainterPathStroker::setWidth(qreal width)
+{
+ Q_D(QPainterPathStroker);
+ if (width <= 0)
+ width = 1;
+ d->stroker.setStrokeWidth(qt_real_to_fixed(width));
+}
+
+/*!
+ Returns the width of the generated outlines.
+*/
+qreal QPainterPathStroker::width() const
+{
+ return qt_fixed_to_real(d_func()->stroker.strokeWidth());
+}
+
+
+/*!
+ Sets the cap style of the generated outlines to \a style. If a
+ dash pattern is set, each segment of the pattern is subject to the
+ cap \a style.
+*/
+void QPainterPathStroker::setCapStyle(Qt::PenCapStyle style)
+{
+ d_func()->stroker.setCapStyle(style);
+}
+
+
+/*!
+ Returns the cap style of the generated outlines.
+*/
+Qt::PenCapStyle QPainterPathStroker::capStyle() const
+{
+ return d_func()->stroker.capStyle();
+}
+
+/*!
+ Sets the join style of the generated outlines to \a style.
+*/
+void QPainterPathStroker::setJoinStyle(Qt::PenJoinStyle style)
+{
+ d_func()->stroker.setJoinStyle(style);
+}
+
+/*!
+ Returns the join style of the generated outlines.
+*/
+Qt::PenJoinStyle QPainterPathStroker::joinStyle() const
+{
+ return d_func()->stroker.joinStyle();
+}
+
+/*!
+ Sets the miter limit of the generated outlines to \a limit.
+
+ The miter limit describes how far from each join the miter join
+ can extend. The limit is specified in units of the currently set
+ width. So the pixelwise miter limit will be \c { miterlimit *
+ width}.
+
+ This value is only used if the join style is Qt::MiterJoin.
+*/
+void QPainterPathStroker::setMiterLimit(qreal limit)
+{
+ d_func()->stroker.setMiterLimit(qt_real_to_fixed(limit));
+}
+
+/*!
+ Returns the miter limit for the generated outlines.
+*/
+qreal QPainterPathStroker::miterLimit() const
+{
+ return qt_fixed_to_real(d_func()->stroker.miterLimit());
+}
+
+
+/*!
+ Specifies the curve flattening \a threshold, controlling the
+ granularity with which the generated outlines' curve is drawn.
+
+ The default threshold is a well adjusted value (0.25), and
+ normally you should not need to modify it. However, you can make
+ the curve's appearance smoother by decreasing its value.
+*/
+void QPainterPathStroker::setCurveThreshold(qreal threshold)
+{
+ d_func()->stroker.setCurveThreshold(qt_real_to_fixed(threshold));
+}
+
+/*!
+ Returns the curve flattening threshold for the generated
+ outlines.
+*/
+qreal QPainterPathStroker::curveThreshold() const
+{
+ return qt_fixed_to_real(d_func()->stroker.curveThreshold());
+}
+
+/*!
+ Sets the dash pattern for the generated outlines to \a style.
+*/
+void QPainterPathStroker::setDashPattern(Qt::PenStyle style)
+{
+ d_func()->dashPattern = QDashStroker::patternForStyle(style);
+}
+
+/*!
+ \overload
+
+ Sets the dash pattern for the generated outlines to \a
+ dashPattern. This function makes it possible to specify custom
+ dash patterns.
+
+ Each element in the vector contains the lengths of the dashes and spaces
+ in the stroke, beginning with the first dash in the first element, the
+ first space in the second element, and alternating between dashes and
+ spaces for each following pair of elements.
+
+ The vector can contain an odd number of elements, in which case the last
+ element will be extended by the length of the first element when the
+ pattern repeats.
+*/
+void QPainterPathStroker::setDashPattern(const QVector<qreal> &dashPattern)
+{
+ d_func()->dashPattern.clear();
+ for (int i=0; i<dashPattern.size(); ++i)
+ d_func()->dashPattern << qt_real_to_fixed(dashPattern.at(i));
+}
+
+/*!
+ Returns the dash pattern for the generated outlines.
+*/
+QVector<qreal> QPainterPathStroker::dashPattern() const
+{
+ return d_func()->dashPattern;
+}
+
+/*!
+ Returns the dash offset for the generated outlines.
+ */
+qreal QPainterPathStroker::dashOffset() const
+{
+ return d_func()->dashOffset;
+}
+
+/*!
+ Sets the dash offset for the generated outlines to \a offset.
+
+ See the documentation for QPen::setDashOffset() for a description of the
+ dash offset.
+ */
+void QPainterPathStroker::setDashOffset(qreal offset)
+{
+ d_func()->dashOffset = offset;
+}
+
+/*!
+ Converts the path into a polygon using the QTransform
+ \a matrix, and returns the polygon.
+
+ The polygon is created by first converting all subpaths to
+ polygons, then using a rewinding technique to make sure that
+ overlapping subpaths can be filled using the correct fill rule.
+
+ Note that rewinding inserts addition lines in the polygon so
+ the outline of the fill polygon does not match the outline of
+ the path.
+
+ \sa toSubpathPolygons(), toFillPolygons(),
+ {QPainterPath#QPainterPath Conversion}{QPainterPath Conversion}
+*/
+QPolygonF QPainterPath::toFillPolygon(const QTransform &matrix) const
+{
+
+ QList<QPolygonF> flats = toSubpathPolygons(matrix);
+ QPolygonF polygon;
+ if (flats.isEmpty())
+ return polygon;
+ QPointF first = flats.first().first();
+ for (int i=0; i<flats.size(); ++i) {
+ polygon += flats.at(i);
+ if (!flats.at(i).isClosed())
+ polygon += flats.at(i).first();
+ if (i > 0)
+ polygon += first;
+ }
+ return polygon;
+}
+
+/*!
+ \overload
+*/
+QPolygonF QPainterPath::toFillPolygon(const QMatrix &matrix) const
+{
+ return toFillPolygon(QTransform(matrix));
+}
+
+
+//derivative of the equation
+static inline qreal slopeAt(qreal t, qreal a, qreal b, qreal c, qreal d)
+{
+ return 3*t*t*(d - 3*c + 3*b - a) + 6*t*(c - 2*b + a) + 3*(b - a);
+}
+
+/*!
+ Returns the length of the current path.
+*/
+qreal QPainterPath::length() const
+{
+ Q_D(QPainterPath);
+ if (isEmpty())
+ return 0;
+
+ qreal len = 0;
+ for (int i=1; i<d->elements.size(); ++i) {
+ const Element &e = d->elements.at(i);
+
+ switch (e.type) {
+ case MoveToElement:
+ break;
+ case LineToElement:
+ {
+ len += QLineF(d->elements.at(i-1), e).length();
+ break;
+ }
+ case CurveToElement:
+ {
+ QBezier b = QBezier::fromPoints(d->elements.at(i-1),
+ e,
+ d->elements.at(i+1),
+ d->elements.at(i+2));
+ len += b.length();
+ i += 2;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ return len;
+}
+
+/*!
+ Returns percentage of the whole path at the specified length \a len.
+
+ Note that similarly to other percent methods, the percentage measurement
+ is not linear with regards to the length, if curves are present
+ in the path. When curves are present the percentage argument is mapped
+ to the t parameter of the Bezier equations.
+*/
+qreal QPainterPath::percentAtLength(qreal len) const
+{
+ Q_D(QPainterPath);
+ if (isEmpty() || len <= 0)
+ return 0;
+
+ qreal totalLength = length();
+ if (len > totalLength)
+ return 1;
+
+ qreal curLen = 0;
+ for (int i=1; i<d->elements.size(); ++i) {
+ const Element &e = d->elements.at(i);
+
+ switch (e.type) {
+ case MoveToElement:
+ break;
+ case LineToElement:
+ {
+ QLineF line(d->elements.at(i-1), e);
+ qreal llen = line.length();
+ curLen += llen;
+ if (curLen >= len) {
+ return len/totalLength ;
+ }
+
+ break;
+ }
+ case CurveToElement:
+ {
+ QBezier b = QBezier::fromPoints(d->elements.at(i-1),
+ e,
+ d->elements.at(i+1),
+ d->elements.at(i+2));
+ qreal blen = b.length();
+ qreal prevLen = curLen;
+ curLen += blen;
+
+ if (curLen >= len) {
+ qreal res = b.tAtLength(len - prevLen);
+ return (res * blen + prevLen)/totalLength;
+ }
+
+ i += 2;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static inline QBezier bezierAtT(const QPainterPath &path, qreal t, qreal *startingLength, qreal *bezierLength)
+{
+ *startingLength = 0;
+ if (t > 1)
+ return QBezier();
+
+ qreal curLen = 0;
+ qreal totalLength = path.length();
+
+ const int lastElement = path.elementCount() - 1;
+ for (int i=0; i <= lastElement; ++i) {
+ const QPainterPath::Element &e = path.elementAt(i);
+
+ switch (e.type) {
+ case QPainterPath::MoveToElement:
+ break;
+ case QPainterPath::LineToElement:
+ {
+ QLineF line(path.elementAt(i-1), e);
+ qreal llen = line.length();
+ curLen += llen;
+ if (i == lastElement || curLen/totalLength >= t) {
+ *bezierLength = llen;
+ QPointF a = path.elementAt(i-1);
+ QPointF delta = e - a;
+ return QBezier::fromPoints(a, a + delta / 3, a + 2 * delta / 3, e);
+ }
+ break;
+ }
+ case QPainterPath::CurveToElement:
+ {
+ QBezier b = QBezier::fromPoints(path.elementAt(i-1),
+ e,
+ path.elementAt(i+1),
+ path.elementAt(i+2));
+ qreal blen = b.length();
+ curLen += blen;
+
+ if (i + 2 == lastElement || curLen/totalLength >= t) {
+ *bezierLength = blen;
+ return b;
+ }
+
+ i += 2;
+ break;
+ }
+ default:
+ break;
+ }
+ *startingLength = curLen;
+ }
+ return QBezier();
+}
+
+/*!
+ Returns the point at at the percentage \a t of the current path.
+ The argument \a t has to be between 0 and 1.
+
+ Note that similarly to other percent methods, the percentage measurement
+ is not linear with regards to the length, if curves are present
+ in the path. When curves are present the percentage argument is mapped
+ to the t parameter of the Bezier equations.
+*/
+QPointF QPainterPath::pointAtPercent(qreal t) const
+{
+ if (t < 0 || t > 1) {
+ qWarning("QPainterPath::pointAtPercent accepts only values between 0 and 1");
+ return QPointF();
+ }
+
+ if (!d_ptr || d_ptr->elements.size() == 0)
+ return QPointF();
+
+ if (d_ptr->elements.size() == 1)
+ return d_ptr->elements.at(0);
+
+ qreal totalLength = length();
+ qreal curLen = 0;
+ qreal bezierLen = 0;
+ QBezier b = bezierAtT(*this, t, &curLen, &bezierLen);
+ qreal realT = (totalLength * t - curLen) / bezierLen;
+
+ return b.pointAt(qBound(qreal(0), realT, qreal(1)));
+}
+
+/*!
+ Returns the angle of the path tangent at the percentage \a t.
+ The argument \a t has to be between 0 and 1.
+
+ Positive values for the angles mean counter-clockwise while negative values
+ mean the clockwise direction. Zero degrees is at the 3 o'clock position.
+
+ Note that similarly to the other percent methods, the percentage measurement
+ is not linear with regards to the length if curves are present
+ in the path. When curves are present the percentage argument is mapped
+ to the t parameter of the Bezier equations.
+*/
+qreal QPainterPath::angleAtPercent(qreal t) const
+{
+ if (t < 0 || t > 1) {
+ qWarning("QPainterPath::angleAtPercent accepts only values between 0 and 1");
+ return 0;
+ }
+
+ qreal totalLength = length();
+ qreal curLen = 0;
+ qreal bezierLen = 0;
+ QBezier bez = bezierAtT(*this, t, &curLen, &bezierLen);
+ qreal realT = (totalLength * t - curLen) / bezierLen;
+
+ qreal m1 = slopeAt(realT, bez.x1, bez.x2, bez.x3, bez.x4);
+ qreal m2 = slopeAt(realT, bez.y1, bez.y2, bez.y3, bez.y4);
+
+ return QLineF(0, 0, m1, m2).angle();
+}
+
+#if defined(Q_WS_WINCE)
+#pragma warning( disable : 4056 4756 )
+#endif
+
+/*!
+ Returns the slope of the path at the percentage \a t. The
+ argument \a t has to be between 0 and 1.
+
+ Note that similarly to other percent methods, the percentage measurement
+ is not linear with regards to the length, if curves are present
+ in the path. When curves are present the percentage argument is mapped
+ to the t parameter of the Bezier equations.
+*/
+qreal QPainterPath::slopeAtPercent(qreal t) const
+{
+ if (t < 0 || t > 1) {
+ qWarning("QPainterPath::slopeAtPercent accepts only values between 0 and 1");
+ return 0;
+ }
+
+ qreal totalLength = length();
+ qreal curLen = 0;
+ qreal bezierLen = 0;
+ QBezier bez = bezierAtT(*this, t, &curLen, &bezierLen);
+ qreal realT = (totalLength * t - curLen) / bezierLen;
+
+ qreal m1 = slopeAt(realT, bez.x1, bez.x2, bez.x3, bez.x4);
+ qreal m2 = slopeAt(realT, bez.y1, bez.y2, bez.y3, bez.y4);
+ //tangent line
+ qreal slope = 0;
+
+#define SIGN(x) ((x < 0)?-1:1)
+ if (m1)
+ slope = m2/m1;
+ else {
+ //windows doesn't define INFINITY :(
+#ifdef INFINITY
+ slope = INFINITY*SIGN(m2);
+#else
+ if (sizeof(qreal) == sizeof(double)) {
+ return 1.79769313486231570e+308;
+ } else {
+ return ((qreal)3.40282346638528860e+38);
+ }
+#endif
+ }
+
+ return slope;
+}
+
+/*!
+ \since 4.4
+
+ Adds the given rectangle \a rect with rounded corners to the path.
+
+ The \a xRadius and \a yRadius arguments specify the radii of
+ the ellipses defining the corners of the rounded rectangle.
+ When \a mode is Qt::RelativeSize, \a xRadius and
+ \a yRadius are specified in percentage of half the rectangle's
+ width and height respectively, and should be in the range 0.0 to 100.0.
+
+ \sa addRect()
+*/
+void QPainterPath::addRoundedRect(const QRectF &rect, qreal xRadius, qreal yRadius,
+ Qt::SizeMode mode)
+{
+ QRectF r = rect.normalized();
+
+ if (r.isNull())
+ return;
+
+ if (mode == Qt::AbsoluteSize) {
+ qreal w = r.width() / 2;
+ qreal h = r.height() / 2;
+
+ if (w == 0) {
+ xRadius = 0;
+ } else {
+ xRadius = 100 * qMin(xRadius, w) / w;
+ }
+ if (h == 0) {
+ yRadius = 0;
+ } else {
+ yRadius = 100 * qMin(yRadius, h) / h;
+ }
+ } else {
+ if (xRadius > 100) // fix ranges
+ xRadius = 100;
+
+ if (yRadius > 100)
+ yRadius = 100;
+ }
+
+ if (xRadius <= 0 || yRadius <= 0) { // add normal rectangle
+ addRect(r);
+ return;
+ }
+
+ qreal x = r.x();
+ qreal y = r.y();
+ qreal w = r.width();
+ qreal h = r.height();
+ qreal rxx2 = w*xRadius/100;
+ qreal ryy2 = h*yRadius/100;
+
+ ensureData();
+ detach();
+
+ bool first = d_func()->elements.size() < 2;
+
+ arcMoveTo(x, y, rxx2, ryy2, 180);
+ arcTo(x, y, rxx2, ryy2, 180, -90);
+ arcTo(x+w-rxx2, y, rxx2, ryy2, 90, -90);
+ arcTo(x+w-rxx2, y+h-ryy2, rxx2, ryy2, 0, -90);
+ arcTo(x, y+h-ryy2, rxx2, ryy2, 270, -90);
+ closeSubpath();
+
+ d_func()->require_moveTo = true;
+ d_func()->convex = first;
+}
+
+/*!
+ \fn void QPainterPath::addRoundedRect(qreal x, qreal y, qreal w, qreal h, qreal xRadius, qreal yRadius, Qt::SizeMode mode = Qt::AbsoluteSize);
+ \since 4.4
+ \overload
+
+ Adds the given rectangle \a x, \a y, \a w, \a h with rounded corners to the path.
+ */
+
+/*!
+ \obsolete
+
+ Adds a rectangle \a r with rounded corners to the path.
+
+ The \a xRnd and \a yRnd arguments specify how rounded the corners
+ should be. 0 is angled corners, 99 is maximum roundedness.
+
+ \sa addRoundedRect()
+*/
+void QPainterPath::addRoundRect(const QRectF &r, int xRnd, int yRnd)
+{
+ if(xRnd >= 100) // fix ranges
+ xRnd = 99;
+ if(yRnd >= 100)
+ yRnd = 99;
+ if(xRnd <= 0 || yRnd <= 0) { // add normal rectangle
+ addRect(r);
+ return;
+ }
+
+ QRectF rect = r.normalized();
+
+ if (rect.isNull())
+ return;
+
+ qreal x = rect.x();
+ qreal y = rect.y();
+ qreal w = rect.width();
+ qreal h = rect.height();
+ qreal rxx2 = w*xRnd/100;
+ qreal ryy2 = h*yRnd/100;
+
+ ensureData();
+ detach();
+
+ bool first = d_func()->elements.size() < 2;
+
+ arcMoveTo(x, y, rxx2, ryy2, 180);
+ arcTo(x, y, rxx2, ryy2, 180, -90);
+ arcTo(x+w-rxx2, y, rxx2, ryy2, 90, -90);
+ arcTo(x+w-rxx2, y+h-ryy2, rxx2, ryy2, 0, -90);
+ arcTo(x, y+h-ryy2, rxx2, ryy2, 270, -90);
+ closeSubpath();
+
+ d_func()->require_moveTo = true;
+ d_func()->convex = first;
+}
+
+/*!
+ \obsolete
+
+ \fn bool QPainterPath::addRoundRect(const QRectF &rect, int roundness);
+ \since 4.3
+ \overload
+
+ Adds a rounded rectangle, \a rect, to the path.
+
+ The \a roundness argument specifies uniform roundness for the
+ rectangle. Vertical and horizontal roundness factors will be
+ adjusted accordingly to act uniformly around both axes. Use this
+ method if you want a rectangle equally rounded across both the X and
+ Y axis.
+
+ \sa addRoundedRect()
+*/
+
+/*!
+ \obsolete
+
+ \fn void QPainterPath::addRoundRect(qreal x, qreal y, qreal w, qreal h, int xRnd, int yRnd);
+ \overload
+
+ Adds a rectangle with rounded corners to the path. The rectangle
+ is constructed from \a x, \a y, and the width and height \a w
+ and \a h.
+
+ The \a xRnd and \a yRnd arguments specify how rounded the corners
+ should be. 0 is angled corners, 99 is maximum roundedness.
+
+ \sa addRoundedRect()
+ */
+
+/*!
+ \obsolete
+
+ \fn bool QPainterPath::addRoundRect(qreal x, qreal y, qreal width, qreal height, int roundness);
+ \since 4.3
+ \overload
+
+ Adds a rounded rectangle to the path, defined by the coordinates \a
+ x and \a y with the specified \a width and \a height.
+
+ The \a roundness argument specifies uniform roundness for the
+ rectangle. Vertical and horizontal roundness factors will be
+ adjusted accordingly to act uniformly around both axes. Use this
+ method if you want a rectangle equally rounded across both the X and
+ Y axis.
+
+ \sa addRoundedRect()
+*/
+
+/*!
+ \since 4.3
+
+ Returns a path which is the union of this path's fill area and \a p's fill area.
+
+ Set operations on paths will treat the paths as areas. Non-closed
+ paths will be treated as implicitly closed.
+ Bezier curves may be flattened to line segments due to numerical instability of
+ doing bezier curve intersections.
+
+ \sa intersected(), subtracted()
+*/
+QPainterPath QPainterPath::united(const QPainterPath &p) const
+{
+ if (isEmpty() || p.isEmpty())
+ return isEmpty() ? p : *this;
+ QPathClipper clipper(*this, p);
+ return clipper.clip(QPathClipper::BoolOr);
+}
+
+/*!
+ \since 4.3
+
+ Returns a path which is the intersection of this path's fill area and \a p's fill area.
+ Bezier curves may be flattened to line segments due to numerical instability of
+ doing bezier curve intersections.
+*/
+QPainterPath QPainterPath::intersected(const QPainterPath &p) const
+{
+ if (isEmpty() || p.isEmpty())
+ return QPainterPath();
+ QPathClipper clipper(*this, p);
+ return clipper.clip(QPathClipper::BoolAnd);
+}
+
+/*!
+ \since 4.3
+
+ Returns a path which is \a p's fill area subtracted from this path's fill area.
+
+ Set operations on paths will treat the paths as areas. Non-closed
+ paths will be treated as implicitly closed.
+ Bezier curves may be flattened to line segments due to numerical instability of
+ doing bezier curve intersections.
+*/
+QPainterPath QPainterPath::subtracted(const QPainterPath &p) const
+{
+ if (isEmpty() || p.isEmpty())
+ return *this;
+ QPathClipper clipper(*this, p);
+ return clipper.clip(QPathClipper::BoolSub);
+}
+
+/*!
+ \since 4.3
+ \obsolete
+
+ Use subtracted() instead.
+
+ \sa subtracted()
+*/
+QPainterPath QPainterPath::subtractedInverted(const QPainterPath &p) const
+{
+ return p.subtracted(*this);
+}
+
+/*!
+ \since 4.4
+
+ Returns a simplified version of this path. This implies merging all subpaths that intersect,
+ and returning a path containing no intersecting edges. Consecutive parallel lines will also
+ be merged. The simplified path will always use the default fill rule, Qt::OddEvenFill.
+ Bezier curves may be flattened to line segments due to numerical instability of
+ doing bezier curve intersections.
+*/
+QPainterPath QPainterPath::simplified() const
+{
+ if(isEmpty())
+ return *this;
+ QPathClipper clipper(*this, QPainterPath());
+ return clipper.clip(QPathClipper::Simplify);
+}
+
+/*!
+ \since 4.3
+
+ Returns true if the current path intersects at any point the given path \a p.
+ Also returns true if the current path contains or is contained by any part of \a p.
+
+ Set operations on paths will treat the paths as areas. Non-closed
+ paths will be treated as implicitly closed.
+
+ \sa contains()
+ */
+bool QPainterPath::intersects(const QPainterPath &p) const
+{
+ if (p.elementCount() == 1)
+ return contains(p.elementAt(0));
+ if (isEmpty() || p.isEmpty())
+ return false;
+ QPathClipper clipper(*this, p);
+ return clipper.intersect();
+}
+
+/*!
+ \since 4.3
+
+ Returns true if the given path \a p is contained within
+ the current path. Returns false if any edges of the current path and
+ \a p intersect.
+
+ Set operations on paths will treat the paths as areas. Non-closed
+ paths will be treated as implicitly closed.
+
+ \sa intersects()
+ */
+bool QPainterPath::contains(const QPainterPath &p) const
+{
+ if (p.elementCount() == 1)
+ return contains(p.elementAt(0));
+ if (isEmpty() || p.isEmpty())
+ return false;
+ QPathClipper clipper(*this, p);
+ return clipper.contains();
+}
+
+void QPainterPath::setDirty(bool dirty)
+{
+ d_func()->dirtyBounds = dirty;
+ d_func()->dirtyControlBounds = dirty;
+ delete d_func()->pathConverter;
+ d_func()->pathConverter = 0;
+ d_func()->convex = false;
+}
+
+void QPainterPath::computeBoundingRect() const
+{
+ QPainterPathData *d = d_func();
+ d->dirtyBounds = false;
+ if (!d_ptr) {
+ d->bounds = QRect();
+ return;
+ }
+
+ qreal minx, maxx, miny, maxy;
+ minx = maxx = d->elements.at(0).x;
+ miny = maxy = d->elements.at(0).y;
+ for (int i=1; i<d->elements.size(); ++i) {
+ const Element &e = d->elements.at(i);
+
+ switch (e.type) {
+ case MoveToElement:
+ case LineToElement:
+ if (e.x > maxx) maxx = e.x;
+ else if (e.x < minx) minx = e.x;
+ if (e.y > maxy) maxy = e.y;
+ else if (e.y < miny) miny = e.y;
+ break;
+ case CurveToElement:
+ {
+ QBezier b = QBezier::fromPoints(d->elements.at(i-1),
+ e,
+ d->elements.at(i+1),
+ d->elements.at(i+2));
+ QRectF r = qt_painterpath_bezier_extrema(b);
+ qreal right = r.right();
+ qreal bottom = r.bottom();
+ if (r.x() < minx) minx = r.x();
+ if (right > maxx) maxx = right;
+ if (r.y() < miny) miny = r.y();
+ if (bottom > maxy) maxy = bottom;
+ i += 2;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ d->bounds = QRectF(minx, miny, maxx - minx, maxy - miny);
+}
+
+
+void QPainterPath::computeControlPointRect() const
+{
+ QPainterPathData *d = d_func();
+ d->dirtyControlBounds = false;
+ if (!d_ptr) {
+ d->controlBounds = QRect();
+ return;
+ }
+
+ qreal minx, maxx, miny, maxy;
+ minx = maxx = d->elements.at(0).x;
+ miny = maxy = d->elements.at(0).y;
+ for (int i=1; i<d->elements.size(); ++i) {
+ const Element &e = d->elements.at(i);
+ if (e.x > maxx) maxx = e.x;
+ else if (e.x < minx) minx = e.x;
+ if (e.y > maxy) maxy = e.y;
+ else if (e.y < miny) miny = e.y;
+ }
+ d->controlBounds = QRectF(minx, miny, maxx - minx, maxy - miny);
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug s, const QPainterPath &p)
+{
+ s.nospace() << "QPainterPath: Element count=" << p.elementCount() << endl;
+ const char *types[] = {"MoveTo", "LineTo", "CurveTo", "CurveToData"};
+ for (int i=0; i<p.elementCount(); ++i) {
+ s.nospace() << " -> " << types[p.elementAt(i).type] << "(x=" << p.elementAt(i).x << ", y=" << p.elementAt(i).y << ')' << endl;
+
+ }
+ return s;
+}
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qpainterpath.h b/src/gui/painting/qpainterpath.h
new file mode 100644
index 0000000000..3c23282d8e
--- /dev/null
+++ b/src/gui/painting/qpainterpath.h
@@ -0,0 +1,435 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTERPATH_H
+#define QPAINTERPATH_H
+
+#include <QtGui/qmatrix.h>
+#include <QtCore/qglobal.h>
+#include <QtCore/qrect.h>
+#include <QtCore/qline.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qscopedpointer.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QFont;
+class QPainterPathPrivate;
+struct QPainterPathPrivateDeleter;
+class QPainterPathData;
+class QPainterPathStrokerPrivate;
+class QPolygonF;
+class QRegion;
+class QVectorPath;
+
+class Q_GUI_EXPORT QPainterPath
+{
+public:
+ enum ElementType {
+ MoveToElement,
+ LineToElement,
+ CurveToElement,
+ CurveToDataElement
+ };
+
+ class Element {
+ public:
+ qreal x;
+ qreal y;
+ ElementType type;
+
+ bool isMoveTo() const { return type == MoveToElement; }
+ bool isLineTo() const { return type == LineToElement; }
+ bool isCurveTo() const { return type == CurveToElement; }
+
+ operator QPointF () const { return QPointF(x, y); }
+
+ bool operator==(const Element &e) const { return qFuzzyCompare(x, e.x)
+ && qFuzzyCompare(y, e.y) && type == e.type; }
+ inline bool operator!=(const Element &e) const { return !operator==(e); }
+ };
+
+ QPainterPath();
+ explicit QPainterPath(const QPointF &startPoint);
+ QPainterPath(const QPainterPath &other);
+ QPainterPath &operator=(const QPainterPath &other);
+#ifdef Q_COMPILER_RVALUE_REFS
+ inline QPainterPath &operator=(QPainterPath &&other)
+ { qSwap(d_ptr, other.d_ptr); return *this; }
+#endif
+ ~QPainterPath();
+ inline void swap(QPainterPath &other) { d_ptr.swap(other.d_ptr); }
+
+ void closeSubpath();
+
+ void moveTo(const QPointF &p);
+ inline void moveTo(qreal x, qreal y);
+
+ void lineTo(const QPointF &p);
+ inline void lineTo(qreal x, qreal y);
+
+ void arcMoveTo(const QRectF &rect, qreal angle);
+ inline void arcMoveTo(qreal x, qreal y, qreal w, qreal h, qreal angle);
+
+ void arcTo(const QRectF &rect, qreal startAngle, qreal arcLength);
+ inline void arcTo(qreal x, qreal y, qreal w, qreal h, qreal startAngle, qreal arcLength);
+
+ void cubicTo(const QPointF &ctrlPt1, const QPointF &ctrlPt2, const QPointF &endPt);
+ inline void cubicTo(qreal ctrlPt1x, qreal ctrlPt1y, qreal ctrlPt2x, qreal ctrlPt2y,
+ qreal endPtx, qreal endPty);
+ void quadTo(const QPointF &ctrlPt, const QPointF &endPt);
+ inline void quadTo(qreal ctrlPtx, qreal ctrlPty, qreal endPtx, qreal endPty);
+
+ QPointF currentPosition() const;
+
+ void addRect(const QRectF &rect);
+ inline void addRect(qreal x, qreal y, qreal w, qreal h);
+ void addEllipse(const QRectF &rect);
+ inline void addEllipse(qreal x, qreal y, qreal w, qreal h);
+ inline void addEllipse(const QPointF &center, qreal rx, qreal ry);
+ void addPolygon(const QPolygonF &polygon);
+ void addText(const QPointF &point, const QFont &f, const QString &text);
+ inline void addText(qreal x, qreal y, const QFont &f, const QString &text);
+ void addPath(const QPainterPath &path);
+ void addRegion(const QRegion &region);
+
+ void addRoundedRect(const QRectF &rect, qreal xRadius, qreal yRadius,
+ Qt::SizeMode mode = Qt::AbsoluteSize);
+ inline void addRoundedRect(qreal x, qreal y, qreal w, qreal h,
+ qreal xRadius, qreal yRadius,
+ Qt::SizeMode mode = Qt::AbsoluteSize);
+
+ void addRoundRect(const QRectF &rect, int xRnd, int yRnd);
+ inline void addRoundRect(qreal x, qreal y, qreal w, qreal h,
+ int xRnd, int yRnd);
+ inline void addRoundRect(const QRectF &rect, int roundness);
+ inline void addRoundRect(qreal x, qreal y, qreal w, qreal h,
+ int roundness);
+
+ void connectPath(const QPainterPath &path);
+
+ bool contains(const QPointF &pt) const;
+ bool contains(const QRectF &rect) const;
+ bool intersects(const QRectF &rect) const;
+
+ void translate(qreal dx, qreal dy);
+ inline void translate(const QPointF &offset);
+
+ QPainterPath translated(qreal dx, qreal dy) const;
+ inline QPainterPath translated(const QPointF &offset) const;
+
+ QRectF boundingRect() const;
+ QRectF controlPointRect() const;
+
+ Qt::FillRule fillRule() const;
+ void setFillRule(Qt::FillRule fillRule);
+
+ inline bool isEmpty() const;
+
+ QPainterPath toReversed() const;
+ QList<QPolygonF> toSubpathPolygons(const QMatrix &matrix = QMatrix()) const;
+ QList<QPolygonF> toFillPolygons(const QMatrix &matrix = QMatrix()) const;
+ QPolygonF toFillPolygon(const QMatrix &matrix = QMatrix()) const;
+ QList<QPolygonF> toSubpathPolygons(const QTransform &matrix) const;
+ QList<QPolygonF> toFillPolygons(const QTransform &matrix) const;
+ QPolygonF toFillPolygon(const QTransform &matrix) const;
+
+ inline int elementCount() const;
+ inline const QPainterPath::Element &elementAt(int i) const;
+ inline void setElementPositionAt(int i, qreal x, qreal y);
+
+ qreal length() const;
+ qreal percentAtLength(qreal t) const;
+ QPointF pointAtPercent(qreal t) const;
+ qreal angleAtPercent(qreal t) const;
+ qreal slopeAtPercent(qreal t) const;
+
+ bool intersects(const QPainterPath &p) const;
+ bool contains(const QPainterPath &p) const;
+ QPainterPath united(const QPainterPath &r) const;
+ QPainterPath intersected(const QPainterPath &r) const;
+ QPainterPath subtracted(const QPainterPath &r) const;
+ QPainterPath subtractedInverted(const QPainterPath &r) const;
+
+ QPainterPath simplified() const;
+
+ bool operator==(const QPainterPath &other) const;
+ bool operator!=(const QPainterPath &other) const;
+
+ QPainterPath operator&(const QPainterPath &other) const;
+ QPainterPath operator|(const QPainterPath &other) const;
+ QPainterPath operator+(const QPainterPath &other) const;
+ QPainterPath operator-(const QPainterPath &other) const;
+ QPainterPath &operator&=(const QPainterPath &other);
+ QPainterPath &operator|=(const QPainterPath &other);
+ QPainterPath &operator+=(const QPainterPath &other);
+ QPainterPath &operator-=(const QPainterPath &other);
+
+private:
+ QScopedPointer<QPainterPathPrivate, QPainterPathPrivateDeleter> d_ptr;
+
+ inline void ensureData() { if (!d_ptr) ensureData_helper(); }
+ void ensureData_helper();
+ inline void detach();
+ void detach_helper();
+ void setDirty(bool);
+ void computeBoundingRect() const;
+ void computeControlPointRect() const;
+
+ QPainterPathData *d_func() const { return reinterpret_cast<QPainterPathData *>(d_ptr.data()); }
+
+ friend class QPainterPathData;
+ friend class QPainterPathStroker;
+ friend class QPainterPathStrokerPrivate;
+ friend class QMatrix;
+ friend class QTransform;
+ friend class QVectorPath;
+ friend Q_GUI_EXPORT const QVectorPath &qtVectorPathForPath(const QPainterPath &);
+
+#ifndef QT_NO_DATASTREAM
+ friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QPainterPath &);
+ friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QPainterPath &);
+#endif
+};
+
+class QPainterPathPrivate
+{
+public:
+ friend class QPainterPath;
+ friend class QPainterPathData;
+ friend class QPainterPathStroker;
+ friend class QPainterPathStrokerPrivate;
+ friend class QMatrix;
+ friend class QTransform;
+ friend class QVectorPath;
+ friend struct QPainterPathPrivateDeleter;
+#ifndef QT_NO_DATASTREAM
+ friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QPainterPath &);
+ friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QPainterPath &);
+#endif
+private:
+ QAtomicInt ref;
+ QVector<QPainterPath::Element> elements;
+};
+
+Q_DECLARE_TYPEINFO(QPainterPath::Element, Q_PRIMITIVE_TYPE);
+
+#ifndef QT_NO_DATASTREAM
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QPainterPath &);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QPainterPath &);
+#endif
+
+class Q_GUI_EXPORT QPainterPathStroker
+{
+ Q_DECLARE_PRIVATE(QPainterPathStroker)
+public:
+ QPainterPathStroker();
+ ~QPainterPathStroker();
+
+ void setWidth(qreal width);
+ qreal width() const;
+
+ void setCapStyle(Qt::PenCapStyle style);
+ Qt::PenCapStyle capStyle() const;
+
+ void setJoinStyle(Qt::PenJoinStyle style);
+ Qt::PenJoinStyle joinStyle() const;
+
+ void setMiterLimit(qreal length);
+ qreal miterLimit() const;
+
+ void setCurveThreshold(qreal threshold);
+ qreal curveThreshold() const;
+
+ void setDashPattern(Qt::PenStyle);
+ void setDashPattern(const QVector<qreal> &dashPattern);
+ QVector<qreal> dashPattern() const;
+
+ void setDashOffset(qreal offset);
+ qreal dashOffset() const;
+
+ QPainterPath createStroke(const QPainterPath &path) const;
+
+private:
+ Q_DISABLE_COPY(QPainterPathStroker)
+
+ friend class QX11PaintEngine;
+
+ QScopedPointer<QPainterPathStrokerPrivate> d_ptr;
+};
+
+inline void QPainterPath::moveTo(qreal x, qreal y)
+{
+ moveTo(QPointF(x, y));
+}
+
+inline void QPainterPath::lineTo(qreal x, qreal y)
+{
+ lineTo(QPointF(x, y));
+}
+
+inline void QPainterPath::arcTo(qreal x, qreal y, qreal w, qreal h, qreal startAngle, qreal arcLength)
+{
+ arcTo(QRectF(x, y, w, h), startAngle, arcLength);
+}
+
+inline void QPainterPath::arcMoveTo(qreal x, qreal y, qreal w, qreal h, qreal angle)
+{
+ arcMoveTo(QRectF(x, y, w, h), angle);
+}
+
+inline void QPainterPath::cubicTo(qreal ctrlPt1x, qreal ctrlPt1y, qreal ctrlPt2x, qreal ctrlPt2y,
+ qreal endPtx, qreal endPty)
+{
+ cubicTo(QPointF(ctrlPt1x, ctrlPt1y), QPointF(ctrlPt2x, ctrlPt2y),
+ QPointF(endPtx, endPty));
+}
+
+inline void QPainterPath::quadTo(qreal ctrlPtx, qreal ctrlPty, qreal endPtx, qreal endPty)
+{
+ quadTo(QPointF(ctrlPtx, ctrlPty), QPointF(endPtx, endPty));
+}
+
+inline void QPainterPath::addEllipse(qreal x, qreal y, qreal w, qreal h)
+{
+ addEllipse(QRectF(x, y, w, h));
+}
+
+inline void QPainterPath::addEllipse(const QPointF &center, qreal rx, qreal ry)
+{
+ addEllipse(QRectF(center.x() - rx, center.y() - ry, 2 * rx, 2 * ry));
+}
+
+inline void QPainterPath::addRect(qreal x, qreal y, qreal w, qreal h)
+{
+ addRect(QRectF(x, y, w, h));
+}
+
+inline void QPainterPath::addRoundedRect(qreal x, qreal y, qreal w, qreal h,
+ qreal xRadius, qreal yRadius,
+ Qt::SizeMode mode)
+{
+ addRoundedRect(QRectF(x, y, w, h), xRadius, yRadius, mode);
+}
+
+inline void QPainterPath::addRoundRect(qreal x, qreal y, qreal w, qreal h,
+ int xRnd, int yRnd)
+{
+ addRoundRect(QRectF(x, y, w, h), xRnd, yRnd);
+}
+
+inline void QPainterPath::addRoundRect(const QRectF &rect,
+ int roundness)
+{
+ int xRnd = roundness;
+ int yRnd = roundness;
+ if (rect.width() > rect.height())
+ xRnd = int(roundness * rect.height()/rect.width());
+ else
+ yRnd = int(roundness * rect.width()/rect.height());
+ addRoundRect(rect, xRnd, yRnd);
+}
+
+inline void QPainterPath::addRoundRect(qreal x, qreal y, qreal w, qreal h,
+ int roundness)
+{
+ addRoundRect(QRectF(x, y, w, h), roundness);
+}
+
+inline void QPainterPath::addText(qreal x, qreal y, const QFont &f, const QString &text)
+{
+ addText(QPointF(x, y), f, text);
+}
+
+inline void QPainterPath::translate(const QPointF &offset)
+{ translate(offset.x(), offset.y()); }
+
+inline QPainterPath QPainterPath::translated(const QPointF &offset) const
+{ return translated(offset.x(), offset.y()); }
+
+inline bool QPainterPath::isEmpty() const
+{
+ return !d_ptr || (d_ptr->elements.size() == 1 && d_ptr->elements.first().type == MoveToElement);
+}
+
+inline int QPainterPath::elementCount() const
+{
+ return d_ptr ? d_ptr->elements.size() : 0;
+}
+
+inline const QPainterPath::Element &QPainterPath::elementAt(int i) const
+{
+ Q_ASSERT(d_ptr);
+ Q_ASSERT(i >= 0 && i < elementCount());
+ return d_ptr->elements.at(i);
+}
+
+inline void QPainterPath::setElementPositionAt(int i, qreal x, qreal y)
+{
+ Q_ASSERT(d_ptr);
+ Q_ASSERT(i >= 0 && i < elementCount());
+ detach();
+ QPainterPath::Element &e = d_ptr->elements[i];
+ e.x = x;
+ e.y = y;
+}
+
+
+inline void QPainterPath::detach()
+{
+ if (d_ptr->ref != 1)
+ detach_helper();
+ setDirty(true);
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QPainterPath &);
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPAINTERPATH_H
diff --git a/src/gui/painting/qpainterpath_p.h b/src/gui/painting/qpainterpath_p.h
new file mode 100644
index 0000000000..8e05569893
--- /dev/null
+++ b/src/gui/painting/qpainterpath_p.h
@@ -0,0 +1,278 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTERPATH_P_H
+#define QPAINTERPATH_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtGui/qpainterpath.h"
+#include "QtGui/qregion.h"
+#include "QtCore/qlist.h"
+#include "QtCore/qvarlengtharray.h"
+
+#include <qdebug.h>
+
+#include <private/qvectorpath_p.h>
+#include <private/qstroker_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPainterPathStrokerPrivate
+{
+public:
+ QPainterPathStrokerPrivate();
+
+ QStroker stroker;
+ QVector<qfixed> dashPattern;
+ qreal dashOffset;
+};
+
+class QPolygonF;
+class QVectorPathConverter;
+
+class QVectorPathConverter
+{
+public:
+ QVectorPathConverter(const QVector<QPainterPath::Element> &path, uint fillRule, bool convex)
+ : pathData(path, fillRule, convex),
+ path(pathData.points.data(), path.size(),
+ pathData.elements.data(), pathData.flags) {}
+
+ const QVectorPath &vectorPath() {
+ return path;
+ }
+
+ struct QVectorPathData {
+ QVectorPathData(const QVector<QPainterPath::Element> &path, uint fillRule, bool convex)
+ : elements(path.size()),
+ points(path.size() * 2),
+ flags(0)
+ {
+ int ptsPos = 0;
+ bool isLines = true;
+ for (int i=0; i<path.size(); ++i) {
+ const QPainterPath::Element &e = path.at(i);
+ elements[i] = e.type;
+ points[ptsPos++] = e.x;
+ points[ptsPos++] = e.y;
+ if (e.type == QPainterPath::CurveToElement)
+ flags |= QVectorPath::CurvedShapeMask;
+
+ // This is to check if the path contains only alternating lineTo/moveTo,
+ // in which case we can set the LinesHint in the path. MoveTo is 0 and
+ // LineTo is 1 so the i%2 gets us what we want cheaply.
+ isLines = isLines && e.type == (QPainterPath::ElementType) (i%2);
+ }
+
+ if (fillRule == Qt::WindingFill)
+ flags |= QVectorPath::WindingFill;
+ else
+ flags |= QVectorPath::OddEvenFill;
+
+ if (isLines)
+ flags |= QVectorPath::LinesShapeMask;
+ else {
+ flags |= QVectorPath::AreaShapeMask;
+ if (!convex)
+ flags |= QVectorPath::NonConvexShapeMask;
+ }
+
+ }
+ QVarLengthArray<QPainterPath::ElementType> elements;
+ QVarLengthArray<qreal> points;
+ uint flags;
+ };
+
+ QVectorPathData pathData;
+ QVectorPath path;
+
+private:
+ Q_DISABLE_COPY(QVectorPathConverter)
+};
+
+class QPainterPathData : public QPainterPathPrivate
+{
+public:
+ QPainterPathData() :
+ cStart(0),
+ fillRule(Qt::OddEvenFill),
+ dirtyBounds(false),
+ dirtyControlBounds(false),
+ pathConverter(0)
+ {
+ ref = 1;
+ require_moveTo = false;
+ convex = false;
+ }
+
+ QPainterPathData(const QPainterPathData &other) :
+ QPainterPathPrivate(), cStart(other.cStart), fillRule(other.fillRule),
+ bounds(other.bounds),
+ controlBounds(other.controlBounds),
+ dirtyBounds(other.dirtyBounds),
+ dirtyControlBounds(other.dirtyControlBounds),
+ convex(other.convex),
+ pathConverter(0)
+ {
+ ref = 1;
+ require_moveTo = false;
+ elements = other.elements;
+ }
+
+ ~QPainterPathData() {
+ delete pathConverter;
+ }
+
+ inline bool isClosed() const;
+ inline void close();
+ inline void maybeMoveTo();
+
+ const QVectorPath &vectorPath() {
+ if (!pathConverter)
+ pathConverter = new QVectorPathConverter(elements, fillRule, convex);
+ return pathConverter->path;
+ }
+
+ int cStart;
+ Qt::FillRule fillRule;
+
+ QRectF bounds;
+ QRectF controlBounds;
+
+ uint require_moveTo : 1;
+ uint dirtyBounds : 1;
+ uint dirtyControlBounds : 1;
+ uint convex : 1;
+
+ QVectorPathConverter *pathConverter;
+};
+
+
+inline const QPainterPath QVectorPath::convertToPainterPath() const
+{
+ QPainterPath path;
+ path.ensureData();
+ QPainterPathData *data = path.d_func();
+ data->elements.reserve(m_count);
+ int index = 0;
+ data->elements[0].x = m_points[index++];
+ data->elements[0].y = m_points[index++];
+
+ if (m_elements) {
+ data->elements[0].type = m_elements[0];
+ for (int i=1; i<m_count; ++i) {
+ QPainterPath::Element element;
+ element.x = m_points[index++];
+ element.y = m_points[index++];
+ element.type = m_elements[i];
+ data->elements << element;
+ }
+ } else {
+ data->elements[0].type = QPainterPath::MoveToElement;
+ for (int i=1; i<m_count; ++i) {
+ QPainterPath::Element element;
+ element.x = m_points[index++];
+ element.y = m_points[index++];
+ element.type = QPainterPath::LineToElement;
+ data->elements << element;
+ }
+ }
+
+ if (m_hints & OddEvenFill)
+ data->fillRule = Qt::OddEvenFill;
+ else
+ data->fillRule = Qt::WindingFill;
+ return path;
+}
+
+void Q_GUI_EXPORT qt_find_ellipse_coords(const QRectF &r, qreal angle, qreal length,
+ QPointF* startPoint, QPointF *endPoint);
+
+inline bool QPainterPathData::isClosed() const
+{
+ const QPainterPath::Element &first = elements.at(cStart);
+ const QPainterPath::Element &last = elements.last();
+ return first.x == last.x && first.y == last.y;
+}
+
+inline void QPainterPathData::close()
+{
+ Q_ASSERT(ref == 1);
+ require_moveTo = true;
+ const QPainterPath::Element &first = elements.at(cStart);
+ QPainterPath::Element &last = elements.last();
+ if (first.x != last.x || first.y != last.y) {
+ if (qFuzzyCompare(first.x, last.x) && qFuzzyCompare(first.y, last.y)) {
+ last.x = first.x;
+ last.y = first.y;
+ } else {
+ QPainterPath::Element e = { first.x, first.y, QPainterPath::LineToElement };
+ elements << e;
+ }
+ }
+}
+
+inline void QPainterPathData::maybeMoveTo()
+{
+ if (require_moveTo) {
+ QPainterPath::Element e = elements.last();
+ e.type = QPainterPath::MoveToElement;
+ elements.append(e);
+ require_moveTo = false;
+ }
+}
+
+#define KAPPA 0.5522847498
+
+
+QT_END_NAMESPACE
+
+#endif // QPAINTERPATH_P_H
diff --git a/src/gui/painting/qpathclipper.cpp b/src/gui/painting/qpathclipper.cpp
new file mode 100644
index 0000000000..d18fba2e7c
--- /dev/null
+++ b/src/gui/painting/qpathclipper.cpp
@@ -0,0 +1,2149 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpathclipper_p.h"
+
+#include <private/qbezier_p.h>
+#include <private/qdatabuffer_p.h>
+#include <private/qnumeric_p.h>
+#include <qmath.h>
+
+/**
+ The algorithm is as follows:
+
+ 1. Find all intersections between the two paths (including self-intersections),
+ and build a winged edge structure of non-intersecting parts.
+ 2. While there are more unhandled edges:
+ 3. Pick a y-coordinate from an unhandled edge.
+ 4. Intersect the horizontal line at y-coordinate with all edges.
+ 5. Traverse intersections left to right deciding whether each subpath should be added or not.
+ 6. If the subpath should be added, traverse the winged-edge structure and add the edges to
+ a separate winged edge structure.
+ 7. Mark all edges in subpaths crossing the horizontal line as handled.
+ 8. (Optional) Simplify the resulting winged edge structure by merging shared edges.
+ 9. Convert the resulting winged edge structure to a painter path.
+ */
+
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+static inline bool fuzzyIsNull(qreal d)
+{
+ if (sizeof(qreal) == sizeof(double))
+ return qAbs(d) <= 1e-12;
+ else
+ return qAbs(d) <= 1e-5f;
+}
+
+static inline bool comparePoints(const QPointF &a, const QPointF &b)
+{
+ return fuzzyIsNull(a.x() - b.x())
+ && fuzzyIsNull(a.y() - b.y());
+}
+
+//#define QDEBUG_CLIPPER
+static qreal dot(const QPointF &a, const QPointF &b)
+{
+ return a.x() * b.x() + a.y() * b.y();
+}
+
+static void normalize(double &x, double &y)
+{
+ double reciprocal = 1 / qSqrt(x * x + y * y);
+ x *= reciprocal;
+ y *= reciprocal;
+}
+
+struct QIntersection
+{
+ qreal alphaA;
+ qreal alphaB;
+
+ QPointF pos;
+};
+
+class QIntersectionFinder
+{
+public:
+ void produceIntersections(QPathSegments &segments);
+ bool hasIntersections(const QPathSegments &a, const QPathSegments &b) const;
+
+private:
+ bool linesIntersect(const QLineF &a, const QLineF &b) const;
+};
+
+bool QIntersectionFinder::linesIntersect(const QLineF &a, const QLineF &b) const
+{
+ const QPointF p1 = a.p1();
+ const QPointF p2 = a.p2();
+
+ const QPointF q1 = b.p1();
+ const QPointF q2 = b.p2();
+
+ if (comparePoints(p1, p2) || comparePoints(q1, q2))
+ return false;
+
+ const bool p1_equals_q1 = comparePoints(p1, q1);
+ const bool p2_equals_q2 = comparePoints(p2, q2);
+
+ if (p1_equals_q1 && p2_equals_q2)
+ return true;
+
+ const bool p1_equals_q2 = comparePoints(p1, q2);
+ const bool p2_equals_q1 = comparePoints(p2, q1);
+
+ if (p1_equals_q2 && p2_equals_q1)
+ return true;
+
+ const QPointF pDelta = p2 - p1;
+ const QPointF qDelta = q2 - q1;
+
+ const qreal par = pDelta.x() * qDelta.y() - pDelta.y() * qDelta.x();
+
+ if (qFuzzyIsNull(par)) {
+ const QPointF normal(-pDelta.y(), pDelta.x());
+
+ // coinciding?
+ if (qFuzzyIsNull(dot(normal, q1 - p1))) {
+ const qreal dp = dot(pDelta, pDelta);
+
+ const qreal tq1 = dot(pDelta, q1 - p1);
+ const qreal tq2 = dot(pDelta, q2 - p1);
+
+ if ((tq1 > 0 && tq1 < dp) || (tq2 > 0 && tq2 < dp))
+ return true;
+
+ const qreal dq = dot(qDelta, qDelta);
+
+ const qreal tp1 = dot(qDelta, p1 - q1);
+ const qreal tp2 = dot(qDelta, p2 - q1);
+
+ if ((tp1 > 0 && tp1 < dq) || (tp2 > 0 && tp2 < dq))
+ return true;
+ }
+
+ return false;
+ }
+
+ const qreal invPar = 1 / par;
+
+ const qreal tp = (qDelta.y() * (q1.x() - p1.x()) -
+ qDelta.x() * (q1.y() - p1.y())) * invPar;
+
+ if (tp < 0 || tp > 1)
+ return false;
+
+ const qreal tq = (pDelta.y() * (q1.x() - p1.x()) -
+ pDelta.x() * (q1.y() - p1.y())) * invPar;
+
+ return tq >= 0 && tq <= 1;
+}
+
+bool QIntersectionFinder::hasIntersections(const QPathSegments &a, const QPathSegments &b) const
+{
+ if (a.segments() == 0 || b.segments() == 0)
+ return false;
+
+ const QRectF &rb0 = b.elementBounds(0);
+
+ qreal minX = rb0.left();
+ qreal minY = rb0.top();
+ qreal maxX = rb0.right();
+ qreal maxY = rb0.bottom();
+
+ for (int i = 1; i < b.segments(); ++i) {
+ const QRectF &r = b.elementBounds(i);
+ minX = qMin(minX, r.left());
+ minY = qMin(minY, r.top());
+ maxX = qMax(maxX, r.right());
+ maxY = qMax(maxY, r.bottom());
+ }
+
+ QRectF rb(minX, minY, maxX - minX, maxY - minY);
+
+ for (int i = 0; i < a.segments(); ++i) {
+ const QRectF &r1 = a.elementBounds(i);
+
+ if (r1.left() > rb.right() || rb.left() > r1.right())
+ continue;
+ if (r1.top() > rb.bottom() || rb.top() > r1.bottom())
+ continue;
+
+ for (int j = 0; j < b.segments(); ++j) {
+ const QRectF &r2 = b.elementBounds(j);
+
+ if (r1.left() > r2.right() || r2.left() > r1.right())
+ continue;
+ if (r1.top() > r2.bottom() || r2.top() > r1.bottom())
+ continue;
+
+ if (linesIntersect(a.lineAt(i), b.lineAt(j)))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+namespace {
+struct TreeNode
+{
+ qreal splitLeft;
+ qreal splitRight;
+ bool leaf;
+
+ int lowestLeftIndex;
+ int lowestRightIndex;
+
+ union {
+ struct {
+ int first;
+ int last;
+ } interval;
+ struct {
+ int left;
+ int right;
+ } children;
+ } index;
+};
+
+struct RectF
+{
+ qreal x1;
+ qreal y1;
+ qreal x2;
+ qreal y2;
+};
+
+class SegmentTree
+{
+public:
+ SegmentTree(QPathSegments &segments);
+
+ QRectF boundingRect() const;
+
+ void produceIntersections(int segment);
+
+private:
+ TreeNode buildTree(int first, int last, int depth, const RectF &bounds);
+
+ void produceIntersectionsLeaf(const TreeNode &node, int segment);
+ void produceIntersections(const TreeNode &node, int segment, const RectF &segmentBounds, const RectF &nodeBounds, int axis);
+ void intersectLines(const QLineF &a, const QLineF &b, QDataBuffer<QIntersection> &intersections);
+
+ QPathSegments &m_segments;
+ QVector<int> m_index;
+
+ RectF m_bounds;
+
+ QVector<TreeNode> m_tree;
+ QDataBuffer<QIntersection> m_intersections;
+};
+
+SegmentTree::SegmentTree(QPathSegments &segments)
+ : m_segments(segments),
+ m_intersections(0)
+{
+ m_bounds.x1 = qt_inf();
+ m_bounds.y1 = qt_inf();
+ m_bounds.x2 = -qt_inf();
+ m_bounds.y2 = -qt_inf();
+
+ m_index.resize(m_segments.segments());
+
+ for (int i = 0; i < m_index.size(); ++i) {
+ m_index[i] = i;
+
+ const QRectF &segmentBounds = m_segments.elementBounds(i);
+
+ if (segmentBounds.left() < m_bounds.x1)
+ m_bounds.x1 = segmentBounds.left();
+ if (segmentBounds.top() < m_bounds.y1)
+ m_bounds.y1 = segmentBounds.top();
+ if (segmentBounds.right() > m_bounds.x2)
+ m_bounds.x2 = segmentBounds.right();
+ if (segmentBounds.bottom() > m_bounds.y2)
+ m_bounds.y2 = segmentBounds.bottom();
+ }
+
+ m_tree.resize(1);
+
+ TreeNode root = buildTree(0, m_index.size(), 0, m_bounds);
+ m_tree[0] = root;
+}
+
+QRectF SegmentTree::boundingRect() const
+{
+ return QRectF(QPointF(m_bounds.x1, m_bounds.y1),
+ QPointF(m_bounds.x2, m_bounds.y2));
+}
+
+static inline qreal coordinate(const QPointF &pos, int axis)
+{
+ return axis == 0 ? pos.x() : pos.y();
+}
+
+TreeNode SegmentTree::buildTree(int first, int last, int depth, const RectF &bounds)
+{
+ if (depth >= 24 || (last - first) <= 10) {
+ TreeNode node;
+ node.leaf = true;
+ node.index.interval.first = first;
+ node.index.interval.last = last;
+
+ return node;
+ }
+
+ int splitAxis = (depth & 1);
+
+ TreeNode node;
+ node.leaf = false;
+
+ qreal split = 0.5f * ((&bounds.x1)[splitAxis] + (&bounds.x2)[splitAxis]);
+
+ node.splitLeft = (&bounds.x1)[splitAxis];
+ node.splitRight = (&bounds.x2)[splitAxis];
+
+ node.lowestLeftIndex = INT_MAX;
+ node.lowestRightIndex = INT_MAX;
+
+ const int treeSize = m_tree.size();
+
+ node.index.children.left = treeSize;
+ node.index.children.right = treeSize + 1;
+
+ m_tree.resize(treeSize + 2);
+
+ int l = first;
+ int r = last - 1;
+
+ // partition into left and right sets
+ while (l <= r) {
+ const int index = m_index.at(l);
+ const QRectF &segmentBounds = m_segments.elementBounds(index);
+
+ qreal lowCoordinate = coordinate(segmentBounds.topLeft(), splitAxis);
+
+ if (coordinate(segmentBounds.center(), splitAxis) < split) {
+ qreal highCoordinate = coordinate(segmentBounds.bottomRight(), splitAxis);
+ if (highCoordinate > node.splitLeft)
+ node.splitLeft = highCoordinate;
+ if (index < node.lowestLeftIndex)
+ node.lowestLeftIndex = index;
+ ++l;
+ } else {
+ if (lowCoordinate < node.splitRight)
+ node.splitRight = lowCoordinate;
+ if (index < node.lowestRightIndex)
+ node.lowestRightIndex = index;
+ qSwap(m_index[l], m_index[r]);
+ --r;
+ }
+ }
+
+ RectF lbounds = bounds;
+ (&lbounds.x2)[splitAxis] = node.splitLeft;
+
+ RectF rbounds = bounds;
+ (&rbounds.x1)[splitAxis] = node.splitRight;
+
+ TreeNode left = buildTree(first, l, depth + 1, lbounds);
+ m_tree[node.index.children.left] = left;
+
+ TreeNode right = buildTree(l, last, depth + 1, rbounds);
+ m_tree[node.index.children.right] = right;
+
+ return node;
+}
+
+void SegmentTree::intersectLines(const QLineF &a, const QLineF &b, QDataBuffer<QIntersection> &intersections)
+{
+ const QPointF p1 = a.p1();
+ const QPointF p2 = a.p2();
+
+ const QPointF q1 = b.p1();
+ const QPointF q2 = b.p2();
+
+ if (comparePoints(p1, p2) || comparePoints(q1, q2))
+ return;
+
+ const bool p1_equals_q1 = comparePoints(p1, q1);
+ const bool p2_equals_q2 = comparePoints(p2, q2);
+
+ if (p1_equals_q1 && p2_equals_q2)
+ return;
+
+ const bool p1_equals_q2 = comparePoints(p1, q2);
+ const bool p2_equals_q1 = comparePoints(p2, q1);
+
+ if (p1_equals_q2 && p2_equals_q1)
+ return;
+
+ const QPointF pDelta = p2 - p1;
+ const QPointF qDelta = q2 - q1;
+
+ const qreal par = pDelta.x() * qDelta.y() - pDelta.y() * qDelta.x();
+
+ if (qFuzzyIsNull(par)) {
+ const QPointF normal(-pDelta.y(), pDelta.x());
+
+ // coinciding?
+ if (qFuzzyIsNull(dot(normal, q1 - p1))) {
+ const qreal invDp = 1 / dot(pDelta, pDelta);
+
+ const qreal tq1 = dot(pDelta, q1 - p1) * invDp;
+ const qreal tq2 = dot(pDelta, q2 - p1) * invDp;
+
+ if (tq1 > 0 && tq1 < 1) {
+ QIntersection intersection;
+ intersection.alphaA = tq1;
+ intersection.alphaB = 0;
+ intersection.pos = q1;
+ intersections.add(intersection);
+ }
+
+ if (tq2 > 0 && tq2 < 1) {
+ QIntersection intersection;
+ intersection.alphaA = tq2;
+ intersection.alphaB = 1;
+ intersection.pos = q2;
+ intersections.add(intersection);
+ }
+
+ const qreal invDq = 1 / dot(qDelta, qDelta);
+
+ const qreal tp1 = dot(qDelta, p1 - q1) * invDq;
+ const qreal tp2 = dot(qDelta, p2 - q1) * invDq;
+
+ if (tp1 > 0 && tp1 < 1) {
+ QIntersection intersection;
+ intersection.alphaA = 0;
+ intersection.alphaB = tp1;
+ intersection.pos = p1;
+ intersections.add(intersection);
+ }
+
+ if (tp2 > 0 && tp2 < 1) {
+ QIntersection intersection;
+ intersection.alphaA = 1;
+ intersection.alphaB = tp2;
+ intersection.pos = p2;
+ intersections.add(intersection);
+ }
+ }
+
+ return;
+ }
+
+ // if the lines are not parallel and share a common end point, then they
+ // don't intersect
+ if (p1_equals_q1 || p1_equals_q2 || p2_equals_q1 || p2_equals_q2)
+ return;
+
+
+ const qreal tp = (qDelta.y() * (q1.x() - p1.x()) -
+ qDelta.x() * (q1.y() - p1.y())) / par;
+ const qreal tq = (pDelta.y() * (q1.x() - p1.x()) -
+ pDelta.x() * (q1.y() - p1.y())) / par;
+
+ if (tp<0 || tp>1 || tq<0 || tq>1)
+ return;
+
+ const bool p_zero = qFuzzyIsNull(tp);
+ const bool p_one = qFuzzyIsNull(tp - 1);
+
+ const bool q_zero = qFuzzyIsNull(tq);
+ const bool q_one = qFuzzyIsNull(tq - 1);
+
+ if ((q_zero || q_one) && (p_zero || p_one))
+ return;
+
+ QPointF pt;
+ if (p_zero) {
+ pt = p1;
+ } else if (p_one) {
+ pt = p2;
+ } else if (q_zero) {
+ pt = q1;
+ } else if (q_one) {
+ pt = q2;
+ } else {
+ pt = q1 + (q2 - q1) * tq;
+ }
+
+ QIntersection intersection;
+ intersection.alphaA = tp;
+ intersection.alphaB = tq;
+ intersection.pos = pt;
+ intersections.add(intersection);
+}
+
+void SegmentTree::produceIntersections(int segment)
+{
+ const QRectF &segmentBounds = m_segments.elementBounds(segment);
+
+ RectF sbounds;
+ sbounds.x1 = segmentBounds.left();
+ sbounds.y1 = segmentBounds.top();
+ sbounds.x2 = segmentBounds.right();
+ sbounds.y2 = segmentBounds.bottom();
+
+ produceIntersections(m_tree.at(0), segment, sbounds, m_bounds, 0);
+}
+
+void SegmentTree::produceIntersectionsLeaf(const TreeNode &node, int segment)
+{
+ const QRectF &r1 = m_segments.elementBounds(segment);
+ const QLineF lineA = m_segments.lineAt(segment);
+
+ for (int i = node.index.interval.first; i < node.index.interval.last; ++i) {
+ const int other = m_index.at(i);
+ if (other >= segment)
+ continue;
+
+ const QRectF &r2 = m_segments.elementBounds(other);
+
+ if (r1.left() > r2.right() || r2.left() > r1.right())
+ continue;
+ if (r1.top() > r2.bottom() || r2.top() > r1.bottom())
+ continue;
+
+ m_intersections.reset();
+
+ const QLineF lineB = m_segments.lineAt(other);
+
+ intersectLines(lineA, lineB, m_intersections);
+
+ for (int k = 0; k < m_intersections.size(); ++k) {
+ QPathSegments::Intersection i_isect, j_isect;
+ i_isect.vertex = j_isect.vertex = m_segments.addPoint(m_intersections.at(k).pos);
+
+ i_isect.t = m_intersections.at(k).alphaA;
+ j_isect.t = m_intersections.at(k).alphaB;
+
+ i_isect.next = 0;
+ j_isect.next = 0;
+
+ m_segments.addIntersection(segment, i_isect);
+ m_segments.addIntersection(other, j_isect);
+ }
+ }
+}
+
+void SegmentTree::produceIntersections(const TreeNode &node, int segment, const RectF &segmentBounds, const RectF &nodeBounds, int axis)
+{
+ if (node.leaf) {
+ produceIntersectionsLeaf(node, segment);
+ return;
+ }
+
+ RectF lbounds = nodeBounds;
+ (&lbounds.x2)[axis] = node.splitLeft;
+
+ RectF rbounds = nodeBounds;
+ (&rbounds.x1)[axis] = node.splitRight;
+
+ if (segment > node.lowestLeftIndex && (&segmentBounds.x1)[axis] <= node.splitLeft)
+ produceIntersections(m_tree.at(node.index.children.left), segment, segmentBounds, lbounds, !axis);
+
+ if (segment > node.lowestRightIndex && (&segmentBounds.x2)[axis] >= node.splitRight)
+ produceIntersections(m_tree.at(node.index.children.right), segment, segmentBounds, rbounds, !axis);
+}
+
+}
+
+void QIntersectionFinder::produceIntersections(QPathSegments &segments)
+{
+ SegmentTree tree(segments);
+
+ for (int i = 0; i < segments.segments(); ++i)
+ tree.produceIntersections(i);
+}
+
+class QKdPointTree
+{
+public:
+ enum Traversal {
+ TraverseBoth,
+ TraverseLeft,
+ TraverseRight,
+ TraverseNone
+ };
+
+ struct Node {
+ int point;
+ int id;
+
+ Node *left;
+ Node *right;
+ };
+
+ QKdPointTree(const QPathSegments &segments)
+ : m_segments(&segments)
+ , m_nodes(m_segments->points())
+ , m_id(0)
+ {
+ m_nodes.resize(m_segments->points());
+
+ for (int i = 0; i < m_nodes.size(); ++i) {
+ m_nodes.at(i).point = i;
+ m_nodes.at(i).id = -1;
+ }
+
+ m_rootNode = build(0, m_nodes.size());
+ }
+
+ int build(int begin, int end, int depth = 0);
+
+ Node *rootNode()
+ {
+ return &m_nodes.at(m_rootNode);
+ }
+
+ inline int nextId()
+ {
+ return m_id++;
+ }
+
+private:
+ const QPathSegments *m_segments;
+ QDataBuffer<Node> m_nodes;
+
+ int m_rootNode;
+ int m_id;
+};
+
+template <typename T>
+void qTraverseKdPointTree(QKdPointTree::Node &node, T &t, int depth = 0)
+{
+ QKdPointTree::Traversal status = t(node, depth);
+
+ const bool traverseRight = (status == QKdPointTree::TraverseBoth || status == QKdPointTree::TraverseRight);
+ const bool traverseLeft = (status == QKdPointTree::TraverseBoth || status == QKdPointTree::TraverseLeft);
+
+ if (traverseLeft && node.left)
+ QT_PREPEND_NAMESPACE(qTraverseKdPointTree<T>)(*node.left, t, depth + 1);
+
+ if (traverseRight && node.right)
+ QT_PREPEND_NAMESPACE(qTraverseKdPointTree<T>)(*node.right, t, depth + 1);
+}
+
+static inline qreal component(const QPointF &point, unsigned int i)
+{
+ Q_ASSERT(i < 2);
+ const qreal components[] = { point.x(), point.y() };
+ return components[i];
+}
+
+int QKdPointTree::build(int begin, int end, int depth)
+{
+ Q_ASSERT(end > begin);
+
+ const qreal pivot = component(m_segments->pointAt(m_nodes.at(begin).point), depth & 1);
+
+ int first = begin + 1;
+ int last = end - 1;
+
+ while (first <= last) {
+ const qreal value = component(m_segments->pointAt(m_nodes.at(first).point), depth & 1);
+
+ if (value < pivot)
+ ++first;
+ else {
+ qSwap(m_nodes.at(first), m_nodes.at(last));
+ --last;
+ }
+ }
+
+ qSwap(m_nodes.at(last), m_nodes.at(begin));
+
+ if (last > begin)
+ m_nodes.at(last).left = &m_nodes.at(build(begin, last, depth + 1));
+ else
+ m_nodes.at(last).left = 0;
+
+ if (last + 1 < end)
+ m_nodes.at(last).right = &m_nodes.at(build(last + 1, end, depth + 1));
+ else
+ m_nodes.at(last).right = 0;
+
+ return last;
+}
+
+class QKdPointFinder
+{
+public:
+ QKdPointFinder(int point, const QPathSegments &segments, QKdPointTree &tree)
+ : m_point(point)
+ , m_result(-1)
+ , m_segments(&segments)
+ , m_tree(&tree)
+ {
+ pointComponents[0] = segments.pointAt(point).x();
+ pointComponents[1] = segments.pointAt(point).y();
+ }
+
+ inline QKdPointTree::Traversal operator()(QKdPointTree::Node &node, int depth)
+ {
+ if (m_result != -1)
+ return QKdPointTree::TraverseNone;
+
+ const QPointF &nodePoint = m_segments->pointAt(node.point);
+
+ const qreal pivotComponents[] = { nodePoint.x(), nodePoint.y() };
+
+ const qreal pivot = pivotComponents[depth & 1];
+ const qreal value = pointComponents[depth & 1];
+
+ if (fuzzyIsNull(pivot - value)) {
+ const qreal pivot2 = pivotComponents[(depth + 1) & 1];
+ const qreal value2 = pointComponents[(depth + 1) & 1];
+
+ if (fuzzyIsNull(pivot2 - value2)) {
+ if (node.id < 0)
+ node.id = m_tree->nextId();
+
+ m_result = node.id;
+ return QKdPointTree::TraverseNone;
+ } else
+ return QKdPointTree::TraverseBoth;
+ } else if (value < pivot) {
+ return QKdPointTree::TraverseLeft;
+ } else {
+ return QKdPointTree::TraverseRight;
+ }
+ }
+
+ int result() const
+ {
+ return m_result;
+ }
+
+private:
+ int m_point;
+ qreal pointComponents[2];
+ int m_result;
+ const QPathSegments *m_segments;
+ QKdPointTree *m_tree;
+};
+
+// merge all points that are within qFuzzyCompare range of each other
+void QPathSegments::mergePoints()
+{
+ QKdPointTree tree(*this);
+
+ if (tree.rootNode()) {
+ QDataBuffer<QPointF> mergedPoints(points());
+ QDataBuffer<int> pointIndices(points());
+
+ for (int i = 0; i < points(); ++i) {
+ QKdPointFinder finder(i, *this, tree);
+ QT_PREPEND_NAMESPACE(qTraverseKdPointTree<QKdPointFinder>)(*tree.rootNode(), finder);
+
+ Q_ASSERT(finder.result() != -1);
+
+ if (finder.result() >= mergedPoints.size())
+ mergedPoints << m_points.at(i);
+
+ pointIndices << finder.result();
+ }
+
+ for (int i = 0; i < m_segments.size(); ++i) {
+ m_segments.at(i).va = pointIndices.at(m_segments.at(i).va);
+ m_segments.at(i).vb = pointIndices.at(m_segments.at(i).vb);
+ }
+
+ for (int i = 0; i < m_intersections.size(); ++i)
+ m_intersections.at(i).vertex = pointIndices.at(m_intersections.at(i).vertex);
+
+ m_points.swap(mergedPoints);
+ }
+}
+
+void QWingedEdge::intersectAndAdd()
+{
+ QIntersectionFinder finder;
+ finder.produceIntersections(m_segments);
+
+ m_segments.mergePoints();
+
+ for (int i = 0; i < m_segments.points(); ++i)
+ addVertex(m_segments.pointAt(i));
+
+ QDataBuffer<QPathSegments::Intersection> intersections(m_segments.segments());
+ for (int i = 0; i < m_segments.segments(); ++i) {
+ intersections.reset();
+
+ int pathId = m_segments.pathId(i);
+
+ const QPathSegments::Intersection *isect = m_segments.intersectionAt(i);
+ while (isect) {
+ intersections << *isect;
+
+ if (isect->next) {
+ isect += isect->next;
+ } else {
+ isect = 0;
+ }
+ }
+
+ qSort(intersections.data(), intersections.data() + intersections.size());
+
+ int first = m_segments.segmentAt(i).va;
+ int second = m_segments.segmentAt(i).vb;
+
+ int last = first;
+ for (int j = 0; j < intersections.size(); ++j) {
+ const QPathSegments::Intersection &isect = intersections.at(j);
+
+ QPathEdge *ep = edge(addEdge(last, isect.vertex));
+
+ if (ep) {
+ const int dir = m_segments.pointAt(last).y() < m_segments.pointAt(isect.vertex).y() ? 1 : -1;
+ if (pathId == 0)
+ ep->windingA += dir;
+ else
+ ep->windingB += dir;
+ }
+
+ last = isect.vertex;
+ }
+
+ QPathEdge *ep = edge(addEdge(last, second));
+
+ if (ep) {
+ const int dir = m_segments.pointAt(last).y() < m_segments.pointAt(second).y() ? 1 : -1;
+ if (pathId == 0)
+ ep->windingA += dir;
+ else
+ ep->windingB += dir;
+ }
+ }
+}
+
+QWingedEdge::QWingedEdge() :
+ m_edges(0),
+ m_vertices(0),
+ m_segments(0)
+{
+}
+
+QWingedEdge::QWingedEdge(const QPainterPath &subject, const QPainterPath &clip) :
+ m_edges(subject.elementCount()),
+ m_vertices(subject.elementCount()),
+ m_segments(subject.elementCount())
+{
+ m_segments.setPath(subject);
+ m_segments.addPath(clip);
+
+ intersectAndAdd();
+}
+
+QWingedEdge::TraversalStatus QWingedEdge::next(const QWingedEdge::TraversalStatus &status) const
+{
+ const QPathEdge *sp = edge(status.edge);
+ Q_ASSERT(sp);
+
+ TraversalStatus result;
+ result.edge = sp->next(status.traversal, status.direction);
+ result.traversal = status.traversal;
+ result.direction = status.direction;
+
+ const QPathEdge *rp = edge(result.edge);
+ Q_ASSERT(rp);
+
+ if (sp->vertex(status.direction) == rp->vertex(status.direction))
+ result.flip();
+
+ return result;
+}
+
+static bool isLine(const QBezier &bezier)
+{
+ const bool equal_1_2 = comparePoints(bezier.pt1(), bezier.pt2());
+ const bool equal_2_3 = comparePoints(bezier.pt2(), bezier.pt3());
+ const bool equal_3_4 = comparePoints(bezier.pt3(), bezier.pt4());
+
+ // point?
+ if (equal_1_2 && equal_2_3 && equal_3_4)
+ return true;
+
+ if (comparePoints(bezier.pt1(), bezier.pt4()))
+ return equal_1_2 || equal_3_4;
+
+ return (equal_1_2 && equal_3_4) || (equal_1_2 && equal_2_3) || (equal_2_3 && equal_3_4);
+}
+
+void QPathSegments::setPath(const QPainterPath &path)
+{
+ m_points.reset();
+ m_intersections.reset();
+ m_segments.reset();
+
+ m_pathId = 0;
+
+ addPath(path);
+}
+
+void QPathSegments::addPath(const QPainterPath &path)
+{
+ int firstSegment = m_segments.size();
+
+ bool hasMoveTo = false;
+ int lastMoveTo = 0;
+ int last = 0;
+ for (int i = 0; i < path.elementCount(); ++i) {
+ int current = m_points.size();
+
+ QPointF currentPoint;
+ if (path.elementAt(i).type == QPainterPath::CurveToElement)
+ currentPoint = path.elementAt(i+2);
+ else
+ currentPoint = path.elementAt(i);
+
+ if (i > 0 && comparePoints(m_points.at(lastMoveTo), currentPoint))
+ current = lastMoveTo;
+ else
+ m_points << currentPoint;
+
+ switch (path.elementAt(i).type) {
+ case QPainterPath::MoveToElement:
+ if (hasMoveTo && last != lastMoveTo && !comparePoints(m_points.at(last), m_points.at(lastMoveTo)))
+ m_segments << Segment(m_pathId, last, lastMoveTo);
+ hasMoveTo = true;
+ last = lastMoveTo = current;
+ break;
+ case QPainterPath::LineToElement:
+ m_segments << Segment(m_pathId, last, current);
+ last = current;
+ break;
+ case QPainterPath::CurveToElement:
+ {
+ QBezier bezier = QBezier::fromPoints(m_points.at(last), path.elementAt(i), path.elementAt(i+1), path.elementAt(i+2));
+ if (isLine(bezier)) {
+ m_segments << Segment(m_pathId, last, current);
+ } else {
+ QRectF bounds = bezier.bounds();
+
+ // threshold based on similar algorithm as in qtriangulatingstroker.cpp
+ int threshold = qMin<float>(64, qMax(bounds.width(), bounds.height()) * (2 * qreal(3.14) / 6));
+
+ if (threshold < 3) threshold = 3;
+ qreal one_over_threshold_minus_1 = qreal(1) / (threshold - 1);
+
+ for (int t = 1; t < threshold - 1; ++t) {
+ currentPoint = bezier.pointAt(t * one_over_threshold_minus_1);
+
+ int index = m_points.size();
+ m_segments << Segment(m_pathId, last, index);
+ last = index;
+
+ m_points << currentPoint;
+ }
+
+ m_segments << Segment(m_pathId, last, current);
+ }
+ }
+ last = current;
+ i += 2;
+ break;
+ default:
+ Q_ASSERT(false);
+ break;
+ }
+ }
+
+ if (hasMoveTo && last != lastMoveTo && !comparePoints(m_points.at(last), m_points.at(lastMoveTo)))
+ m_segments << Segment(m_pathId, last, lastMoveTo);
+
+ for (int i = firstSegment; i < m_segments.size(); ++i) {
+ const QLineF line = lineAt(i);
+
+ qreal x1 = line.p1().x();
+ qreal y1 = line.p1().y();
+ qreal x2 = line.p2().x();
+ qreal y2 = line.p2().y();
+
+ if (x2 < x1)
+ qSwap(x1, x2);
+ if (y2 < y1)
+ qSwap(y1, y2);
+
+ m_segments.at(i).bounds = QRectF(x1, y1, x2 - x1, y2 - y1);
+ }
+
+ ++m_pathId;
+}
+
+qreal QWingedEdge::delta(int vertex, int a, int b) const
+{
+ const QPathEdge *ap = edge(a);
+ const QPathEdge *bp = edge(b);
+
+ double a_angle = ap->angle;
+ double b_angle = bp->angle;
+
+ if (vertex == ap->second)
+ a_angle = ap->invAngle;
+
+ if (vertex == bp->second)
+ b_angle = bp->invAngle;
+
+ double result = b_angle - a_angle;
+
+ if (result >= 128.)
+ return result - 128.;
+ else if (result < 0)
+ return result + 128.;
+ else
+ return result;
+}
+
+static inline QPointF midPoint(const QWingedEdge &list, int ei)
+{
+ const QPathEdge *ep = list.edge(ei);
+ Q_ASSERT(ep);
+
+ const QPointF a = *list.vertex(ep->first);
+ const QPointF b = *list.vertex(ep->second);
+ return a + 0.5 * (b - a);
+}
+
+QWingedEdge::TraversalStatus QWingedEdge::findInsertStatus(int vi, int ei) const
+{
+ const QPathVertex *vp = vertex(vi);
+
+ Q_ASSERT(vp);
+ Q_ASSERT(ei >= 0);
+ Q_ASSERT(vp->edge >= 0);
+
+ int position = vp->edge;
+ qreal d = 128.;
+
+ TraversalStatus status;
+ status.direction = edge(vp->edge)->directionTo(vi);
+ status.traversal = QPathEdge::RightTraversal;
+ status.edge = vp->edge;
+
+#ifdef QDEBUG_CLIPPER
+ const QPathEdge *ep = edge(ei);
+ qDebug() << "Finding insert status for edge" << ei << "at vertex" << QPointF(*vp) << ", angles: " << ep->angle << ep->invAngle;
+#endif
+
+ do {
+ status = next(status);
+ status.flip();
+
+ Q_ASSERT(edge(status.edge)->vertex(status.direction) == vi);
+ qreal d2 = delta(vi, ei, status.edge);
+
+#ifdef QDEBUG_CLIPPER
+ const QPathEdge *op = edge(status.edge);
+ qDebug() << "Delta to edge" << status.edge << d2 << ", angles: " << op->angle << op->invAngle;
+#endif
+
+ if (d2 < d) {
+ position = status.edge;
+ d = d2;
+ }
+ } while (status.edge != vp->edge);
+
+ status.traversal = QPathEdge::LeftTraversal;
+ status.direction = QPathEdge::Forward;
+ status.edge = position;
+
+ if (edge(status.edge)->vertex(status.direction) != vi)
+ status.flip();
+
+#ifdef QDEBUG_CLIPPER
+ qDebug() << "Inserting edge" << ei << "to" << (status.traversal == QPathEdge::LeftTraversal ? "left" : "right") << "of edge" << status.edge;
+#endif
+
+ Q_ASSERT(edge(status.edge)->vertex(status.direction) == vi);
+
+ return status;
+}
+
+void QWingedEdge::removeEdge(int ei)
+{
+ QPathEdge *ep = edge(ei);
+
+ TraversalStatus status;
+ status.direction = QPathEdge::Forward;
+ status.traversal = QPathEdge::RightTraversal;
+ status.edge = ei;
+
+ TraversalStatus forwardRight = next(status);
+ forwardRight.flipDirection();
+
+ status.traversal = QPathEdge::LeftTraversal;
+ TraversalStatus forwardLeft = next(status);
+ forwardLeft.flipDirection();
+
+ status.direction = QPathEdge::Backward;
+ TraversalStatus backwardLeft = next(status);
+ backwardLeft.flipDirection();
+
+ status.traversal = QPathEdge::RightTraversal;
+ TraversalStatus backwardRight = next(status);
+ backwardRight.flipDirection();
+
+ edge(forwardRight.edge)->setNext(forwardRight.traversal, forwardRight.direction, forwardLeft.edge);
+ edge(forwardLeft.edge)->setNext(forwardLeft.traversal, forwardLeft.direction, forwardRight.edge);
+
+ edge(backwardRight.edge)->setNext(backwardRight.traversal, backwardRight.direction, backwardLeft.edge);
+ edge(backwardLeft.edge)->setNext(backwardLeft.traversal, backwardLeft.direction, backwardRight.edge);
+
+ ep->setNext(QPathEdge::Forward, ei);
+ ep->setNext(QPathEdge::Backward, ei);
+
+ QPathVertex *a = vertex(ep->first);
+ QPathVertex *b = vertex(ep->second);
+
+ a->edge = backwardRight.edge;
+ b->edge = forwardRight.edge;
+}
+
+static int commonEdge(const QWingedEdge &list, int a, int b)
+{
+ const QPathVertex *ap = list.vertex(a);
+ Q_ASSERT(ap);
+
+ const QPathVertex *bp = list.vertex(b);
+ Q_ASSERT(bp);
+
+ if (ap->edge < 0 || bp->edge < 0)
+ return -1;
+
+ QWingedEdge::TraversalStatus status;
+ status.edge = ap->edge;
+ status.direction = list.edge(status.edge)->directionTo(a);
+ status.traversal = QPathEdge::RightTraversal;
+
+ do {
+ const QPathEdge *ep = list.edge(status.edge);
+
+ if ((ep->first == a && ep->second == b)
+ || (ep->first == b && ep->second == a))
+ return status.edge;
+
+ status = list.next(status);
+ status.flip();
+ } while (status.edge != ap->edge);
+
+ return -1;
+}
+
+static double computeAngle(const QPointF &v)
+{
+#if 1
+ if (v.x() == 0) {
+ return v.y() <= 0 ? 0 : 64.;
+ } else if (v.y() == 0) {
+ return v.x() <= 0 ? 32. : 96.;
+ }
+
+ double vx = v.x();
+ double vy = v.y();
+ normalize(vx, vy);
+ if (vy < 0) {
+ if (vx < 0) { // 0 - 32
+ return -32. * vx;
+ } else { // 96 - 128
+ return 128. - 32. * vx;
+ }
+ } else { // 32 - 96
+ return 64. + 32. * vx;
+ }
+#else
+ // doesn't seem to be robust enough
+ return qAtan2(v.x(), v.y()) + Q_PI;
+#endif
+}
+
+int QWingedEdge::addEdge(const QPointF &a, const QPointF &b)
+{
+ int fi = insert(a);
+ int si = insert(b);
+
+ return addEdge(fi, si);
+}
+
+int QWingedEdge::addEdge(int fi, int si)
+{
+ if (fi == si)
+ return -1;
+
+ int common = commonEdge(*this, fi, si);
+ if (common >= 0)
+ return common;
+
+ m_edges << QPathEdge(fi, si);
+
+ int ei = m_edges.size() - 1;
+
+ QPathVertex *fp = vertex(fi);
+ QPathVertex *sp = vertex(si);
+
+ QPathEdge *ep = edge(ei);
+
+ const QPointF tangent = QPointF(*sp) - QPointF(*fp);
+ ep->angle = computeAngle(tangent);
+ ep->invAngle = ep->angle + 64;
+ if (ep->invAngle >= 128)
+ ep->invAngle -= 128;
+
+ QPathVertex *vertices[2] = { fp, sp };
+ QPathEdge::Direction dirs[2] = { QPathEdge::Backward, QPathEdge::Forward };
+
+#ifdef QDEBUG_CLIPPER
+ printf("** Adding edge %d / vertices: %.07f %.07f, %.07f %.07f\n", ei, fp->x, fp->y, sp->x, sp->y);
+#endif
+
+ for (int i = 0; i < 2; ++i) {
+ QPathVertex *vp = vertices[i];
+ if (vp->edge < 0) {
+ vp->edge = ei;
+ ep->setNext(dirs[i], ei);
+ } else {
+ int vi = ep->vertex(dirs[i]);
+ Q_ASSERT(vertex(vi) == vertices[i]);
+
+ TraversalStatus os = findInsertStatus(vi, ei);
+ QPathEdge *op = edge(os.edge);
+
+ Q_ASSERT(vertex(op->vertex(os.direction)) == vertices[i]);
+
+ TraversalStatus ns = next(os);
+ ns.flipDirection();
+ QPathEdge *np = edge(ns.edge);
+
+ op->setNext(os.traversal, os.direction, ei);
+ np->setNext(ns.traversal, ns.direction, ei);
+
+ int oe = os.edge;
+ int ne = ns.edge;
+
+ os = next(os);
+ ns = next(ns);
+
+ os.flipDirection();
+ ns.flipDirection();
+
+ Q_ASSERT(os.edge == ei);
+ Q_ASSERT(ns.edge == ei);
+
+ ep->setNext(os.traversal, os.direction, oe);
+ ep->setNext(ns.traversal, ns.direction, ne);
+ }
+ }
+
+ Q_ASSERT(ep->next(QPathEdge::RightTraversal, QPathEdge::Forward) >= 0);
+ Q_ASSERT(ep->next(QPathEdge::RightTraversal, QPathEdge::Backward) >= 0);
+ Q_ASSERT(ep->next(QPathEdge::LeftTraversal, QPathEdge::Forward) >= 0);
+ Q_ASSERT(ep->next(QPathEdge::LeftTraversal, QPathEdge::Backward) >= 0);
+
+ return ei;
+}
+
+int QWingedEdge::insert(const QPathVertex &vertex)
+{
+ if (!m_vertices.isEmpty()) {
+ const QPathVertex &last = m_vertices.last();
+ if (vertex.x == last.x && vertex.y == last.y)
+ return m_vertices.size() - 1;
+
+ for (int i = 0; i < m_vertices.size(); ++i) {
+ const QPathVertex &v = m_vertices.at(i);
+ if (qFuzzyCompare(v.x, vertex.x) && qFuzzyCompare(v.y, vertex.y)) {
+ return i;
+ }
+ }
+ }
+
+ m_vertices << vertex;
+ return m_vertices.size() - 1;
+}
+
+static void addLineTo(QPainterPath &path, const QPointF &point)
+{
+ const int elementCount = path.elementCount();
+ if (elementCount >= 2) {
+ const QPainterPath::Element &middle = path.elementAt(elementCount - 1);
+ if (middle.type == QPainterPath::LineToElement) {
+ const QPointF first = path.elementAt(elementCount - 2);
+ const QPointF d1 = point - first;
+ const QPointF d2 = middle - first;
+
+ const QPointF p(-d1.y(), d1.x());
+
+ if (qFuzzyIsNull(dot(p, d2))) {
+ path.setElementPositionAt(elementCount - 1, point.x(), point.y());
+ return;
+ }
+ }
+ }
+
+ path.lineTo(point);
+}
+
+static void add(QPainterPath &path, const QWingedEdge &list, int edge, QPathEdge::Traversal traversal)
+{
+ QWingedEdge::TraversalStatus status;
+ status.edge = edge;
+ status.traversal = traversal;
+ status.direction = QPathEdge::Forward;
+
+ path.moveTo(*list.vertex(list.edge(edge)->first));
+
+ do {
+ const QPathEdge *ep = list.edge(status.edge);
+
+ addLineTo(path, *list.vertex(ep->vertex(status.direction)));
+
+ if (status.traversal == QPathEdge::LeftTraversal)
+ ep->flag &= ~16;
+ else
+ ep->flag &= ~32;
+
+ status = list.next(status);
+ } while (status.edge != edge);
+}
+
+void QWingedEdge::simplify()
+{
+ for (int i = 0; i < edgeCount(); ++i) {
+ const QPathEdge *ep = edge(i);
+
+ // if both sides are part of the inside then we can collapse the edge
+ int flag = 0x3 << 4;
+ if ((ep->flag & flag) == flag) {
+ removeEdge(i);
+
+ ep->flag &= ~flag;
+ }
+ }
+}
+
+QPainterPath QWingedEdge::toPath() const
+{
+ QPainterPath path;
+
+ for (int i = 0; i < edgeCount(); ++i) {
+ const QPathEdge *ep = edge(i);
+
+ if (ep->flag & 16) {
+ add(path, *this, i, QPathEdge::LeftTraversal);
+ }
+
+ if (ep->flag & 32)
+ add(path, *this, i, QPathEdge::RightTraversal);
+ }
+
+ return path;
+}
+
+bool QPathClipper::intersect()
+{
+ if (subjectPath == clipPath)
+ return true;
+
+ QRectF r1 = subjectPath.controlPointRect();
+ QRectF r2 = clipPath.controlPointRect();
+ if (qMax(r1.x(), r2.x()) > qMin(r1.x() + r1.width(), r2.x() + r2.width()) ||
+ qMax(r1.y(), r2.y()) > qMin(r1.y() + r1.height(), r2.y() + r2.height())) {
+ // no way we could intersect
+ return false;
+ }
+
+ bool subjectIsRect = pathToRect(subjectPath);
+ bool clipIsRect = pathToRect(clipPath);
+
+ if (subjectIsRect && clipIsRect)
+ return true;
+ else if (subjectIsRect)
+ return clipPath.intersects(r1);
+ else if (clipIsRect)
+ return subjectPath.intersects(r2);
+
+ QPathSegments a(subjectPath.elementCount());
+ a.setPath(subjectPath);
+ QPathSegments b(clipPath.elementCount());
+ b.setPath(clipPath);
+
+ QIntersectionFinder finder;
+ if (finder.hasIntersections(a, b))
+ return true;
+
+ for (int i = 0; i < clipPath.elementCount(); ++i) {
+ if (clipPath.elementAt(i).type == QPainterPath::MoveToElement) {
+ const QPointF point = clipPath.elementAt(i);
+ if (r1.contains(point) && subjectPath.contains(point))
+ return true;
+ }
+ }
+
+ for (int i = 0; i < subjectPath.elementCount(); ++i) {
+ if (subjectPath.elementAt(i).type == QPainterPath::MoveToElement) {
+ const QPointF point = subjectPath.elementAt(i);
+ if (r2.contains(point) && clipPath.contains(point))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool QPathClipper::contains()
+{
+ if (subjectPath == clipPath)
+ return false;
+
+ QRectF r1 = subjectPath.controlPointRect();
+ QRectF r2 = clipPath.controlPointRect();
+ if (qMax(r1.x(), r2.x()) > qMin(r1.x() + r1.width(), r2.x() + r2.width()) ||
+ qMax(r1.y(), r2.y()) > qMin(r1.y() + r1.height(), r2.y() + r2.height())) {
+ // no intersection -> not contained
+ return false;
+ }
+
+ bool clipIsRect = pathToRect(clipPath);
+ if (clipIsRect)
+ return subjectPath.contains(r2);
+
+ QPathSegments a(subjectPath.elementCount());
+ a.setPath(subjectPath);
+ QPathSegments b(clipPath.elementCount());
+ b.setPath(clipPath);
+
+ QIntersectionFinder finder;
+ if (finder.hasIntersections(a, b))
+ return false;
+
+ for (int i = 0; i < clipPath.elementCount(); ++i) {
+ if (clipPath.elementAt(i).type == QPainterPath::MoveToElement) {
+ const QPointF point = clipPath.elementAt(i);
+ if (!r1.contains(point) || !subjectPath.contains(point))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+QPathClipper::QPathClipper(const QPainterPath &subject,
+ const QPainterPath &clip)
+ : subjectPath(subject)
+ , clipPath(clip)
+{
+ aMask = subjectPath.fillRule() == Qt::WindingFill ? ~0x0 : 0x1;
+ bMask = clipPath.fillRule() == Qt::WindingFill ? ~0x0 : 0x1;
+}
+
+template <typename Iterator, typename Equality>
+Iterator qRemoveDuplicates(Iterator begin, Iterator end, Equality eq)
+{
+ if (begin == end)
+ return end;
+
+ Iterator last = begin;
+ ++begin;
+ Iterator insert = begin;
+ for (Iterator it = begin; it != end; ++it) {
+ if (!eq(*it, *last)) {
+ *insert++ = *it;
+ last = it;
+ }
+ }
+
+ return insert;
+}
+
+static void clear(QWingedEdge& list, int edge, QPathEdge::Traversal traversal)
+{
+ QWingedEdge::TraversalStatus status;
+ status.edge = edge;
+ status.traversal = traversal;
+ status.direction = QPathEdge::Forward;
+
+ do {
+ if (status.traversal == QPathEdge::LeftTraversal)
+ list.edge(status.edge)->flag |= 1;
+ else
+ list.edge(status.edge)->flag |= 2;
+
+ status = list.next(status);
+ } while (status.edge != edge);
+}
+
+template <typename InputIterator>
+InputIterator qFuzzyFind(InputIterator first, InputIterator last, qreal val)
+{
+ while (first != last && !QT_PREPEND_NAMESPACE(qFuzzyCompare)(qreal(*first), qreal(val)))
+ ++first;
+ return first;
+}
+
+static bool fuzzyCompare(qreal a, qreal b)
+{
+ return qFuzzyCompare(a, b);
+}
+
+bool QPathClipper::pathToRect(const QPainterPath &path, QRectF *rect)
+{
+ if (path.elementCount() != 5)
+ return false;
+
+ const bool mightBeRect = path.elementAt(0).isMoveTo()
+ && path.elementAt(1).isLineTo()
+ && path.elementAt(2).isLineTo()
+ && path.elementAt(3).isLineTo()
+ && path.elementAt(4).isLineTo();
+
+ if (!mightBeRect)
+ return false;
+
+ const qreal x1 = path.elementAt(0).x;
+ const qreal y1 = path.elementAt(0).y;
+
+ const qreal x2 = path.elementAt(1).x;
+ const qreal y2 = path.elementAt(2).y;
+
+ if (path.elementAt(1).y != y1)
+ return false;
+
+ if (path.elementAt(2).x != x2)
+ return false;
+
+ if (path.elementAt(3).x != x1 || path.elementAt(3).y != y2)
+ return false;
+
+ if (path.elementAt(4).x != x1 || path.elementAt(4).y != y1)
+ return false;
+
+ if (rect)
+ rect->setCoords(x1, y1, x2, y2);
+
+ return true;
+}
+
+
+QPainterPath QPathClipper::clip(Operation operation)
+{
+ op = operation;
+
+ if (op != Simplify) {
+ if (subjectPath == clipPath)
+ return op == BoolSub ? QPainterPath() : subjectPath;
+
+ bool subjectIsRect = pathToRect(subjectPath, 0);
+ bool clipIsRect = pathToRect(clipPath, 0);
+
+ const QRectF clipBounds = clipPath.boundingRect();
+ const QRectF subjectBounds = subjectPath.boundingRect();
+
+ if (!clipBounds.intersects(subjectBounds)) {
+ switch (op) {
+ case BoolSub:
+ return subjectPath;
+ case BoolAnd:
+ return QPainterPath();
+ case BoolOr: {
+ QPainterPath result = subjectPath;
+ if (result.fillRule() == clipPath.fillRule()) {
+ result.addPath(clipPath);
+ } else if (result.fillRule() == Qt::WindingFill) {
+ result = result.simplified();
+ result.addPath(clipPath);
+ } else {
+ result.addPath(clipPath.simplified());
+ }
+ return result;
+ }
+ default:
+ break;
+ }
+ }
+
+ if (clipBounds.contains(subjectBounds)) {
+ if (clipIsRect) {
+ switch (op) {
+ case BoolSub:
+ return QPainterPath();
+ case BoolAnd:
+ return subjectPath;
+ case BoolOr:
+ return clipPath;
+ default:
+ break;
+ }
+ }
+ } else if (subjectBounds.contains(clipBounds)) {
+ if (subjectIsRect) {
+ switch (op) {
+ case BoolSub:
+ if (clipPath.fillRule() == Qt::OddEvenFill) {
+ QPainterPath result = clipPath;
+ result.addRect(subjectBounds);
+ return result;
+ } else {
+ QPainterPath result = clipPath.simplified();
+ result.addRect(subjectBounds);
+ return result;
+ }
+ break;
+ case BoolAnd:
+ return clipPath;
+ case BoolOr:
+ return subjectPath;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (op == BoolAnd) {
+ if (subjectIsRect)
+ return intersect(clipPath, subjectBounds);
+ else if (clipIsRect)
+ return intersect(subjectPath, clipBounds);
+ }
+ }
+
+ QWingedEdge list(subjectPath, clipPath);
+
+ doClip(list, ClipMode);
+
+ QPainterPath path = list.toPath();
+ return path;
+}
+
+bool QPathClipper::doClip(QWingedEdge &list, ClipperMode mode)
+{
+ QVector<qreal> y_coords;
+ y_coords.reserve(list.vertexCount());
+ for (int i = 0; i < list.vertexCount(); ++i)
+ y_coords << list.vertex(i)->y;
+
+ qSort(y_coords.begin(), y_coords.end());
+ y_coords.resize(qRemoveDuplicates(y_coords.begin(), y_coords.end(), fuzzyCompare) - y_coords.begin());
+
+#ifdef QDEBUG_CLIPPER
+ printf("sorted y coords:\n");
+ for (int i = 0; i < y_coords.size(); ++i) {
+ printf("%.9f\n", y_coords[i]);
+ }
+#endif
+
+ bool found;
+ do {
+ found = false;
+ int index = 0;
+ qreal maxHeight = 0;
+ for (int i = 0; i < list.edgeCount(); ++i) {
+ QPathEdge *edge = list.edge(i);
+
+ // have both sides of this edge already been handled?
+ if ((edge->flag & 0x3) == 0x3)
+ continue;
+
+ QPathVertex *a = list.vertex(edge->first);
+ QPathVertex *b = list.vertex(edge->second);
+
+ if (qFuzzyCompare(a->y, b->y))
+ continue;
+
+ found = true;
+
+ qreal height = qAbs(a->y - b->y);
+ if (height > maxHeight) {
+ index = i;
+ maxHeight = height;
+ }
+ }
+
+ if (found) {
+ QPathEdge *edge = list.edge(index);
+
+ QPathVertex *a = list.vertex(edge->first);
+ QPathVertex *b = list.vertex(edge->second);
+
+ // FIXME: this can be optimized by using binary search
+ const int first = qFuzzyFind(y_coords.begin(), y_coords.end(), qMin(a->y, b->y)) - y_coords.begin();
+ const int last = qFuzzyFind(y_coords.begin() + first, y_coords.end(), qMax(a->y, b->y)) - y_coords.begin();
+
+ Q_ASSERT(first < y_coords.size() - 1);
+ Q_ASSERT(last < y_coords.size());
+
+ qreal bestY = 0.5 * (y_coords[first] + y_coords[first+1]);
+ qreal biggestGap = y_coords[first+1] - y_coords[first];
+
+ for (int i = first + 1; i < last; ++i) {
+ qreal gap = y_coords[i+1] - y_coords[i];
+
+ if (gap > biggestGap) {
+ bestY = 0.5 * (y_coords[i] + y_coords[i+1]);
+ biggestGap = gap;
+ }
+ }
+
+#ifdef QDEBUG_CLIPPER
+ printf("y: %.9f, gap: %.9f\n", bestY, biggestGap);
+#endif
+
+ if (handleCrossingEdges(list, bestY, mode) && mode == CheckMode)
+ return true;
+
+ edge->flag |= 0x3;
+ }
+ } while (found);
+
+ if (mode == ClipMode)
+ list.simplify();
+
+ return false;
+}
+
+static void traverse(QWingedEdge &list, int edge, QPathEdge::Traversal traversal)
+{
+ QWingedEdge::TraversalStatus status;
+ status.edge = edge;
+ status.traversal = traversal;
+ status.direction = QPathEdge::Forward;
+
+ do {
+ int flag = status.traversal == QPathEdge::LeftTraversal ? 1 : 2;
+
+ QPathEdge *ep = list.edge(status.edge);
+
+ ep->flag |= (flag | (flag << 4));
+
+#ifdef QDEBUG_CLIPPER
+ qDebug() << "traverse: adding edge " << status.edge << ", mask:" << (flag << 4) <<ep->flag;
+#endif
+
+ status = list.next(status);
+ } while (status.edge != edge);
+}
+
+struct QCrossingEdge
+{
+ int edge;
+ qreal x;
+
+ bool operator<(const QCrossingEdge &edge) const
+ {
+ return x < edge.x;
+ }
+};
+
+static bool bool_op(bool a, bool b, QPathClipper::Operation op)
+{
+ switch (op) {
+ case QPathClipper::BoolAnd:
+ return a && b;
+ case QPathClipper::BoolOr: // fall-through
+ case QPathClipper::Simplify:
+ return a || b;
+ case QPathClipper::BoolSub:
+ return a && !b;
+ default:
+ Q_ASSERT(false);
+ return false;
+ }
+}
+
+bool QWingedEdge::isInside(qreal x, qreal y) const
+{
+ int winding = 0;
+ for (int i = 0; i < edgeCount(); ++i) {
+ const QPathEdge *ep = edge(i);
+
+ // left xor right
+ int w = ((ep->flag >> 4) ^ (ep->flag >> 5)) & 1;
+
+ if (!w)
+ continue;
+
+ QPointF a = *vertex(ep->first);
+ QPointF b = *vertex(ep->second);
+
+ if ((a.y() < y && b.y() > y) || (a.y() > y && b.y() < y)) {
+ qreal intersectionX = a.x() + (b.x() - a.x()) * (y - a.y()) / (b.y() - a.y());
+
+ if (intersectionX > x)
+ winding += w;
+ }
+ }
+
+ return winding & 1;
+}
+
+static QVector<QCrossingEdge> findCrossings(const QWingedEdge &list, qreal y)
+{
+ QVector<QCrossingEdge> crossings;
+ for (int i = 0; i < list.edgeCount(); ++i) {
+ const QPathEdge *edge = list.edge(i);
+ QPointF a = *list.vertex(edge->first);
+ QPointF b = *list.vertex(edge->second);
+
+ if ((a.y() < y && b.y() > y) || (a.y() > y && b.y() < y)) {
+ const qreal intersection = a.x() + (b.x() - a.x()) * (y - a.y()) / (b.y() - a.y());
+ const QCrossingEdge edge = { i, intersection };
+ crossings << edge;
+ }
+ }
+ return crossings;
+}
+
+bool QPathClipper::handleCrossingEdges(QWingedEdge &list, qreal y, ClipperMode mode)
+{
+ QVector<QCrossingEdge> crossings = findCrossings(list, y);
+
+ Q_ASSERT(!crossings.isEmpty());
+ qSort(crossings.begin(), crossings.end());
+
+ int windingA = 0;
+ int windingB = 0;
+
+ int windingD = 0;
+
+#ifdef QDEBUG_CLIPPER
+ qDebug() << "crossings:" << crossings.size();
+#endif
+ for (int i = 0; i < crossings.size() - 1; ++i) {
+ int ei = crossings.at(i).edge;
+ const QPathEdge *edge = list.edge(ei);
+
+ windingA += edge->windingA;
+ windingB += edge->windingB;
+
+ const bool hasLeft = (edge->flag >> 4) & 1;
+ const bool hasRight = (edge->flag >> 4) & 2;
+
+ windingD += hasLeft ^ hasRight;
+
+ const bool inA = (windingA & aMask) != 0;
+ const bool inB = (windingB & bMask) != 0;
+ const bool inD = (windingD & 0x1) != 0;
+
+ const bool inside = bool_op(inA, inB, op);
+ const bool add = inD ^ inside;
+
+#ifdef QDEBUG_CLIPPER
+ printf("y %f, x %f, inA: %d, inB: %d, inD: %d, inside: %d, flag: %x, bezier: %p, edge: %d\n", y, crossings.at(i).x, inA, inB, inD, inside, edge->flag, edge->bezier, ei);
+#endif
+
+ if (add) {
+ if (mode == CheckMode)
+ return true;
+
+ qreal y0 = list.vertex(edge->first)->y;
+ qreal y1 = list.vertex(edge->second)->y;
+
+ if (y0 < y1) {
+ if (!(edge->flag & 1))
+ traverse(list, ei, QPathEdge::LeftTraversal);
+
+ if (!(edge->flag & 2))
+ clear(list, ei, QPathEdge::RightTraversal);
+ } else {
+ if (!(edge->flag & 1))
+ clear(list, ei, QPathEdge::LeftTraversal);
+
+ if (!(edge->flag & 2))
+ traverse(list, ei, QPathEdge::RightTraversal);
+ }
+
+ ++windingD;
+ } else {
+ if (!(edge->flag & 1))
+ clear(list, ei, QPathEdge::LeftTraversal);
+
+ if (!(edge->flag & 2))
+ clear(list, ei, QPathEdge::RightTraversal);
+ }
+ }
+
+ return false;
+}
+
+namespace {
+
+QList<QPainterPath> toSubpaths(const QPainterPath &path)
+{
+
+ QList<QPainterPath> subpaths;
+ if (path.isEmpty())
+ return subpaths;
+
+ QPainterPath current;
+ for (int i = 0; i < path.elementCount(); ++i) {
+ const QPainterPath::Element &e = path.elementAt(i);
+ switch (e.type) {
+ case QPainterPath::MoveToElement:
+ if (current.elementCount() > 1)
+ subpaths += current;
+ current = QPainterPath();
+ current.moveTo(e);
+ break;
+ case QPainterPath::LineToElement:
+ current.lineTo(e);
+ break;
+ case QPainterPath::CurveToElement: {
+ current.cubicTo(e, path.elementAt(i + 1), path.elementAt(i + 2));
+ i+=2;
+ break;
+ }
+ case QPainterPath::CurveToDataElement:
+ Q_ASSERT(!"toSubpaths(), bad element type");
+ break;
+ }
+ }
+
+ if (current.elementCount() > 1)
+ subpaths << current;
+
+ return subpaths;
+}
+
+enum Edge
+{
+ Left, Top, Right, Bottom
+};
+
+static bool isVertical(Edge edge)
+{
+ return edge == Left || edge == Right;
+}
+
+template <Edge edge>
+bool compare(const QPointF &p, qreal t)
+{
+ switch (edge)
+ {
+ case Left:
+ return p.x() < t;
+ case Right:
+ return p.x() > t;
+ case Top:
+ return p.y() < t;
+ default:
+ return p.y() > t;
+ }
+}
+
+template <Edge edge>
+QPointF intersectLine(const QPointF &a, const QPointF &b, qreal t)
+{
+ QLineF line(a, b);
+ switch (edge) {
+ case Left: // fall-through
+ case Right:
+ return line.pointAt((t - a.x()) / (b.x() - a.x()));
+ default:
+ return line.pointAt((t - a.y()) / (b.y() - a.y()));
+ }
+}
+
+void addLine(QPainterPath &path, const QLineF &line)
+{
+ if (path.elementCount() > 0)
+ path.lineTo(line.p1());
+ else
+ path.moveTo(line.p1());
+
+ path.lineTo(line.p2());
+}
+
+template <Edge edge>
+void clipLine(const QPointF &a, const QPointF &b, qreal t, QPainterPath &result)
+{
+ bool outA = compare<edge>(a, t);
+ bool outB = compare<edge>(b, t);
+ if (outA && outB)
+ return;
+
+ if (outA)
+ addLine(result, QLineF(intersectLine<edge>(a, b, t), b));
+ else if(outB)
+ addLine(result, QLineF(a, intersectLine<edge>(a, b, t)));
+ else
+ addLine(result, QLineF(a, b));
+}
+
+void addBezier(QPainterPath &path, const QBezier &bezier)
+{
+ if (path.elementCount() > 0)
+ path.lineTo(bezier.pt1());
+ else
+ path.moveTo(bezier.pt1());
+
+ path.cubicTo(bezier.pt2(), bezier.pt3(), bezier.pt4());
+}
+
+template <Edge edge>
+void clipBezier(const QPointF &a, const QPointF &b, const QPointF &c, const QPointF &d, qreal t, QPainterPath &result)
+{
+ QBezier bezier = QBezier::fromPoints(a, b, c, d);
+
+ bool outA = compare<edge>(a, t);
+ bool outB = compare<edge>(b, t);
+ bool outC = compare<edge>(c, t);
+ bool outD = compare<edge>(d, t);
+
+ int outCount = int(outA) + int(outB) + int(outC) + int(outD);
+
+ if (outCount == 4)
+ return;
+
+ if (outCount == 0) {
+ addBezier(result, bezier);
+ return;
+ }
+
+ QTransform flip = isVertical(edge) ? QTransform(0, 1, 1, 0, 0, 0) : QTransform();
+ QBezier unflipped = bezier;
+ QBezier flipped = bezier.mapBy(flip);
+
+ qreal t0 = 0, t1 = 1;
+ int stationary = flipped.stationaryYPoints(t0, t1);
+
+ qreal segments[4];
+ QPointF points[4];
+ points[0] = unflipped.pt1();
+ segments[0] = 0;
+
+ int segmentCount = 0;
+ if (stationary > 0) {
+ ++segmentCount;
+ segments[segmentCount] = t0;
+ points[segmentCount] = unflipped.pointAt(t0);
+ }
+ if (stationary > 1) {
+ ++segmentCount;
+ segments[segmentCount] = t1;
+ points[segmentCount] = unflipped.pointAt(t1);
+ }
+ ++segmentCount;
+ segments[segmentCount] = 1;
+ points[segmentCount] = unflipped.pt4();
+
+ qreal lastIntersection = 0;
+ for (int i = 0; i < segmentCount; ++i) {
+ outA = compare<edge>(points[i], t);
+ outB = compare<edge>(points[i+1], t);
+
+ if (outA != outB) {
+ qreal intersection = flipped.tForY(segments[i], segments[i+1], t);
+
+ if (outB)
+ addBezier(result, unflipped.getSubRange(lastIntersection, intersection));
+
+ lastIntersection = intersection;
+ }
+ }
+
+ if (!outB)
+ addBezier(result, unflipped.getSubRange(lastIntersection, 1));
+}
+
+// clips a single subpath against a single edge
+template <Edge edge>
+QPainterPath clip(const QPainterPath &path, qreal t)
+{
+ QPainterPath result;
+ for (int i = 1; i < path.elementCount(); ++i) {
+ const QPainterPath::Element &element = path.elementAt(i);
+ Q_ASSERT(!element.isMoveTo());
+ if (element.isLineTo()) {
+ clipLine<edge>(path.elementAt(i-1), path.elementAt(i), t, result);
+ } else {
+ clipBezier<edge>(path.elementAt(i-1), path.elementAt(i), path.elementAt(i+1), path.elementAt(i+2), t, result);
+ i += 2;
+ }
+ }
+
+ int last = path.elementCount() - 1;
+ if (QPointF(path.elementAt(last)) != QPointF(path.elementAt(0)))
+ clipLine<edge>(path.elementAt(last), path.elementAt(0), t, result);
+
+ return result;
+}
+
+QPainterPath intersectPath(const QPainterPath &path, const QRectF &rect)
+{
+ QList<QPainterPath> subpaths = toSubpaths(path);
+
+ QPainterPath result;
+ result.setFillRule(path.fillRule());
+ for (int i = 0; i < subpaths.size(); ++i) {
+ QPainterPath subPath = subpaths.at(i);
+ QRectF bounds = subPath.boundingRect();
+ if (bounds.intersects(rect)) {
+ if (bounds.left() < rect.left())
+ subPath = clip<Left>(subPath, rect.left());
+ if (bounds.right() > rect.right())
+ subPath = clip<Right>(subPath, rect.right());
+
+ bounds = subPath.boundingRect();
+
+ if (bounds.top() < rect.top())
+ subPath = clip<Top>(subPath, rect.top());
+ if (bounds.bottom() > rect.bottom())
+ subPath = clip<Bottom>(subPath, rect.bottom());
+
+ if (subPath.elementCount() > 1)
+ result.addPath(subPath);
+ }
+ }
+ return result;
+}
+
+}
+
+QPainterPath QPathClipper::intersect(const QPainterPath &path, const QRectF &rect)
+{
+ return intersectPath(path, rect);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qpathclipper_p.h b/src/gui/painting/qpathclipper_p.h
new file mode 100644
index 0000000000..226c55d29e
--- /dev/null
+++ b/src/gui/painting/qpathclipper_p.h
@@ -0,0 +1,494 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPATHCLIPPER_P_H
+#define QPATHCLIPPER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/qpainterpath.h>
+#include <QtCore/qlist.h>
+
+#include <private/qbezier_p.h>
+#include <private/qdatabuffer_p.h>
+#include <stdio.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QWingedEdge;
+
+class Q_AUTOTEST_EXPORT QPathClipper
+{
+public:
+ enum Operation {
+ BoolAnd,
+ BoolOr,
+ BoolSub,
+ Simplify
+ };
+public:
+ QPathClipper(const QPainterPath &subject,
+ const QPainterPath &clip);
+
+ QPainterPath clip(Operation op = BoolAnd);
+
+ bool intersect();
+ bool contains();
+
+ static bool pathToRect(const QPainterPath &path, QRectF *rect = 0);
+ static QPainterPath intersect(const QPainterPath &path, const QRectF &rect);
+
+private:
+ Q_DISABLE_COPY(QPathClipper)
+
+ enum ClipperMode {
+ ClipMode, // do the full clip
+ CheckMode // for contains/intersects (only interested in whether the result path is non-empty)
+ };
+
+ bool handleCrossingEdges(QWingedEdge &list, qreal y, ClipperMode mode);
+ bool doClip(QWingedEdge &list, ClipperMode mode);
+
+ QPainterPath subjectPath;
+ QPainterPath clipPath;
+ Operation op;
+
+ int aMask;
+ int bMask;
+};
+
+struct QPathVertex
+{
+public:
+ QPathVertex(const QPointF &p = QPointF(), int e = -1);
+ operator QPointF() const;
+
+ int edge;
+
+ qreal x;
+ qreal y;
+};
+
+class QPathEdge
+{
+public:
+ enum Traversal {
+ RightTraversal,
+ LeftTraversal
+ };
+
+ enum Direction {
+ Forward,
+ Backward
+ };
+
+ enum Type {
+ Line,
+ Curve
+ };
+
+ QPathEdge(int a = -1, int b = -1);
+
+ mutable int flag;
+
+ int windingA;
+ int windingB;
+
+ int first;
+ int second;
+
+ double angle;
+ double invAngle;
+
+ int next(Traversal traversal, Direction direction) const;
+
+ void setNext(Traversal traversal, Direction direction, int next);
+ void setNext(Direction direction, int next);
+
+ Direction directionTo(int vertex) const;
+ int vertex(Direction direction) const;
+
+private:
+ int m_next[2][2];
+};
+
+class QPathSegments
+{
+public:
+ struct Intersection {
+ int vertex;
+ qreal t;
+
+ int next;
+
+ bool operator<(const Intersection &o) const {
+ return t < o.t;
+ }
+ };
+
+ struct Segment {
+ Segment(int pathId, int vertexA, int vertexB)
+ : path(pathId)
+ , va(vertexA)
+ , vb(vertexB)
+ , intersection(-1)
+ {
+ }
+
+ int path;
+
+ // vertices
+ int va;
+ int vb;
+
+ // intersection index
+ int intersection;
+
+ QRectF bounds;
+ };
+
+
+ QPathSegments(int reserve);
+
+ void setPath(const QPainterPath &path);
+ void addPath(const QPainterPath &path);
+
+ int intersections() const;
+ int segments() const;
+ int points() const;
+
+ const Segment &segmentAt(int index) const;
+ const QLineF lineAt(int index) const;
+ const QRectF &elementBounds(int index) const;
+ int pathId(int index) const;
+
+ const QPointF &pointAt(int vertex) const;
+ int addPoint(const QPointF &point);
+
+ const Intersection *intersectionAt(int index) const;
+ void addIntersection(int index, const Intersection &intersection);
+
+ void mergePoints();
+
+private:
+ QDataBuffer<QPointF> m_points;
+ QDataBuffer<Segment> m_segments;
+ QDataBuffer<Intersection> m_intersections;
+
+ int m_pathId;
+};
+
+class Q_AUTOTEST_EXPORT QWingedEdge
+{
+public:
+ struct TraversalStatus
+ {
+ int edge;
+ QPathEdge::Traversal traversal;
+ QPathEdge::Direction direction;
+
+ void flipDirection();
+ void flipTraversal();
+
+ void flip();
+ };
+
+ QWingedEdge();
+ QWingedEdge(const QPainterPath &subject, const QPainterPath &clip);
+
+ void simplify();
+ QPainterPath toPath() const;
+
+ int edgeCount() const;
+
+ QPathEdge *edge(int edge);
+ const QPathEdge *edge(int edge) const;
+
+ int vertexCount() const;
+
+ int addVertex(const QPointF &p);
+
+ QPathVertex *vertex(int vertex);
+ const QPathVertex *vertex(int vertex) const;
+
+ TraversalStatus next(const TraversalStatus &status) const;
+
+ int addEdge(const QPointF &a, const QPointF &b);
+ int addEdge(int vertexA, int vertexB);
+
+ bool isInside(qreal x, qreal y) const;
+
+ static QPathEdge::Traversal flip(QPathEdge::Traversal traversal);
+ static QPathEdge::Direction flip(QPathEdge::Direction direction);
+
+private:
+ void intersectAndAdd();
+
+ void printNode(int i, FILE *handle);
+
+ void removeEdge(int ei);
+
+ int insert(const QPathVertex &vertex);
+ TraversalStatus findInsertStatus(int vertex, int edge) const;
+
+ qreal delta(int vertex, int a, int b) const;
+
+ QDataBuffer<QPathEdge> m_edges;
+ QDataBuffer<QPathVertex> m_vertices;
+
+ QVector<qreal> m_splitPoints;
+
+ QPathSegments m_segments;
+};
+
+inline QPathEdge::QPathEdge(int a, int b)
+ : flag(0)
+ , windingA(0)
+ , windingB(0)
+ , first(a)
+ , second(b)
+ , angle(0)
+ , invAngle(0)
+{
+ m_next[0][0] = -1;
+ m_next[1][0] = -1;
+ m_next[0][0] = -1;
+ m_next[1][0] = -1;
+}
+
+inline int QPathEdge::next(Traversal traversal, Direction direction) const
+{
+ return m_next[int(traversal)][int(direction)];
+}
+
+inline void QPathEdge::setNext(Traversal traversal, Direction direction, int next)
+{
+ m_next[int(traversal)][int(direction)] = next;
+}
+
+inline void QPathEdge::setNext(Direction direction, int next)
+{
+ m_next[0][int(direction)] = next;
+ m_next[1][int(direction)] = next;
+}
+
+inline QPathEdge::Direction QPathEdge::directionTo(int vertex) const
+{
+ return first == vertex ? Backward : Forward;
+}
+
+inline int QPathEdge::vertex(Direction direction) const
+{
+ return direction == Backward ? first : second;
+}
+
+inline QPathVertex::QPathVertex(const QPointF &p, int e)
+ : edge(e)
+ , x(p.x())
+ , y(p.y())
+{
+}
+
+inline QPathVertex::operator QPointF() const
+{
+ return QPointF(x, y);
+}
+
+inline QPathSegments::QPathSegments(int reserve) :
+ m_points(reserve),
+ m_segments(reserve),
+ m_intersections(reserve)
+{
+}
+
+inline int QPathSegments::segments() const
+{
+ return m_segments.size();
+}
+
+inline int QPathSegments::points() const
+{
+ return m_points.size();
+}
+
+inline const QPointF &QPathSegments::pointAt(int i) const
+{
+ return m_points.at(i);
+}
+
+inline int QPathSegments::addPoint(const QPointF &point)
+{
+ m_points << point;
+ return m_points.size() - 1;
+}
+
+inline const QPathSegments::Segment &QPathSegments::segmentAt(int index) const
+{
+ return m_segments.at(index);
+}
+
+inline const QLineF QPathSegments::lineAt(int index) const
+{
+ const Segment &segment = m_segments.at(index);
+ return QLineF(m_points.at(segment.va), m_points.at(segment.vb));
+}
+
+inline const QRectF &QPathSegments::elementBounds(int index) const
+{
+ return m_segments.at(index).bounds;
+}
+
+inline int QPathSegments::pathId(int index) const
+{
+ return m_segments.at(index).path;
+}
+
+inline const QPathSegments::Intersection *QPathSegments::intersectionAt(int index) const
+{
+ const int intersection = m_segments.at(index).intersection;
+ if (intersection < 0)
+ return 0;
+ else
+ return &m_intersections.at(intersection);
+}
+
+inline int QPathSegments::intersections() const
+{
+ return m_intersections.size();
+}
+
+inline void QPathSegments::addIntersection(int index, const Intersection &intersection)
+{
+ m_intersections << intersection;
+
+ Segment &segment = m_segments.at(index);
+ if (segment.intersection < 0) {
+ segment.intersection = m_intersections.size() - 1;
+ } else {
+ Intersection *isect = &m_intersections.at(segment.intersection);
+
+ while (isect->next != 0)
+ isect += isect->next;
+
+ isect->next = (m_intersections.size() - 1) - (isect - m_intersections.data());
+ }
+}
+
+inline void QWingedEdge::TraversalStatus::flipDirection()
+{
+ direction = QWingedEdge::flip(direction);
+}
+
+inline void QWingedEdge::TraversalStatus::flipTraversal()
+{
+ traversal = QWingedEdge::flip(traversal);
+}
+
+inline void QWingedEdge::TraversalStatus::flip()
+{
+ flipDirection();
+ flipTraversal();
+}
+
+inline int QWingedEdge::edgeCount() const
+{
+ return m_edges.size();
+}
+
+inline QPathEdge *QWingedEdge::edge(int edge)
+{
+ return edge < 0 ? 0 : &m_edges.at(edge);
+}
+
+inline const QPathEdge *QWingedEdge::edge(int edge) const
+{
+ return edge < 0 ? 0 : &m_edges.at(edge);
+}
+
+inline int QWingedEdge::vertexCount() const
+{
+ return m_vertices.size();
+}
+
+inline int QWingedEdge::addVertex(const QPointF &p)
+{
+ m_vertices << p;
+ return m_vertices.size() - 1;
+}
+
+inline QPathVertex *QWingedEdge::vertex(int vertex)
+{
+ return vertex < 0 ? 0 : &m_vertices.at(vertex);
+}
+
+inline const QPathVertex *QWingedEdge::vertex(int vertex) const
+{
+ return vertex < 0 ? 0 : &m_vertices.at(vertex);
+}
+
+inline QPathEdge::Traversal QWingedEdge::flip(QPathEdge::Traversal traversal)
+{
+ return traversal == QPathEdge::RightTraversal ? QPathEdge::LeftTraversal : QPathEdge::RightTraversal;
+}
+
+inline QPathEdge::Direction QWingedEdge::flip(QPathEdge::Direction direction)
+{
+ return direction == QPathEdge::Forward ? QPathEdge::Backward : QPathEdge::Forward;
+}
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPATHCLIPPER_P_H
diff --git a/src/gui/painting/qpdf.cpp b/src/gui/painting/qpdf.cpp
new file mode 100644
index 0000000000..0271a39b16
--- /dev/null
+++ b/src/gui/painting/qpdf.cpp
@@ -0,0 +1,2106 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qplatformdefs.h"
+#include <qdebug.h>
+#include "qpdf_p.h"
+#include <qfile.h>
+#include <qtemporaryfile.h>
+#include <private/qmath_p.h>
+#include "private/qcups_p.h"
+#include "qprinterinfo.h"
+#include <qnumeric.h>
+#include "private/qfont_p.h"
+
+#ifdef Q_OS_UNIX
+#include "private/qcore_unix_p.h" // overrides QT_OPEN
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_PRINTER
+
+extern QSizeF qt_paperSizeToQSizeF(QPrinter::PaperSize size);
+
+/* also adds a space at the end of the number */
+const char *qt_real_to_string(qreal val, char *buf) {
+ const char *ret = buf;
+
+ if (qIsNaN(val)) {
+ *(buf++) = '0';
+ *(buf++) = ' ';
+ *buf = 0;
+ return ret;
+ }
+
+ if (val < 0) {
+ *(buf++) = '-';
+ val = -val;
+ }
+ unsigned int ival = (unsigned int) val;
+ qreal frac = val - (qreal)ival;
+
+ int ifrac = (int)(frac * 1000000000);
+ if (ifrac == 1000000000) {
+ ++ival;
+ ifrac = 0;
+ }
+ char output[256];
+ int i = 0;
+ while (ival) {
+ output[i] = '0' + (ival % 10);
+ ++i;
+ ival /= 10;
+ }
+ int fact = 100000000;
+ if (i == 0) {
+ *(buf++) = '0';
+ } else {
+ while (i) {
+ *(buf++) = output[--i];
+ fact /= 10;
+ ifrac /= 10;
+ }
+ }
+
+ if (ifrac) {
+ *(buf++) = '.';
+ while (fact) {
+ *(buf++) = '0' + ((ifrac/fact) % 10);
+ fact /= 10;
+ }
+ }
+ *(buf++) = ' ';
+ *buf = 0;
+ return ret;
+}
+
+const char *qt_int_to_string(int val, char *buf) {
+ const char *ret = buf;
+ if (val < 0) {
+ *(buf++) = '-';
+ val = -val;
+ }
+ char output[256];
+ int i = 0;
+ while (val) {
+ output[i] = '0' + (val % 10);
+ ++i;
+ val /= 10;
+ }
+ if (i == 0) {
+ *(buf++) = '0';
+ } else {
+ while (i)
+ *(buf++) = output[--i];
+ }
+ *(buf++) = ' ';
+ *buf = 0;
+ return ret;
+}
+
+
+namespace QPdf {
+ ByteStream::ByteStream(QByteArray *byteArray, bool fileBacking)
+ : dev(new QBuffer(byteArray)),
+ fileBackingEnabled(fileBacking),
+ fileBackingActive(false),
+ handleDirty(false)
+ {
+ dev->open(QIODevice::ReadWrite | QIODevice::Append);
+ }
+
+ ByteStream::ByteStream(bool fileBacking)
+ : dev(new QBuffer(&ba)),
+ fileBackingEnabled(fileBacking),
+ fileBackingActive(false),
+ handleDirty(false)
+ {
+ dev->open(QIODevice::ReadWrite);
+ }
+
+ ByteStream::~ByteStream()
+ {
+ delete dev;
+ }
+
+ ByteStream &ByteStream::operator <<(char chr)
+ {
+ if (handleDirty) prepareBuffer();
+ dev->write(&chr, 1);
+ return *this;
+ }
+
+ ByteStream &ByteStream::operator <<(const char *str)
+ {
+ if (handleDirty) prepareBuffer();
+ dev->write(str, strlen(str));
+ return *this;
+ }
+
+ ByteStream &ByteStream::operator <<(const QByteArray &str)
+ {
+ if (handleDirty) prepareBuffer();
+ dev->write(str);
+ return *this;
+ }
+
+ ByteStream &ByteStream::operator <<(const ByteStream &src)
+ {
+ Q_ASSERT(!src.dev->isSequential());
+ if (handleDirty) prepareBuffer();
+ // We do play nice here, even though it looks ugly.
+ // We save the position and restore it afterwards.
+ ByteStream &s = const_cast<ByteStream&>(src);
+ qint64 pos = s.dev->pos();
+ s.dev->reset();
+ while (!s.dev->atEnd()) {
+ QByteArray buf = s.dev->read(chunkSize());
+ dev->write(buf);
+ }
+ s.dev->seek(pos);
+ return *this;
+ }
+
+ ByteStream &ByteStream::operator <<(qreal val) {
+ char buf[256];
+ qt_real_to_string(val, buf);
+ *this << buf;
+ return *this;
+ }
+
+ ByteStream &ByteStream::operator <<(int val) {
+ char buf[256];
+ qt_int_to_string(val, buf);
+ *this << buf;
+ return *this;
+ }
+
+ ByteStream &ByteStream::operator <<(const QPointF &p) {
+ char buf[256];
+ qt_real_to_string(p.x(), buf);
+ *this << buf;
+ qt_real_to_string(p.y(), buf);
+ *this << buf;
+ return *this;
+ }
+
+ QIODevice *ByteStream::stream()
+ {
+ dev->reset();
+ handleDirty = true;
+ return dev;
+ }
+
+ void ByteStream::clear()
+ {
+ dev->open(QIODevice::ReadWrite | QIODevice::Truncate);
+ }
+
+ void ByteStream::constructor_helper(QByteArray *ba)
+ {
+ delete dev;
+ dev = new QBuffer(ba);
+ dev->open(QIODevice::ReadWrite);
+ }
+
+ void ByteStream::prepareBuffer()
+ {
+ Q_ASSERT(!dev->isSequential());
+ qint64 size = dev->size();
+ if (fileBackingEnabled && !fileBackingActive
+ && size > maxMemorySize()) {
+ // Switch to file backing.
+ QTemporaryFile *newFile = new QTemporaryFile;
+ newFile->open();
+ dev->reset();
+ while (!dev->atEnd()) {
+ QByteArray buf = dev->read(chunkSize());
+ newFile->write(buf);
+ }
+ delete dev;
+ dev = newFile;
+ ba.clear();
+ fileBackingActive = true;
+ }
+ if (dev->pos() != size) {
+ dev->seek(size);
+ handleDirty = false;
+ }
+ }
+}
+
+#define QT_PATH_ELEMENT(elm)
+
+QByteArray QPdf::generatePath(const QPainterPath &path, const QTransform &matrix, PathFlags flags)
+{
+ QByteArray result;
+ if (!path.elementCount())
+ return result;
+
+ ByteStream s(&result);
+
+ int start = -1;
+ for (int i = 0; i < path.elementCount(); ++i) {
+ const QPainterPath::Element &elm = path.elementAt(i);
+ switch (elm.type) {
+ case QPainterPath::MoveToElement:
+ if (start >= 0
+ && path.elementAt(start).x == path.elementAt(i-1).x
+ && path.elementAt(start).y == path.elementAt(i-1).y)
+ s << "h\n";
+ s << matrix.map(QPointF(elm.x, elm.y)) << "m\n";
+ start = i;
+ break;
+ case QPainterPath::LineToElement:
+ s << matrix.map(QPointF(elm.x, elm.y)) << "l\n";
+ break;
+ case QPainterPath::CurveToElement:
+ Q_ASSERT(path.elementAt(i+1).type == QPainterPath::CurveToDataElement);
+ Q_ASSERT(path.elementAt(i+2).type == QPainterPath::CurveToDataElement);
+ s << matrix.map(QPointF(elm.x, elm.y))
+ << matrix.map(QPointF(path.elementAt(i+1).x, path.elementAt(i+1).y))
+ << matrix.map(QPointF(path.elementAt(i+2).x, path.elementAt(i+2).y))
+ << "c\n";
+ i += 2;
+ break;
+ default:
+ qFatal("QPdf::generatePath(), unhandled type: %d", elm.type);
+ }
+ }
+ if (start >= 0
+ && path.elementAt(start).x == path.elementAt(path.elementCount()-1).x
+ && path.elementAt(start).y == path.elementAt(path.elementCount()-1).y)
+ s << "h\n";
+
+ Qt::FillRule fillRule = path.fillRule();
+
+ const char *op = "";
+ switch (flags) {
+ case ClipPath:
+ op = (fillRule == Qt::WindingFill) ? "W n\n" : "W* n\n";
+ break;
+ case FillPath:
+ op = (fillRule == Qt::WindingFill) ? "f\n" : "f*\n";
+ break;
+ case StrokePath:
+ op = "S\n";
+ break;
+ case FillAndStrokePath:
+ op = (fillRule == Qt::WindingFill) ? "B\n" : "B*\n";
+ break;
+ }
+ s << op;
+ return result;
+}
+
+QByteArray QPdf::generateMatrix(const QTransform &matrix)
+{
+ QByteArray result;
+ ByteStream s(&result);
+ s << matrix.m11()
+ << matrix.m12()
+ << matrix.m21()
+ << matrix.m22()
+ << matrix.dx()
+ << matrix.dy()
+ << "cm\n";
+ return result;
+}
+
+QByteArray QPdf::generateDashes(const QPen &pen)
+{
+ QByteArray result;
+ ByteStream s(&result);
+ s << '[';
+
+ QVector<qreal> dasharray = pen.dashPattern();
+ qreal w = pen.widthF();
+ if (w < 0.001)
+ w = 1;
+ for (int i = 0; i < dasharray.size(); ++i) {
+ qreal dw = dasharray.at(i)*w;
+ if (dw < 0.0001) dw = 0.0001;
+ s << dw;
+ }
+ s << ']';
+ //qDebug() << "dasharray: pen has" << dasharray;
+ //qDebug() << " => " << result;
+ return result;
+}
+
+
+
+static const char* pattern_for_brush[] = {
+ 0, // NoBrush
+ 0, // SolidPattern
+ "0 J\n"
+ "6 w\n"
+ "[] 0 d\n"
+ "4 0 m\n"
+ "4 8 l\n"
+ "0 4 m\n"
+ "8 4 l\n"
+ "S\n", // Dense1Pattern
+
+ "0 J\n"
+ "2 w\n"
+ "[6 2] 1 d\n"
+ "0 0 m\n"
+ "0 8 l\n"
+ "8 0 m\n"
+ "8 8 l\n"
+ "S\n"
+ "[] 0 d\n"
+ "2 0 m\n"
+ "2 8 l\n"
+ "6 0 m\n"
+ "6 8 l\n"
+ "S\n"
+ "[6 2] -3 d\n"
+ "4 0 m\n"
+ "4 8 l\n"
+ "S\n", // Dense2Pattern
+
+ "0 J\n"
+ "2 w\n"
+ "[6 2] 1 d\n"
+ "0 0 m\n"
+ "0 8 l\n"
+ "8 0 m\n"
+ "8 8 l\n"
+ "S\n"
+ "[2 2] -1 d\n"
+ "2 0 m\n"
+ "2 8 l\n"
+ "6 0 m\n"
+ "6 8 l\n"
+ "S\n"
+ "[6 2] -3 d\n"
+ "4 0 m\n"
+ "4 8 l\n"
+ "S\n", // Dense3Pattern
+
+ "0 J\n"
+ "2 w\n"
+ "[2 2] 1 d\n"
+ "0 0 m\n"
+ "0 8 l\n"
+ "8 0 m\n"
+ "8 8 l\n"
+ "S\n"
+ "[2 2] -1 d\n"
+ "2 0 m\n"
+ "2 8 l\n"
+ "6 0 m\n"
+ "6 8 l\n"
+ "S\n"
+ "[2 2] 1 d\n"
+ "4 0 m\n"
+ "4 8 l\n"
+ "S\n", // Dense4Pattern
+
+ "0 J\n"
+ "2 w\n"
+ "[2 6] -1 d\n"
+ "0 0 m\n"
+ "0 8 l\n"
+ "8 0 m\n"
+ "8 8 l\n"
+ "S\n"
+ "[2 2] 1 d\n"
+ "2 0 m\n"
+ "2 8 l\n"
+ "6 0 m\n"
+ "6 8 l\n"
+ "S\n"
+ "[2 6] 3 d\n"
+ "4 0 m\n"
+ "4 8 l\n"
+ "S\n", // Dense5Pattern
+
+ "0 J\n"
+ "2 w\n"
+ "[2 6] -1 d\n"
+ "0 0 m\n"
+ "0 8 l\n"
+ "8 0 m\n"
+ "8 8 l\n"
+ "S\n"
+ "[2 6] 3 d\n"
+ "4 0 m\n"
+ "4 8 l\n"
+ "S\n", // Dense6Pattern
+
+ "0 J\n"
+ "2 w\n"
+ "[2 6] -1 d\n"
+ "0 0 m\n"
+ "0 8 l\n"
+ "8 0 m\n"
+ "8 8 l\n"
+ "S\n", // Dense7Pattern
+
+ "1 w\n"
+ "0 4 m\n"
+ "8 4 l\n"
+ "S\n", // HorPattern
+
+ "1 w\n"
+ "4 0 m\n"
+ "4 8 l\n"
+ "S\n", // VerPattern
+
+ "1 w\n"
+ "4 0 m\n"
+ "4 8 l\n"
+ "0 4 m\n"
+ "8 4 l\n"
+ "S\n", // CrossPattern
+
+ "1 w\n"
+ "-1 5 m\n"
+ "5 -1 l\n"
+ "3 9 m\n"
+ "9 3 l\n"
+ "S\n", // BDiagPattern
+
+ "1 w\n"
+ "-1 3 m\n"
+ "5 9 l\n"
+ "3 -1 m\n"
+ "9 5 l\n"
+ "S\n", // FDiagPattern
+
+ "1 w\n"
+ "-1 3 m\n"
+ "5 9 l\n"
+ "3 -1 m\n"
+ "9 5 l\n"
+ "-1 5 m\n"
+ "5 -1 l\n"
+ "3 9 m\n"
+ "9 3 l\n"
+ "S\n", // DiagCrossPattern
+};
+
+QByteArray QPdf::patternForBrush(const QBrush &b)
+{
+ int style = b.style();
+ if (style > Qt::DiagCrossPattern)
+ return QByteArray();
+ return pattern_for_brush[style];
+}
+
+#ifdef USE_NATIVE_GRADIENTS
+static void writeTriangleLine(uchar *&data, int xpos, int ypos, int xoff, int yoff, uint rgb, uchar flag, bool alpha)
+{
+ data[0] = flag;
+ data[1] = (uchar)(xpos >> 16);
+ data[2] = (uchar)(xpos >> 8);
+ data[3] = (uchar)(xpos >> 0);
+ data[4] = (uchar)(ypos >> 16);
+ data[5] = (uchar)(ypos >> 8);
+ data[6] = (uchar)(ypos >> 0);
+ data += 7;
+ if (alpha) {
+ *data++ = (uchar)qAlpha(rgb);
+ } else {
+ *data++ = (uchar)qRed(rgb);
+ *data++ = (uchar)qGreen(rgb);
+ *data++ = (uchar)qBlue(rgb);
+ }
+ xpos += xoff;
+ ypos += yoff;
+ data[0] = flag;
+ data[1] = (uchar)(xpos >> 16);
+ data[2] = (uchar)(xpos >> 8);
+ data[3] = (uchar)(xpos >> 0);
+ data[4] = (uchar)(ypos >> 16);
+ data[5] = (uchar)(ypos >> 8);
+ data[6] = (uchar)(ypos >> 0);
+ data += 7;
+ if (alpha) {
+ *data++ = (uchar)qAlpha(rgb);
+ } else {
+ *data++ = (uchar)qRed(rgb);
+ *data++ = (uchar)qGreen(rgb);
+ *data++ = (uchar)qBlue(rgb);
+ }
+}
+
+
+QByteArray QPdf::generateLinearGradientShader(const QLinearGradient *gradient, const QPointF *page_rect, bool alpha)
+{
+ // generate list of triangles with colors
+ QPointF start = gradient->start();
+ QPointF stop = gradient->finalStop();
+ QGradientStops stops = gradient->stops();
+ QPointF offset = stop - start;
+ QGradient::Spread spread = gradient->spread();
+
+ if (gradient->spread() == QGradient::ReflectSpread) {
+ offset *= 2;
+ for (int i = stops.size() - 2; i >= 0; --i) {
+ QGradientStop stop = stops.at(i);
+ stop.first = 2. - stop.first;
+ stops.append(stop);
+ }
+ for (int i = 0 ; i < stops.size(); ++i)
+ stops[i].first /= 2.;
+ }
+
+ QPointF orthogonal(offset.y(), -offset.x());
+ qreal length = offset.x()*offset.x() + offset.y()*offset.y();
+
+ // find the max and min values in offset and orth direction that are needed to cover
+ // the whole page
+ int off_min = INT_MAX;
+ int off_max = INT_MIN;
+ qreal ort_min = INT_MAX;
+ qreal ort_max = INT_MIN;
+ for (int i = 0; i < 4; ++i) {
+ qreal off = ((page_rect[i].x() - start.x()) * offset.x() + (page_rect[i].y() - start.y()) * offset.y())/length;
+ qreal ort = ((page_rect[i].x() - start.x()) * orthogonal.x() + (page_rect[i].y() - start.y()) * orthogonal.y())/length;
+ off_min = qMin(off_min, qFloor(off));
+ off_max = qMax(off_max, qCeil(off));
+ ort_min = qMin(ort_min, ort);
+ ort_max = qMax(ort_max, ort);
+ }
+ ort_min -= 1;
+ ort_max += 1;
+
+ start += off_min * offset + ort_min * orthogonal;
+ orthogonal *= (ort_max - ort_min);
+ int num = off_max - off_min;
+
+ QPointF gradient_rect[4] = { start,
+ start + orthogonal,
+ start + num*offset,
+ start + num*offset + orthogonal };
+ qreal xmin = gradient_rect[0].x();
+ qreal xmax = gradient_rect[0].x();
+ qreal ymin = gradient_rect[0].y();
+ qreal ymax = gradient_rect[0].y();
+ for (int i = 1; i < 4; ++i) {
+ xmin = qMin(xmin, gradient_rect[i].x());
+ xmax = qMax(xmax, gradient_rect[i].x());
+ ymin = qMin(ymin, gradient_rect[i].y());
+ ymax = qMax(ymax, gradient_rect[i].y());
+ }
+ xmin -= 1000;
+ xmax += 1000;
+ ymin -= 1000;
+ ymax += 1000;
+ start -= QPointF(xmin, ymin);
+ qreal factor_x = qreal(1<<24)/(xmax - xmin);
+ qreal factor_y = qreal(1<<24)/(ymax - ymin);
+ int xoff = (int)(orthogonal.x()*factor_x);
+ int yoff = (int)(orthogonal.y()*factor_y);
+
+ QByteArray triangles;
+ triangles.resize(spread == QGradient::PadSpread ? 20*(stops.size()+2) : 20*num*stops.size());
+ uchar *data = (uchar *) triangles.data();
+ if (spread == QGradient::PadSpread) {
+ if (off_min > 0 || off_max < 1) {
+ // linear gradient outside of page
+ const QGradientStop &current_stop = off_min > 0 ? stops.at(stops.size()-1) : stops.at(0);
+ uint rgb = current_stop.second.rgba();
+ int xpos = (int)(start.x()*factor_x);
+ int ypos = (int)(start.y()*factor_y);
+ writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, 0, alpha);
+ start += num*offset;
+ xpos = (int)(start.x()*factor_x);
+ ypos = (int)(start.y()*factor_y);
+ writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, 1, alpha);
+ } else {
+ int flag = 0;
+ if (off_min < 0) {
+ uint rgb = stops.at(0).second.rgba();
+ int xpos = (int)(start.x()*factor_x);
+ int ypos = (int)(start.y()*factor_y);
+ writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, flag, alpha);
+ start -= off_min*offset;
+ flag = 1;
+ }
+ for (int s = 0; s < stops.size(); ++s) {
+ const QGradientStop &current_stop = stops.at(s);
+ uint rgb = current_stop.second.rgba();
+ int xpos = (int)(start.x()*factor_x);
+ int ypos = (int)(start.y()*factor_y);
+ writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, flag, alpha);
+ if (s < stops.size()-1)
+ start += offset*(stops.at(s+1).first - stops.at(s).first);
+ flag = 1;
+ }
+ if (off_max > 1) {
+ start += (off_max - 1)*offset;
+ uint rgb = stops.at(stops.size()-1).second.rgba();
+ int xpos = (int)(start.x()*factor_x);
+ int ypos = (int)(start.y()*factor_y);
+ writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, flag, alpha);
+ }
+ }
+ } else {
+ for (int i = 0; i < num; ++i) {
+ uchar flag = 0;
+ for (int s = 0; s < stops.size(); ++s) {
+ uint rgb = stops.at(s).second.rgba();
+ int xpos = (int)(start.x()*factor_x);
+ int ypos = (int)(start.y()*factor_y);
+ writeTriangleLine(data, xpos, ypos, xoff, yoff, rgb, flag, alpha);
+ if (s < stops.size()-1)
+ start += offset*(stops.at(s+1).first - stops.at(s).first);
+ flag = 1;
+ }
+ }
+ }
+ triangles.resize((char *)data - triangles.constData());
+
+ QByteArray shader;
+ QPdf::ByteStream s(&shader);
+ s << "<<\n"
+ "/ShadingType 4\n"
+ "/ColorSpace " << (alpha ? "/DeviceGray\n" : "/DeviceRGB\n") <<
+ "/AntiAlias true\n"
+ "/BitsPerCoordinate 24\n"
+ "/BitsPerComponent 8\n"
+ "/BitsPerFlag 8\n"
+ "/Decode [" << xmin << xmax << ymin << ymax << (alpha ? "0 1]\n" : "0 1 0 1 0 1]\n") <<
+ "/AntiAlias true\n"
+ "/Length " << triangles.length() << "\n"
+ ">>\n"
+ "stream\n" << triangles << "endstream\n"
+ "endobj\n";
+ return shader;
+}
+#endif
+
+static void moveToHook(qfixed x, qfixed y, void *data)
+{
+ QPdf::Stroker *t = (QPdf::Stroker *)data;
+ if (!t->first)
+ *t->stream << "h\n";
+ if (!t->cosmeticPen)
+ t->matrix.map(x, y, &x, &y);
+ *t->stream << x << y << "m\n";
+ t->first = false;
+}
+
+static void lineToHook(qfixed x, qfixed y, void *data)
+{
+ QPdf::Stroker *t = (QPdf::Stroker *)data;
+ if (!t->cosmeticPen)
+ t->matrix.map(x, y, &x, &y);
+ *t->stream << x << y << "l\n";
+}
+
+static void cubicToHook(qfixed c1x, qfixed c1y,
+ qfixed c2x, qfixed c2y,
+ qfixed ex, qfixed ey,
+ void *data)
+{
+ QPdf::Stroker *t = (QPdf::Stroker *)data;
+ if (!t->cosmeticPen) {
+ t->matrix.map(c1x, c1y, &c1x, &c1y);
+ t->matrix.map(c2x, c2y, &c2x, &c2y);
+ t->matrix.map(ex, ey, &ex, &ey);
+ }
+ *t->stream << c1x << c1y
+ << c2x << c2y
+ << ex << ey
+ << "c\n";
+}
+
+QPdf::Stroker::Stroker()
+ : stream(0),
+ first(true),
+ dashStroker(&basicStroker)
+{
+ stroker = &basicStroker;
+ basicStroker.setMoveToHook(moveToHook);
+ basicStroker.setLineToHook(lineToHook);
+ basicStroker.setCubicToHook(cubicToHook);
+ cosmeticPen = true;
+ basicStroker.setStrokeWidth(.1);
+}
+
+void QPdf::Stroker::setPen(const QPen &pen)
+{
+ if (pen.style() == Qt::NoPen) {
+ stroker = 0;
+ return;
+ }
+ qreal w = pen.widthF();
+ bool zeroWidth = w < 0.0001;
+ cosmeticPen = pen.isCosmetic();
+ if (zeroWidth)
+ w = .1;
+
+ basicStroker.setStrokeWidth(w);
+ basicStroker.setCapStyle(pen.capStyle());
+ basicStroker.setJoinStyle(pen.joinStyle());
+ basicStroker.setMiterLimit(pen.miterLimit());
+
+ QVector<qreal> dashpattern = pen.dashPattern();
+ if (zeroWidth) {
+ for (int i = 0; i < dashpattern.size(); ++i)
+ dashpattern[i] *= 10.;
+ }
+ if (!dashpattern.isEmpty()) {
+ dashStroker.setDashPattern(dashpattern);
+ dashStroker.setDashOffset(pen.dashOffset());
+ stroker = &dashStroker;
+ } else {
+ stroker = &basicStroker;
+ }
+}
+
+void QPdf::Stroker::strokePath(const QPainterPath &path)
+{
+ if (!stroker)
+ return;
+ first = true;
+
+ stroker->strokePath(path, this, cosmeticPen ? matrix : QTransform());
+ *stream << "h f\n";
+}
+
+QByteArray QPdf::ascii85Encode(const QByteArray &input)
+{
+ int isize = input.size()/4*4;
+ QByteArray output;
+ output.resize(input.size()*5/4+7);
+ char *out = output.data();
+ const uchar *in = (const uchar *)input.constData();
+ for (int i = 0; i < isize; i += 4) {
+ uint val = (((uint)in[i])<<24) + (((uint)in[i+1])<<16) + (((uint)in[i+2])<<8) + (uint)in[i+3];
+ if (val == 0) {
+ *out = 'z';
+ ++out;
+ } else {
+ char base[5];
+ base[4] = val % 85;
+ val /= 85;
+ base[3] = val % 85;
+ val /= 85;
+ base[2] = val % 85;
+ val /= 85;
+ base[1] = val % 85;
+ val /= 85;
+ base[0] = val % 85;
+ *(out++) = base[0] + '!';
+ *(out++) = base[1] + '!';
+ *(out++) = base[2] + '!';
+ *(out++) = base[3] + '!';
+ *(out++) = base[4] + '!';
+ }
+ }
+ //write the last few bytes
+ int remaining = input.size() - isize;
+ if (remaining) {
+ uint val = 0;
+ for (int i = isize; i < input.size(); ++i)
+ val = (val << 8) + in[i];
+ val <<= 8*(4-remaining);
+ char base[5];
+ base[4] = val % 85;
+ val /= 85;
+ base[3] = val % 85;
+ val /= 85;
+ base[2] = val % 85;
+ val /= 85;
+ base[1] = val % 85;
+ val /= 85;
+ base[0] = val % 85;
+ for (int i = 0; i < remaining+1; ++i)
+ *(out++) = base[i] + '!';
+ }
+ *(out++) = '~';
+ *(out++) = '>';
+ output.resize(out-output.data());
+ return output;
+}
+
+const char *QPdf::toHex(ushort u, char *buffer)
+{
+ int i = 3;
+ while (i >= 0) {
+ ushort hex = (u & 0x000f);
+ if (hex < 0x0a)
+ buffer[i] = '0'+hex;
+ else
+ buffer[i] = 'A'+(hex-0x0a);
+ u = u >> 4;
+ i--;
+ }
+ buffer[4] = '\0';
+ return buffer;
+}
+
+const char *QPdf::toHex(uchar u, char *buffer)
+{
+ int i = 1;
+ while (i >= 0) {
+ ushort hex = (u & 0x000f);
+ if (hex < 0x0a)
+ buffer[i] = '0'+hex;
+ else
+ buffer[i] = 'A'+(hex-0x0a);
+ u = u >> 4;
+ i--;
+ }
+ buffer[2] = '\0';
+ return buffer;
+}
+
+#define Q_MM(n) int((n * 720 + 127) / 254)
+#define Q_IN(n) int(n * 72)
+
+static const char * const psToStr[QPrinter::NPaperSize+1] =
+{
+ "A4", "B5", "Letter", "Legal", "Executive",
+ "A0", "A1", "A2", "A3", "A5", "A6", "A7", "A8", "A9", "B0", "B1",
+ "B10", "B2", "B3", "B4", "B6", "B7", "B8", "B9", "C5E", "Comm10E",
+ "DLE", "Folio", "Ledger", "Tabloid", 0
+};
+
+QPdf::PaperSize QPdf::paperSize(QPrinter::PaperSize paperSize)
+{
+ QSizeF s = qt_paperSizeToQSizeF(paperSize);
+ PaperSize p = { Q_MM(s.width()), Q_MM(s.height()) };
+ return p;
+}
+
+const char *QPdf::paperSizeToString(QPrinter::PaperSize paperSize)
+{
+ return psToStr[paperSize];
+}
+
+// -------------------------- base engine, shared code between PS and PDF -----------------------
+
+QPdfBaseEngine::QPdfBaseEngine(QPdfBaseEnginePrivate &dd, PaintEngineFeatures f)
+ : QAlphaPaintEngine(dd, f)
+{
+ Q_D(QPdfBaseEngine);
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ if (QCUPSSupport::isAvailable()) {
+ QCUPSSupport cups;
+ const cups_dest_t* printers = cups.availablePrinters();
+ int prnCount = cups.availablePrintersCount();
+
+ for (int i = 0; i < prnCount; ++i) {
+ if (printers[i].is_default) {
+ d->printerName = QString::fromLocal8Bit(printers[i].name);
+ break;
+ }
+ }
+
+ } else
+#endif
+ {
+ d->printerName = QString::fromLocal8Bit(qgetenv("PRINTER"));
+ if (d->printerName.isEmpty())
+ d->printerName = QString::fromLocal8Bit(qgetenv("LPDEST"));
+ if (d->printerName.isEmpty())
+ d->printerName = QString::fromLocal8Bit(qgetenv("NPRINTER"));
+ if (d->printerName.isEmpty())
+ d->printerName = QString::fromLocal8Bit(qgetenv("NGPRINTER"));
+ }
+}
+
+void QPdfBaseEngine::drawPoints (const QPointF *points, int pointCount)
+{
+ if (!points)
+ return;
+
+ Q_D(QPdfBaseEngine);
+ QPainterPath p;
+ for (int i=0; i!=pointCount;++i) {
+ p.moveTo(points[i]);
+ p.lineTo(points[i] + QPointF(0, 0.001));
+ }
+
+ bool hadBrush = d->hasBrush;
+ d->hasBrush = false;
+ drawPath(p);
+ d->hasBrush = hadBrush;
+}
+
+void QPdfBaseEngine::drawLines (const QLineF *lines, int lineCount)
+{
+ if (!lines)
+ return;
+
+ Q_D(QPdfBaseEngine);
+ QPainterPath p;
+ for (int i=0; i!=lineCount;++i) {
+ p.moveTo(lines[i].p1());
+ p.lineTo(lines[i].p2());
+ }
+ bool hadBrush = d->hasBrush;
+ d->hasBrush = false;
+ drawPath(p);
+ d->hasBrush = hadBrush;
+}
+
+void QPdfBaseEngine::drawRects (const QRectF *rects, int rectCount)
+{
+ if (!rects)
+ return;
+
+ Q_D(QPdfBaseEngine);
+ if (d->useAlphaEngine) {
+ QAlphaPaintEngine::drawRects(rects, rectCount);
+ if (!continueCall())
+ return;
+ }
+
+ if (d->clipEnabled && d->allClipped)
+ return;
+ if (!d->hasPen && !d->hasBrush)
+ return;
+
+ QBrush penBrush = d->pen.brush();
+ if (d->simplePen || !d->hasPen) {
+ // draw strokes natively in this case for better output
+ if(!d->simplePen && !d->stroker.matrix.isIdentity())
+ *d->currentPage << "q\n" << QPdf::generateMatrix(d->stroker.matrix);
+ for (int i = 0; i < rectCount; ++i)
+ *d->currentPage << rects[i].x() << rects[i].y() << rects[i].width() << rects[i].height() << "re\n";
+ *d->currentPage << (d->hasPen ? (d->hasBrush ? "B\n" : "S\n") : "f\n");
+ if(!d->simplePen && !d->stroker.matrix.isIdentity())
+ *d->currentPage << "Q\n";
+ } else {
+ QPainterPath p;
+ for (int i=0; i!=rectCount; ++i)
+ p.addRect(rects[i]);
+ drawPath(p);
+ }
+}
+
+void QPdfBaseEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
+{
+ Q_D(QPdfBaseEngine);
+
+ if (d->useAlphaEngine) {
+ QAlphaPaintEngine::drawPolygon(points, pointCount, mode);
+ if (!continueCall())
+ return;
+ }
+
+ if (!points || !pointCount)
+ return;
+
+ bool hb = d->hasBrush;
+ QPainterPath p;
+
+ switch(mode) {
+ case OddEvenMode:
+ p.setFillRule(Qt::OddEvenFill);
+ break;
+ case ConvexMode:
+ case WindingMode:
+ p.setFillRule(Qt::WindingFill);
+ break;
+ case PolylineMode:
+ d->hasBrush = false;
+ break;
+ default:
+ break;
+ }
+
+ p.moveTo(points[0]);
+ for (int i = 1; i < pointCount; ++i)
+ p.lineTo(points[i]);
+
+ if (mode != PolylineMode)
+ p.closeSubpath();
+ drawPath(p);
+
+ d->hasBrush = hb;
+}
+
+void QPdfBaseEngine::drawPath (const QPainterPath &p)
+{
+ Q_D(QPdfBaseEngine);
+
+ if (d->useAlphaEngine) {
+ QAlphaPaintEngine::drawPath(p);
+ if (!continueCall())
+ return;
+ }
+
+ if (d->clipEnabled && d->allClipped)
+ return;
+ if (!d->hasPen && !d->hasBrush)
+ return;
+
+ if (d->simplePen) {
+ // draw strokes natively in this case for better output
+ *d->currentPage << QPdf::generatePath(p, QTransform(), d->hasBrush ? QPdf::FillAndStrokePath : QPdf::StrokePath);
+ } else {
+ if (d->hasBrush)
+ *d->currentPage << QPdf::generatePath(p, d->stroker.matrix, QPdf::FillPath);
+ if (d->hasPen) {
+ *d->currentPage << "q\n";
+ QBrush b = d->brush;
+ d->brush = d->pen.brush();
+ setBrush();
+ d->stroker.strokePath(p);
+ *d->currentPage << "Q\n";
+ d->brush = b;
+ }
+ }
+}
+
+void QPdfBaseEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
+{
+ Q_D(QPdfBaseEngine);
+
+ if (d->useAlphaEngine) {
+ QAlphaPaintEngine::drawTextItem(p, textItem);
+ if (!continueCall())
+ return;
+ }
+
+ if (!d->hasPen || (d->clipEnabled && d->allClipped))
+ return;
+
+ if (d->stroker.matrix.type() >= QTransform::TxProject) {
+ QPaintEngine::drawTextItem(p, textItem);
+ return;
+ }
+
+ *d->currentPage << "q\n";
+ if(!d->simplePen)
+ *d->currentPage << QPdf::generateMatrix(d->stroker.matrix);
+
+ bool hp = d->hasPen;
+ d->hasPen = false;
+ QBrush b = d->brush;
+ d->brush = d->pen.brush();
+ setBrush();
+
+ const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
+ Q_ASSERT(ti.fontEngine->type() != QFontEngine::Multi);
+ d->drawTextItem(p, ti);
+ d->hasPen = hp;
+ d->brush = b;
+ *d->currentPage << "Q\n";
+}
+
+
+void QPdfBaseEngine::updateState(const QPaintEngineState &state)
+{
+ Q_D(QPdfBaseEngine);
+
+ if (d->useAlphaEngine) {
+ QAlphaPaintEngine::updateState(state);
+ if (!continueCall())
+ return;
+ }
+
+ QPaintEngine::DirtyFlags flags = state.state();
+
+ if (flags & DirtyTransform)
+ d->stroker.matrix = state.transform();
+
+ if (flags & DirtyPen) {
+ d->pen = state.pen();
+ d->hasPen = d->pen.style() != Qt::NoPen;
+ d->stroker.setPen(d->pen);
+ QBrush penBrush = d->pen.brush();
+ bool oldSimple = d->simplePen;
+ d->simplePen = (d->hasPen && (penBrush.style() == Qt::SolidPattern) && penBrush.isOpaque());
+ if (oldSimple != d->simplePen)
+ flags |= DirtyTransform;
+ }
+ if (flags & DirtyBrush) {
+ d->brush = state.brush();
+ if (d->brush.color().alpha() == 0 && d->brush.style() == Qt::SolidPattern)
+ d->brush.setStyle(Qt::NoBrush);
+ d->hasBrush = d->brush.style() != Qt::NoBrush;
+ }
+ if (flags & DirtyBrushOrigin) {
+ d->brushOrigin = state.brushOrigin();
+ flags |= DirtyBrush;
+ }
+ if (flags & DirtyOpacity)
+ d->opacity = state.opacity();
+
+ bool ce = d->clipEnabled;
+ if (flags & DirtyClipPath) {
+ d->clipEnabled = true;
+ updateClipPath(state.clipPath(), state.clipOperation());
+ } else if (flags & DirtyClipRegion) {
+ d->clipEnabled = true;
+ QPainterPath path;
+ QVector<QRect> rects = state.clipRegion().rects();
+ for (int i = 0; i < rects.size(); ++i)
+ path.addRect(rects.at(i));
+ updateClipPath(path, state.clipOperation());
+ flags |= DirtyClipPath;
+ } else if (flags & DirtyClipEnabled) {
+ d->clipEnabled = state.isClipEnabled();
+ }
+
+ if (ce != d->clipEnabled)
+ flags |= DirtyClipPath;
+ else if (!d->clipEnabled)
+ flags &= ~DirtyClipPath;
+
+ setupGraphicsState(flags);
+}
+
+void QPdfBaseEngine::setupGraphicsState(QPaintEngine::DirtyFlags flags)
+{
+ Q_D(QPdfBaseEngine);
+ if (flags & DirtyClipPath)
+ flags |= DirtyTransform|DirtyPen|DirtyBrush;
+
+ if (flags & DirtyTransform) {
+ *d->currentPage << "Q\n";
+ flags |= DirtyPen|DirtyBrush;
+ }
+
+ if (flags & DirtyClipPath) {
+ *d->currentPage << "Q q\n";
+
+ d->allClipped = false;
+ if (d->clipEnabled && !d->clips.isEmpty()) {
+ for (int i = 0; i < d->clips.size(); ++i) {
+ if (d->clips.at(i).isEmpty()) {
+ d->allClipped = true;
+ break;
+ }
+ }
+ if (!d->allClipped) {
+ for (int i = 0; i < d->clips.size(); ++i) {
+ *d->currentPage << QPdf::generatePath(d->clips.at(i), QTransform(), QPdf::ClipPath);
+ }
+ }
+ }
+ }
+
+ if (flags & DirtyTransform) {
+ *d->currentPage << "q\n";
+ if (d->simplePen && !d->stroker.matrix.isIdentity())
+ *d->currentPage << QPdf::generateMatrix(d->stroker.matrix);
+ }
+ if (flags & DirtyBrush)
+ setBrush();
+ if (d->simplePen && (flags & DirtyPen))
+ setPen();
+}
+
+extern QPainterPath qt_regionToPath(const QRegion &region);
+
+void QPdfBaseEngine::updateClipPath(const QPainterPath &p, Qt::ClipOperation op)
+{
+ Q_D(QPdfBaseEngine);
+ QPainterPath path = d->stroker.matrix.map(p);
+ //qDebug() << "updateClipPath: " << d->stroker.matrix << p.boundingRect() << path.boundingRect() << op;
+
+ if (op == Qt::NoClip) {
+ d->clipEnabled = false;
+ d->clips.clear();
+ } else if (op == Qt::ReplaceClip) {
+ d->clips.clear();
+ d->clips.append(path);
+ } else if (op == Qt::IntersectClip) {
+ d->clips.append(path);
+ } else { // UniteClip
+ // ask the painter for the current clipping path. that's the easiest solution
+ path = painter()->clipPath();
+ path = d->stroker.matrix.map(path);
+ d->clips.clear();
+ d->clips.append(path);
+ }
+
+ if (d->useAlphaEngine) {
+ // if we have an alpha region, we have to subtract that from the
+ // any existing clip region since that region will be filled in
+ // later with images
+ QPainterPath alphaClip = qt_regionToPath(alphaClipping());
+ if (!alphaClip.isEmpty()) {
+ if (!d->clipEnabled) {
+ QRect r = d->fullPage ? d->paperRect() : d->pageRect();
+ QPainterPath dev;
+ dev.addRect(QRect(0, 0, r.width(), r.height()));
+ if (path.isEmpty())
+ path = dev;
+ else
+ path = path.intersected(dev);
+ d->clipEnabled = true;
+ } else {
+ path = painter()->clipPath();
+ path = d->stroker.matrix.map(path);
+ }
+ path = path.subtracted(alphaClip);
+ d->clips.clear();
+ d->clips.append(path);
+ }
+ }
+}
+
+void QPdfBaseEngine::setPen()
+{
+ Q_D(QPdfBaseEngine);
+ if (d->pen.style() == Qt::NoPen)
+ return;
+ QBrush b = d->pen.brush();
+ Q_ASSERT(b.style() == Qt::SolidPattern && b.isOpaque());
+
+ QColor rgba = b.color();
+ if (d->colorMode == QPrinter::GrayScale) {
+ qreal gray = qGray(rgba.rgba())/255.;
+ *d->currentPage << gray << gray << gray;
+ } else {
+ *d->currentPage << rgba.redF()
+ << rgba.greenF()
+ << rgba.blueF();
+ }
+ *d->currentPage << "SCN\n";
+
+ *d->currentPage << d->pen.widthF() << "w ";
+
+ int pdfCapStyle = 0;
+ switch(d->pen.capStyle()) {
+ case Qt::FlatCap:
+ pdfCapStyle = 0;
+ break;
+ case Qt::SquareCap:
+ pdfCapStyle = 2;
+ break;
+ case Qt::RoundCap:
+ pdfCapStyle = 1;
+ break;
+ default:
+ break;
+ }
+ *d->currentPage << pdfCapStyle << "J ";
+
+ int pdfJoinStyle = 0;
+ switch(d->pen.joinStyle()) {
+ case Qt::MiterJoin:
+ pdfJoinStyle = 0;
+ break;
+ case Qt::BevelJoin:
+ pdfJoinStyle = 2;
+ break;
+ case Qt::RoundJoin:
+ pdfJoinStyle = 1;
+ break;
+ default:
+ break;
+ }
+ *d->currentPage << pdfJoinStyle << "j ";
+
+ *d->currentPage << QPdf::generateDashes(d->pen) << " 0 d\n";
+}
+
+bool QPdfBaseEngine::newPage()
+{
+ Q_D(QPdfBaseEngine);
+ setupGraphicsState(DirtyBrush|DirtyPen|DirtyClipPath);
+ QFile *outfile = qobject_cast<QFile*> (d->outDevice);
+ if (outfile && outfile->error() != QFile::NoError)
+ return false;
+ return true;
+}
+
+
+int QPdfBaseEngine::metric(QPaintDevice::PaintDeviceMetric metricType) const
+{
+ Q_D(const QPdfBaseEngine);
+ int val;
+ QRect r = d->fullPage ? d->paperRect() : d->pageRect();
+ switch (metricType) {
+ case QPaintDevice::PdmWidth:
+ val = r.width();
+ break;
+ case QPaintDevice::PdmHeight:
+ val = r.height();
+ break;
+ case QPaintDevice::PdmDpiX:
+ case QPaintDevice::PdmDpiY:
+ val = d->resolution;
+ break;
+ case QPaintDevice::PdmPhysicalDpiX:
+ case QPaintDevice::PdmPhysicalDpiY:
+ val = 1200;
+ break;
+ case QPaintDevice::PdmWidthMM:
+ val = qRound(r.width()*25.4/d->resolution);
+ break;
+ case QPaintDevice::PdmHeightMM:
+ val = qRound(r.height()*25.4/d->resolution);
+ break;
+ case QPaintDevice::PdmNumColors:
+ val = INT_MAX;
+ break;
+ case QPaintDevice::PdmDepth:
+ val = 32;
+ break;
+ default:
+ qWarning("QPrinter::metric: Invalid metric command");
+ return 0;
+ }
+ return val;
+}
+
+void QPdfBaseEngine::setProperty(PrintEnginePropertyKey key, const QVariant &value)
+{
+ Q_D(QPdfBaseEngine);
+ switch (int(key)) {
+ case PPK_CollateCopies:
+ d->collate = value.toBool();
+ break;
+ case PPK_ColorMode:
+ d->colorMode = QPrinter::ColorMode(value.toInt());
+ break;
+ case PPK_Creator:
+ d->creator = value.toString();
+ break;
+ case PPK_DocumentName:
+ d->title = value.toString();
+ break;
+ case PPK_FullPage:
+ d->fullPage = value.toBool();
+ break;
+ case PPK_CopyCount: // fallthrough
+ case PPK_NumberOfCopies:
+ d->copies = value.toInt();
+ break;
+ case PPK_Orientation:
+ d->orientation = QPrinter::Orientation(value.toInt());
+ break;
+ case PPK_OutputFileName:
+ d->outputFileName = value.toString();
+ break;
+ case PPK_PageOrder:
+ d->pageOrder = QPrinter::PageOrder(value.toInt());
+ break;
+ case PPK_PaperSize:
+ d->paperSize = QPrinter::PaperSize(value.toInt());
+ break;
+ case PPK_PaperSource:
+ d->paperSource = QPrinter::PaperSource(value.toInt());
+ break;
+ case PPK_PrinterName:
+ d->printerName = value.toString();
+ break;
+ case PPK_PrinterProgram:
+ d->printProgram = value.toString();
+ break;
+ case PPK_Resolution:
+ d->resolution = value.toInt();
+ break;
+ case PPK_SelectionOption:
+ d->selectionOption = value.toString();
+ break;
+ case PPK_FontEmbedding:
+ d->embedFonts = value.toBool();
+ break;
+ case PPK_Duplex:
+ d->duplex = static_cast<QPrinter::DuplexMode> (value.toInt());
+ break;
+ case PPK_CupsPageRect:
+ d->cupsPageRect = value.toRect();
+ break;
+ case PPK_CupsPaperRect:
+ d->cupsPaperRect = value.toRect();
+ break;
+ case PPK_CupsOptions:
+ d->cupsOptions = value.toStringList();
+ break;
+ case PPK_CupsStringPageSize:
+ d->cupsStringPageSize = value.toString();
+ break;
+ case PPK_CustomPaperSize:
+ d->paperSize = QPrinter::Custom;
+ d->customPaperSize = value.toSizeF();
+ break;
+ case PPK_PageMargins:
+ {
+ QList<QVariant> margins(value.toList());
+ Q_ASSERT(margins.size() == 4);
+ d->leftMargin = margins.at(0).toReal();
+ d->topMargin = margins.at(1).toReal();
+ d->rightMargin = margins.at(2).toReal();
+ d->bottomMargin = margins.at(3).toReal();
+ d->hasCustomPageMargins = true;
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+QVariant QPdfBaseEngine::property(PrintEnginePropertyKey key) const
+{
+ Q_D(const QPdfBaseEngine);
+
+ QVariant ret;
+ switch (int(key)) {
+ case PPK_CollateCopies:
+ ret = d->collate;
+ break;
+ case PPK_ColorMode:
+ ret = d->colorMode;
+ break;
+ case PPK_Creator:
+ ret = d->creator;
+ break;
+ case PPK_DocumentName:
+ ret = d->title;
+ break;
+ case PPK_FullPage:
+ ret = d->fullPage;
+ break;
+ case PPK_CopyCount:
+ ret = d->copies;
+ break;
+ case PPK_SupportsMultipleCopies:
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ if (QCUPSSupport::isAvailable())
+ ret = true;
+ else
+#endif
+ ret = false;
+ break;
+ case PPK_NumberOfCopies:
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ if (QCUPSSupport::isAvailable())
+ ret = 1;
+ else
+#endif
+ ret = d->copies;
+ break;
+ case PPK_Orientation:
+ ret = d->orientation;
+ break;
+ case PPK_OutputFileName:
+ ret = d->outputFileName;
+ break;
+ case PPK_PageOrder:
+ ret = d->pageOrder;
+ break;
+ case PPK_PaperSize:
+ ret = d->paperSize;
+ break;
+ case PPK_PaperSource:
+ ret = d->paperSource;
+ break;
+ case PPK_PrinterName:
+ ret = d->printerName;
+ break;
+ case PPK_PrinterProgram:
+ ret = d->printProgram;
+ break;
+ case PPK_Resolution:
+ ret = d->resolution;
+ break;
+ case PPK_SupportedResolutions:
+ ret = QList<QVariant>() << 72;
+ break;
+ case PPK_PaperRect:
+ ret = d->paperRect();
+ break;
+ case PPK_PageRect:
+ ret = d->pageRect();
+ break;
+ case PPK_SelectionOption:
+ ret = d->selectionOption;
+ break;
+ case PPK_FontEmbedding:
+ ret = d->embedFonts;
+ break;
+ case PPK_Duplex:
+ ret = d->duplex;
+ break;
+ case PPK_CupsPageRect:
+ ret = d->cupsPageRect;
+ break;
+ case PPK_CupsPaperRect:
+ ret = d->cupsPaperRect;
+ break;
+ case PPK_CupsOptions:
+ ret = d->cupsOptions;
+ break;
+ case PPK_CupsStringPageSize:
+ ret = d->cupsStringPageSize;
+ break;
+ case PPK_CustomPaperSize:
+ ret = d->customPaperSize;
+ break;
+ case PPK_PageMargins:
+ {
+ QList<QVariant> margins;
+ if (d->hasCustomPageMargins) {
+ margins << d->leftMargin << d->topMargin
+ << d->rightMargin << d->bottomMargin;
+ } else {
+ const qreal defaultMargin = 10; // ~3.5 mm
+ margins << defaultMargin << defaultMargin
+ << defaultMargin << defaultMargin;
+ }
+ ret = margins;
+ break;
+ }
+ default:
+ break;
+ }
+ return ret;
+}
+
+QPdfBaseEnginePrivate::QPdfBaseEnginePrivate(QPrinter::PrinterMode m)
+ : clipEnabled(false), allClipped(false), hasPen(true), hasBrush(false), simplePen(false),
+ useAlphaEngine(false),
+ outDevice(0), fd(-1),
+ duplex(QPrinter::DuplexNone), collate(false), fullPage(false), embedFonts(true), copies(1),
+ pageOrder(QPrinter::FirstPageFirst), orientation(QPrinter::Portrait),
+ paperSize(QPrinter::A4), colorMode(QPrinter::Color), paperSource(QPrinter::Auto),
+ hasCustomPageMargins(false),
+ leftMargin(0), topMargin(0), rightMargin(0), bottomMargin(0)
+{
+ resolution = 72;
+ if (m == QPrinter::HighResolution)
+ resolution = 1200;
+ else if (m == QPrinter::ScreenResolution)
+ resolution = qt_defaultDpi();
+
+ postscript = false;
+ currentObject = 1;
+ currentPage = 0;
+ stroker.stream = 0;
+}
+
+bool QPdfBaseEngine::begin(QPaintDevice *pdev)
+{
+ Q_D(QPdfBaseEngine);
+ d->pdev = pdev;
+
+ d->postscript = false;
+ d->currentObject = 1;
+
+ d->currentPage = new QPdfPage;
+ d->stroker.stream = d->currentPage;
+ d->opacity = 1.0;
+
+ return d->openPrintDevice();
+}
+
+bool QPdfBaseEngine::end()
+{
+ Q_D(QPdfBaseEngine);
+ qDeleteAll(d->fonts);
+ d->fonts.clear();
+ delete d->currentPage;
+ d->currentPage = 0;
+
+ d->closePrintDevice();
+ return true;
+}
+
+#ifndef QT_NO_LPR
+static void closeAllOpenFds()
+{
+ // hack time... getting the maximum number of open
+ // files, if possible. if not we assume it's the
+ // larger of 256 and the fd we got
+ int i;
+#if defined(_SC_OPEN_MAX)
+ i = (int)sysconf(_SC_OPEN_MAX);
+#elif defined(_POSIX_OPEN_MAX)
+ i = (int)_POSIX_OPEN_MAX;
+#elif defined(OPEN_MAX)
+ i = (int)OPEN_MAX;
+#else
+ i = 256;
+#endif
+ // leave stdin/out/err untouched
+ while(--i > 2)
+ QT_CLOSE(i);
+}
+#endif
+
+bool QPdfBaseEnginePrivate::openPrintDevice()
+{
+ if(outDevice)
+ return false;
+
+ if (!outputFileName.isEmpty()) {
+ QFile *file = new QFile(outputFileName);
+ if (! file->open(QFile::WriteOnly|QFile::Truncate)) {
+ delete file;
+ return false;
+ }
+ outDevice = file;
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ } else if (QCUPSSupport::isAvailable()) {
+ QCUPSSupport cups;
+ QPair<int, QString> ret = cups.tempFd();
+ if (ret.first < 0) {
+ qWarning("QPdfPrinter: Could not open temporary file to print");
+ return false;
+ }
+ cupsTempFile = ret.second;
+ outDevice = new QFile();
+ static_cast<QFile *>(outDevice)->open(ret.first, QIODevice::WriteOnly);
+#endif
+#ifndef QT_NO_LPR
+ } else {
+ QString pr;
+ if (!printerName.isEmpty())
+ pr = printerName;
+ int fds[2];
+ if (qt_safe_pipe(fds) != 0) {
+ qWarning("QPdfPrinter: Could not open pipe to print");
+ return false;
+ }
+
+ pid_t pid = fork();
+ if (pid == 0) { // child process
+ // if possible, exit quickly, so the actual lp/lpr
+ // becomes a child of init, and ::waitpid() is
+ // guaranteed not to wait.
+ if (fork() > 0) {
+ closeAllOpenFds();
+
+ // try to replace this process with "true" - this prevents
+ // global destructors from being called (that could possibly
+ // do wrong things to the parent process)
+ (void)execlp("true", "true", (char *)0);
+ (void)execl("/bin/true", "true", (char *)0);
+ (void)execl("/usr/bin/true", "true", (char *)0);
+ ::_exit(0);
+ }
+ qt_safe_dup2(fds[0], 0, 0);
+
+ closeAllOpenFds();
+
+ if (!printProgram.isEmpty()) {
+ if (!selectionOption.isEmpty())
+ pr.prepend(selectionOption);
+ else
+ pr.prepend(QLatin1String("-P"));
+ (void)execlp(printProgram.toLocal8Bit().data(), printProgram.toLocal8Bit().data(),
+ pr.toLocal8Bit().data(), (char *)0);
+ } else {
+ // if no print program has been specified, be smart
+ // about the option string too.
+ QList<QByteArray> lprhack;
+ QList<QByteArray> lphack;
+ QByteArray media;
+ if (!pr.isEmpty() || !selectionOption.isEmpty()) {
+ if (!selectionOption.isEmpty()) {
+ QStringList list = selectionOption.split(QLatin1Char(' '));
+ for (int i = 0; i < list.size(); ++i)
+ lprhack.append(list.at(i).toLocal8Bit());
+ lphack = lprhack;
+ } else {
+ lprhack.append("-P");
+ lphack.append("-d");
+ }
+ lprhack.append(pr.toLocal8Bit());
+ lphack.append(pr.toLocal8Bit());
+ }
+ lphack.append("-s");
+
+ char ** lpargs = new char *[lphack.size()+6];
+ char lp[] = "lp";
+ lpargs[0] = lp;
+ int i;
+ for (i = 0; i < lphack.size(); ++i)
+ lpargs[i+1] = (char *)lphack.at(i).constData();
+#ifndef Q_OS_OSF
+ if (QPdf::paperSizeToString(paperSize)) {
+ char dash_o[] = "-o";
+ lpargs[++i] = dash_o;
+ lpargs[++i] = const_cast<char *>(QPdf::paperSizeToString(paperSize));
+ lpargs[++i] = dash_o;
+ media = "media=";
+ media += QPdf::paperSizeToString(paperSize);
+ lpargs[++i] = media.data();
+ }
+#endif
+ lpargs[++i] = 0;
+ char **lprargs = new char *[lprhack.size()+2];
+ char lpr[] = "lpr";
+ lprargs[0] = lpr;
+ for (int i = 0; i < lprhack.size(); ++i)
+ lprargs[i+1] = (char *)lprhack[i].constData();
+ lprargs[lprhack.size() + 1] = 0;
+ (void)execvp("lp", lpargs);
+ (void)execvp("lpr", lprargs);
+ (void)execv("/bin/lp", lpargs);
+ (void)execv("/bin/lpr", lprargs);
+ (void)execv("/usr/bin/lp", lpargs);
+ (void)execv("/usr/bin/lpr", lprargs);
+
+ delete []lpargs;
+ delete []lprargs;
+ }
+ // if we couldn't exec anything, close the fd,
+ // wait for a second so the parent process (the
+ // child of the GUI process) has exited. then
+ // exit.
+ QT_CLOSE(0);
+ (void)::sleep(1);
+ ::_exit(0);
+ }
+ // parent process
+ QT_CLOSE(fds[0]);
+ fd = fds[1];
+ (void)qt_safe_waitpid(pid, 0, 0);
+
+ if (fd < 0)
+ return false;
+
+ outDevice = new QFile();
+ static_cast<QFile *>(outDevice)->open(fd, QIODevice::WriteOnly);
+#endif
+ }
+
+ return true;
+}
+
+void QPdfBaseEnginePrivate::closePrintDevice()
+{
+ if (!outDevice)
+ return;
+ outDevice->close();
+ if (fd >= 0)
+#if defined(Q_OS_WIN) && defined(_MSC_VER) && _MSC_VER >= 1400
+ ::_close(fd);
+#else
+ ::close(fd);
+#endif
+ fd = -1;
+ delete outDevice;
+ outDevice = 0;
+
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ if (!cupsTempFile.isEmpty()) {
+ QString tempFile = cupsTempFile;
+ cupsTempFile.clear();
+ QCUPSSupport cups;
+
+ // Set up print options.
+ QByteArray prnName;
+ QList<QPair<QByteArray, QByteArray> > options;
+ QVector<cups_option_t> cupsOptStruct;
+
+ if (!printerName.isEmpty()) {
+ prnName = printerName.toLocal8Bit();
+ } else {
+ QPrinterInfo def = QPrinterInfo::defaultPrinter();
+ if (def.isNull()) {
+ qWarning("Could not determine printer to print to");
+ QFile::remove(tempFile);
+ return;
+ }
+ prnName = def.printerName().toLocal8Bit();
+ }
+
+ if (!cupsStringPageSize.isEmpty()) {
+ options.append(QPair<QByteArray, QByteArray>("media", cupsStringPageSize.toLocal8Bit()));
+ }
+
+ if (copies > 1) {
+ options.append(QPair<QByteArray, QByteArray>("copies", QString::number(copies).toLocal8Bit()));
+ }
+
+ if (collate) {
+ options.append(QPair<QByteArray, QByteArray>("Collate", "True"));
+ }
+
+ if (duplex != QPrinter::DuplexNone) {
+ switch(duplex) {
+ case QPrinter::DuplexNone: break;
+ case QPrinter::DuplexAuto:
+ if (orientation == QPrinter::Portrait)
+ options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-long-edge"));
+ else
+ options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-short-edge"));
+ break;
+ case QPrinter::DuplexLongSide:
+ options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-long-edge"));
+ break;
+ case QPrinter::DuplexShortSide:
+ options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-short-edge"));
+ break;
+ }
+ }
+
+ if (QCUPSSupport::cupsVersion() >= 10300 && orientation == QPrinter::Landscape) {
+ options.append(QPair<QByteArray, QByteArray>("landscape", ""));
+ }
+
+ QStringList::const_iterator it = cupsOptions.constBegin();
+ while (it != cupsOptions.constEnd()) {
+ options.append(QPair<QByteArray, QByteArray>((*it).toLocal8Bit(), (*(it+1)).toLocal8Bit()));
+ it += 2;
+ }
+
+ for (int c = 0; c < options.size(); ++c) {
+ cups_option_t opt;
+ opt.name = options[c].first.data();
+ opt.value = options[c].second.data();
+ cupsOptStruct.append(opt);
+ }
+
+ // Print the file.
+ cups_option_t* optPtr = cupsOptStruct.size() ? &cupsOptStruct.first() : 0;
+ cups.printFile(prnName.constData(), tempFile.toLocal8Bit().constData(),
+ title.toLocal8Bit().constData(), cupsOptStruct.size(), optPtr);
+
+ QFile::remove(tempFile);
+ }
+#endif
+}
+
+QPdfBaseEnginePrivate::~QPdfBaseEnginePrivate()
+{
+ qDeleteAll(fonts);
+ delete currentPage;
+}
+
+void QPdfBaseEnginePrivate::drawTextItem(const QPointF &p, const QTextItemInt &ti)
+{
+ Q_Q(QPdfBaseEngine);
+
+ QFontEngine *fe = ti.fontEngine;
+
+ QFontEngine::FaceId face_id = fe->faceId();
+ bool noEmbed = false;
+ if (face_id.filename.isEmpty()
+ || (!postscript && ((fe->fsType & 0x200) /* bitmap embedding only */
+ || (fe->fsType == 2) /* no embedding allowed */))) {
+ *currentPage << "Q\n";
+ q->QPaintEngine::drawTextItem(p, ti);
+ *currentPage << "q\n";
+ if (face_id.filename.isEmpty())
+ return;
+ noEmbed = true;
+ }
+
+ QFontSubset *font = fonts.value(face_id, 0);
+ if (!font) {
+ font = new QFontSubset(fe, requestObject());
+ font->noEmbed = noEmbed;
+ }
+ fonts.insert(face_id, font);
+
+ if (!currentPage->fonts.contains(font->object_id))
+ currentPage->fonts.append(font->object_id);
+
+ qreal size = ti.fontEngine->fontDef.pixelSize;
+#ifdef Q_WS_WIN
+ if (ti.fontEngine->type() == QFontEngine::Win) {
+ QFontEngineWin *fe = static_cast<QFontEngineWin *>(ti.fontEngine);
+ size = fe->tm.tmHeight;
+ }
+#endif
+
+ QVarLengthArray<glyph_t> glyphs;
+ QVarLengthArray<QFixedPoint> positions;
+ QTransform m = QTransform::fromTranslate(p.x(), p.y());
+ ti.fontEngine->getGlyphPositions(ti.glyphs, m, ti.flags,
+ glyphs, positions);
+ if (glyphs.size() == 0)
+ return;
+ int synthesized = ti.fontEngine->synthesized();
+ qreal stretch = synthesized & QFontEngine::SynthesizedStretch ? ti.fontEngine->fontDef.stretch/100. : 1.;
+
+ *currentPage << "BT\n"
+ << "/F" << font->object_id << size << "Tf "
+ << stretch << (synthesized & QFontEngine::SynthesizedItalic
+ ? "0 .3 -1 0 0 Tm\n"
+ : "0 0 -1 0 0 Tm\n");
+
+
+#if 0
+ // #### implement actual text for complex languages
+ const unsigned short *logClusters = ti.logClusters;
+ int pos = 0;
+ do {
+ int end = pos + 1;
+ while (end < ti.num_chars && logClusters[end] == logClusters[pos])
+ ++end;
+ *currentPage << "/Span << /ActualText <FEFF";
+ for (int i = pos; i < end; ++i) {
+ s << toHex((ushort)ti.chars[i].unicode(), buf);
+ }
+ *currentPage << "> >>\n"
+ "BDC\n"
+ "<";
+ int ge = end == ti.num_chars ? ti.num_glyphs : logClusters[end];
+ for (int gs = logClusters[pos]; gs < ge; ++gs)
+ *currentPage << toHex((ushort)ti.glyphs[gs].glyph, buf);
+ *currentPage << "> Tj\n"
+ "EMC\n";
+ pos = end;
+ } while (pos < ti.num_chars);
+#else
+ qreal last_x = 0.;
+ qreal last_y = 0.;
+ for (int i = 0; i < glyphs.size(); ++i) {
+ qreal x = positions[i].x.toReal();
+ qreal y = positions[i].y.toReal();
+ if (synthesized & QFontEngine::SynthesizedItalic)
+ x += .3*y;
+ x /= stretch;
+ char buf[5];
+ int g = font->addGlyph(glyphs[i]);
+ *currentPage << x - last_x << last_y - y << "Td <"
+ << QPdf::toHex((ushort)g, buf) << "> Tj\n";
+ last_x = x;
+ last_y = y;
+ }
+ if (synthesized & QFontEngine::SynthesizedBold) {
+ *currentPage << stretch << (synthesized & QFontEngine::SynthesizedItalic
+ ? "0 .3 -1 0 0 Tm\n"
+ : "0 0 -1 0 0 Tm\n");
+ *currentPage << "/Span << /ActualText <> >> BDC\n";
+ last_x = 0.5*fe->lineThickness().toReal();
+ last_y = 0.;
+ for (int i = 0; i < glyphs.size(); ++i) {
+ qreal x = positions[i].x.toReal();
+ qreal y = positions[i].y.toReal();
+ if (synthesized & QFontEngine::SynthesizedItalic)
+ x += .3*y;
+ x /= stretch;
+ char buf[5];
+ int g = font->addGlyph(glyphs[i]);
+ *currentPage << x - last_x << last_y - y << "Td <"
+ << QPdf::toHex((ushort)g, buf) << "> Tj\n";
+ last_x = x;
+ last_y = y;
+ }
+ *currentPage << "EMC\n";
+ }
+#endif
+
+ *currentPage << "ET\n";
+}
+
+QRect QPdfBaseEnginePrivate::paperRect() const
+{
+ int w;
+ int h;
+ if (paperSize == QPrinter::Custom) {
+ w = qRound(customPaperSize.width()*resolution/72.);
+ h = qRound(customPaperSize.height()*resolution/72.);
+ } else {
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ if (QCUPSSupport::isAvailable() && !cupsPaperRect.isNull()) {
+ QRect r = cupsPaperRect;
+ w = r.width();
+ h = r.height();
+ } else
+#endif
+ {
+ QPdf::PaperSize s = QPdf::paperSize(paperSize);
+ w = s.width;
+ h = s.height;
+ }
+ w = qRound(w*resolution/72.);
+ h = qRound(h*resolution/72.);
+ }
+ if (orientation == QPrinter::Portrait)
+ return QRect(0, 0, w, h);
+ else
+ return QRect(0, 0, h, w);
+}
+
+QRect QPdfBaseEnginePrivate::pageRect() const
+{
+ if(fullPage)
+ return paperRect();
+
+ QRect r;
+
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ if (!hasCustomPageMargins && QCUPSSupport::isAvailable() && !cupsPageRect.isNull()) {
+ r = cupsPageRect;
+ if (r == cupsPaperRect) {
+ // if cups doesn't define any margins, give it at least approx 3.5 mm
+ r = QRect(10, 10, r.width() - 20, r.height() - 20);
+ }
+ } else
+#endif
+ {
+ QPdf::PaperSize s;
+ if (paperSize == QPrinter::Custom) {
+ s.width = qRound(customPaperSize.width());
+ s.height = qRound(customPaperSize.height());
+ } else {
+ s = QPdf::paperSize(paperSize);
+ }
+ if (hasCustomPageMargins)
+ r = QRect(0, 0, s.width, s.height);
+ else
+ r = QRect(72/3, 72/3, s.width - 2*72/3, s.height - 2*72/3);
+ }
+
+ int x = qRound(r.left()*resolution/72.);
+ int y = qRound(r.top()*resolution/72.);
+ int w = qRound(r.width()*resolution/72.);
+ int h = qRound(r.height()*resolution/72.);
+ if (orientation == QPrinter::Portrait)
+ r = QRect(x, y, w, h);
+ else
+ r = QRect(y, x, h, w);
+
+ if (hasCustomPageMargins) {
+ r.adjust(qRound(leftMargin*(resolution/72.)),
+ qRound(topMargin*(resolution/72.)),
+ -qRound(rightMargin*(resolution/72.)),
+ -qRound(bottomMargin*(resolution/72.)));
+ }
+ return r;
+}
+
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qpdf_p.h b/src/gui/painting/qpdf_p.h
new file mode 100644
index 0000000000..39e6c21620
--- /dev/null
+++ b/src/gui/painting/qpdf_p.h
@@ -0,0 +1,299 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPDF_P_H
+#define QPDF_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+#include "QtGui/qmatrix.h"
+#include "QtCore/qstring.h"
+#include "QtCore/qvector.h"
+#include "private/qstroker_p.h"
+#include "private/qfontengine_p.h"
+#include "QtGui/qprinter.h"
+#include "private/qfontsubset_p.h"
+#include "private/qpaintengine_alpha_p.h"
+#include "qprintengine.h"
+#include "qbuffer.h"
+
+#ifndef QT_NO_PRINTER
+
+QT_BEGIN_NAMESPACE
+
+#define PPK_CupsOptions QPrintEngine::PrintEnginePropertyKey(0xfe00)
+#define PPK_CupsPageRect QPrintEngine::PrintEnginePropertyKey(0xfe01)
+#define PPK_CupsPaperRect QPrintEngine::PrintEnginePropertyKey(0xfe02)
+#define PPK_CupsStringPageSize QPrintEngine::PrintEnginePropertyKey(0xfe03)
+
+const char *qt_real_to_string(qreal val, char *buf);
+const char *qt_int_to_string(int val, char *buf);
+
+namespace QPdf {
+
+ class ByteStream
+ {
+ public:
+ // fileBacking means that ByteStream will buffer the contents on disk
+ // if the size exceeds a certain threshold. In this case, if a byte
+ // array was passed in, its contents may no longer correspond to the
+ // ByteStream contents.
+ explicit ByteStream(bool fileBacking = false);
+ explicit ByteStream(QByteArray *ba, bool fileBacking = false);
+ ~ByteStream();
+ ByteStream &operator <<(char chr);
+ ByteStream &operator <<(const char *str);
+ ByteStream &operator <<(const QByteArray &str);
+ ByteStream &operator <<(const ByteStream &src);
+ ByteStream &operator <<(qreal val);
+ ByteStream &operator <<(int val);
+ ByteStream &operator <<(const QPointF &p);
+ // Note that the stream may be invalidated by calls that insert data.
+ QIODevice *stream();
+ void clear();
+
+ static inline int maxMemorySize() { return 100000000; }
+ static inline int chunkSize() { return 10000000; }
+
+ protected:
+ void constructor_helper(QIODevice *dev);
+ void constructor_helper(QByteArray *ba);
+
+ private:
+ void prepareBuffer();
+
+ private:
+ QIODevice *dev;
+ QByteArray ba;
+ bool fileBackingEnabled;
+ bool fileBackingActive;
+ bool handleDirty;
+ };
+
+ enum PathFlags {
+ ClipPath,
+ FillPath,
+ StrokePath,
+ FillAndStrokePath
+ };
+ QByteArray generatePath(const QPainterPath &path, const QTransform &matrix, PathFlags flags);
+ QByteArray generateMatrix(const QTransform &matrix);
+ QByteArray generateDashes(const QPen &pen);
+ QByteArray patternForBrush(const QBrush &b);
+#ifdef USE_NATIVE_GRADIENTS
+ QByteArray generateLinearGradientShader(const QLinearGradient *lg, const QPointF *page_rect, bool alpha = false);
+#endif
+
+ struct Stroker {
+ Stroker();
+ void setPen(const QPen &pen);
+ void strokePath(const QPainterPath &path);
+ ByteStream *stream;
+ bool first;
+ QTransform matrix;
+ bool cosmeticPen;
+ private:
+ QStroker basicStroker;
+ QDashStroker dashStroker;
+ QStrokerOps *stroker;
+ };
+
+ QByteArray ascii85Encode(const QByteArray &input);
+
+ const char *toHex(ushort u, char *buffer);
+ const char *toHex(uchar u, char *buffer);
+
+
+ struct PaperSize {
+ int width, height; // in postscript points
+ };
+ PaperSize paperSize(QPrinter::PaperSize paperSize);
+ const char *paperSizeToString(QPrinter::PaperSize paperSize);
+
+}
+
+
+class QPdfPage : public QPdf::ByteStream
+{
+public:
+ QPdfPage();
+
+ QVector<uint> images;
+ QVector<uint> graphicStates;
+ QVector<uint> patterns;
+ QVector<uint> fonts;
+ QVector<uint> annotations;
+
+ void streamImage(int w, int h, int object);
+
+ QSize pageSize;
+private:
+};
+
+
+class QPdfBaseEnginePrivate;
+
+class QPdfBaseEngine : public QAlphaPaintEngine, public QPrintEngine
+{
+ Q_DECLARE_PRIVATE(QPdfBaseEngine)
+public:
+ QPdfBaseEngine(QPdfBaseEnginePrivate &d, PaintEngineFeatures f);
+ ~QPdfBaseEngine() {}
+
+ // reimplementations QPaintEngine
+ bool begin(QPaintDevice *pdev);
+ bool end();
+
+ void drawPoints(const QPointF *points, int pointCount);
+ void drawLines(const QLineF *lines, int lineCount);
+ void drawRects(const QRectF *rects, int rectCount);
+ void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode);
+ void drawPath (const QPainterPath & path);
+
+ void drawTextItem(const QPointF &p, const QTextItem &textItem);
+
+ void updateState(const QPaintEngineState &state);
+
+ int metric(QPaintDevice::PaintDeviceMetric metricType) const;
+ // end reimplementations QPaintEngine
+
+ // Printer stuff...
+ bool newPage();
+ void setProperty(PrintEnginePropertyKey key, const QVariant &value);
+ QVariant property(PrintEnginePropertyKey key) const;
+
+ void setPen();
+ virtual void setBrush() = 0;
+ void setupGraphicsState(QPaintEngine::DirtyFlags flags);
+
+private:
+ void updateClipPath(const QPainterPath & path, Qt::ClipOperation op);
+};
+
+class QPdfBaseEnginePrivate : public QAlphaPaintEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QPdfBaseEngine)
+public:
+ QPdfBaseEnginePrivate(QPrinter::PrinterMode m);
+ ~QPdfBaseEnginePrivate();
+
+ bool openPrintDevice();
+ void closePrintDevice();
+
+
+ virtual void drawTextItem(const QPointF &p, const QTextItemInt &ti);
+ inline uint requestObject() { return currentObject++; }
+
+ QRect paperRect() const;
+ QRect pageRect() const;
+
+ bool postscript;
+ int currentObject;
+
+ QPdfPage* currentPage;
+ QPdf::Stroker stroker;
+
+ QPointF brushOrigin;
+ QBrush brush;
+ QPen pen;
+ QList<QPainterPath> clips;
+ bool clipEnabled;
+ bool allClipped;
+ bool hasPen;
+ bool hasBrush;
+ bool simplePen;
+ qreal opacity;
+ bool useAlphaEngine;
+
+ QHash<QFontEngine::FaceId, QFontSubset *> fonts;
+
+ QPaintDevice *pdev;
+
+ // the device the output is in the end streamed to.
+ QIODevice *outDevice;
+ int fd;
+
+ // printer options
+ QString outputFileName;
+ QString printerName;
+ QString printProgram;
+ QString selectionOption;
+ QString title;
+ QString creator;
+ QPrinter::DuplexMode duplex;
+ bool collate;
+ bool fullPage;
+ bool embedFonts;
+ int copies;
+ int resolution;
+ QPrinter::PageOrder pageOrder;
+ QPrinter::Orientation orientation;
+ QPrinter::PaperSize paperSize;
+ QPrinter::ColorMode colorMode;
+ QPrinter::PaperSource paperSource;
+
+ QStringList cupsOptions;
+ QRect cupsPaperRect;
+ QRect cupsPageRect;
+ QString cupsStringPageSize;
+ QSizeF customPaperSize; // in postscript points
+ bool hasCustomPageMargins;
+ qreal leftMargin, topMargin, rightMargin, bottomMargin;
+
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ QString cupsTempFile;
+#endif
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
+
+#endif // QPDF_P_H
+
diff --git a/src/gui/painting/qpen.cpp b/src/gui/painting/qpen.cpp
new file mode 100644
index 0000000000..b69458dca5
--- /dev/null
+++ b/src/gui/painting/qpen.cpp
@@ -0,0 +1,1032 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qpen.h"
+#include "qpen_p.h"
+#include "qdatastream.h"
+#include "qvariant.h"
+#include "qbrush.h"
+
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+typedef QPenPrivate QPenData;
+
+/*!
+ \class QPen
+ \ingroup painting
+ \ingroup shared
+
+
+ \brief The QPen class defines how a QPainter should draw lines and outlines
+ of shapes.
+
+ A pen has a style(), width(), brush(), capStyle() and joinStyle().
+
+ The pen style defines the line type. The brush is used to fill
+ strokes generated with the pen. Use the QBrush class to specify
+ fill styles. The cap style determines the line end caps that can
+ be drawn using QPainter, while the join style describes how joins
+ between two lines are drawn. The pen width can be specified in
+ both integer (width()) and floating point (widthF()) precision. A
+ line width of zero indicates a cosmetic pen. This means that the
+ pen width is always drawn one pixel wide, independent of the \l
+ {QPainter#Coordinate Transformations}{transformation} set on the
+ painter.
+
+ The various settings can easily be modified using the
+ corresponding setStyle(), setWidth(), setBrush(), setCapStyle()
+ and setJoinStyle() functions (note that the painter's pen must be
+ reset when altering the pen's properties).
+
+ For example:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qpen.cpp 0
+
+ which is equivalent to
+
+ \snippet doc/src/snippets/code/src_gui_painting_qpen.cpp 1
+
+ The default pen is a solid black brush with 0 width, square
+ cap style (Qt::SquareCap), and bevel join style (Qt::BevelJoin).
+
+ In addition QPen provides the color() and setColor()
+ convenience functions to extract and set the color of the pen's
+ brush, respectively. Pens may also be compared and streamed.
+
+ For more information about painting in general, see the \l{Paint
+ System} documentation.
+
+ \tableofcontents
+
+ \section1 Pen Style
+
+ Qt provides several built-in styles represented by the
+ Qt::PenStyle enum:
+
+ \table
+ \row
+ \o \inlineimage qpen-solid.png
+ \o \inlineimage qpen-dash.png
+ \o \inlineimage qpen-dot.png
+ \row
+ \o Qt::SolidLine
+ \o Qt::DashLine
+ \o Qt::DotLine
+ \row
+ \o \inlineimage qpen-dashdot.png
+ \o \inlineimage qpen-dashdotdot.png
+ \o \inlineimage qpen-custom.png
+ \row
+ \o Qt::DashDotLine
+ \o Qt::DashDotDotLine
+ \o Qt::CustomDashLine
+ \endtable
+
+ Simply use the setStyle() function to convert the pen style to
+ either of the built-in styles, except the Qt::CustomDashLine style
+ which we will come back to shortly. Setting the style to Qt::NoPen
+ tells the painter to not draw lines or outlines. The default pen
+ style is Qt::SolidLine.
+
+ Since Qt 4.1 it is also possible to specify a custom dash pattern
+ using the setDashPattern() function which implicitly converts the
+ style of the pen to Qt::CustomDashLine. The pattern argument, a
+ QVector, must be specified as an even number of \l qreal entries
+ where the entries 1, 3, 5... are the dashes and 2, 4, 6... are the
+ spaces. For example, the custom pattern shown above is created
+ using the following code:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qpen.cpp 2
+
+ Note that the dash pattern is specified in units of the pens
+ width, e.g. a dash of length 5 in width 10 is 50 pixels long.
+
+ The currently set dash pattern can be retrieved using the
+ dashPattern() function. Use the isSolid() function to determine
+ whether the pen has a solid fill, or not.
+
+ \section1 Cap Style
+
+ The cap style defines how the end points of lines are drawn using
+ QPainter. The cap style only apply to wide lines, i.e. when the
+ width is 1 or greater. The Qt::PenCapStyle enum provides the
+ following styles:
+
+ \table
+ \row
+ \o \inlineimage qpen-square.png
+ \o \inlineimage qpen-flat.png
+ \o \inlineimage qpen-roundcap.png
+ \row
+ \o Qt::SquareCap
+ \o Qt::FlatCap
+ \o Qt::RoundCap
+ \endtable
+
+ The Qt::SquareCap style is a square line end that covers the end
+ point and extends beyond it by half the line width. The
+ Qt::FlatCap style is a square line end that does not cover the end
+ point of the line. And the Qt::RoundCap style is a rounded line
+ end covering the end point.
+
+ The default is Qt::SquareCap.
+
+ Whether or not end points are drawn when the pen width is 0 or 1
+ depends on the cap style. Using Qt::SquareCap or Qt::RoundCap they
+ are drawn, using Qt::FlatCap they are not drawn.
+
+ \section1 Join Style
+
+ The join style defines how joins between two connected lines can
+ be drawn using QPainter. The join style only apply to wide lines,
+ i.e. when the width is 1 or greater. The Qt::PenJoinStyle enum
+ provides the following styles:
+
+ \table
+ \row
+ \o \inlineimage qpen-bevel.png
+ \o \inlineimage qpen-miter.png
+ \o \inlineimage qpen-roundjoin.png
+ \row
+ \o Qt::BevelJoin
+ \o Qt::MiterJoin
+ \o Qt::RoundJoin
+ \endtable
+
+ The Qt::BevelJoin style fills the triangular notch between the two
+ lines. The Qt::MiterJoin style extends the lines to meet at an
+ angle. And the Qt::RoundJoin style fills a circular arc between
+ the two lines.
+
+ The default is Qt::BevelJoin.
+
+ \image qpen-miterlimit.png
+
+ When the Qt::MiterJoin style is applied, it is possible to use the
+ setMiterLimit() function to specify how far the miter join can
+ extend from the join point. The miterLimit() is used to reduce
+ artifacts between line joins where the lines are close to
+ parallel.
+
+ The miterLimit() must be specified in units of the pens width,
+ e.g. a miter limit of 5 in width 10 is 50 pixels long. The
+ default miter limit is 2, i.e. twice the pen width in pixels.
+
+ \table 100%
+ \row
+ \o \inlineimage qpen-demo.png
+ \o \bold {\l {demos/pathstroke}{The Path Stroking Demo}}
+
+ The Path Stroking demo shows Qt's built-in dash patterns and shows
+ how custom patterns can be used to extend the range of available
+ patterns.
+ \endtable
+
+ \sa QPainter, QBrush, {demos/pathstroke}{Path Stroking Demo},
+ {Scribble Example}
+*/
+
+/*!
+ \internal
+*/
+inline QPenPrivate::QPenPrivate(const QBrush &_brush, qreal _width, Qt::PenStyle penStyle,
+ Qt::PenCapStyle _capStyle, Qt::PenJoinStyle _joinStyle)
+ : dashOffset(0), miterLimit(2),
+ cosmetic(false)
+{
+ ref = 1;
+ width = _width;
+ brush = _brush;
+ style = penStyle;
+ capStyle = _capStyle;
+ joinStyle = _joinStyle;
+}
+
+static const Qt::PenCapStyle qpen_default_cap = Qt::SquareCap;
+static const Qt::PenJoinStyle qpen_default_join = Qt::BevelJoin;
+
+#ifndef QT_NO_THREAD
+// Special deleter that only deletes if the ref-count goes to zero
+template <>
+class QGlobalStaticDeleter<QPenPrivate>
+{
+public:
+ QGlobalStatic<QPenPrivate> &globalStatic;
+ QGlobalStaticDeleter(QGlobalStatic<QPenPrivate> &_globalStatic)
+ : globalStatic(_globalStatic)
+ { }
+
+ inline ~QGlobalStaticDeleter()
+ {
+ if (!globalStatic.pointer->ref.deref())
+ delete globalStatic.pointer;
+ globalStatic.pointer = 0;
+ globalStatic.destroyed = true;
+ }
+};
+#endif
+
+Q_GLOBAL_STATIC_WITH_ARGS(QPenData, defaultPenInstance,
+ (Qt::black, 0, Qt::SolidLine, qpen_default_cap, qpen_default_join))
+Q_GLOBAL_STATIC_WITH_ARGS(QPenData, nullPenInstance,
+ (Qt::black, 0, Qt::NoPen, qpen_default_cap, qpen_default_join))
+
+/*!
+ Constructs a default black solid line pen with 0 width.
+*/
+
+QPen::QPen()
+{
+ d = defaultPenInstance();
+ d->ref.ref();
+}
+
+/*!
+ Constructs a black pen with 0 width and the given \a style.
+
+ \sa setStyle()
+*/
+
+QPen::QPen(Qt::PenStyle style)
+{
+ if (style == Qt::NoPen) {
+ d = nullPenInstance();
+ d->ref.ref();
+ } else {
+ d = new QPenData(Qt::black, 0, style, qpen_default_cap, qpen_default_join);
+ }
+}
+
+
+/*!
+ Constructs a solid line pen with 0 width and the given \a color.
+
+ \sa setBrush(), setColor()
+*/
+
+QPen::QPen(const QColor &color)
+{
+ d = new QPenData(color, 0, Qt::SolidLine, qpen_default_cap, qpen_default_join);
+}
+
+
+/*!
+ \fn QPen::QPen(const QBrush &brush, qreal width, Qt::PenStyle style, Qt::PenCapStyle cap, Qt::PenJoinStyle join)
+
+ Constructs a pen with the specified \a brush, \a width, pen \a style,
+ \a cap style and \a join style.
+
+ \sa setBrush(), setWidth(), setStyle(), setCapStyle(), setJoinStyle()
+*/
+
+QPen::QPen(const QBrush &brush, qreal width, Qt::PenStyle s, Qt::PenCapStyle c, Qt::PenJoinStyle j)
+{
+ d = new QPenData(brush, width, s, c, j);
+}
+
+/*!
+ \fn QPen::QPen(const QPen &pen)
+
+ Constructs a pen that is a copy of the given \a pen.
+*/
+
+QPen::QPen(const QPen &p)
+{
+ d = p.d;
+ d->ref.ref();
+}
+
+
+/*!
+ Destroys the pen.
+*/
+
+QPen::~QPen()
+{
+ if (!d->ref.deref())
+ delete d;
+}
+
+/*!
+ \fn void QPen::detach()
+ Detaches from shared pen data to make sure that this pen is the
+ only one referring the data.
+
+ If multiple pens share common data, this pen dereferences the data
+ and gets a copy of the data. Nothing is done if there is just a
+ single reference.
+*/
+
+void QPen::detach()
+{
+ if (d->ref == 1)
+ return;
+
+ QPenData *x = new QPenData(*static_cast<QPenData *>(d));
+ if (!d->ref.deref())
+ delete d;
+ x->ref = 1;
+ d = x;
+}
+
+
+/*!
+ \fn QPen &QPen::operator=(const QPen &pen)
+
+ Assigns the given \a pen to this pen and returns a reference to
+ this pen.
+*/
+
+QPen &QPen::operator=(const QPen &p)
+{
+ qAtomicAssign(d, p.d);
+ return *this;
+}
+
+/*!
+ \fn void QPen::swap(QPen &other)
+ \since 4.8
+
+ Swaps pen \a other with this pen. This operation is very
+ fast and never fails.
+*/
+
+/*!
+ Returns the pen as a QVariant.
+*/
+QPen::operator QVariant() const
+{
+ return QVariant(QVariant::Pen, this);
+}
+
+/*!
+ \fn Qt::PenStyle QPen::style() const
+
+ Returns the pen style.
+
+ \sa setStyle(), {QPen#Pen Style}{Pen Style}
+*/
+Qt::PenStyle QPen::style() const
+{
+ return d->style;
+}
+/*!
+ \fn void QPen::setStyle(Qt::PenStyle style)
+
+ Sets the pen style to the given \a style.
+
+ See the \l Qt::PenStyle documentation for a list of the available
+ styles. Since Qt 4.1 it is also possible to specify a custom dash
+ pattern using the setDashPattern() function which implicitly
+ converts the style of the pen to Qt::CustomDashLine.
+
+ \note This function resets the dash offset to zero.
+
+ \sa style(), {QPen#Pen Style}{Pen Style}
+*/
+
+void QPen::setStyle(Qt::PenStyle s)
+{
+ if (d->style == s)
+ return;
+ detach();
+ d->style = s;
+ QPenData *dd = static_cast<QPenData *>(d);
+ dd->dashPattern.clear();
+ dd->dashOffset = 0;
+}
+
+/*!
+ Returns the dash pattern of this pen.
+
+ \sa style(), isSolid()
+ */
+QVector<qreal> QPen::dashPattern() const
+{
+ QPenData *dd = static_cast<QPenData *>(d);
+ if (d->style == Qt::SolidLine || d->style == Qt::NoPen) {
+ return QVector<qreal>();
+ } else if (dd->dashPattern.isEmpty()) {
+ const qreal space = 2;
+ const qreal dot = 1;
+ const qreal dash = 4;
+
+ switch (d->style) {
+ case Qt::DashLine:
+ dd->dashPattern << dash << space;
+ break;
+ case Qt::DotLine:
+ dd->dashPattern << dot << space;
+ break;
+ case Qt::DashDotLine:
+ dd->dashPattern << dash << space << dot << space;
+ break;
+ case Qt::DashDotDotLine:
+ dd->dashPattern << dash << space << dot << space << dot << space;
+ break;
+ default:
+ break;
+ }
+ }
+ return dd->dashPattern;
+}
+
+/*!
+ Sets the dash pattern for this pen to the given \a pattern. This
+ implicitly converts the style of the pen to Qt::CustomDashLine.
+
+ The pattern must be specified as an even number of positive entries
+ where the entries 1, 3, 5... are the dashes and 2, 4, 6... are the
+ spaces. For example:
+
+ \table 100%
+ \row
+ \o \inlineimage qpen-custom.png
+ \o
+ \snippet doc/src/snippets/code/src_gui_painting_qpen.cpp 3
+ \endtable
+
+ The dash pattern is specified in units of the pens width; e.g. a
+ dash of length 5 in width 10 is 50 pixels long. Note that a pen
+ with zero width is equivalent to a cosmetic pen with a width of 1
+ pixel.
+
+ Each dash is also subject to cap styles so a dash of 1 with square
+ cap set will extend 0.5 pixels out in each direction resulting in
+ a total width of 2.
+
+ Note that the default cap style is Qt::SquareCap, meaning that a
+ square line end covers the end point and extends beyond it by half
+ the line width.
+
+ \sa setStyle(), dashPattern(), setCapStyle(), setCosmetic()
+ */
+void QPen::setDashPattern(const QVector<qreal> &pattern)
+{
+ if (pattern.isEmpty())
+ return;
+ detach();
+
+ QPenData *dd = static_cast<QPenData *>(d);
+ dd->dashPattern = pattern;
+ d->style = Qt::CustomDashLine;
+
+ if ((dd->dashPattern.size() % 2) == 1) {
+ qWarning("QPen::setDashPattern: Pattern not of even length");
+ dd->dashPattern << 1;
+ }
+}
+
+
+/*!
+ Returns the dash offset for the pen.
+
+ \sa setDashOffset()
+*/
+qreal QPen::dashOffset() const
+{
+ QPenData *dd = static_cast<QPenData *>(d);
+ return dd->dashOffset;
+}
+/*!
+ Sets the dash offset (the starting point on the dash pattern) for this pen
+ to the \a offset specified. The offset is measured in terms of the units used
+ to specify the dash pattern.
+
+ \table
+ \row \o \inlineimage qpen-dashpattern.png
+ \o For example, a pattern where each stroke is four units long, followed by a gap
+ of two units, will begin with the stroke when drawn as a line.
+
+ However, if the dash offset is set to 4.0, any line drawn will begin with the gap.
+ Values of the offset up to 4.0 will cause part of the stroke to be drawn first,
+ and values of the offset between 4.0 and 6.0 will cause the line to begin with
+ part of the gap.
+ \endtable
+
+ \note This implicitly converts the style of the pen to Qt::CustomDashLine.
+*/
+void QPen::setDashOffset(qreal offset)
+{
+ if (qFuzzyCompare(offset, static_cast<QPenData *>(d)->dashOffset))
+ return;
+ detach();
+ QPenData *dd = static_cast<QPenData *>(d);
+ dd->dashOffset = offset;
+ if (d->style != Qt::CustomDashLine) {
+ dd->dashPattern = dashPattern();
+ d->style = Qt::CustomDashLine;
+ }
+}
+
+/*!
+ Returns the miter limit of the pen. The miter limit is only
+ relevant when the join style is set to Qt::MiterJoin.
+
+ \sa setMiterLimit(), {QPen#Join Style}{Join Style}
+*/
+qreal QPen::miterLimit() const
+{
+ const QPenData *dd = static_cast<QPenData *>(d);
+ return dd->miterLimit;
+}
+
+/*!
+ Sets the miter limit of this pen to the given \a limit.
+
+ \image qpen-miterlimit.png
+
+ The miter limit describes how far a miter join can extend from the
+ join point. This is used to reduce artifacts between line joins
+ where the lines are close to parallel.
+
+ This value does only have effect when the pen style is set to
+ Qt::MiterJoin. The value is specified in units of the pen's width,
+ e.g. a miter limit of 5 in width 10 is 50 pixels long. The default
+ miter limit is 2, i.e. twice the pen width in pixels.
+
+ \sa miterLimit(), setJoinStyle(), {QPen#Join Style}{Join Style}
+*/
+void QPen::setMiterLimit(qreal limit)
+{
+ detach();
+ QPenData *dd = static_cast<QPenData *>(d);
+ dd->miterLimit = limit;
+}
+
+
+/*!
+ \fn qreal QPen::width() const
+
+ Returns the pen width with integer precision.
+
+ \sa setWidth(), widthF()
+*/
+
+int QPen::width() const
+{
+ return qRound(d->width);
+}
+
+/*!
+ \fn qreal QPen::widthF() const
+
+ Returns the pen width with floating point precision.
+
+ \sa setWidthF() width()
+*/
+qreal QPen::widthF() const
+{
+ return d->width;
+}
+
+/*!
+ \fn QPen::setWidth(int width)
+
+ Sets the pen width to the given \a width in pixels with integer
+ precision.
+
+ A line width of zero indicates a cosmetic pen. This means that the
+ pen width is always drawn one pixel wide, independent of the \l
+ {QPainter#Coordinate Transformations}{transformation} set on the
+ painter.
+
+ Setting a pen width with a negative value is not supported.
+
+ \sa setWidthF(), width()
+*/
+void QPen::setWidth(int width)
+{
+ if (width < 0)
+ qWarning("QPen::setWidth: Setting a pen width with a negative value is not defined");
+ if ((qreal)width == d->width)
+ return;
+ detach();
+ d->width = width;
+}
+
+/*!
+ Sets the pen width to the given \a width in pixels with floating point
+ precision.
+
+ A line width of zero indicates a cosmetic pen. This means that the
+ pen width is always drawn one pixel wide, independent of the \l
+ {QPainter#Coordinate Transformations}{transformation} on the
+ painter.
+
+ Setting a pen width with a negative value is not supported.
+
+ \sa setWidth() widthF()
+*/
+
+void QPen::setWidthF(qreal width)
+{
+ if (width < 0.f)
+ qWarning("QPen::setWidthF: Setting a pen width with a negative value is not defined");
+ if (qAbs(d->width - width) < 0.00000001f)
+ return;
+ detach();
+ d->width = width;
+}
+
+
+/*!
+ Returns the pen's cap style.
+
+ \sa setCapStyle(), {QPen#Cap Style}{Cap Style}
+*/
+Qt::PenCapStyle QPen::capStyle() const
+{
+ return d->capStyle;
+}
+
+/*!
+ \fn void QPen::setCapStyle(Qt::PenCapStyle style)
+
+ Sets the pen's cap style to the given \a style. The default value
+ is Qt::SquareCap.
+
+ \sa capStyle(), {QPen#Cap Style}{Cap Style}
+*/
+
+void QPen::setCapStyle(Qt::PenCapStyle c)
+{
+ if (d->capStyle == c)
+ return;
+ detach();
+ d->capStyle = c;
+}
+
+/*!
+ Returns the pen's join style.
+
+ \sa setJoinStyle(), {QPen#Join Style}{Join Style}
+*/
+Qt::PenJoinStyle QPen::joinStyle() const
+{
+ return d->joinStyle;
+}
+
+/*!
+ \fn void QPen::setJoinStyle(Qt::PenJoinStyle style)
+
+ Sets the pen's join style to the given \a style. The default value
+ is Qt::BevelJoin.
+
+ \sa joinStyle(), {QPen#Join Style}{Join Style}
+*/
+
+void QPen::setJoinStyle(Qt::PenJoinStyle j)
+{
+ if (d->joinStyle == j)
+ return;
+ detach();
+ d->joinStyle = j;
+}
+
+/*!
+ \fn const QColor &QPen::color() const
+
+ Returns the color of this pen's brush.
+
+ \sa brush(), setColor()
+*/
+QColor QPen::color() const
+{
+ return d->brush.color();
+}
+
+/*!
+ \fn void QPen::setColor(const QColor &color)
+
+ Sets the color of this pen's brush to the given \a color.
+
+ \sa setBrush(), color()
+*/
+
+void QPen::setColor(const QColor &c)
+{
+ detach();
+ d->brush = QBrush(c);
+}
+
+
+/*!
+ Returns the brush used to fill strokes generated with this pen.
+*/
+QBrush QPen::brush() const
+{
+ return d->brush;
+}
+
+/*!
+ Sets the brush used to fill strokes generated with this pen to the given
+ \a brush.
+
+ \sa brush(), setColor()
+*/
+void QPen::setBrush(const QBrush &brush)
+{
+ detach();
+ d->brush = brush;
+}
+
+
+/*!
+ Returns true if the pen has a solid fill, otherwise false.
+
+ \sa style(), dashPattern()
+*/
+bool QPen::isSolid() const
+{
+ return d->brush.style() == Qt::SolidPattern;
+}
+
+
+/*!
+ Returns true if the pen is cosmetic; otherwise returns false.
+
+ Cosmetic pens are used to draw strokes that have a constant width
+ regardless of any transformations applied to the QPainter they are
+ used with. Drawing a shape with a cosmetic pen ensures that its
+ outline will have the same thickness at different scale factors.
+
+ A zero width pen is cosmetic by default; pens with a non-zero width
+ are non-cosmetic.
+
+ \sa setCosmetic(), widthF()
+*/
+
+bool QPen::isCosmetic() const
+{
+ QPenData *dd = static_cast<QPenData *>(d);
+ return (dd->cosmetic == true) || d->width == 0;
+}
+
+
+/*!
+ Sets this pen to cosmetic or non-cosmetic, depending on the value of
+ \a cosmetic.
+
+ \sa isCosmetic()
+*/
+
+void QPen::setCosmetic(bool cosmetic)
+{
+ detach();
+ QPenData *dd = static_cast<QPenData *>(d);
+ dd->cosmetic = cosmetic;
+}
+
+
+
+/*!
+ \fn bool QPen::operator!=(const QPen &pen) const
+
+ Returns true if the pen is different from the given \a pen;
+ otherwise false. Two pens are different if they have different
+ styles, widths or colors.
+
+ \sa operator==()
+*/
+
+/*!
+ \fn bool QPen::operator==(const QPen &pen) const
+
+ Returns true if the pen is equal to the given \a pen; otherwise
+ false. Two pens are equal if they have equal styles, widths and
+ colors.
+
+ \sa operator!=()
+*/
+
+bool QPen::operator==(const QPen &p) const
+{
+ QPenData *dd = static_cast<QPenData *>(d);
+ QPenData *pdd = static_cast<QPenData *>(p.d);
+ return (p.d == d)
+ || (p.d->style == d->style
+ && p.d->capStyle == d->capStyle
+ && p.d->joinStyle == d->joinStyle
+ && p.d->width == d->width
+ && pdd->miterLimit == dd->miterLimit
+ && (d->style != Qt::CustomDashLine
+ || (qFuzzyCompare(pdd->dashOffset, dd->dashOffset) &&
+ pdd->dashPattern == dd->dashPattern))
+ && p.d->brush == d->brush
+ && pdd->cosmetic == dd->cosmetic);
+}
+
+
+/*!
+ \fn bool QPen::isDetached()
+
+ \internal
+*/
+
+bool QPen::isDetached()
+{
+ return d->ref == 1;
+}
+
+
+/*****************************************************************************
+ QPen stream functions
+ *****************************************************************************/
+#ifndef QT_NO_DATASTREAM
+/*!
+ \fn QDataStream &operator<<(QDataStream &stream, const QPen &pen)
+ \relates QPen
+
+ Writes the given \a pen to the given \a stream and returns a reference to
+ the \a stream.
+
+ \sa {Serializing Qt Data Types}
+*/
+
+QDataStream &operator<<(QDataStream &s, const QPen &p)
+{
+ QPenData *dd = static_cast<QPenData *>(p.d);
+ if (s.version() < 3) {
+ s << (quint8)p.style();
+ } else if (s.version() < QDataStream::Qt_4_3) {
+ s << (quint8)(p.style() | p.capStyle() | p.joinStyle());
+ } else {
+ s << (quint16)(p.style() | p.capStyle() | p.joinStyle());
+ s << (bool)(dd->cosmetic);
+ }
+
+ if (s.version() < 7) {
+ s << (quint8)p.width();
+ s << p.color();
+ } else {
+ s << double(p.widthF());
+ s << p.brush();
+ s << double(p.miterLimit());
+ if (sizeof(qreal) == sizeof(double)) {
+ s << p.dashPattern();
+ } else {
+ // ensure that we write doubles here instead of streaming the pattern
+ // directly; otherwise, platforms that redefine qreal might generate
+ // data that cannot be read on other platforms.
+ QVector<qreal> pattern = p.dashPattern();
+ s << quint32(pattern.size());
+ for (int i = 0; i < pattern.size(); ++i)
+ s << double(pattern.at(i));
+ }
+ if (s.version() >= 9)
+ s << double(p.dashOffset());
+ }
+ return s;
+}
+
+/*!
+ \fn QDataStream &operator>>(QDataStream &stream, QPen &pen)
+ \relates QPen
+
+ Reads a pen from the given \a stream into the given \a pen and
+ returns a reference to the \a stream.
+
+ \sa {Serializing Qt Data Types}
+*/
+
+QDataStream &operator>>(QDataStream &s, QPen &p)
+{
+ quint16 style;
+ quint8 width8 = 0;
+ double width = 0;
+ QColor color;
+ QBrush brush;
+ double miterLimit = 2;
+ QVector<qreal> dashPattern;
+ double dashOffset = 0;
+ bool cosmetic = false;
+ if (s.version() < QDataStream::Qt_4_3) {
+ quint8 style8;
+ s >> style8;
+ style = style8;
+ } else {
+ s >> style;
+ s >> cosmetic;
+ }
+ if (s.version() < 7) {
+ s >> width8;
+ s >> color;
+ brush = color;
+ width = width8;
+ } else {
+ s >> width;
+ s >> brush;
+ s >> miterLimit;
+ if (sizeof(qreal) == sizeof(double)) {
+ s >> dashPattern;
+ } else {
+ quint32 numDashes;
+ s >> numDashes;
+ double dash;
+ for (quint32 i = 0; i < numDashes; ++i) {
+ s >> dash;
+ dashPattern << dash;
+ }
+ }
+ if (s.version() >= 9)
+ s >> dashOffset;
+ }
+
+ p.detach();
+ QPenData *dd = static_cast<QPenData *>(p.d);
+ dd->width = width;
+ dd->brush = brush;
+ dd->style = Qt::PenStyle(style & Qt::MPenStyle);
+ dd->capStyle = Qt::PenCapStyle(style & Qt::MPenCapStyle);
+ dd->joinStyle = Qt::PenJoinStyle(style & Qt::MPenJoinStyle);
+ dd->dashPattern = dashPattern;
+ dd->miterLimit = miterLimit;
+ dd->dashOffset = dashOffset;
+ dd->cosmetic = cosmetic;
+
+ return s;
+}
+#endif //QT_NO_DATASTREAM
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QPen &p)
+{
+#ifndef Q_BROKEN_DEBUG_STREAM
+ const char *PEN_STYLES[] = {
+ "NoPen",
+ "SolidLine",
+ "DashLine",
+ "DotLine",
+ "DashDotLine",
+ "DashDotDotLine",
+ "CustomDashLine"
+ };
+
+ dbg.nospace() << "QPen(" << p.width() << ',' << p.brush()
+ << ',' << PEN_STYLES[p.style()] << ',' << int(p.capStyle())
+ << ',' << int(p.joinStyle()) << ',' << p.dashPattern()
+ << ',' << p.dashOffset()
+ << ',' << p.miterLimit() << ')';
+ return dbg.space();
+#else
+ qWarning("This compiler doesn't support streaming QPen to QDebug");
+ return dbg;
+ Q_UNUSED(p);
+#endif
+}
+#endif
+
+/*!
+ \fn DataPtr &QPen::data_ptr()
+ \internal
+*/
+
+/*!
+ \typedef QPen::DataPtr
+
+ \internal
+*/
+
+QT_END_NAMESPACE
+
+#undef QT_COMPILING_QPEN
diff --git a/src/gui/painting/qpen.h b/src/gui/painting/qpen.h
new file mode 100644
index 0000000000..001cd448e0
--- /dev/null
+++ b/src/gui/painting/qpen.h
@@ -0,0 +1,145 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPEN_H
+#define QPEN_H
+
+#include <QtGui/qcolor.h>
+#include <QtGui/qbrush.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QVariant;
+class QPenPrivate;
+class QBrush;
+class QPen;
+
+#ifndef QT_NO_DATASTREAM
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QPen &);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QPen &);
+#endif
+
+class Q_GUI_EXPORT QPen
+{
+public:
+ QPen();
+ QPen(Qt::PenStyle);
+ QPen(const QColor &color);
+ QPen(const QBrush &brush, qreal width, Qt::PenStyle s = Qt::SolidLine,
+ Qt::PenCapStyle c = Qt::SquareCap, Qt::PenJoinStyle j = Qt::BevelJoin);
+ QPen(const QPen &pen);
+
+ ~QPen();
+
+ QPen &operator=(const QPen &pen);
+#ifdef Q_COMPILER_RVALUE_REFS
+ inline QPen &operator=(QPen &&other)
+ { qSwap(d, other.d); return *this; }
+#endif
+ inline void swap(QPen &other) { qSwap(d, other.d); }
+
+ Qt::PenStyle style() const;
+ void setStyle(Qt::PenStyle);
+
+ QVector<qreal> dashPattern() const;
+ void setDashPattern(const QVector<qreal> &pattern);
+
+ qreal dashOffset() const;
+ void setDashOffset(qreal doffset);
+
+ qreal miterLimit() const;
+ void setMiterLimit(qreal limit);
+
+ qreal widthF() const;
+ void setWidthF(qreal width);
+
+ int width() const;
+ void setWidth(int width);
+
+ QColor color() const;
+ void setColor(const QColor &color);
+
+ QBrush brush() const;
+ void setBrush(const QBrush &brush);
+
+ bool isSolid() const;
+
+ Qt::PenCapStyle capStyle() const;
+ void setCapStyle(Qt::PenCapStyle pcs);
+
+ Qt::PenJoinStyle joinStyle() const;
+ void setJoinStyle(Qt::PenJoinStyle pcs);
+
+ bool isCosmetic() const;
+ void setCosmetic(bool cosmetic);
+
+ bool operator==(const QPen &p) const;
+ inline bool operator!=(const QPen &p) const { return !(operator==(p)); }
+ operator QVariant() const;
+
+ bool isDetached();
+private:
+ friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QPen &);
+ friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QPen &);
+
+ void detach();
+ class QPenPrivate *d;
+
+public:
+ typedef QPenPrivate * DataPtr;
+ inline DataPtr &data_ptr() { return d; }
+};
+Q_DECLARE_TYPEINFO(QPen, Q_MOVABLE_TYPE);
+Q_DECLARE_SHARED(QPen)
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QPen &);
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPEN_H
diff --git a/src/gui/painting/qpen_p.h b/src/gui/painting/qpen_p.h
new file mode 100644
index 0000000000..ef63b7650f
--- /dev/null
+++ b/src/gui/painting/qpen_p.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPEN_P_H
+#define QPEN_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPenPrivate {
+public:
+ QPenPrivate(const QBrush &brush, qreal width, Qt::PenStyle, Qt::PenCapStyle,
+ Qt::PenJoinStyle _joinStyle);
+ QAtomicInt ref;
+ qreal width;
+ QBrush brush;
+ Qt::PenStyle style;
+ Qt::PenCapStyle capStyle;
+ Qt::PenJoinStyle joinStyle;
+ mutable QVector<qreal> dashPattern;
+ qreal dashOffset;
+ qreal miterLimit;
+ uint cosmetic : 1;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPEN_P_H
diff --git a/src/gui/painting/qpolygon.cpp b/src/gui/painting/qpolygon.cpp
new file mode 100644
index 0000000000..41b686adef
--- /dev/null
+++ b/src/gui/painting/qpolygon.cpp
@@ -0,0 +1,1003 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qpolygon.h"
+#include "qrect.h"
+#include "qdatastream.h"
+#include "qmatrix.h"
+#include "qdebug.h"
+#include "qpainterpath.h"
+#include "qvariant.h"
+#include "qpainterpath_p.h"
+#include "qbezier_p.h"
+
+#include <stdarg.h>
+
+QT_BEGIN_NAMESPACE
+
+//same as qt_painterpath_isect_line in qpainterpath.cpp
+static void qt_polygon_isect_line(const QPointF &p1, const QPointF &p2, const QPointF &pos,
+ int *winding)
+{
+ qreal x1 = p1.x();
+ qreal y1 = p1.y();
+ qreal x2 = p2.x();
+ qreal y2 = p2.y();
+ qreal y = pos.y();
+
+ int dir = 1;
+
+ if (qFuzzyCompare(y1, y2)) {
+ // ignore horizontal lines according to scan conversion rule
+ return;
+ } else if (y2 < y1) {
+ qreal x_tmp = x2; x2 = x1; x1 = x_tmp;
+ qreal y_tmp = y2; y2 = y1; y1 = y_tmp;
+ dir = -1;
+ }
+
+ if (y >= y1 && y < y2) {
+ qreal x = x1 + ((x2 - x1) / (y2 - y1)) * (y - y1);
+
+ // count up the winding number if we're
+ if (x<=pos.x()) {
+ (*winding) += dir;
+ }
+ }
+}
+
+/*!
+ \class QPolygon
+ \brief The QPolygon class provides a vector of points using
+ integer precision.
+
+ \reentrant
+
+ \ingroup painting
+ \ingroup shared
+
+ A QPolygon object is a QVector<QPoint>. The easiest way to add
+ points to a QPolygon is to use QVector's streaming operator, as
+ illustrated below:
+
+ \snippet doc/src/snippets/polygon/polygon.cpp 0
+
+ In addition to the functions provided by QVector, QPolygon
+ provides some point-specific functions.
+
+ Each point in a polygon can be retrieved by passing its index to
+ the point() function. To populate the polygon, QPolygon provides
+ the setPoint() function to set the point at a given index, the
+ setPoints() function to set all the points in the polygon
+ (resizing it to the given number of points), and the putPoints()
+ function which copies a number of given points into the polygon
+ from a specified index (resizing the polygon if necessary).
+
+ QPolygon provides the boundingRect() and translate() functions for
+ geometry functions. Use the QMatrix::map() function for more
+ general transformations of QPolygons.
+
+ The QPolygon class is \l {Implicit Data Sharing}{implicitly
+ shared}.
+
+ \sa QVector, QPolygonF, QLine
+*/
+
+
+/*****************************************************************************
+ QPolygon member functions
+ *****************************************************************************/
+
+/*!
+ \fn QPolygon::QPolygon()
+
+ Constructs a polygon with no points.
+
+ \sa QVector::isEmpty()
+*/
+
+/*!
+ \fn QPolygon::QPolygon(int size)
+
+ Constructs a polygon of the given \a size. Creates an empty
+ polygon if \a size == 0.
+
+ \sa QVector::isEmpty()
+*/
+
+/*!
+ \fn QPolygon::QPolygon(const QPolygon &polygon)
+
+ Constructs a copy of the given \a polygon.
+
+ \sa setPoints()
+*/
+
+/*!
+ \fn QPolygon::QPolygon(const QVector<QPoint> &points)
+
+ Constructs a polygon containing the specified \a points.
+
+ \sa setPoints()
+*/
+
+/*!
+ \fn QPolygon::QPolygon(const QRect &rectangle, bool closed)
+
+ Constructs a polygon from the given \a rectangle. If \a closed is
+ false, the polygon just contains the four points of the rectangle
+ ordered clockwise, otherwise the polygon's fifth point is set to
+ \a {rectangle}.topLeft().
+
+ Note that the bottom-right corner of the rectangle is located at
+ (rectangle.x() + rectangle.width(), rectangle.y() +
+ rectangle.height()).
+
+ \sa setPoints()
+*/
+
+QPolygon::QPolygon(const QRect &r, bool closed)
+{
+ reserve(closed ? 5 : 4);
+ *this << QPoint(r.x(), r.y())
+ << QPoint(r.x() + r.width(), r.y())
+ << QPoint(r.x() + r.width(), r.y() + r.height())
+ << QPoint(r.x(), r.y() + r.height());
+ if (closed)
+ *this << QPoint(r.left(), r.top());
+}
+
+/*!
+ \internal
+ Constructs a point array with \a nPoints points, taken from the
+ \a points array.
+
+ Equivalent to setPoints(nPoints, points).
+*/
+
+QPolygon::QPolygon(int nPoints, const int *points)
+{
+ setPoints(nPoints, points);
+}
+
+
+/*!
+ \fn QPolygon::~QPolygon()
+
+ Destroys the polygon.
+*/
+
+
+/*!
+ Translates all points in the polygon by (\a{dx}, \a{dy}).
+
+ \sa translated()
+*/
+
+void QPolygon::translate(int dx, int dy)
+{
+ if (dx == 0 && dy == 0)
+ return;
+
+ register QPoint *p = data();
+ register int i = size();
+ QPoint pt(dx, dy);
+ while (i--) {
+ *p += pt;
+ ++p;
+ }
+}
+
+/*!
+ \fn void QPolygon::translate(const QPoint &offset)
+ \overload
+
+ Translates all points in the polygon by the given \a offset.
+
+ \sa translated()
+*/
+
+/*!
+ Returns a copy of the polygon that is translated by (\a{dx}, \a{dy}).
+
+ \since 4.6
+ \sa translate()
+*/
+QPolygon QPolygon::translated(int dx, int dy) const
+{
+ QPolygon copy(*this);
+ copy.translate(dx, dy);
+ return copy;
+}
+
+/*!
+ \fn void QPolygon::translated(const QPoint &offset) const
+ \overload
+ \since 4.6
+
+ Returns a copy of the polygon that is translated by the given \a offset.
+
+ \sa translate()
+*/
+
+/*!
+ Extracts the coordinates of the point at the given \a index to
+ *\a{x} and *\a{y} (if they are valid pointers).
+
+ \sa setPoint()
+*/
+
+void QPolygon::point(int index, int *x, int *y) const
+{
+ QPoint p = at(index);
+ if (x)
+ *x = (int)p.x();
+ if (y)
+ *y = (int)p.y();
+}
+
+/*!
+ \fn QPoint QPolygon::point(int index) const
+ \overload
+
+ Returns the point at the given \a index.
+*/
+
+/*!
+ \fn void QPolygon::setPoint(int index, const QPoint &point)
+ \overload
+
+ Sets the point at the given \a index to the given \a point.
+*/
+
+/*!
+ \fn void QPolygon::setPoint(int index, int x, int y)
+
+ Sets the point at the given \a index to the point specified by
+ (\a{x}, \a{y}).
+
+ \sa point(), putPoints(), setPoints(),
+*/
+
+/*!
+ Resizes the polygon to \a nPoints and populates it with the given
+ \a points.
+
+ The example code creates a polygon with two points (10, 20) and
+ (30, 40):
+
+ \snippet doc/src/snippets/polygon/polygon.cpp 2
+
+ \sa setPoint() putPoints()
+*/
+
+void QPolygon::setPoints(int nPoints, const int *points)
+{
+ resize(nPoints);
+ int i = 0;
+ while (nPoints--) {
+ setPoint(i++, *points, *(points+1));
+ points += 2;
+ }
+}
+
+/*!
+ \overload
+
+ Resizes the polygon to \a nPoints and populates it with the points
+ specified by the variable argument list. The points are given as a
+ sequence of integers, starting with \a firstx then \a firsty, and
+ so on.
+
+ The example code creates a polygon with two points (10, 20) and
+ (30, 40):
+
+ \snippet doc/src/snippets/polygon/polygon.cpp 3
+*/
+
+void QPolygon::setPoints(int nPoints, int firstx, int firsty, ...)
+{
+ va_list ap;
+ resize(nPoints);
+ setPoint(0, firstx, firsty);
+ int i = 0, x, y;
+ va_start(ap, firsty);
+ while (--nPoints) {
+ x = va_arg(ap, int);
+ y = va_arg(ap, int);
+ setPoint(++i, x, y);
+ }
+ va_end(ap);
+}
+
+/*!
+ \overload
+ \internal
+
+ Copies \a nPoints points from the \a points coord array into this
+ point array, and resizes the point array if \c{index+nPoints}
+ exceeds the size of the array.
+
+ \sa setPoint()
+*/
+
+void QPolygon::putPoints(int index, int nPoints, const int *points)
+{
+ if (index + nPoints > size())
+ resize(index + nPoints);
+ int i = index;
+ while (nPoints--) {
+ setPoint(i++, *points, *(points+1));
+ points += 2;
+ }
+}
+
+/*!
+ Copies \a nPoints points from the variable argument list into this
+ polygon from the given \a index.
+
+ The points are given as a sequence of integers, starting with \a
+ firstx then \a firsty, and so on. The polygon is resized if
+ \c{index+nPoints} exceeds its current size.
+
+ The example code creates a polygon with three points (4,5), (6,7)
+ and (8,9), by expanding the polygon from 1 to 3 points:
+
+ \snippet doc/src/snippets/polygon/polygon.cpp 4
+
+ The following code has the same result, but here the putPoints()
+ function overwrites rather than extends:
+
+ \snippet doc/src/snippets/polygon/polygon.cpp 5
+
+ \sa setPoints()
+*/
+
+void QPolygon::putPoints(int index, int nPoints, int firstx, int firsty, ...)
+{
+ va_list ap;
+ if (index + nPoints > size())
+ resize(index + nPoints);
+ if (nPoints <= 0)
+ return;
+ setPoint(index, firstx, firsty);
+ int i = index, x, y;
+ va_start(ap, firsty);
+ while (--nPoints) {
+ x = va_arg(ap, int);
+ y = va_arg(ap, int);
+ setPoint(++i, x, y);
+ }
+ va_end(ap);
+}
+
+
+/*!
+ \fn void QPolygon::putPoints(int index, int nPoints, const QPolygon &fromPolygon, int fromIndex)
+ \overload
+
+ Copies \a nPoints points from the given \a fromIndex ( 0 by
+ default) in \a fromPolygon into this polygon, starting at the
+ specified \a index. For example:
+
+ \snippet doc/src/snippets/polygon/polygon.cpp 6
+*/
+
+void QPolygon::putPoints(int index, int nPoints, const QPolygon & from, int fromIndex)
+{
+ if (index + nPoints > size())
+ resize(index + nPoints);
+ if (nPoints <= 0)
+ return;
+ int n = 0;
+ while(n < nPoints) {
+ setPoint(index + n, from[fromIndex+n]);
+ ++n;
+ }
+}
+
+
+/*!
+ Returns the bounding rectangle of the polygon, or QRect(0, 0, 0,
+ 0) if the polygon is empty.
+
+ \sa QVector::isEmpty()
+*/
+
+QRect QPolygon::boundingRect() const
+{
+ if (isEmpty())
+ return QRect(0, 0, 0, 0);
+ register const QPoint *pd = constData();
+ int minx, maxx, miny, maxy;
+ minx = maxx = pd->x();
+ miny = maxy = pd->y();
+ ++pd;
+ for (int i = 1; i < size(); ++i) {
+ if (pd->x() < minx)
+ minx = pd->x();
+ else if (pd->x() > maxx)
+ maxx = pd->x();
+ if (pd->y() < miny)
+ miny = pd->y();
+ else if (pd->y() > maxy)
+ maxy = pd->y();
+ ++pd;
+ }
+ return QRect(QPoint(minx,miny), QPoint(maxx,maxy));
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QPolygon &a)
+{
+#ifndef Q_BROKEN_DEBUG_STREAM
+ dbg.nospace() << "QPolygon(";
+ for (int i = 0; i < a.count(); ++i)
+ dbg.nospace() << a.at(i);
+ dbg.nospace() << ')';
+ return dbg.space();
+#else
+ qWarning("This compiler doesn't support streaming QPolygon to QDebug");
+ return dbg;
+ Q_UNUSED(a);
+#endif
+}
+#endif
+
+
+/*!
+ \class QPolygonF
+ \brief The QPolygonF class provides a vector of points using
+ floating point precision.
+
+ \reentrant
+ \ingroup painting
+ \ingroup shared
+
+ A QPolygonF is a QVector<QPointF>. The easiest way to add points
+ to a QPolygonF is to use its streaming operator, as illustrated
+ below:
+
+ \snippet doc/src/snippets/polygon/polygon.cpp 1
+
+ In addition to the functions provided by QVector, QPolygonF
+ provides the boundingRect() and translate() functions for geometry
+ operations. Use the QMatrix::map() function for more general
+ transformations of QPolygonFs.
+
+ QPolygonF also provides the isClosed() function to determine
+ whether a polygon's start and end points are the same, and the
+ toPolygon() function returning an integer precision copy of this
+ polygon.
+
+ The QPolygonF class is \l {Implicit Data Sharing}{implicitly
+ shared}.
+
+ \sa QVector, QPolygon, QLineF
+*/
+
+
+/*****************************************************************************
+ QPolygonF member functions
+ *****************************************************************************/
+
+/*!
+ \fn QPolygonF::QPolygonF()
+
+ Constructs a polygon with no points.
+
+ \sa QVector::isEmpty()
+*/
+
+/*!
+ \fn QPolygonF::QPolygonF(int size)
+
+ Constructs a polygon of the given \a size. Creates an empty
+ polygon if \a size == 0.
+
+ \sa QVector::isEmpty()
+*/
+
+/*!
+ \fn QPolygonF::QPolygonF(const QPolygonF &polygon)
+
+ Constructs a copy of the given \a polygon.
+*/
+
+/*!
+ \fn QPolygonF::QPolygonF(const QVector<QPointF> &points)
+
+ Constructs a polygon containing the specified \a points.
+*/
+
+/*!
+ \fn QPolygonF::QPolygonF(const QRectF &rectangle)
+
+ Constructs a closed polygon from the specified \a rectangle.
+
+ The polygon contains the four vertices of the rectangle in
+ clockwise order starting and ending with the top-left vertex.
+
+ \sa isClosed()
+*/
+
+QPolygonF::QPolygonF(const QRectF &r)
+{
+ reserve(5);
+ append(QPointF(r.x(), r.y()));
+ append(QPointF(r.x() + r.width(), r.y()));
+ append(QPointF(r.x() + r.width(), r.y() + r.height()));
+ append(QPointF(r.x(), r.y() + r.height()));
+ append(QPointF(r.x(), r.y()));
+}
+
+/*!
+ \fn QPolygonF::QPolygonF(const QPolygon &polygon)
+
+ Constructs a float based polygon from the specified integer based
+ \a polygon.
+
+ \sa toPolygon()
+*/
+
+QPolygonF::QPolygonF(const QPolygon &a)
+{
+ reserve(a.size());
+ for (int i=0; i<a.size(); ++i)
+ append(a.at(i));
+}
+
+/*!
+ \fn QPolygonF::~QPolygonF()
+
+ Destroys the polygon.
+*/
+
+
+/*!
+ Translate all points in the polygon by the given \a offset.
+
+ \sa translated()
+*/
+
+void QPolygonF::translate(const QPointF &offset)
+{
+ if (offset.isNull())
+ return;
+
+ register QPointF *p = data();
+ register int i = size();
+ while (i--) {
+ *p += offset;
+ ++p;
+ }
+}
+
+/*!
+ \fn void QPolygonF::translate(qreal dx, qreal dy)
+ \overload
+
+ Translates all points in the polygon by (\a{dx}, \a{dy}).
+
+ \sa translated()
+*/
+
+/*!
+ Returns a copy of the polygon that is translated by the given \a offset.
+
+ \since 4.6
+ \sa translate()
+*/
+QPolygonF QPolygonF::translated(const QPointF &offset) const
+{
+ QPolygonF copy(*this);
+ copy.translate(offset);
+ return copy;
+}
+
+/*!
+ \fn void QPolygonF::translated(qreal dx, qreal dy) const
+ \overload
+ \since 4.6
+
+ Returns a copy of the polygon that is translated by (\a{dx}, \a{dy}).
+
+ \sa translate()
+*/
+
+/*!
+ \fn bool QPolygonF::isClosed() const
+
+ Returns true if the polygon is closed; otherwise returns false.
+
+ A polygon is said to be closed if its start point and end point are equal.
+
+ \sa QVector::first(), QVector::last()
+*/
+
+/*!
+ Returns the bounding rectangle of the polygon, or QRectF(0,0,0,0)
+ if the polygon is empty.
+
+ \sa QVector::isEmpty()
+*/
+
+QRectF QPolygonF::boundingRect() const
+{
+ if (isEmpty())
+ return QRectF(0, 0, 0, 0);
+ register const QPointF *pd = constData();
+ qreal minx, maxx, miny, maxy;
+ minx = maxx = pd->x();
+ miny = maxy = pd->y();
+ ++pd;
+ for (int i = 1; i < size(); ++i) {
+ if (pd->x() < minx)
+ minx = pd->x();
+ else if (pd->x() > maxx)
+ maxx = pd->x();
+ if (pd->y() < miny)
+ miny = pd->y();
+ else if (pd->y() > maxy)
+ maxy = pd->y();
+ ++pd;
+ }
+ return QRectF(minx,miny, maxx - minx, maxy - miny);
+}
+
+/*!
+ Creates and returns a QPolygon by converting each QPointF to a
+ QPoint.
+
+ \sa QPointF::toPoint()
+*/
+
+QPolygon QPolygonF::toPolygon() const
+{
+ QPolygon a;
+ a.reserve(size());
+ for (int i=0; i<size(); ++i)
+ a.append(at(i).toPoint());
+ return a;
+}
+
+/*!
+ \fn void QPolygon::swap(QPolygon &other)
+ \since 4.8
+
+ Swaps polygon \a other with this polygon. This operation is very
+ fast and never fails.
+*/
+
+/*!
+ \fn void QPolygonF::swap(QPolygonF &other)
+ \since 4.8
+
+ Swaps polygon \a other with this polygon. This operation is very
+ fast and never fails.
+*/
+
+/*!
+ Returns the polygon as a QVariant
+*/
+QPolygon::operator QVariant() const
+{
+ return QVariant(QVariant::Polygon, this);
+}
+
+/*****************************************************************************
+ QPolygon stream functions
+ *****************************************************************************/
+#ifndef QT_NO_DATASTREAM
+/*!
+ \fn QDataStream &operator<<(QDataStream &stream, const QPolygon &polygon)
+ \since 4.4
+ \relates QPolygon
+
+ Writes the given \a polygon to the given \a stream, and returns a
+ reference to the stream.
+
+ \sa {Serializing Qt Data Types}
+*/
+QDataStream &operator<<(QDataStream &s, const QPolygon &a)
+{
+ const QVector<QPoint> &v = a;
+ return s << v;
+}
+
+/*!
+ \fn QDataStream &operator>>(QDataStream &stream, QPolygon &polygon)
+ \since 4.4
+ \relates QPolygon
+
+ Reads a polygon from the given \a stream into the given \a
+ polygon, and returns a reference to the stream.
+
+ \sa {Serializing Qt Data Types}
+*/
+QDataStream &operator>>(QDataStream &s, QPolygon &a)
+{
+ QVector<QPoint> &v = a;
+ return s >> v;
+}
+#endif // QT_NO_DATASTREAM
+
+/*****************************************************************************
+ QPolygonF stream functions
+ *****************************************************************************/
+#ifndef QT_NO_DATASTREAM
+/*!
+ \fn QDataStream &operator<<(QDataStream &stream, const QPolygonF &polygon)
+ \relates QPolygonF
+
+ Writes the given \a polygon to the given \a stream, and returns a
+ reference to the stream.
+
+ \sa {Serializing Qt Data Types}
+*/
+
+QDataStream &operator<<(QDataStream &s, const QPolygonF &a)
+{
+ quint32 len = a.size();
+ uint i;
+
+ s << len;
+ for (i = 0; i < len; ++i)
+ s << a.at(i);
+ return s;
+}
+
+/*!
+ \fn QDataStream &operator>>(QDataStream &stream, QPolygonF &polygon)
+ \relates QPolygonF
+
+ Reads a polygon from the given \a stream into the given \a
+ polygon, and returns a reference to the stream.
+
+ \sa {Serializing Qt Data Types}
+*/
+
+QDataStream &operator>>(QDataStream &s, QPolygonF &a)
+{
+ quint32 len;
+ uint i;
+
+ s >> len;
+ a.reserve(a.size() + (int)len);
+ QPointF p;
+ for (i = 0; i < len; ++i) {
+ s >> p;
+ a.insert(i, p);
+ }
+ return s;
+}
+#endif //QT_NO_DATASTREAM
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QPolygonF &a)
+{
+#ifndef Q_BROKEN_DEBUG_STREAM
+ dbg.nospace() << "QPolygonF(";
+ for (int i = 0; i < a.count(); ++i)
+ dbg.nospace() << a.at(i);
+ dbg.nospace() << ')';
+ return dbg.space();
+#else
+ qWarning("This compiler doesn't support streaming QPolygonF to QDebug");
+ return dbg;
+ Q_UNUSED(a);
+#endif
+}
+#endif
+
+
+/*!
+ \since 4.3
+
+ \fn bool QPolygonF::containsPoint(const QPointF &point, Qt::FillRule fillRule) const
+
+ Returns true if the given \a point is inside the polygon according to
+ the specified \a fillRule; otherwise returns false.
+*/
+bool QPolygonF::containsPoint(const QPointF &pt, Qt::FillRule fillRule) const
+{
+ if (isEmpty())
+ return false;
+
+ int winding_number = 0;
+
+ QPointF last_pt = at(0);
+ QPointF last_start = at(0);
+ for (int i = 1; i < size(); ++i) {
+ const QPointF &e = at(i);
+ qt_polygon_isect_line(last_pt, e, pt, &winding_number);
+ last_pt = e;
+ }
+
+ // implicitly close last subpath
+ if (last_pt != last_start)
+ qt_polygon_isect_line(last_pt, last_start, pt, &winding_number);
+
+ return (fillRule == Qt::WindingFill
+ ? (winding_number != 0)
+ : ((winding_number % 2) != 0));
+}
+
+/*!
+ \since 4.3
+
+ \fn bool QPolygon::containsPoint(const QPoint &point, Qt::FillRule fillRule) const
+ Returns true if the given \a point is inside the polygon according to
+ the specified \a fillRule; otherwise returns false.
+*/
+bool QPolygon::containsPoint(const QPoint &pt, Qt::FillRule fillRule) const
+{
+ if (isEmpty())
+ return false;
+
+ int winding_number = 0;
+
+ QPoint last_pt = at(0);
+ QPoint last_start = at(0);
+ for (int i = 1; i < size(); ++i) {
+ const QPoint &e = at(i);
+ qt_polygon_isect_line(last_pt, e, pt, &winding_number);
+ last_pt = e;
+ }
+
+ // implicitly close last subpath
+ if (last_pt != last_start)
+ qt_polygon_isect_line(last_pt, last_start, pt, &winding_number);
+
+ return (fillRule == Qt::WindingFill
+ ? (winding_number != 0)
+ : ((winding_number % 2) != 0));
+}
+
+/*!
+ \since 4.3
+
+ Returns a polygon which is the union of this polygon and \a r.
+
+ Set operations on polygons, will treat the polygons as areas, and
+ implicitly close the polygon.
+
+ \sa intersected(), subtracted()
+*/
+
+QPolygon QPolygon::united(const QPolygon &r) const
+{
+ QPainterPath subject; subject.addPolygon(*this);
+ QPainterPath clip; clip.addPolygon(r);
+
+ return subject.united(clip).toFillPolygon().toPolygon();
+}
+
+/*!
+ \since 4.3
+
+ Returns a polygon which is the intersection of this polygon and \a r.
+
+ Set operations on polygons will treat the polygons as
+ areas. Non-closed polygons will be treated as implicitly closed.
+*/
+
+QPolygon QPolygon::intersected(const QPolygon &r) const
+{
+ QPainterPath subject; subject.addPolygon(*this);
+ QPainterPath clip; clip.addPolygon(r);
+
+ return subject.intersected(clip).toFillPolygon().toPolygon();
+}
+
+/*!
+ \since 4.3
+
+ Returns a polygon which is \a r subtracted from this polygon.
+
+ Set operations on polygons will treat the polygons as
+ areas. Non-closed polygons will be treated as implicitly closed.
+
+*/
+
+QPolygon QPolygon::subtracted(const QPolygon &r) const
+{
+ QPainterPath subject; subject.addPolygon(*this);
+ QPainterPath clip; clip.addPolygon(r);
+
+ return subject.subtracted(clip).toFillPolygon().toPolygon();
+}
+
+/*!
+ \since 4.3
+
+ Returns a polygon which is the union of this polygon and \a r.
+
+ Set operations on polygons will treat the polygons as
+ areas. Non-closed polygons will be treated as implicitly closed.
+
+ \sa intersected(), subtracted()
+*/
+
+QPolygonF QPolygonF::united(const QPolygonF &r) const
+{
+ QPainterPath subject; subject.addPolygon(*this);
+ QPainterPath clip; clip.addPolygon(r);
+
+ return subject.united(clip).toFillPolygon();
+}
+
+/*!
+ \since 4.3
+
+ Returns a polygon which is the intersection of this polygon and \a r.
+
+ Set operations on polygons will treat the polygons as
+ areas. Non-closed polygons will be treated as implicitly closed.
+
+*/
+
+QPolygonF QPolygonF::intersected(const QPolygonF &r) const
+{
+ QPainterPath subject; subject.addPolygon(*this);
+ QPainterPath clip; clip.addPolygon(r);
+
+ return subject.intersected(clip).toFillPolygon();
+}
+
+/*!
+ \since 4.3
+
+ Returns a polygon which is \a r subtracted from this polygon.
+
+ Set operations on polygons will treat the polygons as
+ areas. Non-closed polygons will be treated as implicitly closed.
+
+*/
+
+QPolygonF QPolygonF::subtracted(const QPolygonF &r) const
+{
+ QPainterPath subject; subject.addPolygon(*this);
+ QPainterPath clip; clip.addPolygon(r);
+ return subject.subtracted(clip).toFillPolygon();
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qpolygon.h b/src/gui/painting/qpolygon.h
new file mode 100644
index 0000000000..0f4a8b3450
--- /dev/null
+++ b/src/gui/painting/qpolygon.h
@@ -0,0 +1,189 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPOLYGON_H
+#define QPOLYGON_H
+
+#include <QtCore/qvector.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qrect.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QMatrix;
+class QTransform;
+class QRect;
+class QVariant;
+
+class Q_GUI_EXPORT QPolygon : public QVector<QPoint>
+{
+public:
+ inline QPolygon() {}
+ inline ~QPolygon() {}
+ inline QPolygon(int size);
+ inline QPolygon(const QPolygon &a) : QVector<QPoint>(a) {}
+ inline QPolygon(const QVector<QPoint> &v) : QVector<QPoint>(v) {}
+ QPolygon(const QRect &r, bool closed=false);
+ QPolygon(int nPoints, const int *points);
+ inline void swap(QPolygon &other) { QVector<QPoint>::swap(other); } // prevent QVector<QPoint><->QPolygon swaps
+
+ operator QVariant() const;
+
+ void translate(int dx, int dy);
+ void translate(const QPoint &offset);
+
+ QPolygon translated(int dx, int dy) const;
+ inline QPolygon translated(const QPoint &offset) const;
+
+ QRect boundingRect() const;
+
+ void point(int i, int *x, int *y) const;
+ QPoint point(int i) const;
+ void setPoint(int index, int x, int y);
+ void setPoint(int index, const QPoint &p);
+ void setPoints(int nPoints, const int *points);
+ void setPoints(int nPoints, int firstx, int firsty, ...);
+ void putPoints(int index, int nPoints, const int *points);
+ void putPoints(int index, int nPoints, int firstx, int firsty, ...);
+ void putPoints(int index, int nPoints, const QPolygon & from, int fromIndex=0);
+
+ bool containsPoint(const QPoint &pt, Qt::FillRule fillRule) const;
+
+ QPolygon united(const QPolygon &r) const;
+ QPolygon intersected(const QPolygon &r) const;
+ QPolygon subtracted(const QPolygon &r) const;
+};
+
+inline QPolygon::QPolygon(int asize) : QVector<QPoint>(asize) {}
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QPolygon &);
+#endif
+
+/*****************************************************************************
+ QPolygon stream functions
+ *****************************************************************************/
+#ifndef QT_NO_DATASTREAM
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &stream, const QPolygon &polygon);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QPolygon &polygon);
+#endif
+
+/*****************************************************************************
+ Misc. QPolygon functions
+ *****************************************************************************/
+
+inline void QPolygon::setPoint(int index, const QPoint &pt)
+{ (*this)[index] = pt; }
+
+inline void QPolygon::setPoint(int index, int x, int y)
+{ (*this)[index] = QPoint(x, y); }
+
+inline QPoint QPolygon::point(int index) const
+{ return at(index); }
+
+inline void QPolygon::translate(const QPoint &offset)
+{ translate(offset.x(), offset.y()); }
+
+inline QPolygon QPolygon::translated(const QPoint &offset) const
+{ return translated(offset.x(), offset.y()); }
+
+class QRectF;
+
+class Q_GUI_EXPORT QPolygonF : public QVector<QPointF>
+{
+public:
+ inline QPolygonF() {}
+ inline ~QPolygonF() {}
+ inline QPolygonF(int size);
+ inline QPolygonF(const QPolygonF &a) : QVector<QPointF>(a) {}
+ inline QPolygonF(const QVector<QPointF> &v) : QVector<QPointF>(v) {}
+ QPolygonF(const QRectF &r);
+ QPolygonF(const QPolygon &a);
+ inline void swap(QPolygonF &other) { QVector<QPointF>::swap(other); } // prevent QVector<QPointF><->QPolygonF swaps
+
+ inline void translate(qreal dx, qreal dy);
+ void translate(const QPointF &offset);
+
+ inline QPolygonF translated(qreal dx, qreal dy) const;
+ QPolygonF translated(const QPointF &offset) const;
+
+ QPolygon toPolygon() const;
+
+ bool isClosed() const { return !isEmpty() && first() == last(); }
+
+ QRectF boundingRect() const;
+
+ bool containsPoint(const QPointF &pt, Qt::FillRule fillRule) const;
+
+ QPolygonF united(const QPolygonF &r) const;
+ QPolygonF intersected(const QPolygonF &r) const;
+ QPolygonF subtracted(const QPolygonF &r) const;
+};
+
+inline QPolygonF::QPolygonF(int asize) : QVector<QPointF>(asize) {}
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QPolygonF &);
+#endif
+
+/*****************************************************************************
+ QPolygonF stream functions
+ *****************************************************************************/
+#ifndef QT_NO_DATASTREAM
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &stream, const QPolygonF &array);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QPolygonF &array);
+#endif
+
+inline void QPolygonF::translate(qreal dx, qreal dy)
+{ translate(QPointF(dx, dy)); }
+
+inline QPolygonF QPolygonF::translated(qreal dx, qreal dy) const
+{ return translated(QPointF(dx, dy)); }
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPOLYGON_H
diff --git a/src/gui/painting/qpolygonclipper_p.h b/src/gui/painting/qpolygonclipper_p.h
new file mode 100644
index 0000000000..287190ab8b
--- /dev/null
+++ b/src/gui/painting/qpolygonclipper_p.h
@@ -0,0 +1,317 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPOLYGONCLIPPER_P_H
+#define QPOLYGONCLIPPER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qdatabuffer_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/* based on sutherland-hodgman line-by-line clipping, as described in
+ Computer Graphics and Principles */
+template <typename InType, typename OutType, typename CastType> class QPolygonClipper
+{
+public:
+ QPolygonClipper() :
+ buffer1(0), buffer2(0)
+ {
+ x1 = y1 = x2 = y2 = 0;
+ }
+
+ ~QPolygonClipper()
+ {
+ }
+
+ void setBoundingRect(const QRect bounds)
+ {
+ x1 = bounds.x();
+ x2 = bounds.x() + bounds.width();
+ y1 = bounds.y();
+ y2 = bounds.y() + bounds.height();
+ }
+
+ QRect boundingRect()
+ {
+ return QRect(QPoint(x1, y1), QPoint(x2, y2));
+ }
+
+ inline OutType intersectLeft(const OutType &p1, const OutType &p2)
+ {
+ OutType t;
+ qreal dy = (p1.y - p2.y) / qreal(p1.x - p2.x);
+ t.x = x1;
+ t.y = static_cast<CastType>(p2.y + (x1 - p2.x) * dy);
+ return t;
+ }
+
+
+ inline OutType intersectRight(const OutType &p1, const OutType &p2)
+ {
+ OutType t;
+ qreal dy = (p1.y - p2.y) / qreal(p1.x - p2.x);
+ t.x = x2;
+ t.y = static_cast<CastType>(p2.y + (x2 - p2.x) * dy);
+ return t;
+ }
+
+
+ inline OutType intersectTop(const OutType &p1, const OutType &p2)
+ {
+ OutType t;
+ qreal dx = (p1.x - p2.x) / qreal(p1.y - p2.y);
+ t.x = static_cast<CastType>(p2.x + (y1 - p2.y) * dx);
+ t.y = y1;
+ return t;
+ }
+
+
+ inline OutType intersectBottom(const OutType &p1, const OutType &p2)
+ {
+ OutType t;
+ qreal dx = (p1.x - p2.x) / qreal(p1.y - p2.y);
+ t.x = static_cast<CastType>(p2.x + (y2 - p2.y) * dx);
+ t.y = y2;
+ return t;
+ }
+
+
+ void clipPolygon(const InType *inPoints, int inCount, OutType **outPoints, int *outCount,
+ bool closePolygon = true)
+ {
+ Q_ASSERT(outPoints);
+ Q_ASSERT(outCount);
+
+ if (inCount < 2) {
+ *outCount = 0;
+ return;
+ }
+
+ buffer1.reset();
+ buffer2.reset();
+
+ QDataBuffer<OutType> *source = &buffer1;
+ QDataBuffer<OutType> *clipped = &buffer2;
+
+ // Gather some info since we are iterating through the points anyway..
+ bool doLeft = false, doRight = false, doTop = false, doBottom = false;
+ OutType ot;
+ for (int i=0; i<inCount; ++i) {
+ ot = inPoints[i];
+ clipped->add(ot);
+
+ if (ot.x < x1)
+ doLeft = true;
+ else if (ot.x > x2)
+ doRight = true;
+ if (ot.y < y1)
+ doTop = true;
+ else if (ot.y > y2)
+ doBottom = true;
+ }
+
+ if (doLeft && clipped->size() > 1) {
+ QDataBuffer<OutType> *tmp = source;
+ source = clipped;
+ clipped = tmp;
+ clipped->reset();
+ int lastPos, start;
+ if (closePolygon) {
+ lastPos = source->size() - 1;
+ start = 0;
+ } else {
+ lastPos = 0;
+ start = 1;
+ if (source->at(0).x >= x1)
+ clipped->add(source->at(0));
+ }
+ for (int i=start; i<inCount; ++i) {
+ const OutType &cpt = source->at(i);
+ const OutType &ppt = source->at(lastPos);
+
+ if (cpt.x >= x1) {
+ if (ppt.x >= x1) {
+ clipped->add(cpt);
+ } else {
+ clipped->add(intersectLeft(cpt, ppt));
+ clipped->add(cpt);
+ }
+ } else if (ppt.x >= x1) {
+ clipped->add(intersectLeft(cpt, ppt));
+ }
+ lastPos = i;
+ }
+ }
+
+ if (doRight && clipped->size() > 1) {
+ QDataBuffer<OutType> *tmp = source;
+ source = clipped;
+ clipped = tmp;
+ clipped->reset();
+ int lastPos, start;
+ if (closePolygon) {
+ lastPos = source->size() - 1;
+ start = 0;
+ } else {
+ lastPos = 0;
+ start = 1;
+ if (source->at(0).x <= x2)
+ clipped->add(source->at(0));
+ }
+ for (int i=start; i<source->size(); ++i) {
+ const OutType &cpt = source->at(i);
+ const OutType &ppt = source->at(lastPos);
+
+ if (cpt.x <= x2) {
+ if (ppt.x <= x2) {
+ clipped->add(cpt);
+ } else {
+ clipped->add(intersectRight(cpt, ppt));
+ clipped->add(cpt);
+ }
+ } else if (ppt.x <= x2) {
+ clipped->add(intersectRight(cpt, ppt));
+ }
+
+ lastPos = i;
+ }
+
+ }
+
+ if (doTop && clipped->size() > 1) {
+ QDataBuffer<OutType> *tmp = source;
+ source = clipped;
+ clipped = tmp;
+ clipped->reset();
+ int lastPos, start;
+ if (closePolygon) {
+ lastPos = source->size() - 1;
+ start = 0;
+ } else {
+ lastPos = 0;
+ start = 1;
+ if (source->at(0).y >= y1)
+ clipped->add(source->at(0));
+ }
+ for (int i=start; i<source->size(); ++i) {
+ const OutType &cpt = source->at(i);
+ const OutType &ppt = source->at(lastPos);
+
+ if (cpt.y >= y1) {
+ if (ppt.y >= y1) {
+ clipped->add(cpt);
+ } else {
+ clipped->add(intersectTop(cpt, ppt));
+ clipped->add(cpt);
+ }
+ } else if (ppt.y >= y1) {
+ clipped->add(intersectTop(cpt, ppt));
+ }
+
+ lastPos = i;
+ }
+ }
+
+ if (doBottom && clipped->size() > 1) {
+ QDataBuffer<OutType> *tmp = source;
+ source = clipped;
+ clipped = tmp;
+ clipped->reset();
+ int lastPos, start;
+ if (closePolygon) {
+ lastPos = source->size() - 1;
+ start = 0;
+ } else {
+ lastPos = 0;
+ start = 1;
+ if (source->at(0).y <= y2)
+ clipped->add(source->at(0));
+ }
+ for (int i=start; i<source->size(); ++i) {
+ const OutType &cpt = source->at(i);
+ const OutType &ppt = source->at(lastPos);
+
+ if (cpt.y <= y2) {
+ if (ppt.y <= y2) {
+ clipped->add(cpt);
+ } else {
+ clipped->add(intersectBottom(cpt, ppt));
+ clipped->add(cpt);
+ }
+ } else if (ppt.y <= y2) {
+ clipped->add(intersectBottom(cpt, ppt));
+ }
+ lastPos = i;
+ }
+ }
+
+ if (closePolygon && clipped->size() > 0) {
+ // close clipped polygon
+ if (clipped->at(0).x != clipped->at(clipped->size()-1).x ||
+ clipped->at(0).y != clipped->at(clipped->size()-1).y) {
+ OutType ot = clipped->at(0);
+ clipped->add(ot);
+ }
+ }
+ *outCount = clipped->size();
+ *outPoints = clipped->data();
+ }
+
+private:
+ int x1, x2, y1, y2;
+ QDataBuffer<OutType> buffer1;
+ QDataBuffer<OutType> buffer2;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPOLYGONCLIPPER_P_H
diff --git a/src/gui/painting/qprintengine.h b/src/gui/painting/qprintengine.h
new file mode 100644
index 0000000000..b04ea068a3
--- /dev/null
+++ b/src/gui/painting/qprintengine.h
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPRINTENGINE_H
+#define QPRINTENGINE_H
+
+#include <QtCore/qvariant.h>
+#include <QtGui/qprinter.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_PRINTER
+
+class Q_GUI_EXPORT QPrintEngine
+{
+public:
+ virtual ~QPrintEngine() {}
+ enum PrintEnginePropertyKey {
+ PPK_CollateCopies,
+ PPK_ColorMode,
+ PPK_Creator,
+ PPK_DocumentName,
+ PPK_FullPage,
+ PPK_NumberOfCopies,
+ PPK_Orientation,
+ PPK_OutputFileName,
+ PPK_PageOrder,
+ PPK_PageRect,
+ PPK_PageSize,
+ PPK_PaperRect,
+ PPK_PaperSource,
+ PPK_PrinterName,
+ PPK_PrinterProgram,
+ PPK_Resolution,
+ PPK_SelectionOption,
+ PPK_SupportedResolutions,
+
+ PPK_WindowsPageSize,
+ PPK_FontEmbedding,
+ PPK_SuppressSystemPrintStatus,
+
+ PPK_Duplex,
+
+ PPK_PaperSources,
+ PPK_CustomPaperSize,
+ PPK_PageMargins,
+ PPK_CopyCount,
+ PPK_SupportsMultipleCopies,
+ PPK_PaperSize = PPK_PageSize,
+
+ PPK_CustomBase = 0xff00
+ };
+
+ virtual void setProperty(PrintEnginePropertyKey key, const QVariant &value) = 0;
+ virtual QVariant property(PrintEnginePropertyKey key) const = 0;
+
+ virtual bool newPage() = 0;
+ virtual bool abort() = 0;
+
+ virtual int metric(QPaintDevice::PaintDeviceMetric) const = 0;
+
+ virtual QPrinter::PrinterState printerState() const = 0;
+
+#ifdef Q_WS_WIN
+ virtual HDC getPrinterDC() const { return 0; }
+ virtual void releasePrinterDC(HDC) const { }
+#endif
+
+};
+
+#endif // QT_NO_PRINTER
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPRINTENGINE_H
diff --git a/src/gui/painting/qprintengine_mac.mm b/src/gui/painting/qprintengine_mac.mm
new file mode 100644
index 0000000000..1dce30301f
--- /dev/null
+++ b/src/gui/painting/qprintengine_mac.mm
@@ -0,0 +1,911 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qprintengine_mac_p.h>
+#include <qdebug.h>
+#include <qthread.h>
+#include <QtCore/qcoreapplication.h>
+
+#ifndef QT_NO_PRINTER
+
+QT_BEGIN_NAMESPACE
+
+extern QSizeF qt_paperSizeToQSizeF(QPrinter::PaperSize size);
+
+QMacPrintEngine::QMacPrintEngine(QPrinter::PrinterMode mode) : QPaintEngine(*(new QMacPrintEnginePrivate))
+{
+ Q_D(QMacPrintEngine);
+ d->mode = mode;
+ d->initialize();
+}
+
+bool QMacPrintEngine::begin(QPaintDevice *dev)
+{
+ Q_D(QMacPrintEngine);
+
+ Q_ASSERT(dev && dev->devType() == QInternal::Printer);
+ if (!static_cast<QPrinter *>(dev)->isValid())
+ return false;
+
+ if (d->state == QPrinter::Idle && !d->isPrintSessionInitialized()) // Need to reinitialize
+ d->initialize();
+
+ d->paintEngine->state = state;
+ d->paintEngine->begin(dev);
+ Q_ASSERT_X(d->state == QPrinter::Idle, "QMacPrintEngine", "printer already active");
+
+ if (PMSessionValidatePrintSettings(d->session, d->settings, kPMDontWantBoolean) != noErr
+ || PMSessionValidatePageFormat(d->session, d->format, kPMDontWantBoolean) != noErr) {
+ d->state = QPrinter::Error;
+ return false;
+ }
+
+ if (!d->outputFilename.isEmpty()) {
+ QCFType<CFURLRef> outFile = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault,
+ QCFString(d->outputFilename),
+ kCFURLPOSIXPathStyle,
+ false);
+ if (PMSessionSetDestination(d->session, d->settings, kPMDestinationFile,
+ kPMDocumentFormatPDF, outFile) != noErr) {
+ qWarning("QMacPrintEngine::begin: Problem setting file [%s]", d->outputFilename.toUtf8().constData());
+ return false;
+ }
+ }
+ OSStatus status = noErr;
+#ifndef QT_MAC_USE_COCOA
+ status = d->shouldSuppressStatus() ? PMSessionBeginCGDocumentNoDialog(d->session, d->settings, d->format)
+ : PMSessionBeginCGDocument(d->session, d->settings, d->format);
+#else
+ status = PMSessionBeginCGDocumentNoDialog(d->session, d->settings, d->format);
+#endif
+
+ if (status != noErr) {
+ d->state = QPrinter::Error;
+ return false;
+ }
+
+ d->state = QPrinter::Active;
+ setActive(true);
+ d->newPage_helper();
+ return true;
+}
+
+bool QMacPrintEngine::end()
+{
+ Q_D(QMacPrintEngine);
+ if (d->state == QPrinter::Aborted)
+ return true; // I was just here a function call ago :)
+ if(d->paintEngine->type() == QPaintEngine::CoreGraphics) {
+ // We dont need the paint engine to call restoreGraphicsState()
+ static_cast<QCoreGraphicsPaintEngine*>(d->paintEngine)->d_func()->stackCount = 0;
+ static_cast<QCoreGraphicsPaintEngine*>(d->paintEngine)->d_func()->hd = 0;
+ }
+ d->paintEngine->end();
+ if (d->state != QPrinter::Idle)
+ d->releaseSession();
+ d->state = QPrinter::Idle;
+ return true;
+}
+
+QPaintEngine *
+QMacPrintEngine::paintEngine() const
+{
+ return d_func()->paintEngine;
+}
+
+Qt::HANDLE QMacPrintEngine::handle() const
+{
+ QCoreGraphicsPaintEngine *cgEngine = static_cast<QCoreGraphicsPaintEngine*>(paintEngine());
+ return cgEngine->d_func()->hd;
+}
+
+QMacPrintEnginePrivate::~QMacPrintEnginePrivate()
+{
+#ifdef QT_MAC_USE_COCOA
+ [printInfo release];
+#endif
+ delete paintEngine;
+}
+
+void QMacPrintEnginePrivate::setPaperSize(QPrinter::PaperSize ps)
+{
+ Q_Q(QMacPrintEngine);
+ QSizeF newSize = qt_paperSizeToQSizeF(ps);
+ QCFType<CFArrayRef> formats;
+ PMPrinter printer;
+
+ if (PMSessionGetCurrentPrinter(session, &printer) == noErr
+ && PMSessionCreatePageFormatList(session, printer, &formats) == noErr) {
+ CFIndex total = CFArrayGetCount(formats);
+ PMPageFormat tmp;
+ PMRect paper;
+ for (CFIndex idx = 0; idx < total; ++idx) {
+ tmp = static_cast<PMPageFormat>(
+ const_cast<void *>(CFArrayGetValueAtIndex(formats, idx)));
+ PMGetUnadjustedPaperRect(tmp, &paper);
+ int wMM = int((paper.right - paper.left) / 72 * 25.4 + 0.5);
+ int hMM = int((paper.bottom - paper.top) / 72 * 25.4 + 0.5);
+ if (newSize.width() == wMM && newSize.height() == hMM) {
+ PMCopyPageFormat(tmp, format);
+ // reset the orientation and resolution as they are lost in the copy.
+ q->setProperty(QPrintEngine::PPK_Orientation, orient);
+ if (PMSessionValidatePageFormat(session, format, kPMDontWantBoolean) != noErr) {
+ // Don't know, warn for the moment.
+ qWarning("QMacPrintEngine, problem setting format and resolution for this page size");
+ }
+ break;
+ }
+ }
+ }
+}
+
+QPrinter::PaperSize QMacPrintEnginePrivate::paperSize() const
+{
+ PMRect paper;
+ PMGetUnadjustedPaperRect(format, &paper);
+ int wMM = int((paper.right - paper.left) / 72 * 25.4 + 0.5);
+ int hMM = int((paper.bottom - paper.top) / 72 * 25.4 + 0.5);
+ for (int i = QPrinter::A4; i < QPrinter::NPaperSize; ++i) {
+ QSizeF s = qt_paperSizeToQSizeF(QPrinter::PaperSize(i));
+ if (s.width() == wMM && s.height() == hMM)
+ return (QPrinter::PaperSize)i;
+ }
+ return QPrinter::Custom;
+}
+
+QList<QVariant> QMacPrintEnginePrivate::supportedResolutions() const
+{
+ Q_ASSERT_X(session, "QMacPrinterEngine::supportedResolutions",
+ "must have a valid printer session");
+ UInt32 resCount;
+ QList<QVariant> resolutions;
+ PMPrinter printer;
+ if (PMSessionGetCurrentPrinter(session, &printer) == noErr) {
+ PMResolution res;
+ OSStatus status = PMPrinterGetPrinterResolutionCount(printer, &resCount);
+ if (status == kPMNotImplemented) {
+#if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5)
+ // *Sigh* we have to use the non-indexed version.
+ if (PMPrinterGetPrinterResolution(printer, kPMMinSquareResolution, &res) == noErr)
+ resolutions.append(int(res.hRes));
+ if (PMPrinterGetPrinterResolution(printer, kPMMaxSquareResolution, &res) == noErr) {
+ QVariant var(int(res.hRes));
+ if (!resolutions.contains(var))
+ resolutions.append(var);
+ }
+ if (PMPrinterGetPrinterResolution(printer, kPMDefaultResolution, &res) == noErr) {
+ QVariant var(int(res.hRes));
+ if (!resolutions.contains(var))
+ resolutions.append(var);
+ }
+#endif
+ } else if (status == noErr) {
+ // According to the docs, index start at 1.
+ for (UInt32 i = 1; i <= resCount; ++i) {
+ if (PMPrinterGetIndexedPrinterResolution(printer, i, &res) == noErr)
+ resolutions.append(QVariant(int(res.hRes)));
+ }
+ } else {
+ qWarning("QMacPrintEngine::supportedResolutions: Unexpected error: %ld", long(status));
+ }
+ }
+ return resolutions;
+}
+
+bool QMacPrintEnginePrivate::shouldSuppressStatus() const
+{
+ if (suppressStatus == true)
+ return true;
+
+ // Supress displaying the automatic progress dialog if we are printing
+ // from a non-gui thread.
+ return (qApp->thread() != QThread::currentThread());
+}
+
+QPrinter::PrinterState QMacPrintEngine::printerState() const
+{
+ return d_func()->state;
+}
+
+bool QMacPrintEngine::newPage()
+{
+ Q_D(QMacPrintEngine);
+ Q_ASSERT(d->state == QPrinter::Active);
+ OSStatus err =
+#ifndef QT_MAC_USE_COCOA
+ d->shouldSuppressStatus() ? PMSessionEndPageNoDialog(d->session)
+ : PMSessionEndPage(d->session);
+#else
+ PMSessionEndPageNoDialog(d->session);
+#endif
+ if (err != noErr) {
+ if (err == kPMCancel) {
+ // User canceled, we need to abort!
+ abort();
+ } else {
+ // Not sure what the problem is...
+ qWarning("QMacPrintEngine::newPage: Cannot end current page. %ld", long(err));
+ d->state = QPrinter::Error;
+ }
+ return false;
+ }
+ return d->newPage_helper();
+}
+
+bool QMacPrintEngine::abort()
+{
+ Q_D(QMacPrintEngine);
+ if (d->state != QPrinter::Active)
+ return false;
+ bool ret = end();
+ d->state = QPrinter::Aborted;
+ return ret;
+}
+
+static inline int qt_get_PDMWidth(PMPageFormat pformat, bool fullPage,
+ const PMResolution &resolution)
+{
+ int val = 0;
+ PMRect r;
+ qreal hRatio = resolution.hRes / 72;
+ if (fullPage) {
+ if (PMGetAdjustedPaperRect(pformat, &r) == noErr)
+ val = qRound((r.right - r.left) * hRatio);
+ } else {
+ if (PMGetAdjustedPageRect(pformat, &r) == noErr)
+ val = qRound((r.right - r.left) * hRatio);
+ }
+ return val;
+}
+
+static inline int qt_get_PDMHeight(PMPageFormat pformat, bool fullPage,
+ const PMResolution &resolution)
+{
+ int val = 0;
+ PMRect r;
+ qreal vRatio = resolution.vRes / 72;
+ if (fullPage) {
+ if (PMGetAdjustedPaperRect(pformat, &r) == noErr)
+ val = qRound((r.bottom - r.top) * vRatio);
+ } else {
+ if (PMGetAdjustedPageRect(pformat, &r) == noErr)
+ val = qRound((r.bottom - r.top) * vRatio);
+ }
+ return val;
+}
+
+
+int QMacPrintEngine::metric(QPaintDevice::PaintDeviceMetric m) const
+{
+ Q_D(const QMacPrintEngine);
+ int val = 1;
+ switch (m) {
+ case QPaintDevice::PdmWidth:
+ if (d->hasCustomPaperSize) {
+ val = qRound(d->customSize.width());
+ if (d->hasCustomPageMargins) {
+ val -= qRound(d->leftMargin + d->rightMargin);
+ } else {
+ QList<QVariant> margins = property(QPrintEngine::PPK_PageMargins).toList();
+ val -= qRound(margins.at(0).toDouble() + margins.at(2).toDouble());
+ }
+ } else {
+ val = qt_get_PDMWidth(d->format, property(PPK_FullPage).toBool(), d->resolution);
+ }
+ break;
+ case QPaintDevice::PdmHeight:
+ if (d->hasCustomPaperSize) {
+ val = qRound(d->customSize.height());
+ if (d->hasCustomPageMargins) {
+ val -= qRound(d->topMargin + d->bottomMargin);
+ } else {
+ QList<QVariant> margins = property(QPrintEngine::PPK_PageMargins).toList();
+ val -= qRound(margins.at(1).toDouble() + margins.at(3).toDouble());
+ }
+ } else {
+ val = qt_get_PDMHeight(d->format, property(PPK_FullPage).toBool(), d->resolution);
+ }
+ break;
+ case QPaintDevice::PdmWidthMM:
+ val = metric(QPaintDevice::PdmWidth);
+ val = int((val * 254 + 5 * d->resolution.hRes) / (10 * d->resolution.hRes));
+ break;
+ case QPaintDevice::PdmHeightMM:
+ val = metric(QPaintDevice::PdmHeight);
+ val = int((val * 254 + 5 * d->resolution.vRes) / (10 * d->resolution.vRes));
+ break;
+ case QPaintDevice::PdmPhysicalDpiX:
+ case QPaintDevice::PdmPhysicalDpiY: {
+ PMPrinter printer;
+ if(PMSessionGetCurrentPrinter(d->session, &printer) == noErr) {
+ PMResolution resolution;
+#ifndef QT_MAC_USE_COCOA
+# if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
+ PMPrinterGetOutputResolution(printer, d->settings, &resolution);
+ } else
+# endif
+ {
+ PMPrinterGetPrinterResolution(printer, kPMCurrentValue, &resolution);
+ }
+#else
+ PMPrinterGetOutputResolution(printer, d->settings, &resolution);
+#endif
+ val = (int)resolution.vRes;
+ break;
+ }
+ //otherwise fall through
+ }
+ case QPaintDevice::PdmDpiY:
+ val = (int)d->resolution.vRes;
+ break;
+ case QPaintDevice::PdmDpiX:
+ val = (int)d->resolution.hRes;
+ break;
+ case QPaintDevice::PdmNumColors:
+ val = (1 << metric(QPaintDevice::PdmDepth));
+ break;
+ case QPaintDevice::PdmDepth:
+ val = 24;
+ break;
+ default:
+ val = 0;
+ qWarning("QPrinter::metric: Invalid metric command");
+ }
+ return val;
+}
+
+void QMacPrintEnginePrivate::initialize()
+{
+ Q_Q(QMacPrintEngine);
+
+#ifndef QT_MAC_USE_COCOA
+ Q_ASSERT(!session);
+#else
+ Q_ASSERT(!printInfo);
+#endif
+
+ if (!paintEngine)
+ paintEngine = new QCoreGraphicsPaintEngine();
+
+ q->gccaps = paintEngine->gccaps;
+
+ fullPage = false;
+
+#ifndef QT_MAC_USE_COCOA
+ if (PMCreateSession(&session) != 0)
+ session = 0;
+#else
+ QMacCocoaAutoReleasePool pool;
+ printInfo = [[NSPrintInfo alloc] initWithDictionary:[NSDictionary dictionary]];
+ session = static_cast<PMPrintSession>([printInfo PMPrintSession]);
+#endif
+
+ PMPrinter printer;
+ if (session && PMSessionGetCurrentPrinter(session, &printer) == noErr) {
+ QList<QVariant> resolutions = supportedResolutions();
+ if (!resolutions.isEmpty() && mode != QPrinter::ScreenResolution) {
+ if (resolutions.count() > 1 && mode == QPrinter::HighResolution) {
+ int max = 0;
+ for (int i = 0; i < resolutions.count(); ++i) {
+ int value = resolutions.at(i).toInt();
+ if (value > max)
+ max = value;
+ }
+ resolution.hRes = resolution.vRes = max;
+ } else {
+ resolution.hRes = resolution.vRes = resolutions.at(0).toInt();
+ }
+ if(resolution.hRes == 0)
+ resolution.hRes = resolution.vRes = 600;
+ } else {
+ resolution.hRes = resolution.vRes = qt_defaultDpi();
+ }
+ }
+
+#ifndef QT_MAC_USE_COCOA
+ bool settingsInitialized = (settings != 0);
+ bool settingsOK = !settingsInitialized ? PMCreatePrintSettings(&settings) == noErr : true;
+ if (settingsOK && !settingsInitialized)
+ settingsOK = PMSessionDefaultPrintSettings(session, settings) == noErr;
+
+
+ bool formatInitialized = (format != 0);
+ bool formatOK = !formatInitialized ? PMCreatePageFormat(&format) == noErr : true;
+ if (formatOK) {
+ if (!formatInitialized) {
+ formatOK = PMSessionDefaultPageFormat(session, format) == noErr;
+ }
+ formatOK = PMSessionValidatePageFormat(session, format, kPMDontWantBoolean) == noErr;
+ }
+#else
+ settings = static_cast<PMPrintSettings>([printInfo PMPrintSettings]);
+ format = static_cast<PMPageFormat>([printInfo PMPageFormat]);
+#endif
+
+#ifndef QT_MAC_USE_COCOA
+ if (!settingsOK || !formatOK) {
+ qWarning("QMacPrintEngine::initialize: Unable to initialize QPainter");
+ state = QPrinter::Error;
+ }
+#endif
+
+ QHash<QMacPrintEngine::PrintEnginePropertyKey, QVariant>::const_iterator propC;
+ for (propC = valueCache.constBegin(); propC != valueCache.constEnd(); propC++) {
+ q->setProperty(propC.key(), propC.value());
+ }
+}
+
+void QMacPrintEnginePrivate::releaseSession()
+{
+#ifndef QT_MAC_USE_COCOA
+ if (shouldSuppressStatus()) {
+ PMSessionEndPageNoDialog(session);
+ PMSessionEndDocumentNoDialog(session);
+ } else {
+ PMSessionEndPage(session);
+ PMSessionEndDocument(session);
+ }
+ PMRelease(session);
+#else
+ PMSessionEndPageNoDialog(session);
+ PMSessionEndDocumentNoDialog(session);
+ [printInfo release];
+#endif
+ printInfo = 0;
+ session = 0;
+}
+
+bool QMacPrintEnginePrivate::newPage_helper()
+{
+ Q_Q(QMacPrintEngine);
+ Q_ASSERT(state == QPrinter::Active);
+
+ if (PMSessionError(session) != noErr) {
+ q->abort();
+ return false;
+ }
+
+ // pop the stack of saved graphic states, in case we get the same
+ // context back - either way, the stack count should be 0 when we
+ // get the new one
+ QCoreGraphicsPaintEngine *cgEngine = static_cast<QCoreGraphicsPaintEngine*>(paintEngine);
+ while (cgEngine->d_func()->stackCount > 0)
+ cgEngine->d_func()->restoreGraphicsState();
+
+ OSStatus status =
+#ifndef QT_MAC_USE_COCOA
+ shouldSuppressStatus() ? PMSessionBeginPageNoDialog(session, format, 0)
+ : PMSessionBeginPage(session, format, 0);
+#else
+ PMSessionBeginPageNoDialog(session, format, 0);
+#endif
+ if(status != noErr) {
+ state = QPrinter::Error;
+ return false;
+ }
+
+ QRect page = q->property(QPrintEngine::PPK_PageRect).toRect();
+ QRect paper = q->property(QPrintEngine::PPK_PaperRect).toRect();
+
+ CGContextRef cgContext;
+ OSStatus err = noErr;
+ err = PMSessionGetCGGraphicsContext(session, &cgContext);
+ if(err != noErr) {
+ qWarning("QMacPrintEngine::newPage: Cannot retrieve CoreGraphics context: %ld", long(err));
+ state = QPrinter::Error;
+ return false;
+ }
+ cgEngine->d_func()->hd = cgContext;
+
+ // Set the resolution as a scaling ration of 72 (the default).
+ CGContextScaleCTM(cgContext, 72 / resolution.hRes, 72 / resolution.vRes);
+
+ CGContextScaleCTM(cgContext, 1, -1);
+ CGContextTranslateCTM(cgContext, 0, -paper.height());
+ if (!fullPage)
+ CGContextTranslateCTM(cgContext, page.x() - paper.x(), page.y() - paper.y());
+ cgEngine->d_func()->orig_xform = CGContextGetCTM(cgContext);
+ cgEngine->d_func()->setClip(0);
+ cgEngine->state->dirtyFlags = QPaintEngine::DirtyFlag(QPaintEngine::AllDirty
+ & ~(QPaintEngine::DirtyClipEnabled
+ | QPaintEngine::DirtyClipRegion
+ | QPaintEngine::DirtyClipPath));
+ if (cgEngine->painter()->hasClipping())
+ cgEngine->state->dirtyFlags |= QPaintEngine::DirtyClipEnabled;
+ cgEngine->syncState();
+ return true;
+}
+
+
+void QMacPrintEngine::updateState(const QPaintEngineState &state)
+{
+ d_func()->paintEngine->updateState(state);
+}
+
+void QMacPrintEngine::drawRects(const QRectF *r, int num)
+{
+ Q_D(QMacPrintEngine);
+ Q_ASSERT(d->state == QPrinter::Active);
+ d->paintEngine->drawRects(r, num);
+}
+
+void QMacPrintEngine::drawPoints(const QPointF *points, int pointCount)
+{
+ Q_D(QMacPrintEngine);
+ Q_ASSERT(d->state == QPrinter::Active);
+ d->paintEngine->drawPoints(points, pointCount);
+}
+
+void QMacPrintEngine::drawEllipse(const QRectF &r)
+{
+ Q_D(QMacPrintEngine);
+ Q_ASSERT(d->state == QPrinter::Active);
+ d->paintEngine->drawEllipse(r);
+}
+
+void QMacPrintEngine::drawLines(const QLineF *lines, int lineCount)
+{
+ Q_D(QMacPrintEngine);
+ Q_ASSERT(d->state == QPrinter::Active);
+ d->paintEngine->drawLines(lines, lineCount);
+}
+
+void QMacPrintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
+{
+ Q_D(QMacPrintEngine);
+ Q_ASSERT(d->state == QPrinter::Active);
+ d->paintEngine->drawPolygon(points, pointCount, mode);
+}
+
+void QMacPrintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
+{
+ Q_D(QMacPrintEngine);
+ Q_ASSERT(d->state == QPrinter::Active);
+ d->paintEngine->drawPixmap(r, pm, sr);
+}
+
+void QMacPrintEngine::drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, Qt::ImageConversionFlags flags)
+{
+ Q_D(QMacPrintEngine);
+ Q_ASSERT(d->state == QPrinter::Active);
+ d->paintEngine->drawImage(r, pm, sr, flags);
+}
+
+void QMacPrintEngine::drawTextItem(const QPointF &p, const QTextItem &ti)
+{
+ Q_D(QMacPrintEngine);
+ Q_ASSERT(d->state == QPrinter::Active);
+ d->paintEngine->drawTextItem(p, ti);
+}
+
+void QMacPrintEngine::drawTiledPixmap(const QRectF &dr, const QPixmap &pixmap, const QPointF &sr)
+{
+ Q_D(QMacPrintEngine);
+ Q_ASSERT(d->state == QPrinter::Active);
+ d->paintEngine->drawTiledPixmap(dr, pixmap, sr);
+}
+
+void QMacPrintEngine::drawPath(const QPainterPath &path)
+{
+ Q_D(QMacPrintEngine);
+ Q_ASSERT(d->state == QPrinter::Active);
+ d->paintEngine->drawPath(path);
+}
+
+
+void QMacPrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &value)
+{
+ Q_D(QMacPrintEngine);
+
+ d->valueCache.insert(key, value);
+ if (!d->session)
+ return;
+
+ switch (key) {
+ case PPK_CollateCopies:
+ break;
+ case PPK_ColorMode:
+ break;
+ case PPK_Creator:
+ break;
+ case PPK_DocumentName:
+ break;
+ case PPK_PageOrder:
+ break;
+ case PPK_PaperSource:
+ break;
+ case PPK_SelectionOption:
+ break;
+ case PPK_Resolution: {
+ PMPrinter printer;
+ UInt32 count;
+ if (PMSessionGetCurrentPrinter(d->session, &printer) != noErr)
+ break;
+ if (PMPrinterGetPrinterResolutionCount(printer, &count) != noErr)
+ break;
+ PMResolution resolution = { 0.0, 0.0 };
+ PMResolution bestResolution = { 0.0, 0.0 };
+ int dpi = value.toInt();
+ int bestDistance = INT_MAX;
+ for (UInt32 i = 1; i <= count; ++i) { // Yes, it starts at 1
+ if (PMPrinterGetIndexedPrinterResolution(printer, i, &resolution) == noErr) {
+ if (dpi == int(resolution.hRes)) {
+ bestResolution = resolution;
+ break;
+ } else {
+ int distance = qAbs(dpi - int(resolution.hRes));
+ if (distance < bestDistance) {
+ bestDistance = distance;
+ bestResolution = resolution;
+ }
+ }
+ }
+ }
+ PMSessionValidatePageFormat(d->session, d->format, kPMDontWantBoolean);
+ break;
+ }
+
+ case PPK_FullPage:
+ d->fullPage = value.toBool();
+ break;
+ case PPK_CopyCount: // fallthrough
+ case PPK_NumberOfCopies:
+ PMSetCopies(d->settings, value.toInt(), false);
+ break;
+ case PPK_Orientation: {
+ if (d->state == QPrinter::Active) {
+ qWarning("QMacPrintEngine::setOrientation: Orientation cannot be changed during a print job, ignoring change");
+ } else {
+ QPrinter::Orientation newOrientation = QPrinter::Orientation(value.toInt());
+ if (d->hasCustomPaperSize && (d->orient != newOrientation))
+ d->customSize = QSizeF(d->customSize.height(), d->customSize.width());
+ d->orient = newOrientation;
+ PMOrientation o = d->orient == QPrinter::Portrait ? kPMPortrait : kPMLandscape;
+ PMSetOrientation(d->format, o, false);
+ PMSessionValidatePageFormat(d->session, d->format, kPMDontWantBoolean);
+ }
+ break; }
+ case PPK_OutputFileName:
+ d->outputFilename = value.toString();
+ break;
+ case PPK_PaperSize:
+ d->setPaperSize(QPrinter::PaperSize(value.toInt()));
+ break;
+ case PPK_PrinterName: {
+ bool printerNameSet = false;
+ OSStatus status = noErr;
+ QCFType<CFArrayRef> printerList;
+ status = PMServerCreatePrinterList(kPMServerLocal, &printerList);
+ if (status == noErr) {
+ CFIndex count = CFArrayGetCount(printerList);
+ for (CFIndex i=0; i<count; ++i) {
+ PMPrinter printer = static_cast<PMPrinter>(const_cast<void *>(CFArrayGetValueAtIndex(printerList, i)));
+ QString name = QCFString::toQString(PMPrinterGetName(printer));
+ if (name == value.toString()) {
+ status = PMSessionSetCurrentPMPrinter(d->session, printer);
+ printerNameSet = true;
+ break;
+ }
+ }
+ }
+ if (status != noErr)
+ qWarning("QMacPrintEngine::setPrinterName: Error setting printer: %ld", long(status));
+ if (!printerNameSet) {
+ qWarning("QMacPrintEngine::setPrinterName: Failed to set printer named '%s'.", qPrintable(value.toString()));
+ d->releaseSession();
+ d->state = QPrinter::Idle;
+ }
+ break; }
+ case PPK_SuppressSystemPrintStatus:
+ d->suppressStatus = value.toBool();
+ break;
+ case PPK_CustomPaperSize:
+ {
+ PMOrientation orientation;
+ PMGetOrientation(d->format, &orientation);
+ d->hasCustomPaperSize = true;
+ d->customSize = value.toSizeF();
+ if (orientation != kPMPortrait)
+ d->customSize = QSizeF(d->customSize.height(), d->customSize.width());
+ break;
+ }
+ case PPK_PageMargins:
+ {
+ QList<QVariant> margins(value.toList());
+ Q_ASSERT(margins.size() == 4);
+ d->leftMargin = margins.at(0).toDouble();
+ d->topMargin = margins.at(1).toDouble();
+ d->rightMargin = margins.at(2).toDouble();
+ d->bottomMargin = margins.at(3).toDouble();
+ d->hasCustomPageMargins = true;
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+QVariant QMacPrintEngine::property(PrintEnginePropertyKey key) const
+{
+ Q_D(const QMacPrintEngine);
+ QVariant ret;
+
+ if (!d->session && d->valueCache.contains(key))
+ return *d->valueCache.find(key);
+
+ switch (key) {
+ case PPK_CollateCopies:
+ ret = false;
+ break;
+ case PPK_ColorMode:
+ ret = QPrinter::Color;
+ break;
+ case PPK_Creator:
+ break;
+ case PPK_DocumentName:
+ break;
+ case PPK_FullPage:
+ ret = d->fullPage;
+ break;
+ case PPK_NumberOfCopies:
+ ret = 1;
+ break;
+ case PPK_CopyCount: {
+ UInt32 copies = 1;
+ PMGetCopies(d->settings, &copies);
+ ret = (uint) copies;
+ break;
+ }
+ case PPK_SupportsMultipleCopies:
+ ret = true;
+ break;
+ case PPK_Orientation:
+ PMOrientation orientation;
+ PMGetOrientation(d->format, &orientation);
+ ret = orientation == kPMPortrait ? QPrinter::Portrait : QPrinter::Landscape;
+ break;
+ case PPK_OutputFileName:
+ ret = d->outputFilename;
+ break;
+ case PPK_PageOrder:
+ break;
+ case PPK_PaperSource:
+ break;
+ case PPK_PageRect: {
+ // PageRect is returned in device pixels
+ QRect r;
+ PMRect macrect, macpaper;
+ qreal hRatio = d->resolution.hRes / 72;
+ qreal vRatio = d->resolution.vRes / 72;
+ if (d->hasCustomPaperSize) {
+ r = QRect(0, 0, qRound(d->customSize.width() * hRatio), qRound(d->customSize.height() * vRatio));
+ if (d->hasCustomPageMargins) {
+ r.adjust(qRound(d->leftMargin * hRatio), qRound(d->topMargin * vRatio),
+ -qRound(d->rightMargin * hRatio), -qRound(d->bottomMargin * vRatio));
+ } else {
+ QList<QVariant> margins = property(QPrintEngine::PPK_PageMargins).toList();
+ r.adjust(qRound(margins.at(0).toDouble() * hRatio),
+ qRound(margins.at(1).toDouble() * vRatio),
+ -qRound(margins.at(2).toDouble() * hRatio),
+ -qRound(margins.at(3).toDouble()) * vRatio);
+ }
+ } else if (PMGetAdjustedPageRect(d->format, &macrect) == noErr
+ && PMGetAdjustedPaperRect(d->format, &macpaper) == noErr)
+ {
+ if (d->fullPage || d->hasCustomPageMargins) {
+ r.setCoords(int(macpaper.left * hRatio), int(macpaper.top * vRatio),
+ int(macpaper.right * hRatio), int(macpaper.bottom * vRatio));
+ r.translate(-r.x(), -r.y());
+ if (d->hasCustomPageMargins) {
+ r.adjust(qRound(d->leftMargin * hRatio), qRound(d->topMargin * vRatio),
+ -qRound(d->rightMargin * hRatio), -qRound(d->bottomMargin * vRatio));
+ }
+ } else {
+ r.setCoords(int(macrect.left * hRatio), int(macrect.top * vRatio),
+ int(macrect.right * hRatio), int(macrect.bottom * vRatio));
+ r.translate(int(-macpaper.left * hRatio), int(-macpaper.top * vRatio));
+ }
+ }
+ ret = r;
+ break; }
+ case PPK_PaperSize:
+ ret = d->paperSize();
+ break;
+ case PPK_PaperRect: {
+ QRect r;
+ PMRect macrect;
+ if (d->hasCustomPaperSize) {
+ r = QRect(0, 0, qRound(d->customSize.width()), qRound(d->customSize.height()));
+ } else if (PMGetAdjustedPaperRect(d->format, &macrect) == noErr) {
+ qreal hRatio = d->resolution.hRes / 72;
+ qreal vRatio = d->resolution.vRes / 72;
+ r.setCoords(int(macrect.left * hRatio), int(macrect.top * vRatio),
+ int(macrect.right * hRatio), int(macrect.bottom * vRatio));
+ r.translate(-r.x(), -r.y());
+ }
+ ret = r;
+ break; }
+ case PPK_PrinterName: {
+ PMPrinter printer;
+ OSStatus status = PMSessionGetCurrentPrinter(d->session, &printer);
+ if (status != noErr)
+ qWarning("QMacPrintEngine::printerName: Failed getting current PMPrinter: %ld", long(status));
+ if (printer)
+ ret = QCFString::toQString(PMPrinterGetName(printer));
+ break; }
+ case PPK_Resolution: {
+ ret = d->resolution.hRes;
+ break;
+ }
+ case PPK_SupportedResolutions:
+ ret = d->supportedResolutions();
+ break;
+ case PPK_CustomPaperSize:
+ ret = d->customSize;
+ break;
+ case PPK_PageMargins:
+ {
+ QList<QVariant> margins;
+ if (d->hasCustomPageMargins) {
+ margins << d->leftMargin << d->topMargin
+ << d->rightMargin << d->bottomMargin;
+ } else {
+ PMPaperMargins paperMargins;
+ PMPaper paper;
+ PMGetPageFormatPaper(d->format, &paper);
+ PMPaperGetMargins(paper, &paperMargins);
+ margins << paperMargins.left << paperMargins.top
+ << paperMargins.right << paperMargins.bottom;
+ }
+ ret = margins;
+ break;
+ }
+ default:
+ break;
+ }
+ return ret;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
diff --git a/src/gui/painting/qprintengine_mac_p.h b/src/gui/painting/qprintengine_mac_p.h
new file mode 100644
index 0000000000..511705d26f
--- /dev/null
+++ b/src/gui/painting/qprintengine_mac_p.h
@@ -0,0 +1,166 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPRINTENGINE_MAC_P_H
+#define QPRINTENGINE_MAC_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef QT_NO_PRINTER
+
+#include "QtGui/qprinter.h"
+#include "QtGui/qprintengine.h"
+#include "private/qpaintengine_mac_p.h"
+#include "private/qpainter_p.h"
+
+#ifdef __OBJC__
+@class NSPrintInfo;
+#else
+typedef void NSPrintInfo;
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QPrinterPrivate;
+class QMacPrintEnginePrivate;
+class QMacPrintEngine : public QPaintEngine, public QPrintEngine
+{
+ Q_DECLARE_PRIVATE(QMacPrintEngine)
+public:
+ QMacPrintEngine(QPrinter::PrinterMode mode);
+
+ Qt::HANDLE handle() const;
+
+ bool begin(QPaintDevice *dev);
+ bool end();
+ virtual QPaintEngine::Type type() const { return QPaintEngine::MacPrinter; }
+
+ QPaintEngine *paintEngine() const;
+
+ void setProperty(PrintEnginePropertyKey key, const QVariant &value);
+ QVariant property(PrintEnginePropertyKey key) const;
+
+ QPrinter::PrinterState printerState() const;
+
+ bool newPage();
+ bool abort();
+ int metric(QPaintDevice::PaintDeviceMetric) const;
+
+ //forwarded functions
+
+ void updateState(const QPaintEngineState &state);
+
+ virtual void drawLines(const QLineF *lines, int lineCount);
+ virtual void drawRects(const QRectF *r, int num);
+ virtual void drawPoints(const QPointF *p, int pointCount);
+ virtual void drawEllipse(const QRectF &r);
+ virtual void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode);
+ virtual void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
+ virtual void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, Qt::ImageConversionFlags flags);
+ virtual void drawTextItem(const QPointF &p, const QTextItem &ti);
+ virtual void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s);
+ virtual void drawPath(const QPainterPath &);
+
+private:
+ friend class QPrintDialog;
+ friend class QPageSetupDialog;
+};
+
+class QMacPrintEnginePrivate : public QPaintEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QMacPrintEngine)
+public:
+ QPrinter::PrinterMode mode;
+ QPrinter::PrinterState state;
+ QPrinter::Orientation orient;
+ NSPrintInfo *printInfo;
+ PMPageFormat format;
+ PMPrintSettings settings;
+ PMPrintSession session;
+ PMResolution resolution;
+ QString outputFilename;
+ bool fullPage;
+ QPaintEngine *paintEngine;
+ bool suppressStatus;
+ bool hasCustomPaperSize;
+ QSizeF customSize;
+ bool hasCustomPageMargins;
+ qreal leftMargin;
+ qreal topMargin;
+ qreal rightMargin;
+ qreal bottomMargin;
+ QHash<QMacPrintEngine::PrintEnginePropertyKey, QVariant> valueCache;
+ QMacPrintEnginePrivate() : mode(QPrinter::ScreenResolution), state(QPrinter::Idle),
+ orient(QPrinter::Portrait), printInfo(0), format(0), settings(0),
+ session(0), paintEngine(0), suppressStatus(false),
+ hasCustomPaperSize(false), hasCustomPageMargins(false) {}
+ ~QMacPrintEnginePrivate();
+ void initialize();
+ void releaseSession();
+ bool newPage_helper();
+ void setPaperSize(QPrinter::PaperSize ps);
+ QPrinter::PaperSize paperSize() const;
+ QList<QVariant> supportedResolutions() const;
+ inline bool isPrintSessionInitialized() const
+ {
+#ifndef QT_MAC_USE_COCOA
+ return session != 0;
+#else
+ return printInfo != 0;
+#endif
+ }
+ bool shouldSuppressStatus() const;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
+
+#endif // QPRINTENGINE_WIN_P_H
diff --git a/src/gui/painting/qprintengine_pdf.cpp b/src/gui/painting/qprintengine_pdf.cpp
new file mode 100644
index 0000000000..b7f51606da
--- /dev/null
+++ b/src/gui/painting/qprintengine_pdf.cpp
@@ -0,0 +1,1241 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtGui/qprintengine.h>
+
+#include <qiodevice.h>
+#include <qpainter.h>
+#include <qbitmap.h>
+#include <qpainterpath.h>
+#include <qpaintdevice.h>
+#include <qfile.h>
+#include <qdebug.h>
+#include <qimagewriter.h>
+#include <qbuffer.h>
+#include <qdatetime.h>
+
+#ifndef QT_NO_PRINTER
+#include <limits.h>
+#include <math.h>
+#ifndef QT_NO_COMPRESS
+#include <zlib.h>
+#endif
+
+#if defined(Q_OS_WINCE)
+#include "qwinfunctions_wince.h"
+#endif
+
+#include "qprintengine_pdf_p.h"
+#include "private/qdrawhelper_p.h"
+
+QT_BEGIN_NAMESPACE
+
+extern qint64 qt_pixmap_id(const QPixmap &pixmap);
+extern qint64 qt_image_id(const QImage &image);
+
+//#define FONT_DUMP
+
+// might be helpful for smooth transforms of images
+// Can't use it though, as gs generates completely wrong images if this is true.
+static const bool interpolateImages = false;
+
+#ifdef QT_NO_COMPRESS
+static const bool do_compress = false;
+#else
+static const bool do_compress = true;
+#endif
+
+QPdfPage::QPdfPage()
+ : QPdf::ByteStream(true) // Enable file backing
+{
+}
+
+void QPdfPage::streamImage(int w, int h, int object)
+{
+ *this << w << "0 0 " << -h << "0 " << h << "cm /Im" << object << " Do\n";
+ if (!images.contains(object))
+ images.append(object);
+}
+
+
+inline QPaintEngine::PaintEngineFeatures qt_pdf_decide_features()
+{
+ QPaintEngine::PaintEngineFeatures f = QPaintEngine::AllFeatures;
+ f &= ~(QPaintEngine::PorterDuff | QPaintEngine::PerspectiveTransform
+ | QPaintEngine::ObjectBoundingModeGradients
+#ifndef USE_NATIVE_GRADIENTS
+ | QPaintEngine::LinearGradientFill
+#endif
+ | QPaintEngine::RadialGradientFill
+ | QPaintEngine::ConicalGradientFill);
+ return f;
+}
+
+QPdfEngine::QPdfEngine(QPrinter::PrinterMode m)
+ : QPdfBaseEngine(*new QPdfEnginePrivate(m), qt_pdf_decide_features())
+{
+ state = QPrinter::Idle;
+}
+
+QPdfEngine::~QPdfEngine()
+{
+}
+
+bool QPdfEngine::begin(QPaintDevice *pdev)
+{
+ Q_D(QPdfEngine);
+
+ if(!QPdfBaseEngine::begin(pdev)) {
+ state = QPrinter::Error;
+ return false;
+ }
+ d->stream->setDevice(d->outDevice);
+
+ d->streampos = 0;
+ d->hasPen = true;
+ d->hasBrush = false;
+ d->clipEnabled = false;
+ d->allClipped = false;
+
+ d->xrefPositions.clear();
+ d->pageRoot = 0;
+ d->catalog = 0;
+ d->info = 0;
+ d->graphicsState = 0;
+ d->patternColorSpace = 0;
+
+ d->pages.clear();
+ d->imageCache.clear();
+
+ setActive(true);
+ state = QPrinter::Active;
+ d->writeHeader();
+ newPage();
+
+ return true;
+}
+
+bool QPdfEngine::end()
+{
+ Q_D(QPdfEngine);
+ d->writeTail();
+
+ d->stream->unsetDevice();
+ QPdfBaseEngine::end();
+ setActive(false);
+ state = QPrinter::Idle;
+ return true;
+}
+
+
+void QPdfEngine::drawPixmap (const QRectF &rectangle, const QPixmap &pixmap, const QRectF &sr)
+{
+ if (sr.isEmpty() || rectangle.isEmpty() || pixmap.isNull())
+ return;
+ Q_D(QPdfEngine);
+
+ QBrush b = d->brush;
+
+ QRect sourceRect = sr.toRect();
+ QPixmap pm = sourceRect != pixmap.rect() ? pixmap.copy(sourceRect) : pixmap;
+ QImage image = pm.toImage();
+ bool bitmap = true;
+ const int object = d->addImage(image, &bitmap, pm.cacheKey());
+ if (object < 0)
+ return;
+
+ *d->currentPage << "q\n/GSa gs\n";
+ *d->currentPage
+ << QPdf::generateMatrix(QTransform(rectangle.width() / sr.width(), 0, 0, rectangle.height() / sr.height(),
+ rectangle.x(), rectangle.y()) * (d->simplePen ? QTransform() : d->stroker.matrix));
+ if (bitmap) {
+ // set current pen as d->brush
+ d->brush = d->pen.brush();
+ }
+ setBrush();
+ d->currentPage->streamImage(image.width(), image.height(), object);
+ *d->currentPage << "Q\n";
+
+ d->brush = b;
+}
+
+void QPdfEngine::drawImage(const QRectF &rectangle, const QImage &image, const QRectF &sr, Qt::ImageConversionFlags)
+{
+ if (sr.isEmpty() || rectangle.isEmpty() || image.isNull())
+ return;
+ Q_D(QPdfEngine);
+
+ QRect sourceRect = sr.toRect();
+ QImage im = sourceRect != image.rect() ? image.copy(sourceRect) : image;
+ bool bitmap = true;
+ const int object = d->addImage(im, &bitmap, im.cacheKey());
+ if (object < 0)
+ return;
+
+ *d->currentPage << "q\n/GSa gs\n";
+ *d->currentPage
+ << QPdf::generateMatrix(QTransform(rectangle.width() / sr.width(), 0, 0, rectangle.height() / sr.height(),
+ rectangle.x(), rectangle.y()) * (d->simplePen ? QTransform() : d->stroker.matrix));
+ setBrush();
+ d->currentPage->streamImage(im.width(), im.height(), object);
+ *d->currentPage << "Q\n";
+}
+
+void QPdfEngine::drawTiledPixmap (const QRectF &rectangle, const QPixmap &pixmap, const QPointF &point)
+{
+ Q_D(QPdfEngine);
+
+ bool bitmap = (pixmap.depth() == 1);
+ QBrush b = d->brush;
+ QPointF bo = d->brushOrigin;
+ bool hp = d->hasPen;
+ d->hasPen = false;
+ bool hb = d->hasBrush;
+ d->hasBrush = true;
+
+ d->brush = QBrush(pixmap);
+ if (bitmap)
+ // #### fix bitmap case where we have a brush pen
+ d->brush.setColor(d->pen.color());
+
+ d->brushOrigin = -point;
+ *d->currentPage << "q\n";
+ setBrush();
+
+ drawRects(&rectangle, 1);
+ *d->currentPage << "Q\n";
+
+ d->hasPen = hp;
+ d->hasBrush = hb;
+ d->brush = b;
+ d->brushOrigin = bo;
+}
+
+
+void QPdfEngine::setBrush()
+{
+ Q_D(QPdfEngine);
+ Qt::BrushStyle style = d->brush.style();
+ if (style == Qt::NoBrush)
+ return;
+
+ bool specifyColor;
+ int gStateObject = 0;
+ int patternObject = d->addBrushPattern(d->stroker.matrix, &specifyColor, &gStateObject);
+
+ *d->currentPage << (patternObject ? "/PCSp cs " : "/CSp cs ");
+ if (specifyColor) {
+ QColor rgba = d->brush.color();
+ if (d->colorMode == QPrinter::GrayScale) {
+ qreal gray = qGray(rgba.rgba())/255.;
+ *d->currentPage << gray << gray << gray;
+ } else {
+ *d->currentPage << rgba.redF()
+ << rgba.greenF()
+ << rgba.blueF();
+ }
+ }
+ if (patternObject)
+ *d->currentPage << "/Pat" << patternObject;
+ *d->currentPage << "scn\n";
+
+ if (gStateObject)
+ *d->currentPage << "/GState" << gStateObject << "gs\n";
+ else
+ *d->currentPage << "/GSa gs\n";
+}
+
+QPaintEngine::Type QPdfEngine::type() const
+{
+ return QPaintEngine::Pdf;
+}
+
+bool QPdfEngine::newPage()
+{
+ Q_D(QPdfEngine);
+ if (!isActive())
+ return false;
+ d->newPage();
+ return QPdfBaseEngine::newPage();
+}
+
+QPdfEnginePrivate::QPdfEnginePrivate(QPrinter::PrinterMode m)
+ : QPdfBaseEnginePrivate(m)
+{
+ streampos = 0;
+
+ stream = new QDataStream;
+ pageOrder = QPrinter::FirstPageFirst;
+ orientation = QPrinter::Portrait;
+ fullPage = false;
+}
+
+QPdfEnginePrivate::~QPdfEnginePrivate()
+{
+ delete stream;
+}
+
+
+#ifdef USE_NATIVE_GRADIENTS
+int QPdfEnginePrivate::gradientBrush(const QBrush &b, const QMatrix &matrix, int *gStateObject)
+{
+ const QGradient *gradient = b.gradient();
+ if (!gradient)
+ return 0;
+
+ QTransform inv = matrix.inverted();
+ QPointF page_rect[4] = { inv.map(QPointF(0, 0)),
+ inv.map(QPointF(width_, 0)),
+ inv.map(QPointF(0, height_)),
+ inv.map(QPointF(width_, height_)) };
+
+ bool opaque = b.isOpaque();
+
+ QByteArray shader;
+ QByteArray alphaShader;
+ if (gradient->type() == QGradient::LinearGradient) {
+ const QLinearGradient *lg = static_cast<const QLinearGradient *>(gradient);
+ shader = QPdf::generateLinearGradientShader(lg, page_rect);
+ if (!opaque)
+ alphaShader = QPdf::generateLinearGradientShader(lg, page_rect, true);
+ } else {
+ // #############
+ return 0;
+ }
+ int shaderObject = addXrefEntry(-1);
+ write(shader);
+
+ QByteArray str;
+ QPdf::ByteStream s(&str);
+ s << "<<\n"
+ "/Type /Pattern\n"
+ "/PatternType 2\n"
+ "/Shading " << shaderObject << "0 R\n"
+ "/Matrix ["
+ << matrix.m11()
+ << matrix.m12()
+ << matrix.m21()
+ << matrix.m22()
+ << matrix.dx()
+ << matrix.dy() << "]\n";
+ s << ">>\n"
+ "endobj\n";
+
+ int patternObj = addXrefEntry(-1);
+ write(str);
+ currentPage->patterns.append(patternObj);
+
+ if (!opaque) {
+ bool ca = true;
+ QGradientStops stops = gradient->stops();
+ int a = stops.at(0).second.alpha();
+ for (int i = 1; i < stops.size(); ++i) {
+ if (stops.at(i).second.alpha() != a) {
+ ca = false;
+ break;
+ }
+ }
+ if (ca) {
+ *gStateObject = addConstantAlphaObject(stops.at(0).second.alpha());
+ } else {
+ int alphaShaderObject = addXrefEntry(-1);
+ write(alphaShader);
+
+ QByteArray content;
+ QPdf::ByteStream c(&content);
+ c << "/Shader" << alphaShaderObject << "sh\n";
+
+ QByteArray form;
+ QPdf::ByteStream f(&form);
+ f << "<<\n"
+ "/Type /XObject\n"
+ "/Subtype /Form\n"
+ "/BBox [0 0 " << width_ << height_ << "]\n"
+ "/Group <</S /Transparency >>\n"
+ "/Resources <<\n"
+ "/Shading << /Shader" << alphaShaderObject << alphaShaderObject << "0 R >>\n"
+ ">>\n";
+
+ f << "/Length " << content.length() << "\n"
+ ">>\n"
+ "stream\n"
+ << content
+ << "endstream\n"
+ "endobj\n";
+
+ int softMaskFormObject = addXrefEntry(-1);
+ write(form);
+ *gStateObject = addXrefEntry(-1);
+ xprintf("<< /SMask << /S /Alpha /G %d 0 R >> >>\n"
+ "endobj\n", softMaskFormObject);
+ currentPage->graphicStates.append(*gStateObject);
+ }
+ }
+
+ return patternObj;
+}
+#endif
+
+int QPdfEnginePrivate::addConstantAlphaObject(int brushAlpha, int penAlpha)
+{
+ if (brushAlpha == 255 && penAlpha == 255)
+ return 0;
+ int object = alphaCache.value(QPair<uint, uint>(brushAlpha, penAlpha), 0);
+ if (!object) {
+ object = addXrefEntry(-1);
+ QByteArray alphaDef;
+ QPdf::ByteStream s(&alphaDef);
+ s << "<<\n/ca " << (brushAlpha/qreal(255.)) << '\n';
+ s << "/CA " << (penAlpha/qreal(255.)) << "\n>>";
+ xprintf("%s\nendobj\n", alphaDef.constData());
+ alphaCache.insert(QPair<uint, uint>(brushAlpha, penAlpha), object);
+ }
+ if (currentPage->graphicStates.indexOf(object) < 0)
+ currentPage->graphicStates.append(object);
+
+ return object;
+}
+
+int QPdfEnginePrivate::addBrushPattern(const QTransform &m, bool *specifyColor, int *gStateObject)
+{
+ int paintType = 2; // Uncolored tiling
+ int w = 8;
+ int h = 8;
+
+ *specifyColor = true;
+ *gStateObject = 0;
+
+ QTransform matrix = m;
+ matrix.translate(brushOrigin.x(), brushOrigin.y());
+ matrix = matrix * pageMatrix();
+ //qDebug() << brushOrigin << matrix;
+
+ Qt::BrushStyle style = brush.style();
+ if (style == Qt::LinearGradientPattern) {// && style <= Qt::ConicalGradientPattern) {
+#ifdef USE_NATIVE_GRADIENTS
+ *specifyColor = false;
+ return gradientBrush(b, matrix, gStateObject);
+#else
+ return 0;
+#endif
+ }
+
+ if ((!brush.isOpaque() && brush.style() < Qt::LinearGradientPattern) || opacity != 1.0)
+ *gStateObject = addConstantAlphaObject(qRound(brush.color().alpha() * opacity),
+ qRound(pen.color().alpha() * opacity));
+
+ int imageObject = -1;
+ QByteArray pattern = QPdf::patternForBrush(brush);
+ if (pattern.isEmpty()) {
+ if (brush.style() != Qt::TexturePattern)
+ return 0;
+ QImage image = brush.texture().toImage();
+ bool bitmap = true;
+ imageObject = addImage(image, &bitmap, qt_pixmap_id(brush.texture()));
+ if (imageObject != -1) {
+ QImage::Format f = image.format();
+ if (f != QImage::Format_MonoLSB && f != QImage::Format_Mono) {
+ paintType = 1; // Colored tiling
+ *specifyColor = false;
+ }
+ w = image.width();
+ h = image.height();
+ QTransform m(w, 0, 0, -h, 0, h);
+ QPdf::ByteStream s(&pattern);
+ s << QPdf::generateMatrix(m);
+ s << "/Im" << imageObject << " Do\n";
+ }
+ }
+
+ QByteArray str;
+ QPdf::ByteStream s(&str);
+ s << "<<\n"
+ "/Type /Pattern\n"
+ "/PatternType 1\n"
+ "/PaintType " << paintType << "\n"
+ "/TilingType 1\n"
+ "/BBox [0 0 " << w << h << "]\n"
+ "/XStep " << w << "\n"
+ "/YStep " << h << "\n"
+ "/Matrix ["
+ << matrix.m11()
+ << matrix.m12()
+ << matrix.m21()
+ << matrix.m22()
+ << matrix.dx()
+ << matrix.dy() << "]\n"
+ "/Resources \n<< "; // open resource tree
+ if (imageObject > 0) {
+ s << "/XObject << /Im" << imageObject << ' ' << imageObject << "0 R >> ";
+ }
+ s << ">>\n"
+ "/Length " << pattern.length() << "\n"
+ ">>\n"
+ "stream\n"
+ << pattern
+ << "endstream\n"
+ "endobj\n";
+
+ int patternObj = addXrefEntry(-1);
+ write(str);
+ currentPage->patterns.append(patternObj);
+ return patternObj;
+}
+
+/*!
+ * Adds an image to the pdf and return the pdf-object id. Returns -1 if adding the image failed.
+ */
+int QPdfEnginePrivate::addImage(const QImage &img, bool *bitmap, qint64 serial_no)
+{
+ if (img.isNull())
+ return -1;
+
+ int object = imageCache.value(serial_no);
+ if(object)
+ return object;
+
+ QImage image = img;
+ QImage::Format format = image.format();
+ if (image.depth() == 1 && *bitmap && img.colorTable().size() == 0) {
+ if (format == QImage::Format_MonoLSB)
+ image = image.convertToFormat(QImage::Format_Mono);
+ format = QImage::Format_Mono;
+ } else {
+ *bitmap = false;
+ if (format != QImage::Format_RGB32 && format != QImage::Format_ARGB32) {
+ image = image.convertToFormat(QImage::Format_ARGB32);
+ format = QImage::Format_ARGB32;
+ }
+ }
+
+ int w = image.width();
+ int h = image.height();
+ int d = image.depth();
+
+ if (format == QImage::Format_Mono) {
+ int bytesPerLine = (w + 7) >> 3;
+ QByteArray data;
+ data.resize(bytesPerLine * h);
+ char *rawdata = data.data();
+ for (int y = 0; y < h; ++y) {
+ memcpy(rawdata, image.scanLine(y), bytesPerLine);
+ rawdata += bytesPerLine;
+ }
+ object = writeImage(data, w, h, d, 0, 0);
+ } else {
+ QByteArray softMaskData;
+ bool dct = false;
+ QByteArray imageData;
+ bool hasAlpha = false;
+ bool hasMask = false;
+
+ if (QImageWriter::supportedImageFormats().contains("jpeg") && colorMode != QPrinter::GrayScale) {
+ QBuffer buffer(&imageData);
+ QImageWriter writer(&buffer, "jpeg");
+ writer.setQuality(94);
+ writer.write(image);
+ dct = true;
+
+ if (format != QImage::Format_RGB32) {
+ softMaskData.resize(w * h);
+ uchar *sdata = (uchar *)softMaskData.data();
+ for (int y = 0; y < h; ++y) {
+ const QRgb *rgb = (const QRgb *)image.scanLine(y);
+ for (int x = 0; x < w; ++x) {
+ uchar alpha = qAlpha(*rgb);
+ *sdata++ = alpha;
+ hasMask |= (alpha < 255);
+ hasAlpha |= (alpha != 0 && alpha != 255);
+ ++rgb;
+ }
+ }
+ }
+ } else {
+ imageData.resize(colorMode == QPrinter::GrayScale ? w * h : 3 * w * h);
+ uchar *data = (uchar *)imageData.data();
+ softMaskData.resize(w * h);
+ uchar *sdata = (uchar *)softMaskData.data();
+ for (int y = 0; y < h; ++y) {
+ const QRgb *rgb = (const QRgb *)image.scanLine(y);
+ if (colorMode == QPrinter::GrayScale) {
+ for (int x = 0; x < w; ++x) {
+ *(data++) = qGray(*rgb);
+ uchar alpha = qAlpha(*rgb);
+ *sdata++ = alpha;
+ hasMask |= (alpha < 255);
+ hasAlpha |= (alpha != 0 && alpha != 255);
+ ++rgb;
+ }
+ } else {
+ for (int x = 0; x < w; ++x) {
+ *(data++) = qRed(*rgb);
+ *(data++) = qGreen(*rgb);
+ *(data++) = qBlue(*rgb);
+ uchar alpha = qAlpha(*rgb);
+ *sdata++ = alpha;
+ hasMask |= (alpha < 255);
+ hasAlpha |= (alpha != 0 && alpha != 255);
+ ++rgb;
+ }
+ }
+ }
+ if (format == QImage::Format_RGB32)
+ hasAlpha = hasMask = false;
+ }
+ int maskObject = 0;
+ int softMaskObject = 0;
+ if (hasAlpha) {
+ softMaskObject = writeImage(softMaskData, w, h, 8, 0, 0);
+ } else if (hasMask) {
+ // dither the soft mask to 1bit and add it. This also helps PDF viewers
+ // without transparency support
+ int bytesPerLine = (w + 7) >> 3;
+ QByteArray mask(bytesPerLine * h, 0);
+ uchar *mdata = (uchar *)mask.data();
+ const uchar *sdata = (const uchar *)softMaskData.constData();
+ for (int y = 0; y < h; ++y) {
+ for (int x = 0; x < w; ++x) {
+ if (*sdata)
+ mdata[x>>3] |= (0x80 >> (x&7));
+ ++sdata;
+ }
+ mdata += bytesPerLine;
+ }
+ maskObject = writeImage(mask, w, h, 1, 0, 0);
+ }
+ object = writeImage(imageData, w, h, colorMode == QPrinter::GrayScale ? 8 : 32,
+ maskObject, softMaskObject, dct);
+ }
+ imageCache.insert(serial_no, object);
+ return object;
+}
+
+void QPdfEnginePrivate::drawTextItem(const QPointF &p, const QTextItemInt &ti)
+{
+ if (ti.charFormat.isAnchor()) {
+ qreal size = ti.fontEngine->fontDef.pixelSize;
+#ifdef Q_WS_WIN
+ if (ti.fontEngine->type() == QFontEngine::Win) {
+ QFontEngineWin *fe = static_cast<QFontEngineWin *>(ti.fontEngine);
+ size = fe->tm.tmHeight;
+ }
+#endif
+ int synthesized = ti.fontEngine->synthesized();
+ qreal stretch = synthesized & QFontEngine::SynthesizedStretch ? ti.fontEngine->fontDef.stretch/100. : 1.;
+
+ QTransform trans;
+ // Build text rendering matrix (Trm). We need it to map the text area to user
+ // space units on the PDF page.
+ trans = QTransform(size*stretch, 0, 0, size, 0, 0);
+ // Apply text matrix (Tm).
+ trans *= QTransform(1,0,0,-1,p.x(),p.y());
+ // Apply page displacement (Identity for first page).
+ trans *= stroker.matrix;
+ // Apply Current Transformation Matrix (CTM)
+ trans *= pageMatrix();
+ qreal x1, y1, x2, y2;
+ trans.map(0, 0, &x1, &y1);
+ trans.map(ti.width.toReal()/size, (ti.ascent.toReal()-ti.descent.toReal())/size, &x2, &y2);
+
+ uint annot = addXrefEntry(-1);
+#ifdef Q_DEBUG_PDF_LINKS
+ xprintf("<<\n/Type /Annot\n/Subtype /Link\n/Rect [%f %f %f %f]\n/Border [16 16 1]\n/A <<\n",
+#else
+ xprintf("<<\n/Type /Annot\n/Subtype /Link\n/Rect [%f %f %f %f]\n/Border [0 0 0]\n/A <<\n",
+#endif
+ static_cast<double>(x1),
+ static_cast<double>(y1),
+ static_cast<double>(x2),
+ static_cast<double>(y2));
+ xprintf("/Type /Action\n/S /URI\n/URI (%s)\n",
+ ti.charFormat.anchorHref().toLatin1().constData());
+ xprintf(">>\n>>\n");
+ xprintf("endobj\n");
+
+ if (!currentPage->annotations.contains(annot)) {
+ currentPage->annotations.append(annot);
+ }
+ }
+
+ QPdfBaseEnginePrivate::drawTextItem(p, ti);
+}
+
+QTransform QPdfEnginePrivate::pageMatrix() const
+{
+ qreal scale = 72./resolution;
+ QTransform tmp(scale, 0.0, 0.0, -scale, 0.0, height());
+ if (!fullPage) {
+ QRect r = pageRect();
+ tmp.translate(r.left(), r.top());
+ }
+ return tmp;
+}
+
+void QPdfEnginePrivate::newPage()
+{
+ if (currentPage && currentPage->pageSize.isEmpty())
+ currentPage->pageSize = QSize(width(), height());
+ writePage();
+
+ delete currentPage;
+ currentPage = new QPdfPage;
+ currentPage->pageSize = QSize(width(), height());
+ stroker.stream = currentPage;
+ pages.append(requestObject());
+
+ *currentPage << "/GSa gs /CSp cs /CSp CS\n"
+ << QPdf::generateMatrix(pageMatrix())
+ << "q q\n";
+}
+
+
+// For strings up to 10000 bytes only !
+void QPdfEnginePrivate::xprintf(const char* fmt, ...)
+{
+ if (!stream)
+ return;
+
+ const int msize = 10000;
+ char buf[msize];
+
+ va_list args;
+ va_start(args, fmt);
+ int bufsize = qvsnprintf(buf, msize, fmt, args);
+
+ Q_ASSERT(bufsize<msize);
+
+ va_end(args);
+
+ stream->writeRawData(buf, bufsize);
+ streampos += bufsize;
+}
+
+int QPdfEnginePrivate::writeCompressed(QIODevice *dev)
+{
+#ifndef QT_NO_COMPRESS
+ if (do_compress) {
+ int size = QPdfPage::chunkSize();
+ int sum = 0;
+ ::z_stream zStruct;
+ zStruct.zalloc = Z_NULL;
+ zStruct.zfree = Z_NULL;
+ zStruct.opaque = Z_NULL;
+ if (::deflateInit(&zStruct, Z_DEFAULT_COMPRESSION) != Z_OK) {
+ qWarning("QPdfStream::writeCompressed: Error in deflateInit()");
+ return sum;
+ }
+ zStruct.avail_in = 0;
+ QByteArray in, out;
+ out.resize(size);
+ while (!dev->atEnd() || zStruct.avail_in != 0) {
+ if (zStruct.avail_in == 0) {
+ in = dev->read(size);
+ zStruct.avail_in = in.size();
+ zStruct.next_in = reinterpret_cast<unsigned char*>(in.data());
+ if (in.size() <= 0) {
+ qWarning("QPdfStream::writeCompressed: Error in read()");
+ ::deflateEnd(&zStruct);
+ return sum;
+ }
+ }
+ zStruct.next_out = reinterpret_cast<unsigned char*>(out.data());
+ zStruct.avail_out = out.size();
+ if (::deflate(&zStruct, 0) != Z_OK) {
+ qWarning("QPdfStream::writeCompressed: Error in deflate()");
+ ::deflateEnd(&zStruct);
+ return sum;
+ }
+ int written = out.size() - zStruct.avail_out;
+ stream->writeRawData(out.constData(), written);
+ streampos += written;
+ sum += written;
+ }
+ int ret;
+ do {
+ zStruct.next_out = reinterpret_cast<unsigned char*>(out.data());
+ zStruct.avail_out = out.size();
+ ret = ::deflate(&zStruct, Z_FINISH);
+ if (ret != Z_OK && ret != Z_STREAM_END) {
+ qWarning("QPdfStream::writeCompressed: Error in deflate()");
+ ::deflateEnd(&zStruct);
+ return sum;
+ }
+ int written = out.size() - zStruct.avail_out;
+ stream->writeRawData(out.constData(), written);
+ streampos += written;
+ sum += written;
+ } while (ret == Z_OK);
+
+ ::deflateEnd(&zStruct);
+
+ return sum;
+ } else
+#endif
+ {
+ QByteArray arr;
+ int sum = 0;
+ while (!dev->atEnd()) {
+ arr = dev->read(QPdfPage::chunkSize());
+ stream->writeRawData(arr.constData(), arr.size());
+ streampos += arr.size();
+ sum += arr.size();
+ }
+ return sum;
+ }
+}
+
+int QPdfEnginePrivate::writeCompressed(const char *src, int len)
+{
+#ifndef QT_NO_COMPRESS
+ if(do_compress) {
+ uLongf destLen = len + len/100 + 13; // zlib requirement
+ Bytef* dest = new Bytef[destLen];
+ if (Z_OK == ::compress(dest, &destLen, (const Bytef*) src, (uLongf)len)) {
+ stream->writeRawData((const char*)dest, destLen);
+ } else {
+ qWarning("QPdfStream::writeCompressed: Error in compress()");
+ destLen = 0;
+ }
+ delete [] dest;
+ len = destLen;
+ } else
+#endif
+ {
+ stream->writeRawData(src,len);
+ }
+ streampos += len;
+ return len;
+}
+
+int QPdfEnginePrivate::writeImage(const QByteArray &data, int width, int height, int depth,
+ int maskObject, int softMaskObject, bool dct)
+{
+ int image = addXrefEntry(-1);
+ xprintf("<<\n"
+ "/Type /XObject\n"
+ "/Subtype /Image\n"
+ "/Width %d\n"
+ "/Height %d\n", width, height);
+
+ if (depth == 1) {
+ xprintf("/ImageMask true\n"
+ "/Decode [1 0]\n");
+ } else {
+ xprintf("/BitsPerComponent 8\n"
+ "/ColorSpace %s\n", (depth == 32) ? "/DeviceRGB" : "/DeviceGray");
+ }
+ if (maskObject > 0)
+ xprintf("/Mask %d 0 R\n", maskObject);
+ if (softMaskObject > 0)
+ xprintf("/SMask %d 0 R\n", softMaskObject);
+
+ int lenobj = requestObject();
+ xprintf("/Length %d 0 R\n", lenobj);
+ if (interpolateImages)
+ xprintf("/Interpolate true\n");
+ int len = 0;
+ if (dct) {
+ //qDebug() << "DCT";
+ xprintf("/Filter /DCTDecode\n>>\nstream\n");
+ write(data);
+ len = data.length();
+ } else {
+ if (do_compress)
+ xprintf("/Filter /FlateDecode\n>>\nstream\n");
+ else
+ xprintf(">>\nstream\n");
+ len = writeCompressed(data);
+ }
+ xprintf("endstream\n"
+ "endobj\n");
+ addXrefEntry(lenobj);
+ xprintf("%d\n"
+ "endobj\n", len);
+ return image;
+}
+
+
+void QPdfEnginePrivate::writeHeader()
+{
+ addXrefEntry(0,false);
+
+ xprintf("%%PDF-1.4\n");
+
+ writeInfo();
+
+ catalog = addXrefEntry(-1);
+ pageRoot = requestObject();
+ xprintf("<<\n"
+ "/Type /Catalog\n"
+ "/Pages %d 0 R\n"
+ ">>\n"
+ "endobj\n", pageRoot);
+
+ // graphics state
+ graphicsState = addXrefEntry(-1);
+ xprintf("<<\n"
+ "/Type /ExtGState\n"
+ "/SA true\n"
+ "/SM 0.02\n"
+ "/ca 1.0\n"
+ "/CA 1.0\n"
+ "/AIS false\n"
+ "/SMask /None"
+ ">>\n"
+ "endobj\n");
+
+ // color space for pattern
+ patternColorSpace = addXrefEntry(-1);
+ xprintf("[/Pattern /DeviceRGB]\n"
+ "endobj\n");
+}
+
+void QPdfEnginePrivate::writeInfo()
+{
+ info = addXrefEntry(-1);
+ xprintf("<<\n/Title ");
+ printString(title);
+ xprintf("\n/Creator ");
+ printString(creator);
+ xprintf("\n/Producer ");
+ printString(QString::fromLatin1("Qt " QT_VERSION_STR " (C) 2011 Nokia Corporation and/or its subsidiary(-ies)"));
+ QDateTime now = QDateTime::currentDateTime().toUTC();
+ QTime t = now.time();
+ QDate d = now.date();
+ xprintf("\n/CreationDate (D:%d%02d%02d%02d%02d%02d)\n",
+ d.year(),
+ d.month(),
+ d.day(),
+ t.hour(),
+ t.minute(),
+ t.second());
+ xprintf(">>\n"
+ "endobj\n");
+}
+
+void QPdfEnginePrivate::writePageRoot()
+{
+ addXrefEntry(pageRoot);
+
+ xprintf("<<\n"
+ "/Type /Pages\n"
+ "/Kids \n"
+ "[\n");
+ int size = pages.size();
+ for (int i = 0; i < size; ++i)
+ xprintf("%d 0 R\n", pages[i]);
+ xprintf("]\n");
+
+ //xprintf("/Group <</S /Transparency /I true /K false>>\n");
+ xprintf("/Count %d\n", pages.size());
+
+ xprintf("/ProcSet [/PDF /Text /ImageB /ImageC]\n"
+ ">>\n"
+ "endobj\n");
+}
+
+
+void QPdfEnginePrivate::embedFont(QFontSubset *font)
+{
+ //qDebug() << "embedFont" << font->object_id;
+ int fontObject = font->object_id;
+ QByteArray fontData = font->toTruetype();
+#ifdef FONT_DUMP
+ static int i = 0;
+ QString fileName("font%1.ttf");
+ fileName = fileName.arg(i++);
+ QFile ff(fileName);
+ ff.open(QFile::WriteOnly);
+ ff.write(fontData);
+ ff.close();
+#endif
+
+ int fontDescriptor = requestObject();
+ int fontstream = requestObject();
+ int cidfont = requestObject();
+ int toUnicode = requestObject();
+
+ QFontEngine::Properties properties = font->fontEngine->properties();
+
+ {
+ qreal scale = 1000/properties.emSquare.toReal();
+ addXrefEntry(fontDescriptor);
+ QByteArray descriptor;
+ QPdf::ByteStream s(&descriptor);
+ s << "<< /Type /FontDescriptor\n"
+ "/FontName /Q";
+ int tag = fontDescriptor;
+ for (int i = 0; i < 5; ++i) {
+ s << (char)('A' + (tag % 26));
+ tag /= 26;
+ }
+ s << '+' << properties.postscriptName << "\n"
+ "/Flags " << 4 << "\n"
+ "/FontBBox ["
+ << properties.boundingBox.x()*scale
+ << -(properties.boundingBox.y() + properties.boundingBox.height())*scale
+ << (properties.boundingBox.x() + properties.boundingBox.width())*scale
+ << -properties.boundingBox.y()*scale << "]\n"
+ "/ItalicAngle " << properties.italicAngle.toReal() << "\n"
+ "/Ascent " << properties.ascent.toReal()*scale << "\n"
+ "/Descent " << -properties.descent.toReal()*scale << "\n"
+ "/CapHeight " << properties.capHeight.toReal()*scale << "\n"
+ "/StemV " << properties.lineWidth.toReal()*scale << "\n"
+ "/FontFile2 " << fontstream << "0 R\n"
+ ">> endobj\n";
+ write(descriptor);
+ }
+ {
+ addXrefEntry(fontstream);
+ QByteArray header;
+ QPdf::ByteStream s(&header);
+
+ int length_object = requestObject();
+ s << "<<\n"
+ "/Length1 " << fontData.size() << "\n"
+ "/Length " << length_object << "0 R\n";
+ if (do_compress)
+ s << "/Filter /FlateDecode\n";
+ s << ">>\n"
+ "stream\n";
+ write(header);
+ int len = writeCompressed(fontData);
+ write("endstream\n"
+ "endobj\n");
+ addXrefEntry(length_object);
+ xprintf("%d\n"
+ "endobj\n", len);
+ }
+ {
+ addXrefEntry(cidfont);
+ QByteArray cid;
+ QPdf::ByteStream s(&cid);
+ s << "<< /Type /Font\n"
+ "/Subtype /CIDFontType2\n"
+ "/BaseFont /" << properties.postscriptName << "\n"
+ "/CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >>\n"
+ "/FontDescriptor " << fontDescriptor << "0 R\n"
+ "/CIDToGIDMap /Identity\n"
+ << font->widthArray() <<
+ ">>\n"
+ "endobj\n";
+ write(cid);
+ }
+ {
+ addXrefEntry(toUnicode);
+ QByteArray touc = font->createToUnicodeMap();
+ xprintf("<< /Length %d >>\n"
+ "stream\n", touc.length());
+ write(touc);
+ write("endstream\n"
+ "endobj\n");
+ }
+ {
+ addXrefEntry(fontObject);
+ QByteArray font;
+ QPdf::ByteStream s(&font);
+ s << "<< /Type /Font\n"
+ "/Subtype /Type0\n"
+ "/BaseFont /" << properties.postscriptName << "\n"
+ "/Encoding /Identity-H\n"
+ "/DescendantFonts [" << cidfont << "0 R]\n"
+ "/ToUnicode " << toUnicode << "0 R"
+ ">>\n"
+ "endobj\n";
+ write(font);
+ }
+}
+
+
+void QPdfEnginePrivate::writeFonts()
+{
+ for (QHash<QFontEngine::FaceId, QFontSubset *>::iterator it = fonts.begin(); it != fonts.end(); ++it) {
+ embedFont(*it);
+ delete *it;
+ }
+ fonts.clear();
+}
+
+void QPdfEnginePrivate::writePage()
+{
+ if (pages.empty())
+ return;
+
+ *currentPage << "Q Q\n";
+
+ uint pageStream = requestObject();
+ uint pageStreamLength = requestObject();
+ uint resources = requestObject();
+ uint annots = requestObject();
+
+ addXrefEntry(pages.last());
+ xprintf("<<\n"
+ "/Type /Page\n"
+ "/Parent %d 0 R\n"
+ "/Contents %d 0 R\n"
+ "/Resources %d 0 R\n"
+ "/Annots %d 0 R\n"
+ "/MediaBox [0 0 %d %d]\n"
+ ">>\n"
+ "endobj\n",
+ pageRoot, pageStream, resources, annots,
+ // make sure we use the pagesize from when we started the page, since the user may have changed it
+ currentPage->pageSize.width(), currentPage->pageSize.height());
+
+ addXrefEntry(resources);
+ xprintf("<<\n"
+ "/ColorSpace <<\n"
+ "/PCSp %d 0 R\n"
+ "/CSp /DeviceRGB\n"
+ "/CSpg /DeviceGray\n"
+ ">>\n"
+ "/ExtGState <<\n"
+ "/GSa %d 0 R\n",
+ patternColorSpace, graphicsState);
+
+ for (int i = 0; i < currentPage->graphicStates.size(); ++i)
+ xprintf("/GState%d %d 0 R\n", currentPage->graphicStates.at(i), currentPage->graphicStates.at(i));
+ xprintf(">>\n");
+
+ xprintf("/Pattern <<\n");
+ for (int i = 0; i < currentPage->patterns.size(); ++i)
+ xprintf("/Pat%d %d 0 R\n", currentPage->patterns.at(i), currentPage->patterns.at(i));
+ xprintf(">>\n");
+
+ xprintf("/Font <<\n");
+ for (int i = 0; i < currentPage->fonts.size();++i)
+ xprintf("/F%d %d 0 R\n", currentPage->fonts[i], currentPage->fonts[i]);
+ xprintf(">>\n");
+
+ xprintf("/XObject <<\n");
+ for (int i = 0; i<currentPage->images.size(); ++i) {
+ xprintf("/Im%d %d 0 R\n", currentPage->images.at(i), currentPage->images.at(i));
+ }
+ xprintf(">>\n");
+
+ xprintf(">>\n"
+ "endobj\n");
+
+ addXrefEntry(annots);
+ xprintf("[ ");
+ for (int i = 0; i<currentPage->annotations.size(); ++i) {
+ xprintf("%d 0 R ", currentPage->annotations.at(i));
+ }
+ xprintf("]\nendobj\n");
+
+ addXrefEntry(pageStream);
+ xprintf("<<\n"
+ "/Length %d 0 R\n", pageStreamLength); // object number for stream length object
+ if (do_compress)
+ xprintf("/Filter /FlateDecode\n");
+
+ xprintf(">>\n");
+ xprintf("stream\n");
+ QIODevice *content = currentPage->stream();
+ int len = writeCompressed(content);
+ xprintf("endstream\n"
+ "endobj\n");
+
+ addXrefEntry(pageStreamLength);
+ xprintf("%d\nendobj\n",len);
+}
+
+void QPdfEnginePrivate::writeTail()
+{
+ writePage();
+ writeFonts();
+ writePageRoot();
+ addXrefEntry(xrefPositions.size(),false);
+ xprintf("xref\n"
+ "0 %d\n"
+ "%010d 65535 f \n", xrefPositions.size()-1, xrefPositions[0]);
+
+ for (int i = 1; i < xrefPositions.size()-1; ++i)
+ xprintf("%010d 00000 n \n", xrefPositions[i]);
+
+ xprintf("trailer\n"
+ "<<\n"
+ "/Size %d\n"
+ "/Info %d 0 R\n"
+ "/Root %d 0 R\n"
+ ">>\n"
+ "startxref\n%d\n"
+ "%%%%EOF\n",
+ xrefPositions.size()-1, info, catalog, xrefPositions.last());
+}
+
+int QPdfEnginePrivate::addXrefEntry(int object, bool printostr)
+{
+ if (object < 0)
+ object = requestObject();
+
+ if (object>=xrefPositions.size())
+ xrefPositions.resize(object+1);
+
+ xrefPositions[object] = streampos;
+ if (printostr)
+ xprintf("%d 0 obj\n",object);
+
+ return object;
+}
+
+void QPdfEnginePrivate::printString(const QString &string) {
+ // The 'text string' type in PDF is encoded either as PDFDocEncoding, or
+ // Unicode UTF-16 with a Unicode byte order mark as the first character
+ // (0xfeff), with the high-order byte first.
+ QByteArray array("(\xfe\xff");
+ const ushort *utf16 = string.utf16();
+
+ for (int i=0; i < string.size(); ++i) {
+ char part[2] = {char((*(utf16 + i)) >> 8), char((*(utf16 + i)) & 0xff)};
+ for(int j=0; j < 2; ++j) {
+ if (part[j] == '(' || part[j] == ')' || part[j] == '\\')
+ array.append('\\');
+ array.append(part[j]);
+ }
+ }
+ array.append(")");
+ write(array);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
diff --git a/src/gui/painting/qprintengine_pdf_p.h b/src/gui/painting/qprintengine_pdf_p.h
new file mode 100644
index 0000000000..13171ae501
--- /dev/null
+++ b/src/gui/painting/qprintengine_pdf_p.h
@@ -0,0 +1,195 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPRINTENGINE_PDF_P_H
+#define QPRINTENGINE_PDF_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtGui/qprintengine.h"
+
+#ifndef QT_NO_PRINTER
+#include "QtCore/qmap.h"
+#include "QtGui/qmatrix.h"
+#include "QtCore/qstring.h"
+#include "QtCore/qvector.h"
+#include "QtGui/qpaintengine.h"
+#include "QtGui/qpainterpath.h"
+#include "QtCore/qdatastream.h"
+
+#include "private/qfontengine_p.h"
+#include "private/qpdf_p.h"
+#include "private/qpaintengine_p.h"
+
+QT_BEGIN_NAMESPACE
+
+// #define USE_NATIVE_GRADIENTS
+
+class QImage;
+class QDataStream;
+class QPen;
+class QPointF;
+class QRegion;
+class QFile;
+class QPdfEngine;
+
+class QPdfEnginePrivate;
+
+class QPdfEngine : public QPdfBaseEngine
+{
+ Q_DECLARE_PRIVATE(QPdfEngine)
+public:
+ QPdfEngine(QPrinter::PrinterMode m);
+ virtual ~QPdfEngine();
+
+ // reimplementations QPaintEngine
+ bool begin(QPaintDevice *pdev);
+ bool end();
+ void drawPixmap (const QRectF & rectangle, const QPixmap & pixmap, const QRectF & sr);
+ void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr,
+ Qt::ImageConversionFlags flags = Qt::AutoColor);
+ void drawTiledPixmap (const QRectF & rectangle, const QPixmap & pixmap, const QPointF & point);
+
+ Type type() const;
+ // end reimplementations QPaintEngine
+
+ // reimplementations QPrintEngine
+ bool abort() {return false;}
+ bool newPage();
+ QPrinter::PrinterState printerState() const {return state;}
+ // end reimplementations QPrintEngine
+
+ void setBrush();
+
+ // ### unused, should have something for this in QPrintEngine
+ void setAuthor(const QString &author);
+ QString author() const;
+
+ void setDevice(QIODevice* dev);
+
+private:
+ Q_DISABLE_COPY(QPdfEngine)
+
+ QPrinter::PrinterState state;
+};
+
+class QPdfEnginePrivate : public QPdfBaseEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QPdfEngine)
+public:
+ QPdfEnginePrivate(QPrinter::PrinterMode m);
+ ~QPdfEnginePrivate();
+
+ void newPage();
+
+ int width() const {
+ QRect r = paperRect();
+ return qRound(r.width()*72./resolution);
+ }
+ int height() const {
+ QRect r = paperRect();
+ return qRound(r.height()*72./resolution);
+ }
+
+ void writeHeader();
+ void writeTail();
+
+ int addImage(const QImage &image, bool *bitmap, qint64 serial_no);
+ int addConstantAlphaObject(int brushAlpha, int penAlpha = 255);
+ int addBrushPattern(const QTransform &matrix, bool *specifyColor, int *gStateObject);
+
+ void drawTextItem(const QPointF &p, const QTextItemInt &ti);
+
+ QTransform pageMatrix() const;
+
+private:
+ Q_DISABLE_COPY(QPdfEnginePrivate)
+
+#ifdef USE_NATIVE_GRADIENTS
+ int gradientBrush(const QBrush &b, const QMatrix &matrix, int *gStateObject);
+#endif
+
+ void writeInfo();
+ void writePageRoot();
+ void writeFonts();
+ void embedFont(QFontSubset *font);
+
+ QVector<int> xrefPositions;
+ QDataStream* stream;
+ int streampos;
+
+ int writeImage(const QByteArray &data, int width, int height, int depth,
+ int maskObject, int softMaskObject, bool dct = false);
+ void writePage();
+
+ int addXrefEntry(int object, bool printostr = true);
+ void printString(const QString &string);
+ void xprintf(const char* fmt, ...);
+ inline void write(const QByteArray &data) {
+ stream->writeRawData(data.constData(), data.size());
+ streampos += data.size();
+ }
+
+ int writeCompressed(const char *src, int len);
+ inline int writeCompressed(const QByteArray &data) { return writeCompressed(data.constData(), data.length()); }
+ int writeCompressed(QIODevice *dev);
+
+ // various PDF objects
+ int pageRoot, catalog, info, graphicsState, patternColorSpace;
+ QVector<uint> pages;
+ QHash<qint64, uint> imageCache;
+ QHash<QPair<uint, uint>, uint > alphaCache;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
+
+#endif // QPRINTENGINE_PDF_P_H
diff --git a/src/gui/painting/qprintengine_ps.cpp b/src/gui/painting/qprintengine_ps.cpp
new file mode 100644
index 0000000000..d55d532fdb
--- /dev/null
+++ b/src/gui/painting/qprintengine_ps.cpp
@@ -0,0 +1,972 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+
+#include <private/qprintengine_ps_p.h>
+#include <private/qpainter_p.h>
+#include <private/qfontengine_p.h>
+#include <private/qpaintengine_p.h>
+#include <private/qpdf_p.h>
+
+#ifndef QT_NO_PRINTER
+
+#include "qprinter.h"
+#include "qpainter.h"
+#include "qapplication.h"
+#include "qpixmap.h"
+#include "qimage.h"
+#include "qdatetime.h"
+#include "qstring.h"
+#include "qbytearray.h"
+#include "qhash.h"
+#include "qbuffer.h"
+#include "qsettings.h"
+#include "qmap.h"
+#include "qbitmap.h"
+#include "qregion.h"
+#include "qimagewriter.h"
+#include <private/qpainterpath_p.h>
+#include <qdebug.h>
+#include <private/qdrawhelper_p.h>
+#include <private/qmutexpool_p.h>
+
+#ifndef Q_OS_WIN
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+static bool qt_gen_epsf = false;
+
+void qt_generate_epsf(bool b)
+{
+ qt_gen_epsf = b;
+}
+
+static const char *const ps_header =
+"/BD{bind def}bind def/d2{dup dup}BD/ED{exch def}BD/D0{0 ED}BD/F{setfont}BD\n"
+"/RL{rlineto}BD/CM{currentmatrix}BD/SM{setmatrix}BD/TR{translate}BD/SD\n"
+"{setdash}BD/SC{aload pop setrgbcolor}BD/CR{currentfile read pop}BD/i{index}\n"
+"BD/scs{setcolorspace}BD/DB{dict dup begin}BD/DE{end def}BD/ie{ifelse}BD/gs\n"
+"{gsave}BD/gr{grestore}BD/w{setlinewidth}BD/d{setdash}BD/J{setlinecap}BD/j\n"
+"{setlinejoin}BD/scn{3 array astore/BCol exch def}BD/SCN{3 array astore/PCol\n"
+"exch def}BD/cm{6 array astore concat}BD/m{moveto}BD/l{lineto}BD/c{curveto}BD\n"
+"/h{closepath}BD/W{clip}BD/W*{eoclip}BD/n{newpath}BD/q{gsave 10 dict begin}BD\n"
+"/Q{end grestore}BD/re{4 2 roll m dup 0 exch RL exch 0 RL 0 exch neg RL h}BD\n"
+"/S{gs PCol SC stroke gr n}BD/BT{gsave 10 dict begin/_m matrix CM def BCol\n"
+"SC}BD/ET{end grestore}BD/Tf{/_fs ED findfont[_fs 0 0 _fs 0 0]makefont F}BD\n"
+"/Tm{6 array astore concat}BD/Td{translate}BD/Tj{0 0 m show}BD/BDC{pop pop}BD\n"
+"/EMC{}BD/BSt 0 def/WFi false def/BCol[1 1 1]def/PCol[0 0 0]def/BDArr[0.94\n"
+"0.88 0.63 0.50 0.37 0.12 0.06]def/level3{/languagelevel where{pop\n"
+"languagelevel 3 ge}{false}ie}BD/QCIgray D0/QCIcolor D0/QCIindex D0/QCI{\n"
+"/colorimage where{pop false 3 colorimage}{exec/QCIcolor ED/QCIgray QCIcolor\n"
+"length 3 idiv string def 0 1 QCIcolor length 3 idiv 1 sub{/QCIindex ED/_x\n"
+"QCIindex 3 mul def QCIgray QCIindex QCIcolor _x get 0.30 mul QCIcolor _x 1\n"
+"add get 0.59 mul QCIcolor _x 2 add get 0.11 mul add add cvi put}for QCIgray\n"
+"image}ie}BD/di{gs TR 1 i 1 eq{pop pop false 3 1 roll BCol SC imagemask}{dup\n"
+"false ne{level3}{false}ie{/_ma ED 8 eq{/_dc[0 1]def/DeviceGray}{/_dc[0 1 0 1\n"
+"0 1]def/DeviceRGB}ie scs/_im ED/_mt ED/_h ED/_w ED <</ImageType 3/DataDict\n"
+"<</ImageType 1/Width _w/Height _h/ImageMatrix _mt/DataSource _im\n"
+"/BitsPerComponent 8/Decode _dc >>/MaskDict <</ImageType 1/Width _w/Height _h\n"
+"/ImageMatrix _mt/DataSource _ma/BitsPerComponent 1/Decode[0 1]>>\n"
+"/InterleaveType 3 >> image}{pop 8 4 1 roll 8 eq{image}{QCI}ie}ie}ie gr}BD/BF\n"
+"{gs BSt 1 eq{BCol SC WFi{fill}{eofill}ie}if BSt 2 ge BSt 8 le and{BDArr BSt\n"
+"2 sub get/_sc ED BCol{1. exch sub _sc mul 1. exch sub}forall 3 array astore\n"
+"SC WFi{fill}{eofill}ie}if BSt 9 ge BSt 14 le and{WFi{W}{W*}ie pathbbox 3 i 3\n"
+"i TR 4 2 roll 3 2 roll exch sub/_h ED sub/_w ED BCol SC 0.3 w n BSt 9 eq BSt\n"
+"11 eq or{0 4 _h{dup 0 exch m _w exch l}for}if BSt 10 eq BSt 11 eq or{0 4 _w{\n"
+"dup 0 m _h l}for}if BSt 12 eq BSt 14 eq or{_w _h gt{0 6 _w _h add{dup 0 m _h\n"
+"sub _h l}for}{0 6 _w _h add{dup 0 exch m _w sub _w exch l}for}ie}if BSt 13\n"
+"eq BSt 14 eq or{_w _h gt{0 6 _w _h add{dup _h m _h sub 0 l}for}{0 6 _w _h\n"
+"add{dup _w exch m _w sub 0 exch l}for}ie}if stroke}if BSt 15 eq{}if BSt 24\n"
+"eq{}if gr}BD/f{/WFi true def BF n}BD/f*{/WFi false def BF n}BD/B{/WFi true\n"
+"def BF S n}BD/B*{/WFi false def BF S n}BD/QI{/C save def pageinit q n}BD/QP{\n"
+"Q C restore showpage}BD/SPD{/setpagedevice where{<< 3 1 roll >>\n"
+"setpagedevice}{pop pop}ie}BD/T1AddMapping{10 dict begin/glyphs ED/fnt ED\n"
+"/current fnt/NumGlyphs get def/CMap fnt/CMap get def 0 1 glyphs length 1 sub\n"
+"{glyphs exch get/gn ED current dup 256 mod/min ED 256 idiv/maj ED CMap dup\n"
+"maj get dup null eq{pop 256 array 0 1 255{1 i exch/.notdef put}for}if dup\n"
+"min gn put maj exch put/current current 1 add def}for fnt/CMap CMap put fnt\n"
+"/NumGlyphs current put end}def/T1AddGlyphs{10 dict begin/glyphs ED/fnt ED\n"
+"/current fnt/NumGlyphs get def/CMap fnt/CMap get def/CharStrings fnt\n"
+"/CharStrings get def 0 1 glyphs length 2 idiv 1 sub{2 mul dup glyphs exch\n"
+"get/gn ED 1 add glyphs exch get/cs ED current dup 256 mod/min ED 256 idiv\n"
+"/maj ED CMap dup maj get dup null eq{pop 256 array 0 1 255{1 i exch/.notdef\n"
+"put}for}if dup min gn put maj exch put CharStrings gn cs put/current current\n"
+"1 add def}for fnt/CharStrings CharStrings put fnt/CMap CMap put fnt\n"
+"/NumGlyphs current put end}def/StringAdd{1 i length 1 i length add string 3\n"
+"1 roll 2 i 0 3 i putinterval 2 i 2 i length 2 i putinterval pop pop}def\n"
+"/T1Setup{10 dict begin dup/FontName ED (-Base) StringAdd cvx cvn/Font ED\n"
+"/MaxPage Font/NumGlyphs get 1 sub 256 idiv def/FDepVector MaxPage 1 add\n"
+"array def/Encoding MaxPage 1 add array def 0 1 MaxPage{dup Encoding exch dup\n"
+"put dup/Page ED FontName (-) StringAdd exch 20 string cvs StringAdd cvn Font\n"
+"0 dict copy d2/CMap get Page get/Encoding exch put definefont FDepVector\n"
+"exch Page exch put}for FontName cvn <</FontType 0/FMapType 2/FontMatrix[1 0\n"
+"0 1 0 0]/Encoding Encoding/FDepVector FDepVector >> definefont pop end}def\n";
+
+
+
+// ------------------------------End of static data ----------------------------------
+
+// make sure DSC comments are not longer than 255 chars per line.
+static QByteArray wrapDSC(const QByteArray &str)
+{
+ QByteArray dsc = str.simplified();
+ const int wrapAt = 254;
+ QByteArray wrapped;
+ if (dsc.length() < wrapAt)
+ wrapped = dsc;
+ else {
+ wrapped = dsc.left(wrapAt);
+ QByteArray tmp = dsc.mid(wrapAt);
+ while (tmp.length() > wrapAt-3) {
+ wrapped += "\n%%+" + tmp.left(wrapAt-3);
+ tmp = tmp.mid(wrapAt-3);
+ }
+ wrapped += "\n%%+" + tmp;
+ }
+ return wrapped + '\n';
+}
+
+// ----------------------------- Internal class declarations -----------------------------
+
+QPSPrintEnginePrivate::QPSPrintEnginePrivate(QPrinter::PrinterMode m)
+ : QPdfBaseEnginePrivate(m),
+ printerState(QPrinter::Idle), hugeDocument(false), headerDone(false)
+{
+ useAlphaEngine = true;
+ postscript = true;
+
+ firstPage = true;
+
+#ifndef QT_NO_SETTINGS
+ QSettings settings(QSettings::UserScope, QLatin1String("Trolltech"));
+ settings.beginGroup(QLatin1String("Qt"));
+ embedFonts = settings.value(QLatin1String("embedFonts"), true).toBool();
+#else
+ embedFonts = true;
+#endif
+}
+
+QPSPrintEnginePrivate::~QPSPrintEnginePrivate()
+{
+}
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <qdebug.h>
+QT_END_INCLUDE_NAMESPACE
+
+static void ps_r7(QPdf::ByteStream& stream, const char * s, int l)
+{
+ int i = 0;
+ uchar line[84];
+ int col = 0;
+
+ while(i < l) {
+ line[col++] = s[i++];
+ if (i < l - 1 && col >= 76) {
+ line[col++] = '\n';
+ line[col++] = '\0';
+ stream << (const char *)line;
+ col = 0;
+ }
+ }
+ if (col > 0) {
+ while((col&3) != 0)
+ line[col++] = '%'; // use a comment as padding
+ line[col++] = '\n';
+ line[col++] = '\0';
+ stream << (const char *)line;
+ }
+}
+
+static QByteArray runlengthEncode(const QByteArray &input)
+{
+ if (!input.length())
+ return input;
+
+ const char *data = input.constData();
+
+ QByteArray out;
+ int start = 0;
+ char last = *data;
+
+ enum State {
+ Undef,
+ Equal,
+ Diff
+ };
+ State state = Undef;
+
+ int i = 1;
+ int written = 0;
+ while (1) {
+ bool flush = (i == input.size());
+ if (!flush) {
+ switch(state) {
+ case Undef:
+ state = (last == data[i]) ? Equal : Diff;
+ break;
+ case Equal:
+ if (data[i] != last)
+ flush = true;
+ break;
+ case Diff:
+ if (data[i] == last) {
+ --i;
+ flush = true;
+ }
+ }
+ }
+ if (flush || i - start == 128) {
+ int size = i - start;
+ if (state == Equal) {
+ out.append((char)(uchar)(257-size));
+ out.append(last);
+ written += size;
+ } else {
+ out.append((char)(uchar)size-1);
+ while (start < i)
+ out.append(data[start++]);
+ written += size;
+ }
+ state = Undef;
+ start = i;
+ if (i == input.size())
+ break;
+ }
+ last = data[i];
+ ++i;
+ };
+ out.append((char)(uchar)128);
+ return out;
+}
+
+enum format {
+ Raw,
+ Runlength,
+ DCT
+};
+static const char *const filters[3] = {
+ " ",
+ "/RunLengthDecode filter ",
+ "/DCTDecode filter "
+};
+
+static QByteArray compressHelper(const QImage &image, bool gray, int *format)
+{
+ // we can't use premultiplied here
+ QByteArray pixelData;
+ int depth = image.depth();
+
+ Q_ASSERT(image.format() != QImage::Format_ARGB32_Premultiplied);
+
+ if (depth != 1 && !gray && QImageWriter::supportedImageFormats().contains("jpeg")) {
+ QBuffer buffer(&pixelData);
+ QImageWriter writer(&buffer, "jpeg");
+ writer.setQuality(94);
+ writer.write(image);
+ *format = DCT;
+ } else {
+ int width = image.width();
+ int height = image.height();
+ int size = width*height;
+
+ if (depth == 1)
+ size = (width+7)/8*height;
+ else if (!gray)
+ size = size*3;
+
+ pixelData.resize(size);
+ uchar *pixel = (uchar *)pixelData.data();
+ int i = 0;
+ if (depth == 1) {
+ QImage::Format format = image.format();
+ memset(pixel, 0xff, size);
+ for(int y=0; y < height; y++) {
+ const uchar * s = image.scanLine(y);
+ for(int x=0; x < width; x++) {
+ // need to copy bit for bit...
+ bool b = (format == QImage::Format_MonoLSB) ?
+ (*(s + (x >> 3)) >> (x & 7)) & 1 :
+ (*(s + (x >> 3)) << (x & 7)) & 0x80 ;
+ if (b)
+ pixel[i >> 3] ^= (0x80 >> (i & 7));
+ i++;
+ }
+ // we need to align to 8 bit here
+ i = (i+7) & 0xffffff8;
+ }
+ } else if (depth == 8) {
+ for(int y=0; y < height; y++) {
+ const uchar * s = image.scanLine(y);
+ for(int x=0; x < width; x++) {
+ QRgb rgb = image.color(s[x]);
+ if (gray) {
+ pixel[i] = (unsigned char) qGray(rgb);
+ i++;
+ } else {
+ pixel[i] = (unsigned char) qRed(rgb);
+ pixel[i+1] = (unsigned char) qGreen(rgb);
+ pixel[i+2] = (unsigned char) qBlue(rgb);
+ i += 3;
+ }
+ }
+ }
+ } else {
+ for(int y=0; y < height; y++) {
+ QRgb * s = (QRgb*)(image.scanLine(y));
+ for(int x=0; x < width; x++) {
+ QRgb rgb = (*s++);
+ if (gray) {
+ pixel[i] = (unsigned char) qGray(rgb);
+ i++;
+ } else {
+ pixel[i] = (unsigned char) qRed(rgb);
+ pixel[i+1] = (unsigned char) qGreen(rgb);
+ pixel[i+2] = (unsigned char) qBlue(rgb);
+ i += 3;
+ }
+ }
+ }
+ }
+ *format = Raw;
+ if (depth == 1) {
+ pixelData = runlengthEncode(pixelData);
+ *format = Runlength;
+ }
+ }
+ QByteArray outarr = QPdf::ascii85Encode(pixelData);
+ return outarr;
+}
+
+void QPSPrintEnginePrivate::drawImageHelper(qreal x, qreal y, qreal w, qreal h, const QImage &img,
+ const QImage &mask, bool gray, qreal scaleX, qreal scaleY)
+{
+ Q_UNUSED(h);
+ Q_UNUSED(w);
+ int width = img.width();
+ int height = img.height();
+
+ QByteArray out;
+ int size = 0;
+ const char *bits;
+
+ if (!mask.isNull()) {
+ int format;
+ out = compressHelper(mask, true, &format);
+ size = (width+7)/8*height;
+ *currentPage << "/mask currentfile/ASCII85Decode filter"
+ << filters[format]
+ << size << " string readstring\n";
+ ps_r7(*currentPage, out, out.size());
+ *currentPage << " pop def\n";
+ }
+ if (img.depth() == 1) {
+ size = (width+7)/8*height;
+ bits = "1 ";
+ } else if (gray) {
+ size = width*height;
+ bits = "8 ";
+ } else {
+ size = width*height*3;
+ bits = "24 ";
+ }
+
+ int format;
+ out = compressHelper(img, gray, &format);
+ *currentPage << "/sl currentfile/ASCII85Decode filter"
+ << filters[format]
+ << size << " string readstring\n";
+ ps_r7(*currentPage, out, out.size());
+ *currentPage << " pop def\n";
+ *currentPage << width << ' ' << height << '[' << scaleX << " 0 0 " << scaleY << " 0 0]sl "
+ << bits << (!mask.isNull() ? "mask " : "false ")
+ << x << ' ' << y << " di\n";
+}
+
+
+void QPSPrintEnginePrivate::drawImage(qreal x, qreal y, qreal w, qreal h,
+ const QImage &image, const QImage &msk)
+{
+ if (!w || !h || image.isNull()) return;
+
+ QImage img(image);
+ QImage mask(msk);
+
+ if (image.format() == QImage::Format_ARGB32_Premultiplied)
+ img = image.convertToFormat(QImage::Format_ARGB32);
+
+ if (!msk.isNull() && msk.format() == QImage::Format_ARGB32_Premultiplied)
+ mask = msk.convertToFormat(QImage::Format_ARGB32);
+
+ int width = img.width();
+ int height = img.height();
+ qreal scaleX = width/w;
+ qreal scaleY = height/h;
+
+ bool gray = (colorMode == QPrinter::GrayScale) || img.allGray();
+ int splitSize = 21830 * (gray ? 3 : 1);
+ if (width * height > splitSize) { // 65535/3, tolerance for broken printers
+ int images, subheight;
+ images = (width * height + splitSize - 1) / splitSize;
+ subheight = (height + images-1) / images;
+ while (subheight * width > splitSize) {
+ images++;
+ subheight = (height + images-1) / images;
+ }
+ int suby = 0;
+ const QImage constImg(img);
+ const QImage constMask(mask);
+ while(suby < height) {
+ qreal subImageHeight = qMin(subheight, height-suby);
+ const QImage subImage(constImg.scanLine(suby), width, subImageHeight,
+ constImg.bytesPerLine(), constImg.format());
+ const QImage subMask = mask.isNull() ? mask : QImage(constMask.scanLine(suby), width, subImageHeight,
+ constMask.bytesPerLine(), constMask.format());
+ drawImageHelper(x, y + suby/scaleY, w, subImageHeight/scaleY,
+ subImage, subMask, gray, scaleX, scaleY);
+ suby += subheight;
+ }
+ } else {
+ drawImageHelper(x, y, width, height, img, mask, gray, scaleX, scaleY);
+ }
+}
+
+void QPSPrintEnginePrivate::emitHeader(bool finished)
+{
+ QPSPrintEngine *q = static_cast<QPSPrintEngine *>(q_ptr);
+ QPrinter *printer = static_cast<QPrinter*>(pdev);
+
+ if (creator.isEmpty())
+ creator = QLatin1String("Qt " QT_VERSION_STR);
+
+ QByteArray header;
+ QPdf::ByteStream s(&header);
+
+ qreal scale = 72. / ((qreal) q->metric(QPaintDevice::PdmDpiY));
+ QRect pageRect = this->pageRect();
+ QRect paperRect = this->paperRect();
+ int mtop = pageRect.top() - paperRect.top();
+ int mleft = pageRect.left() - paperRect.left();
+ int mbottom = paperRect.bottom() - pageRect.bottom();
+ int mright = paperRect.right() - pageRect.right();
+ int width = pageRect.width();
+ int height = pageRect.height();
+ if (finished && pageCount == 1 && copies == 1 &&
+ ((fullPage && qt_gen_epsf) || (outputFileName.endsWith(QLatin1String(".eps")))))
+ {
+ // According to the EPSF 3.0 spec it is required that the PS
+ // version is PS-Adobe-3.0
+ s << "%!PS-Adobe-3.0";
+ if (!boundingBox.isValid())
+ boundingBox.setRect(0, 0, width, height);
+ if (orientation == QPrinter::Landscape) {
+ if (!fullPage)
+ boundingBox.translate(-mleft, -mtop);
+ s << " EPSF-3.0\n%%BoundingBox: "
+ << int((printer->height() - boundingBox.bottom())*scale) // llx
+ << int((printer->width() - boundingBox.right())*scale - 1) // lly
+ << int((printer->height() - boundingBox.top())*scale + 1) // urx
+ << int((printer->width() - boundingBox.left())*scale); // ury
+ } else {
+ if (!fullPage)
+ boundingBox.translate(mleft, -mtop);
+ s << " EPSF-3.0\n%%BoundingBox: "
+ << int((boundingBox.left())*scale)
+ << int((printer->height() - boundingBox.bottom())*scale - 1)
+ << int((boundingBox.right())*scale + 1)
+ << int((printer->height() - boundingBox.top())*scale);
+ }
+ } else {
+ s << "%!PS-Adobe-1.0";
+ int w = width + (fullPage ? 0 : mleft + mright);
+ int h = height + (fullPage ? 0 : mtop + mbottom);
+ w = (int)(w*scale);
+ h = (int)(h*scale);
+ // set a bounding box according to the DSC
+ if (orientation == QPrinter::Landscape)
+ s << "\n%%BoundingBox: 0 0 " << h << w;
+ else
+ s << "\n%%BoundingBox: 0 0 " << w << h;
+ }
+ s << '\n' << wrapDSC("%%Creator: " + creator.toUtf8());
+ if (!title.isEmpty())
+ s << wrapDSC("%%Title: " + title.toUtf8());
+#ifndef QT_NO_DATESTRING
+ s << "%%CreationDate: " << QDateTime::currentDateTime().toString().toUtf8();
+#endif
+ s << "\n%%Orientation: ";
+ if (orientation == QPrinter::Landscape)
+ s << "Landscape";
+ else
+ s << "Portrait";
+
+ s << "\n%%Pages: (atend)"
+ "\n%%DocumentFonts: (atend)"
+ "\n%%EndComments\n"
+
+ "%%BeginProlog\n"
+ "% Prolog copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).\n"
+ "% You may copy this prolog in any way that is directly related to this document.\n"
+ "% For other use of this prolog, see your licensing agreement for Qt.\n"
+ << ps_header << '\n';
+
+
+ s << "/pageinit {\n";
+ if (!fullPage) {
+ if (orientation == QPrinter::Portrait)
+ s << mleft*scale << mbottom*scale << "translate\n";
+ else
+ s << mtop*scale << mleft*scale << "translate\n";
+ }
+ if (orientation == QPrinter::Portrait) {
+ s << "% " << printer->widthMM() << '*' << printer->heightMM()
+ << "mm (portrait)\n0 " << height*scale
+ << "translate " << scale << '-' << scale << "scale } def\n";
+ } else {
+ s << "% " << printer->heightMM() << '*' << printer->widthMM()
+ << " mm (landscape)\n 90 rotate " << scale << '-' << scale << "scale } def\n";
+ }
+ s << "%%EndProlog\n";
+
+ outDevice->write(header);
+ headerDone = true;
+}
+
+
+void QPSPrintEnginePrivate::emitPages()
+{
+ if (!hugeDocument) {
+ for (QHash<QFontEngine::FaceId, QFontSubset *>::const_iterator it = fonts.constBegin();
+ it != fonts.constEnd(); ++it)
+ outDevice->write((*it)->toType1());
+ }
+
+ QIODevice *content = buffer.stream();
+ // Write the page contents in chunks.
+ while (!content->atEnd()) {
+ QByteArray buf = content->read(currentPage->chunkSize());
+ if (!buf.isEmpty())
+ outDevice->write(buf);
+ }
+ content = currentPage->stream();
+ // Write the page contents in chunks.
+ while (!content->atEnd()) {
+ QByteArray buf = content->read(currentPage->chunkSize());
+ if (!buf.isEmpty())
+ outDevice->write(buf);
+ }
+ outDevice->write(trailer);
+
+ buffer.clear();
+ currentPage->clear();
+ trailer = QByteArray();
+ hugeDocument = true;
+}
+
+
+#ifdef Q_WS_QWS
+static const int max_in_memory_size = 2000000;
+#else
+static const int max_in_memory_size = 32000000;
+#endif
+
+void QPSPrintEnginePrivate::flushPage(bool last)
+{
+ if (!last && currentPage->stream()->size() == 0)
+ return;
+
+ QPdf::ByteStream e(&trailer);
+ buffer << "%%Page: "
+ << pageCount << pageCount << "\n"
+ "%%BeginPageSetup\n"
+ "QI\n";
+ if (hugeDocument) {
+ for (QHash<QFontEngine::FaceId, QFontSubset *>::const_iterator it = fonts.constBegin();
+ it != fonts.constEnd(); ++it) {
+ if (currentPage->fonts.contains((*it)->object_id)) {
+ if ((*it)->downloaded_glyphs == 0) {
+ buffer << (*it)->toType1();
+ (*it)->downloaded_glyphs = 0;
+ } else {
+ buffer << (*it)->type1AddedGlyphs();
+ }
+ }
+ }
+ }
+ for (int i = 0; i < currentPage->fonts.size(); ++i)
+ buffer << "(F" << QByteArray::number(currentPage->fonts.at(i)) << ") T1Setup\n";
+
+ buffer << "%%EndPageSetup\nq\n";
+ e << "\nQ QP\n";
+ if (last || hugeDocument
+ || buffer.stream()->size() + currentPage->stream()->size() > max_in_memory_size) {
+// qDebug("emiting header at page %d", pageCount);
+ if (!headerDone)
+ emitHeader(last);
+ emitPages();
+ } else {
+ buffer << *currentPage << e;
+ currentPage->clear();
+ trailer.clear();
+ }
+ pageCount++;
+}
+
+// ================ PSPrinter class ========================
+
+QPSPrintEngine::QPSPrintEngine(QPrinter::PrinterMode m)
+ : QPdfBaseEngine(*(new QPSPrintEnginePrivate(m)),
+ PrimitiveTransform
+ | PatternTransform
+ | PixmapTransform
+ | PainterPaths
+ | PatternBrush
+ )
+{
+}
+
+static void ignoreSigPipe(bool b)
+{
+#ifndef QT_NO_LPR
+ static struct sigaction *users_sigpipe_handler = 0;
+ static int lockCount = 0;
+
+#ifndef QT_NO_THREAD
+ QMutexLocker locker(QMutexPool::globalInstanceGet(&users_sigpipe_handler));
+#endif
+
+ if (b) {
+ if (lockCount++ > 0)
+ return;
+
+ if (users_sigpipe_handler != 0)
+ return; // already ignoring sigpipe
+
+ users_sigpipe_handler = new struct sigaction;
+ struct sigaction tmp_sigpipe_handler;
+ tmp_sigpipe_handler.sa_handler = SIG_IGN;
+ sigemptyset(&tmp_sigpipe_handler.sa_mask);
+ tmp_sigpipe_handler.sa_flags = 0;
+
+ if (sigaction(SIGPIPE, &tmp_sigpipe_handler, users_sigpipe_handler) == -1) {
+ delete users_sigpipe_handler;
+ users_sigpipe_handler = 0;
+ }
+ }
+ else {
+ if (--lockCount > 0)
+ return;
+
+ if (users_sigpipe_handler == 0)
+ return; // not ignoring sigpipe
+
+ if (sigaction(SIGPIPE, users_sigpipe_handler, 0) == -1)
+ qWarning("QPSPrintEngine: Could not restore SIGPIPE handler");
+
+ delete users_sigpipe_handler;
+ users_sigpipe_handler = 0;
+ }
+#else
+ Q_UNUSED(b);
+#endif
+}
+QPSPrintEngine::~QPSPrintEngine()
+{
+ Q_D(QPSPrintEngine);
+ if (d->fd >= 0)
+#if defined(Q_OS_WIN) && defined(_MSC_VER) && _MSC_VER >= 1400
+ ::_close(d->fd);
+#else
+ ::close(d->fd);
+#endif
+}
+
+bool QPSPrintEngine::begin(QPaintDevice *pdev)
+{
+ Q_D(QPSPrintEngine);
+
+ if (d->fd >= 0)
+ return true;
+
+ if (d->useAlphaEngine) {
+ QAlphaPaintEngine::begin(pdev);
+ if (!continueCall())
+ return true;
+ }
+
+ if(!QPdfBaseEngine::begin(pdev)) {
+ d->printerState = QPrinter::Error;
+ return false;
+ }
+
+ d->pageCount = 1; // initialize state
+
+ d->pen = QPen(Qt::black);
+ d->brush = Qt::NoBrush;
+ d->hasPen = true;
+ d->hasBrush = false;
+ d->clipEnabled = false;
+ d->allClipped = false;
+ d->boundingBox = QRect();
+ d->fontsUsed = "";
+ d->hugeDocument = false;
+ d->simplePen = false;
+
+ setActive(true);
+ d->printerState = QPrinter::Active;
+
+ newPage();
+
+ return true;
+}
+
+bool QPSPrintEngine::end()
+{
+ Q_D(QPSPrintEngine);
+
+ if (d->useAlphaEngine) {
+ QAlphaPaintEngine::end();
+ if (!continueCall())
+ return true;
+ }
+
+ // we're writing to lp/lpr through a pipe, we don't want to crash with SIGPIPE
+ // if lp/lpr dies
+ ignoreSigPipe(true);
+ d->flushPage(true);
+ QByteArray trailer;
+ QPdf::ByteStream s(&trailer);
+ s << "%%Trailer\n"
+ "%%Pages: " << d->pageCount - 1 << '\n' <<
+ wrapDSC("%%DocumentFonts: " + d->fontsUsed);
+ s << "%%EOF\n";
+ d->outDevice->write(trailer);
+
+ QPdfBaseEngine::end();
+ ignoreSigPipe(false);
+
+ d->firstPage = true;
+ d->headerDone = false;
+
+ setActive(false);
+ d->printerState = QPrinter::Idle;
+ d->pdev = 0;
+
+ return true;
+}
+
+void QPSPrintEngine::setBrush()
+{
+ Q_D(QPSPrintEngine);
+#if 0
+ bool specifyColor;
+ int gStateObject = 0;
+ int patternObject = d->addBrushPattern(brush, d->stroker.matrix, brushOrigin, &specifyColor, &gStateObject);
+
+ *d->currentPage << (patternObject ? "/PCSp cs " : "/CSp cs ");
+ if (specifyColor) {
+ QColor rgba = brush.color();
+ *d->currentPage << rgba.redF()
+ << rgba.greenF()
+ << rgba.blueF();
+ }
+ if (patternObject)
+ *d->currentPage << "/Pat" << patternObject;
+ *d->currentPage << "scn\n";
+#endif
+ QColor rgba = d->brush.color();
+ if (d->colorMode == QPrinter::GrayScale) {
+ qreal gray = qGray(rgba.rgba())/255.;
+ *d->currentPage << gray << gray << gray;
+ } else {
+ *d->currentPage << rgba.redF()
+ << rgba.greenF()
+ << rgba.blueF();
+ }
+ *d->currentPage << "scn\n"
+ << "/BSt " << d->brush.style() << "def\n";
+}
+
+void QPSPrintEngine::drawImageInternal(const QRectF &r, QImage image, bool bitmap)
+{
+ Q_D(QPSPrintEngine);
+ if (d->clipEnabled && d->allClipped)
+ return;
+ if (bitmap && image.depth() != 1)
+ bitmap = false;
+ QImage mask;
+ // the below is not necessary since it's handled by the alpha
+ // engine
+ if (!d->useAlphaEngine && !bitmap) {
+ if (image.format() == QImage::Format_Mono || image.format() == QImage::Format_MonoLSB)
+ image = image.convertToFormat(QImage::Format_Indexed8);
+ if (image.hasAlphaChannel()) {
+ // get better alpha dithering
+ int xscale = image.width();
+ xscale *= xscale <= 800 ? 4 : (xscale <= 1600 ? 2 : 1);
+ int yscale = image.height();
+ yscale *= yscale <= 800 ? 4 : (yscale <= 1600 ? 2 : 1);
+ image = image.scaled(xscale, yscale);
+ mask = image.createAlphaMask(Qt::OrderedAlphaDither);
+ }
+ }
+ *d->currentPage << "q\n";
+ if(!d->simplePen)
+ *d->currentPage << QPdf::generateMatrix(d->stroker.matrix);
+ QBrush b = d->brush;
+ if (image.depth() == 1) {
+ // set current pen as brush
+ d->brush = d->pen.brush();
+ setBrush();
+ }
+ d->drawImage(r.x(), r.y(), r.width(), r.height(), image, mask);
+ *d->currentPage << "Q\n";
+ d->brush = b;
+}
+
+
+void QPSPrintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
+ Qt::ImageConversionFlags)
+{
+ Q_D(QPSPrintEngine);
+
+ if (d->useAlphaEngine) {
+ QAlphaPaintEngine::drawImage(r, img, sr);
+ if (!continueCall())
+ return;
+ }
+ QImage image = img.copy(sr.toRect());
+ drawImageInternal(r, image, false);
+}
+
+void QPSPrintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
+{
+ Q_D(QPSPrintEngine);
+
+ if (d->useAlphaEngine) {
+ QAlphaPaintEngine::drawPixmap(r, pm, sr);
+ if (!continueCall())
+ return;
+ }
+
+ QImage img = pm.copy(sr.toRect()).toImage();
+ drawImageInternal(r, img, true);
+}
+
+void QPSPrintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &p)
+{
+ Q_D(QPSPrintEngine);
+
+ if (d->useAlphaEngine) {
+ QAlphaPaintEngine::drawTiledPixmap(r, pixmap, p);
+ if (!continueCall())
+ return;
+ }
+
+ if (d->clipEnabled && d->allClipped)
+ return;
+ // ### Optimize implementation!
+ qreal yPos = r.y();
+ qreal yOff = p.y();
+ while(yPos < r.y() + r.height()) {
+ qreal drawH = pixmap.height() - yOff; // Cropping first row
+ if (yPos + drawH > r.y() + r.height()) // Cropping last row
+ drawH = r.y() + r.height() - yPos;
+ qreal xPos = r.x();
+ qreal xOff = p.x();
+ while(xPos < r.x() + r.width()) {
+ qreal drawW = pixmap.width() - xOff; // Cropping first column
+ if (xPos + drawW > r.x() + r.width()) // Cropping last column
+ drawW = r.x() + r.width() - xPos;
+ // ########
+ painter()->drawPixmap(QPointF(xPos, yPos).toPoint(), pixmap,
+ QRectF(xOff, yOff, drawW, drawH).toRect());
+ xPos += drawW;
+ xOff = 0;
+ }
+ yPos += drawH;
+ yOff = 0;
+ }
+
+}
+
+bool QPSPrintEngine::newPage()
+{
+ Q_D(QPSPrintEngine);
+
+ if (!d->firstPage && d->useAlphaEngine)
+ flushAndInit();
+
+ // we're writing to lp/lpr through a pipe, we don't want to crash with SIGPIPE
+ // if lp/lpr dies
+ ignoreSigPipe(true);
+ if (!d->firstPage)
+ d->flushPage();
+ d->firstPage = false;
+ ignoreSigPipe(false);
+
+ delete d->currentPage;
+ d->currentPage = new QPdfPage;
+ d->stroker.stream = d->currentPage;
+
+ return QPdfBaseEngine::newPage();
+}
+
+bool QPSPrintEngine::abort()
+{
+ // ### abort!?!
+ return false;
+}
+
+QPrinter::PrinterState QPSPrintEngine::printerState() const
+{
+ Q_D(const QPSPrintEngine);
+ return d->printerState;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
diff --git a/src/gui/painting/qprintengine_ps_p.h b/src/gui/painting/qprintengine_ps_p.h
new file mode 100644
index 0000000000..a1185d7867
--- /dev/null
+++ b/src/gui/painting/qprintengine_ps_p.h
@@ -0,0 +1,137 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPRINTENGINE_PS_P_H
+#define QPRINTENGINE_PS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qpsprinter.cpp and qprinter_x11.cpp.
+// This header file may change from version to version without notice,
+// or even be removed.
+//
+// We mean it.
+//
+
+#ifndef QT_NO_PRINTER
+
+#include "private/qpdf_p.h"
+#include "qplatformdefs.h"
+#include "QtCore/qlibrary.h"
+#include "QtCore/qstringlist.h"
+#include "QtCore/qhash.h"
+#include "QtCore/qabstractitemmodel.h"
+
+QT_BEGIN_NAMESPACE
+
+class QPrinter;
+class QPSPrintEnginePrivate;
+
+class QPSPrintEngine : public QPdfBaseEngine
+{
+ Q_DECLARE_PRIVATE(QPSPrintEngine)
+public:
+ // QPrinter uses these
+ explicit QPSPrintEngine(QPrinter::PrinterMode m);
+ ~QPSPrintEngine();
+
+
+ virtual bool begin(QPaintDevice *pdev);
+ virtual bool end();
+
+ void setBrush();
+
+ virtual void drawImage(const QRectF &r, const QImage &img, const QRectF &sr, Qt::ImageConversionFlags);
+ virtual void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
+ virtual void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s);
+
+ virtual void drawImageInternal(const QRectF &r, QImage img, bool bitmap);
+
+ virtual QPaintEngine::Type type() const { return QPaintEngine::PostScript; }
+
+ virtual bool newPage();
+ virtual bool abort();
+
+ virtual QPrinter::PrinterState printerState() const;
+
+ virtual Qt::HANDLE handle() const { return 0; }
+
+private:
+ Q_DISABLE_COPY(QPSPrintEngine)
+};
+
+class QPSPrintEnginePrivate : public QPdfBaseEnginePrivate {
+public:
+ QPSPrintEnginePrivate(QPrinter::PrinterMode m);
+ ~QPSPrintEnginePrivate();
+
+ void emitHeader(bool finished);
+ void emitPages();
+ void drawImage(qreal x, qreal y, qreal w, qreal h, const QImage &img, const QImage &mask);
+ void flushPage(bool last = false);
+ void drawImageHelper(qreal x, qreal y, qreal w, qreal h, const QImage &img, const QImage &mask,
+ bool gray, qreal scaleX, qreal scaleY);
+
+ int pageCount;
+ bool epsf;
+ QByteArray fontsUsed;
+
+ // stores the descriptions of the n first pages.
+ QPdf::ByteStream buffer;
+ QByteArray trailer;
+
+ bool firstPage;
+
+ QRect boundingBox;
+
+ QPrinter::PrinterState printerState;
+ bool hugeDocument;
+ bool headerDone;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
+
+#endif // QPRINTENGINE_PS_P_H
diff --git a/src/gui/painting/qprintengine_qws.cpp b/src/gui/painting/qprintengine_qws.cpp
new file mode 100644
index 0000000000..1aef2c68cf
--- /dev/null
+++ b/src/gui/painting/qprintengine_qws.cpp
@@ -0,0 +1,886 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qprintengine_qws_p.h>
+
+#ifndef QT_NO_PRINTER
+
+#include <private/qpaintengine_raster_p.h>
+#include <qimage.h>
+#include <qfile.h>
+#include <qdebug.h>
+#include <QCopChannel>
+
+QT_BEGIN_NAMESPACE
+
+#define MM(n) int((n * 720 + 127) / 254)
+#define IN(n) int(n * 72)
+
+extern QSizeF qt_paperSizeToQSizeF(QPrinter::PaperSize size);
+
+QtopiaPrintEngine::QtopiaPrintEngine(QPrinter::PrinterMode mode)
+ : QPaintEngine(*(new QtopiaPrintEnginePrivate( mode )))
+{
+ d_func()->initialize();
+}
+
+bool QtopiaPrintEngine::begin(QPaintDevice *)
+{
+ Q_D(QtopiaPrintEngine);
+ Q_ASSERT_X(d->printerState == QPrinter::Idle, "QtopiaPrintEngine", "printer already active");
+
+ // Create a new off-screen monochrome image to handle the drawing process.
+ QSize size = paperRect().size();
+ if ( d->pageImage )
+ delete d->pageImage;
+ d->pageImage = new QImage( size, QImage::Format_RGB32 );
+ if ( !(d->pageImage) )
+ return false;
+
+ // Recreate the paint engine on the new image.
+ delete d->_paintEngine;
+ d->_paintEngine = 0;
+ d->paintEngine()->state = state;
+
+ // Begin the paint process on the image.
+ if (!d->paintEngine()->begin(d->pageImage))
+ return false;
+
+ // Clear the first page to all-white.
+ clearPage();
+
+ // Clear the print buffer and output the image header.
+ d->buffer.clear();
+ d->writeG3FaxHeader();
+
+ // The print engine is currently active.
+ d->printerState = QPrinter::Active;
+ return true;
+}
+
+bool QtopiaPrintEngine::end()
+{
+ Q_D(QtopiaPrintEngine);
+
+ d->paintEngine()->end();
+
+ // Flush the last page.
+ flushPage();
+
+ // Output the fax data to a file (TODO: send to the print queuing daemon).
+ QString filename;
+ if ( !d->outputFileName.isEmpty() )
+ filename = QString::fromLocal8Bit(qgetenv("HOME").constData()) + QLatin1String("/Documents/") + d->outputFileName;
+ else
+ filename = QString::fromLocal8Bit(qgetenv("HOME").constData()) + QLatin1String("/tmp/qwsfax.tiff");
+
+ setProperty(QPrintEngine::PPK_OutputFileName, filename);
+ QFile file( filename );
+ if ( !file.open( QIODevice::WriteOnly | QIODevice::Truncate ) ) {
+ qDebug( "Failed to open %s for printer output",
+ filename.toLatin1().constData() );
+ } else {
+ file.write( d->buffer.data() );
+ file.close();
+ }
+
+ // Free up the memory for the image buffer.
+ d->buffer.clear();
+
+ // Finalize the print job.
+ d->printerState = QPrinter::Idle;
+
+ // call qcop service
+ QMap<QString, QVariant> map;
+ for ( int x = 0; x <= QPrintEngine::PPK_Duplex; x++ )
+ map.insert( QString::number(x), property((QPrintEngine::PrintEnginePropertyKey)(x)));
+ QVariant variant(map);
+
+ QByteArray data;
+ QDataStream out(&data, QIODevice::WriteOnly);
+ out << variant;
+ QCopChannel::send(QLatin1String("QPE/Service/Print"), QLatin1String("print(QVariant)"), data);
+
+ return true;
+}
+
+QPaintEngine *QtopiaPrintEngine::paintEngine() const
+{
+ return const_cast<QtopiaPrintEnginePrivate *>(d_func())->paintEngine();
+}
+
+void QtopiaPrintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
+{
+ Q_D(QtopiaPrintEngine);
+ Q_ASSERT(d->printerState == QPrinter::Active);
+ d->paintEngine()->drawPixmap(r, pm, sr);
+}
+
+void QtopiaPrintEngine::drawTextItem(const QPointF &p, const QTextItem &ti)
+{
+ Q_D(QtopiaPrintEngine);
+ Q_ASSERT(d->printerState == QPrinter::Active);
+ d->paintEngine()->drawTextItem(p, ti);
+}
+
+void QtopiaPrintEngine::updateState(const QPaintEngineState &state)
+{
+ Q_D(QtopiaPrintEngine);
+ d->paintEngine()->updateState(state);
+}
+
+QRect QtopiaPrintEngine::paperRect() const
+{
+ QSizeF s = qt_paperSizeToQSizeF(d_func()->paperSize);
+ s.rwidth() = MM(s.width());
+ s.rheight() = MM(s.height());
+ int w = qRound(s.width()*d_func()->resolution/72.);
+ int h = qRound(s.height()*d_func()->resolution/72.);
+ if (d_func()->orientation == QPrinter::Portrait)
+ return QRect(0, 0, w, h);
+ else
+ return QRect(0, 0, h, w);
+}
+
+QRect QtopiaPrintEngine::pageRect() const
+{
+ QRect r = paperRect();
+ if (d_func()->fullPage)
+ return r;
+ // would be nice to get better margins than this.
+ return QRect(d_func()->resolution/3, d_func()->resolution/3, r.width()-2*d_func()->resolution/3, r.height()-2*d_func()->resolution/3);
+}
+
+bool QtopiaPrintEngine::newPage()
+{
+ flushPage();
+ clearPage();
+ ++(d_func()->pageNumber);
+ return true;
+}
+
+bool QtopiaPrintEngine::abort()
+{
+ return false;
+}
+
+QPrinter::PrinterState QtopiaPrintEngine::printerState() const
+{
+ return d_func()->printerState;
+}
+
+int QtopiaPrintEngine::metric(QPaintDevice::PaintDeviceMetric metricType) const
+{
+ int val;
+ QRect r = d_func()->fullPage ? paperRect() : pageRect();
+ switch (metricType) {
+ case QPaintDevice::PdmWidth:
+ val = r.width();
+ break;
+ case QPaintDevice::PdmHeight:
+ val = r.height();
+ break;
+ case QPaintDevice::PdmDpiX:
+ val = d_func()->resolution;
+ break;
+ case QPaintDevice::PdmDpiY:
+ val = d_func()->resolution;
+ break;
+ case QPaintDevice::PdmPhysicalDpiX:
+ case QPaintDevice::PdmPhysicalDpiY:
+ val = QT_QWS_PRINTER_DEFAULT_DPI;
+ break;
+ case QPaintDevice::PdmWidthMM:
+ val = qRound(r.width()*25.4/d_func()->resolution);
+ break;
+ case QPaintDevice::PdmHeightMM:
+ val = qRound(r.height()*25.4/d_func()->resolution);
+ break;
+ case QPaintDevice::PdmNumColors:
+ val = 2;
+ break;
+ case QPaintDevice::PdmDepth:
+ val = 1;
+ break;
+ default:
+ qWarning("QtopiaPrintEngine::metric: Invalid metric command");
+ return 0;
+ }
+ return val;
+}
+
+QVariant QtopiaPrintEngine::property(PrintEnginePropertyKey key) const
+{
+ Q_D(const QtopiaPrintEngine);
+ QVariant ret;
+
+ switch (key) {
+ case PPK_CollateCopies:
+ ret = d->collateCopies;
+ break;
+ case PPK_ColorMode:
+ ret = d->colorMode;
+ break;
+ case PPK_Creator:
+ ret = d->creator;
+ break;
+ case PPK_DocumentName:
+ ret = d->docName;
+ break;
+ case PPK_FullPage:
+ ret = d->fullPage;
+ break;
+ case PPK_CopyCount: // fallthrough
+ case PPK_NumberOfCopies:
+ ret = d->numCopies;
+ break;
+ case PPK_SupportsMultipleCopies:
+ ret = false;
+ break;
+ case PPK_Orientation:
+ ret = d->orientation;
+ break;
+ case PPK_OutputFileName:
+ ret = d->outputFileName;
+ break;
+ case PPK_PageOrder:
+ ret = d->pageOrder;
+ break;
+ case PPK_PageRect:
+ ret = pageRect();
+ break;
+ case PPK_PaperSize:
+ ret = d->paperSize;
+ break;
+ case PPK_PaperRect:
+ ret = paperRect();
+ break;
+ case PPK_PaperSource:
+ ret = d->paperSource;
+ break;
+ case PPK_PrinterName:
+ ret = d->printerName;
+ break;
+ case PPK_PrinterProgram:
+ ret = d->printProgram;
+ break;
+ case PPK_Resolution:
+ ret = d->resolution;
+ break;
+ case PPK_SupportedResolutions:
+ ret = QList<QVariant>() << QT_QWS_PRINTER_DEFAULT_DPI;
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+void QtopiaPrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &value)
+{
+ Q_D(QtopiaPrintEngine);
+ switch (key) {
+ case PPK_CollateCopies:
+ d->collateCopies = value.toBool();
+ break;
+ case PPK_ColorMode:
+ d->colorMode = QPrinter::ColorMode(value.toInt());
+ break;
+ case PPK_Creator:
+ d->creator = value.toString();
+ break;
+ case PPK_DocumentName:
+ d->docName = value.toString();
+ break;
+ case PPK_FullPage:
+ d->fullPage = value.toBool();
+ break;
+ case PPK_CopyCount: // fallthrough
+ case PPK_NumberOfCopies:
+ d->numCopies = value.toInt();
+ break;
+ case PPK_Orientation:
+ d->orientation = QPrinter::Orientation(value.toInt());
+ break;
+ case PPK_OutputFileName:
+ d->outputFileName = value.toString();
+ break;
+ case PPK_PageOrder:
+ d->pageOrder = QPrinter::PageOrder(value.toInt());
+ break;
+ case PPK_PaperSize:
+ d->paperSize = QPrinter::PaperSize(value.toInt());
+ break;
+ case PPK_PaperSource:
+ d->paperSource = QPrinter::PaperSource(value.toInt());
+ case PPK_PrinterName:
+ d->printerName = value.toString();
+ break;
+ case PPK_PrinterProgram:
+ d->printProgram = value.toString();
+ break;
+ case PPK_Resolution:
+ d->resolution = value.toInt();
+ break;
+ default:
+ break;
+ }
+}
+
+void QtopiaPrintEngine::clearPage()
+{
+ d_func()->pageImage->fill(QColor(255, 255, 255).rgb());
+}
+
+void QtopiaPrintEngine::flushPage()
+{
+ d_func()->writeG3FaxPage();
+}
+
+QtopiaPrintEnginePrivate::~QtopiaPrintEnginePrivate()
+{
+ if ( pageImage )
+ delete pageImage;
+}
+
+void QtopiaPrintEnginePrivate::initialize()
+{
+ _paintEngine = 0;
+}
+
+QPaintEngine *QtopiaPrintEnginePrivate::paintEngine()
+{
+ if (!_paintEngine)
+ _paintEngine = new QRasterPaintEngine(pageImage);
+ return _paintEngine;
+}
+
+void QtopiaPrintEnginePrivate::writeG3FaxHeader()
+{
+ // Write the TIFF file magic number (little-endian TIFF).
+ buffer.append( (char)'I' );
+ buffer.append( (char)'I' );
+ buffer.append( (char)42 );
+ buffer.append( (char)0 );
+
+ // Leave a place-holder for the IFD offset of the first page.
+ ifdPatch = buffer.size();
+ buffer.append( (int)0 );
+}
+
+// Tag values, from RFC 2301.
+#define TIFF_IFD_NEW_SUB_FILE_TYPE 254
+#define TIFF_IFD_IMAGE_WIDTH 256
+#define TIFF_IFD_IMAGE_LENGTH 257
+#define TIFF_IFD_BITS_PER_SAMPLE 258
+#define TIFF_IFD_COMPRESSION 259
+#define TIFF_IFD_PHOTOMETRIC_INTERP 262
+#define TIFF_IFD_FILL_ORDER 266
+#define TIFF_IFD_STRIP_OFFSETS 273
+#define TIFF_IFD_ORIENTATION 274
+#define TIFF_IFD_SAMPLES_PER_PIXEL 277
+#define TIFF_IFD_ROWS_PER_STRIP 278
+#define TIFF_IFD_STRIP_BYTE_COUNTS 279
+#define TIFF_IFD_X_RESOLUTION 282
+#define TIFF_IFD_Y_RESOLUTION 283
+#define TIFF_IFD_PLANAR_CONFIG 284
+#define TIFF_IFD_T4_OPTIONS 292
+#define TIFF_IFD_RESOLUTION_UNIT 296
+#define TIFF_IFD_PAGE_NUMBER 297
+#define TIFF_IFD_CLEAN_FAX_DATA 327
+
+// IFD type values.
+#define TIFF_TYPE_SHORT 3
+#define TIFF_TYPE_LONG 4
+#define TIFF_TYPE_RATIONAL 5
+
+// Construct a SHORT pair from two values.
+#define TIFF_SHORT_PAIR(a,b) (((a) & 0xFFFF) | ((b) << 16))
+
+// Width of a FAX page in pixels, in the baseline specification from RFC 2301.
+// This must be hard-wired, as per the RFC. We truncate any pixels that
+// are beyond this limit, or pad lines to reach this limit.
+#define TIFF_FAX_WIDTH 1728
+
+void QtopiaPrintEnginePrivate::writeG3FaxPage()
+{
+ // Pad the image file to a word boundary, just in case.
+ buffer.pad();
+
+ // Back-patch the IFD link for the previous page.
+ buffer.patch( ifdPatch, buffer.size() );
+
+ // Output the contents of the IFD for this page (these must be
+ // in ascending order of tag value).
+ buffer.append( (short)19 ); // Number of IFD entries.
+ writeG3IFDEntry( TIFF_IFD_NEW_SUB_FILE_TYPE, TIFF_TYPE_LONG, 1, 2 );
+ writeG3IFDEntry( TIFF_IFD_IMAGE_WIDTH, TIFF_TYPE_LONG, 1, TIFF_FAX_WIDTH );
+ writeG3IFDEntry
+ ( TIFF_IFD_IMAGE_LENGTH, TIFF_TYPE_LONG, 1, pageImage->height() );
+ writeG3IFDEntry( TIFF_IFD_BITS_PER_SAMPLE, TIFF_TYPE_SHORT, 1, 1 );
+ writeG3IFDEntry( TIFF_IFD_COMPRESSION, TIFF_TYPE_SHORT, 1, 3 );
+ writeG3IFDEntry( TIFF_IFD_PHOTOMETRIC_INTERP, TIFF_TYPE_SHORT, 1, 0 );
+ writeG3IFDEntry( TIFF_IFD_FILL_ORDER, TIFF_TYPE_SHORT, 1, 1 );
+ int stripOffsets =
+ writeG3IFDEntry( TIFF_IFD_STRIP_OFFSETS, TIFF_TYPE_LONG, 1, 0 );
+ writeG3IFDEntry( TIFF_IFD_ORIENTATION, TIFF_TYPE_SHORT, 1, 1 );
+ writeG3IFDEntry( TIFF_IFD_SAMPLES_PER_PIXEL, TIFF_TYPE_SHORT, 1, 1 );
+ writeG3IFDEntry
+ ( TIFF_IFD_ROWS_PER_STRIP, TIFF_TYPE_LONG, 1, pageImage->height() );
+ int stripBytes = writeG3IFDEntry
+ ( TIFF_IFD_STRIP_BYTE_COUNTS, TIFF_TYPE_LONG, 1, 0 );
+ int xres =
+ writeG3IFDEntry( TIFF_IFD_X_RESOLUTION, TIFF_TYPE_RATIONAL, 1, 0 );
+ int yres =
+ writeG3IFDEntry( TIFF_IFD_Y_RESOLUTION, TIFF_TYPE_RATIONAL, 1, 0 );
+ writeG3IFDEntry( TIFF_IFD_PLANAR_CONFIG, TIFF_TYPE_SHORT, 1, 1 );
+ writeG3IFDEntry( TIFF_IFD_T4_OPTIONS, TIFF_TYPE_LONG, 1, 2 );
+ writeG3IFDEntry( TIFF_IFD_RESOLUTION_UNIT, TIFF_TYPE_SHORT, 1, 2 );
+ writeG3IFDEntry( TIFF_IFD_PAGE_NUMBER, TIFF_TYPE_SHORT, 2,
+ TIFF_SHORT_PAIR( pageNumber, 0 ) );
+ writeG3IFDEntry( TIFF_IFD_CLEAN_FAX_DATA, TIFF_TYPE_SHORT, 1, 0 );
+
+ // Leave a place-holder for the IFD offset of the next page.
+ ifdPatch = buffer.size();
+ buffer.append( (int)0 );
+
+ // Output the X and Y resolutions, as rational values (usually 200/1).
+ buffer.patch( xres, buffer.size() );
+ buffer.append( (int)resolution );
+ buffer.append( (int)1 );
+ buffer.patch( yres, buffer.size() );
+ buffer.append( (int)resolution );
+ buffer.append( (int)1 );
+
+ // We are now at the start of the image data - set the strip offset.
+ int start = buffer.size();
+ buffer.patch( stripOffsets, start );
+
+ // Output the image data.
+ int width = pageImage->width();
+ QImage::Format imageFormat = pageImage->format();
+ for ( int y = 0; y < pageImage->height(); ++y ) {
+ unsigned char *scan = pageImage->scanLine(y);
+ int prev, pixel, len;
+ writeG3EOL();
+ prev = 0;
+ len = 0;
+
+ uint currentColor = qRgb(255, 255, 255); // start with white
+
+ for ( int x = 0; x < width && x < TIFF_FAX_WIDTH; ++x ) {
+ if ( imageFormat == QImage::Format_RGB32 ) {
+ // read color of the current pixel
+ uint *p = (uint *)scan + x;
+
+ if ( *p == currentColor ) { // if it is the same color
+ len++; // imcrement length
+ } else { // otherwise write color into the buffer
+ if ( len > 0 ) {
+ if ( currentColor == qRgb(0, 0, 0) )
+ writeG3BlackRun( len );
+ else
+ writeG3WhiteRun( len );
+ }
+ // initialise length and color;
+ len = 1;
+ currentColor = *p;
+ }
+ } else if ( imageFormat == QImage::Format_Mono ) {
+ pixel = ((scan[x >> 3] & (1 << (x & 7))) != 0);
+ if ( pixel != prev ) {
+ if ( prev ) {
+ writeG3BlackRun( len );
+ } else {
+ writeG3WhiteRun( len );
+ }
+ prev = pixel;
+ len = 1;
+ } else {
+ ++len;
+ }
+ }
+ }
+
+ if ( imageFormat == QImage::Format_RGB32 ) {
+ // Output the last run on the line, and pad to TIFF_FAX_WIDTH.
+ if ( len != 0 ) {
+ if ( currentColor == qRgb(0, 0, 0) )
+ writeG3BlackRun( len );
+ else
+ writeG3WhiteRun( len );
+ }
+ if ( width < TIFF_FAX_WIDTH )
+ writeG3WhiteRun( TIFF_FAX_WIDTH - width );
+ } else if ( imageFormat == QImage::Format_Mono ) {
+ if ( len != 0 ) {
+ if ( prev ) {
+ writeG3BlackRun( len );
+ if ( width < TIFF_FAX_WIDTH ) {
+ writeG3WhiteRun( TIFF_FAX_WIDTH - width );
+ }
+ } else {
+ if ( width < TIFF_FAX_WIDTH ) {
+ writeG3WhiteRun( len + ( TIFF_FAX_WIDTH - width ) );
+ } else {
+ writeG3WhiteRun( len );
+ }
+ }
+ }
+ }
+ }
+
+ // Flush the last partial byte, which is padded with zero fill bits.
+ if ( partialBits > 0 ) {
+ buffer.append( (char)( partialByte << ( 8 - partialBits ) ) );
+ partialByte = 0;
+ partialBits = 0;
+ }
+
+ // end of page add six EOLs
+ for ( int i = 0; i < 6; i++ )
+ writeG3EOL();
+
+ // Update the byte count for the image data strip.
+ buffer.patch( stripBytes, buffer.size() - start );
+}
+
+int QtopiaPrintEnginePrivate::writeG3IFDEntry
+ ( int tag, int type, int count, int value )
+{
+ buffer.append( (short)tag );
+ buffer.append( (short)type );
+ buffer.append( count );
+ buffer.append( value );
+ return buffer.size() - 4; // Offset of the value for back-patching.
+}
+
+void QtopiaPrintEnginePrivate::writeG3Code( int code, int bits )
+{
+ partialByte = ( ( partialByte << bits ) | code );
+ partialBits += bits;
+ while ( partialBits >= 8 ) {
+ partialBits -= 8;
+ buffer.append( (char)( partialByte >> partialBits ) );
+ }
+}
+
+void QtopiaPrintEnginePrivate::writeG3WhiteRun( int len )
+{
+ static struct {
+ unsigned short code;
+ unsigned short bits;
+ } whiteCodes[64 + 27] = {
+ {0x0035, 8}, // 0
+ {0x0007, 6},
+ {0x0007, 4},
+ {0x0008, 4},
+ {0x000B, 4},
+ {0x000C, 4},
+ {0x000E, 4},
+ {0x000F, 4},
+ {0x0013, 5}, // 8
+ {0x0014, 5},
+ {0x0007, 5},
+ {0x0008, 5},
+ {0x0008, 6},
+ {0x0003, 6},
+ {0x0034, 6},
+ {0x0035, 6},
+ {0x002A, 6}, // 16
+ {0x002B, 6},
+ {0x0027, 7},
+ {0x000C, 7},
+ {0x0008, 7},
+ {0x0017, 7},
+ {0x0003, 7},
+ {0x0004, 7},
+ {0x0028, 7}, // 24
+ {0x002B, 7},
+ {0x0013, 7},
+ {0x0024, 7},
+ {0x0018, 7},
+ {0x0002, 8},
+ {0x0003, 8},
+ {0x001A, 8},
+ {0x001B, 8}, // 32
+ {0x0012, 8},
+ {0x0013, 8},
+ {0x0014, 8},
+ {0x0015, 8},
+ {0x0016, 8},
+ {0x0017, 8},
+ {0x0028, 8},
+ {0x0029, 8}, // 40
+ {0x002A, 8},
+ {0x002B, 8},
+ {0x002C, 8},
+ {0x002D, 8},
+ {0x0004, 8},
+ {0x0005, 8},
+ {0x000A, 8},
+ {0x000B, 8}, // 48
+ {0x0052, 8},
+ {0x0053, 8},
+ {0x0054, 8},
+ {0x0055, 8},
+ {0x0024, 8},
+ {0x0025, 8},
+ {0x0058, 8},
+ {0x0059, 8}, // 56
+ {0x005A, 8},
+ {0x005B, 8},
+ {0x004A, 8},
+ {0x004B, 8},
+ {0x0032, 8},
+ {0x0033, 8},
+ {0x0034, 8},
+ {0x001B, 5}, // Make up codes: 64
+ {0x0012, 5}, // 128
+ {0x0017, 6}, // 192
+ {0x0037, 7}, // 256
+ {0x0036, 8}, // 320
+ {0x0037, 8}, // 384
+ {0x0064, 8}, // 448
+ {0x0065, 8}, // 512
+ {0x0068, 8}, // 576
+ {0x0067, 8}, // 640
+ {0x00CC, 9}, // 704
+ {0x00CD, 9}, // 768
+ {0x00D2, 9}, // 832
+ {0x00D3, 9}, // 896
+ {0x00D4, 9}, // 960
+ {0x00D5, 9}, // 1024
+ {0x00D6, 9}, // 1088
+ {0x00D7, 9}, // 1152
+ {0x00D8, 9}, // 1216
+ {0x00D9, 9}, // 1280
+ {0x00DA, 9}, // 1344
+ {0x00DB, 9}, // 1408
+ {0x0098, 9}, // 1472
+ {0x0099, 9}, // 1536
+ {0x009A, 9}, // 1600
+ {0x0018, 6}, // 1664
+ {0x009B, 9}, // 1728
+ };
+ if ( len >= 64 ) {
+ int index = 63 + (len >> 6);
+ writeG3Code( whiteCodes[index].code, whiteCodes[index].bits );
+ len &= 63;
+ }
+ writeG3Code( whiteCodes[len].code, whiteCodes[len].bits );
+}
+
+void QtopiaPrintEnginePrivate::writeG3BlackRun( int len )
+{
+ static struct {
+ unsigned short code;
+ unsigned short bits;
+ } blackCodes[64 + 27] = {
+ {0x0037, 10}, // 0
+ {0x0002, 3},
+ {0x0003, 2},
+ {0x0002, 2},
+ {0x0003, 3},
+ {0x0003, 4},
+ {0x0002, 4},
+ {0x0003, 5},
+ {0x0005, 6}, // 8
+ {0x0004, 6},
+ {0x0004, 7},
+ {0x0005, 7},
+ {0x0007, 7},
+ {0x0004, 8},
+ {0x0007, 8},
+ {0x0018, 9},
+ {0x0017, 10}, // 16
+ {0x0018, 10},
+ {0x0008, 10},
+ {0x0067, 11},
+ {0x0068, 11},
+ {0x006C, 11},
+ {0x0037, 11},
+ {0x0028, 11},
+ {0x0017, 11}, // 24
+ {0x0018, 11},
+ {0x00CA, 12},
+ {0x00CB, 12},
+ {0x00CC, 12},
+ {0x00CD, 12},
+ {0x0068, 12},
+ {0x0069, 12},
+ {0x006A, 12}, // 32
+ {0x006B, 12},
+ {0x00D2, 12},
+ {0x00D3, 12},
+ {0x00D4, 12},
+ {0x00D5, 12},
+ {0x00D6, 12},
+ {0x00D7, 12},
+ {0x006C, 12}, // 40
+ {0x006D, 12},
+ {0x00DA, 12},
+ {0x00DB, 12},
+ {0x0054, 12},
+ {0x0055, 12},
+ {0x0056, 12},
+ {0x0057, 12},
+ {0x0064, 12}, // 48
+ {0x0065, 12},
+ {0x0052, 12},
+ {0x0053, 12},
+ {0x0024, 12},
+ {0x0037, 12},
+ {0x0038, 12},
+ {0x0027, 12},
+ {0x0028, 12}, // 56
+ {0x0058, 12},
+ {0x0059, 12},
+ {0x002B, 12},
+ {0x002C, 12},
+ {0x005A, 12},
+ {0x0066, 12},
+ {0x0067, 12},
+ {0x000F, 10}, // Make up codes: 64
+ {0x00C8, 12}, // 128
+ {0x00C9, 12}, // 192
+ {0x005B, 12}, // 256
+ {0x0033, 12}, // 320
+ {0x0034, 12}, // 384
+ {0x0035, 12}, // 448
+ {0x006C, 13}, // 512
+ {0x006D, 13}, // 576
+ {0x004A, 13}, // 640
+ {0x004B, 13}, // 704
+ {0x004C, 13}, // 768
+ {0x004D, 13}, // 832
+ {0x0072, 13}, // 896
+ {0x0073, 13}, // 960
+ {0x0074, 13}, // 1024
+ {0x0075, 13}, // 1088
+ {0x0076, 13}, // 1152
+ {0x0077, 13}, // 1216
+ {0x0052, 13}, // 1280
+ {0x0053, 13}, // 1344
+ {0x0054, 13}, // 1408
+ {0x0055, 13}, // 1472
+ {0x005A, 13}, // 1536
+ {0x005B, 13}, // 1600
+ {0x0064, 13}, // 1664
+ {0x0065, 13}, // 1728
+ };
+ if ( len >= 64 ) {
+ int index = 63 + (len >> 6);
+ writeG3Code( blackCodes[index].code, blackCodes[index].bits );
+ len &= 63;
+ }
+ writeG3Code( blackCodes[len].code, blackCodes[len].bits );
+}
+
+void QtopiaPrintEnginePrivate::writeG3EOL()
+{
+ int bitToPad;
+ if ( partialBits <= 4 ) {
+ bitToPad = 4 - partialBits;
+ } else {
+ bitToPad = 8 - partialBits + 4;
+ }
+
+ partialByte = ((partialByte << (bitToPad + 12)) | 0x0001);
+ partialBits += bitToPad + 12;
+
+ while ( partialBits >= 8 ) {
+ partialBits -= 8;
+ buffer.append( (char)(partialByte >> partialBits ) );
+ }
+// writeG3Code( 0x0001, 12 );
+}
+
+void QtopiaPrintBuffer::append( short value )
+{
+ if ( _bigEndian ) {
+ _data.append( (char)(value >> 8) );
+ _data.append( (char)value );
+ } else {
+ _data.append( (char)value );
+ _data.append( (char)(value >> 8) );
+ }
+}
+
+void QtopiaPrintBuffer::append( int value )
+{
+ if ( _bigEndian ) {
+ _data.append( (char)(value >> 24) );
+ _data.append( (char)(value >> 16) );
+ _data.append( (char)(value >> 8) );
+ _data.append( (char)value );
+ } else {
+ _data.append( (char)value );
+ _data.append( (char)(value >> 8) );
+ _data.append( (char)(value >> 16) );
+ _data.append( (char)(value >> 24) );
+ }
+}
+
+void QtopiaPrintBuffer::patch( int posn, int value )
+{
+ if ( _bigEndian ) {
+ _data[posn] = (char)(value >> 24);
+ _data[posn + 1] = (char)(value >> 16);
+ _data[posn + 2] = (char)(value >> 8);
+ _data[posn + 3] = (char)value;
+ } else {
+ _data[posn] = (char)value;
+ _data[posn + 1] = (char)(value >> 8);
+ _data[posn + 2] = (char)(value >> 16);
+ _data[posn + 3] = (char)(value >> 24);
+ }
+}
+
+void QtopiaPrintBuffer::pad()
+{
+ while ( ( _data.size() % 4 ) != 0 )
+ _data.append( (char)0 );
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
diff --git a/src/gui/painting/qprintengine_qws_p.h b/src/gui/painting/qprintengine_qws_p.h
new file mode 100644
index 0000000000..59cbe3eba8
--- /dev/null
+++ b/src/gui/painting/qprintengine_qws_p.h
@@ -0,0 +1,213 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPRINTENGINE_QWS_P_H
+#define QPRINTENGINE_QWS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtGui/qprinter.h"
+
+#ifndef QT_NO_PRINTER
+
+#include "QtGui/qprintengine.h"
+#include "QtCore/qbytearray.h"
+#include "private/qpaintengine_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QtopiaPrintEnginePrivate;
+class QRasterPaintEngine;
+class QPrinterPrivate;
+class QImage;
+
+class QtopiaPrintEngine : public QPaintEngine, public QPrintEngine
+{
+ Q_DECLARE_PRIVATE(QtopiaPrintEngine)
+public:
+ QtopiaPrintEngine(QPrinter::PrinterMode mode);
+
+ // override QWSPaintEngine
+ bool begin(QPaintDevice *dev);
+ bool end();
+ void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
+ void drawTextItem(const QPointF &p, const QTextItem &ti);
+ QPaintEngine::Type type() const { return QPaintEngine::X11; }
+
+ QPaintEngine *paintEngine() const;
+
+ void updateState(const QPaintEngineState &state);
+
+ QRect paperRect() const;
+ QRect pageRect() const;
+
+ bool newPage();
+ bool abort();
+
+ QPrinter::PrinterState printerState() const;
+
+ int metric(QPaintDevice::PaintDeviceMetric metricType) const;
+
+ QVariant property(PrintEnginePropertyKey key) const;
+ void setProperty(PrintEnginePropertyKey key, const QVariant &value);
+
+private:
+ friend class QPrintDialog;
+ friend class QPageSetupDialog;
+
+ void clearPage();
+ void flushPage();
+};
+
+class QtopiaPrintBuffer
+{
+public:
+ QtopiaPrintBuffer( bool bigEndian=FALSE ) { _bigEndian = bigEndian; }
+ ~QtopiaPrintBuffer() {}
+
+ const QByteArray& data() const { return _data; }
+
+ int size() const { return _data.size(); }
+
+ void clear() { _data.clear(); }
+
+ void append( char value ) { _data.append( value ); }
+ void append( short value );
+ void append( int value );
+ void append( const QByteArray& array ) { _data.append( array ); }
+
+ void patch( int posn, int value );
+
+ void pad();
+
+private:
+ QByteArray _data;
+ bool _bigEndian;
+};
+
+#define QT_QWS_PRINTER_DEFAULT_DPI 200
+
+class QtopiaPrintEnginePrivate : public QPaintEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QtopiaPrintEngine)
+public:
+ QtopiaPrintEnginePrivate(QPrinter::PrinterMode m) :
+ mode(m),
+ printerState(QPrinter::Idle),
+ orientation(QPrinter::Portrait),
+ paperSize(QPrinter::A4),
+ pageOrder(QPrinter::FirstPageFirst),
+ colorMode(QPrinter::GrayScale),
+ paperSource(QPrinter::OnlyOne),
+ resolution(QT_QWS_PRINTER_DEFAULT_DPI),
+ _paintEngine(0),
+ numCopies(1),
+ outputToFile(false),
+ fullPage(false),
+ collateCopies(false),
+ pageNumber(0),
+ pageImage(0),
+ partialByte(0),
+ partialBits(0)
+ {
+ }
+ ~QtopiaPrintEnginePrivate();
+
+ void initialize();
+ QPaintEngine *paintEngine();
+
+ QPrinter::PrinterMode mode;
+
+ QString printerName;
+ QString outputFileName;
+ QString printProgram;
+ QString docName;
+ QString creator;
+
+ QPrinter::PrinterState printerState;
+
+ QPrinter::Orientation orientation;
+ QPrinter::PaperSize paperSize;
+ QPrinter::PageOrder pageOrder;
+ QPrinter::ColorMode colorMode;
+ QPrinter::PaperSource paperSource;
+
+ int resolution;
+ QPaintEngine *_paintEngine;
+ int numCopies;
+
+ bool outputToFile;
+ bool fullPage;
+ bool collateCopies;
+
+ int pageNumber;
+
+ QImage *pageImage;
+
+ QtopiaPrintBuffer buffer;
+
+ // Definitions that are only relevant to G3FAX output.
+ int ifdPatch;
+ int partialByte;
+ int partialBits;
+ void writeG3FaxHeader();
+ void writeG3FaxPage();
+ int writeG3IFDEntry( int tag, int type, int count, int value );
+ void writeG3Code( int code, int bits );
+ void writeG3WhiteRun( int len );
+ void writeG3BlackRun( int len );
+ void writeG3EOL();
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
+
+#endif // QPRINTENGINE_QWS_P_H
diff --git a/src/gui/painting/qprintengine_win.cpp b/src/gui/painting/qprintengine_win.cpp
new file mode 100644
index 0000000000..07d66f5bd0
--- /dev/null
+++ b/src/gui/painting/qprintengine_win.cpp
@@ -0,0 +1,1776 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT_NO_PRINTER
+
+#include "qprinter_p.h"
+#include "qprintengine_win_p.h"
+
+#include <limits.h>
+
+#include <private/qfont_p.h>
+#include <private/qfontengine_p.h>
+#include <private/qpainter_p.h>
+
+#include <qbitmap.h>
+#include <qdebug.h>
+#include <qvector.h>
+#include <qpicture.h>
+#include <private/qpicture_p.h>
+
+QT_BEGIN_NAMESPACE
+
+extern QPainterPath qt_regionToPath(const QRegion &region);
+
+// #define QT_DEBUG_DRAW
+
+static void draw_text_item_win(const QPointF &_pos, const QTextItemInt &ti, HDC hdc,
+ bool convertToText, const QTransform &xform, const QPointF &topLeft);
+
+static const struct {
+ int winSizeName;
+ QPrinter::PaperSize qtSizeName;
+} dmMapping[] = {
+ { DMPAPER_LETTER, QPrinter::Letter },
+ { DMPAPER_LETTERSMALL, QPrinter::Letter },
+ { DMPAPER_TABLOID, QPrinter::Tabloid },
+ { DMPAPER_LEDGER, QPrinter::Ledger },
+ { DMPAPER_LEGAL, QPrinter::Legal },
+ { DMPAPER_EXECUTIVE, QPrinter::Executive },
+ { DMPAPER_A3, QPrinter::A3 },
+ { DMPAPER_A4, QPrinter::A4 },
+ { DMPAPER_A4SMALL, QPrinter::A4 },
+ { DMPAPER_A5, QPrinter::A5 },
+ { DMPAPER_B4, QPrinter::B4 },
+ { DMPAPER_B5, QPrinter::B5 },
+ { DMPAPER_FOLIO, QPrinter::Folio },
+ { DMPAPER_ENV_10, QPrinter::Comm10E },
+ { DMPAPER_ENV_DL, QPrinter::DLE },
+ { DMPAPER_ENV_C3, QPrinter::C5E },
+ { DMPAPER_LETTER_EXTRA, QPrinter::Letter },
+ { DMPAPER_LEGAL_EXTRA, QPrinter::Legal },
+ { DMPAPER_TABLOID_EXTRA, QPrinter::Tabloid },
+ { DMPAPER_A4_EXTRA, QPrinter::A4},
+ { DMPAPER_LETTER_TRANSVERSE, QPrinter::Letter},
+ { DMPAPER_A4_TRANSVERSE, QPrinter::A4},
+ { DMPAPER_LETTER_EXTRA_TRANSVERSE, QPrinter::Letter },
+ { DMPAPER_A_PLUS, QPrinter::A4 },
+ { DMPAPER_B_PLUS, QPrinter::A3 },
+ { DMPAPER_LETTER_PLUS, QPrinter::Letter },
+ { DMPAPER_A4_PLUS, QPrinter::A4 },
+ { DMPAPER_A5_TRANSVERSE, QPrinter::A5 },
+ { DMPAPER_B5_TRANSVERSE, QPrinter::B5 },
+ { DMPAPER_A3_EXTRA, QPrinter::A3 },
+ { DMPAPER_A5_EXTRA, QPrinter::A5 },
+ { DMPAPER_B5_EXTRA, QPrinter::B5 },
+ { DMPAPER_A2, QPrinter::A2 },
+ { DMPAPER_A3_TRANSVERSE, QPrinter::A3 },
+ { DMPAPER_A3_EXTRA_TRANSVERSE,QPrinter::A3 },
+ { 0, QPrinter::Custom }
+};
+
+QPrinter::PaperSize mapDevmodePaperSize(int s)
+{
+ int i = 0;
+ while ((dmMapping[i].winSizeName > 0) && (dmMapping[i].winSizeName != s))
+ i++;
+ return dmMapping[i].qtSizeName;
+}
+
+static int mapPaperSizeDevmode(QPrinter::PaperSize s)
+{
+ int i = 0;
+ while ((dmMapping[i].winSizeName > 0) && (dmMapping[i].qtSizeName != s))
+ i++;
+ return dmMapping[i].winSizeName;
+}
+
+static const struct {
+ int winSourceName;
+ QPrinter::PaperSource qtSourceName;
+} sources[] = {
+ { DMBIN_ONLYONE, QPrinter::OnlyOne },
+ { DMBIN_LOWER, QPrinter::Lower },
+ { DMBIN_MIDDLE, QPrinter::Middle },
+ { DMBIN_MANUAL, QPrinter::Manual },
+ { DMBIN_ENVELOPE, QPrinter::Envelope },
+ { DMBIN_ENVMANUAL, QPrinter::EnvelopeManual },
+ { DMBIN_AUTO, QPrinter::Auto },
+ { DMBIN_TRACTOR, QPrinter::Tractor },
+ { DMBIN_SMALLFMT, QPrinter::SmallFormat },
+ { DMBIN_LARGEFMT, QPrinter::LargeFormat },
+ { DMBIN_LARGECAPACITY, QPrinter::LargeCapacity },
+ { DMBIN_CASSETTE, QPrinter::Cassette },
+ { DMBIN_FORMSOURCE, QPrinter::FormSource },
+ { 0, (QPrinter::PaperSource) -1 }
+};
+
+static QPrinter::PaperSource mapDevmodePaperSource(int s)
+{
+ int i = 0;
+ while ((sources[i].winSourceName > 0) && (sources[i].winSourceName != s))
+ i++;
+ return sources[i].winSourceName ? sources[i].qtSourceName : (QPrinter::PaperSource) s;
+}
+
+static int mapPaperSourceDevmode(QPrinter::PaperSource s)
+{
+ int i = 0;
+ while ((sources[i].qtSourceName >= 0) && (sources[i].qtSourceName != s))
+ i++;
+ return sources[i].winSourceName ? sources[i].winSourceName : s;
+}
+
+QWin32PrintEngine::QWin32PrintEngine(QPrinter::PrinterMode mode)
+ : QAlphaPaintEngine(*(new QWin32PrintEnginePrivate),
+ PaintEngineFeatures(PrimitiveTransform
+ | PixmapTransform
+ | PerspectiveTransform
+ | PainterPaths
+ | Antialiasing
+ | PaintOutsidePaintEvent))
+{
+ Q_D(QWin32PrintEngine);
+ d->docName = QLatin1String("document1");
+ d->mode = mode;
+ d->queryDefault();
+ d->initialize();
+}
+
+bool QWin32PrintEngine::begin(QPaintDevice *pdev)
+{
+ Q_D(QWin32PrintEngine);
+
+ QAlphaPaintEngine::begin(pdev);
+ if (!continueCall())
+ return true;
+
+ if (d->reinit) {
+ d->resetDC();
+ d->reinit = false;
+ }
+
+ // ### set default colors and stuff...
+
+ bool ok = d->state == QPrinter::Idle;
+
+ if (!d->hdc)
+ return false;
+
+ // Assign the FILE: to get the query...
+ if (d->printToFile && d->fileName.isEmpty())
+ d->fileName = d->port;
+
+ d->devMode->dmCopies = d->num_copies;
+
+ DOCINFO di;
+ memset(&di, 0, sizeof(DOCINFO));
+ di.cbSize = sizeof(DOCINFO);
+ di.lpszDocName = reinterpret_cast<const wchar_t *>(d->docName.utf16());
+ if (d->printToFile && !d->fileName.isEmpty())
+ di.lpszOutput = reinterpret_cast<const wchar_t *>(d->fileName.utf16());
+ if (ok && StartDoc(d->hdc, &di) == SP_ERROR) {
+ qErrnoWarning("QWin32PrintEngine::begin: StartDoc failed");
+ ok = false;
+ }
+
+ if (StartPage(d->hdc) <= 0) {
+ qErrnoWarning("QWin32PrintEngine::begin: StartPage failed");
+ ok = false;
+ }
+
+ if (!ok) {
+ d->state = QPrinter::Idle;
+ } else {
+ d->state = QPrinter::Active;
+ }
+
+ d->matrix = QTransform();
+ d->has_pen = true;
+ d->pen = QColor(Qt::black);
+ d->has_brush = false;
+
+ d->complex_xform = false;
+
+ updateMatrix(d->matrix);
+
+ if (!ok)
+ cleanUp();
+
+ return ok;
+}
+
+bool QWin32PrintEngine::end()
+{
+ Q_D(QWin32PrintEngine);
+
+ if (d->hdc) {
+ if (d->state == QPrinter::Aborted) {
+ cleanUp();
+ AbortDoc(d->hdc);
+ return true;
+ }
+ }
+
+ QAlphaPaintEngine::end();
+ if (!continueCall())
+ return true;
+
+ if (d->hdc) {
+ EndPage(d->hdc); // end; printing done
+ EndDoc(d->hdc);
+ }
+
+ d->state = QPrinter::Idle;
+ d->reinit = true;
+ return true;
+}
+
+bool QWin32PrintEngine::newPage()
+{
+ Q_D(QWin32PrintEngine);
+ Q_ASSERT(isActive());
+
+ Q_ASSERT(d->hdc);
+
+ flushAndInit();
+
+ bool transparent = GetBkMode(d->hdc) == TRANSPARENT;
+
+ if (!EndPage(d->hdc)) {
+ qErrnoWarning("QWin32PrintEngine::newPage: EndPage failed");
+ return false;
+ }
+
+ if (d->reinit) {
+ if (!d->resetDC()) {
+ qErrnoWarning("QWin32PrintEngine::newPage: ResetDC failed");
+ return false;
+ }
+ d->reinit = false;
+ }
+
+ if (!StartPage(d->hdc)) {
+ qErrnoWarning("Win32PrintEngine::newPage: StartPage failed");
+ return false;
+ }
+
+ SetTextAlign(d->hdc, TA_BASELINE);
+ if (transparent)
+ SetBkMode(d->hdc, TRANSPARENT);
+
+ // ###
+ return true;
+
+ bool success = false;
+ if (d->hdc && d->state == QPrinter::Active) {
+ if (EndPage(d->hdc) != SP_ERROR) {
+ // reinitialize the DC before StartPage if needed,
+ // because resetdc is disabled between calls to the StartPage and EndPage functions
+ // (see StartPage documentation in the Platform SDK:Windows GDI)
+// state = PST_ACTIVEDOC;
+// reinit();
+// state = PST_ACTIVE;
+ // start the new page now
+ if (d->reinit) {
+ if (!d->resetDC())
+ qErrnoWarning("QWin32PrintEngine::newPage(), ResetDC failed (2)");
+ d->reinit = false;
+ }
+ success = (StartPage(d->hdc) != SP_ERROR);
+ }
+ if (!success) {
+ d->state = QPrinter::Aborted;
+ return false;
+ }
+ }
+ return true;
+}
+
+bool QWin32PrintEngine::abort()
+{
+ // do nothing loop.
+ return false;
+}
+
+void QWin32PrintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
+{
+ Q_D(const QWin32PrintEngine);
+
+ QAlphaPaintEngine::drawTextItem(p, textItem);
+ if (!continueCall())
+ return;
+
+ const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
+ QRgb brushColor = state->pen().brush().color().rgb();
+ bool fallBack = state->pen().brush().style() != Qt::SolidPattern
+ || qAlpha(brushColor) != 0xff
+ || d->txop >= QTransform::TxProject
+ || ti.fontEngine->type() != QFontEngine::Win;
+
+
+ if (!fallBack) {
+ QFontEngineWin *fe = static_cast<QFontEngineWin *>(ti.fontEngine);
+
+ // Try selecting the font to see if we get a substitution font
+ SelectObject(d->hdc, fe->hfont);
+
+ if (GetDeviceCaps(d->hdc, TECHNOLOGY) != DT_CHARSTREAM) {
+ wchar_t n[64];
+ GetTextFace(d->hdc, 64, n);
+ fallBack = QString::fromWCharArray(n)
+ != QString::fromWCharArray(fe->logfont.lfFaceName);
+ }
+ }
+
+
+ if (fallBack) {
+ QPaintEngine::drawTextItem(p, textItem);
+ return ;
+ }
+
+ // We only want to convert the glyphs to text if the entire string is compatible with ASCII
+ // and if we actually have access to the chars.
+ bool convertToText = ti.chars != 0;
+ for (int i=0; i < ti.num_chars; ++i) {
+ if (ti.chars[i].unicode() >= 0x80) {
+ convertToText = false;
+ break;
+ }
+
+ if (ti.logClusters[i] != i) {
+ convertToText = false;
+ break;
+ }
+ }
+
+ COLORREF cf = RGB(qRed(brushColor), qGreen(brushColor), qBlue(brushColor));
+ SelectObject(d->hdc, CreateSolidBrush(cf));
+ SelectObject(d->hdc, CreatePen(PS_SOLID, 1, cf));
+ SetTextColor(d->hdc, cf);
+
+ draw_text_item_win(p, ti, d->hdc, convertToText, d->matrix, d->devPaperRect.topLeft());
+ DeleteObject(SelectObject(d->hdc,GetStockObject(HOLLOW_BRUSH)));
+ DeleteObject(SelectObject(d->hdc,GetStockObject(BLACK_PEN)));
+}
+
+static inline qreal mmToInches(double mm)
+{
+ return mm*0.039370147;
+}
+
+static inline qreal inchesToMM(double in)
+{
+ return in/0.039370147;
+}
+
+int QWin32PrintEngine::metric(QPaintDevice::PaintDeviceMetric m) const
+{
+ Q_D(const QWin32PrintEngine);
+
+ if (!d->hdc)
+ return 0;
+
+ int val;
+ int res = d->resolution;
+
+ switch (m) {
+ case QPaintDevice::PdmWidth:
+ if (d->has_custom_paper_size) {
+ val = qRound(d->paper_size.width() * res / 72.0);
+ } else {
+ int logPixelsX = GetDeviceCaps(d->hdc, LOGPIXELSX);
+ if (logPixelsX == 0) {
+ qWarning("QWin32PrintEngine::metric: GetDeviceCaps() failed, "
+ "might be a driver problem");
+ logPixelsX = 600; // Reasonable default
+ }
+ val = res
+ * GetDeviceCaps(d->hdc, d->fullPage ? PHYSICALWIDTH : HORZRES)
+ / logPixelsX;
+ }
+ if (d->pageMarginsSet)
+ val -= int(mmToInches((d->previousDialogMargins.left() +
+ d->previousDialogMargins.width()) / 100.0) * res);
+ break;
+ case QPaintDevice::PdmHeight:
+ if (d->has_custom_paper_size) {
+ val = qRound(d->paper_size.height() * res / 72.0);
+ } else {
+ int logPixelsY = GetDeviceCaps(d->hdc, LOGPIXELSY);
+ if (logPixelsY == 0) {
+ qWarning("QWin32PrintEngine::metric: GetDeviceCaps() failed, "
+ "might be a driver problem");
+ logPixelsY = 600; // Reasonable default
+ }
+ val = res
+ * GetDeviceCaps(d->hdc, d->fullPage ? PHYSICALHEIGHT : VERTRES)
+ / logPixelsY;
+ }
+ if (d->pageMarginsSet)
+ val -= int(mmToInches((d->previousDialogMargins.top() +
+ d->previousDialogMargins.height()) / 100.0) * res);
+ break;
+ case QPaintDevice::PdmDpiX:
+ val = res;
+ break;
+ case QPaintDevice::PdmDpiY:
+ val = res;
+ break;
+ case QPaintDevice::PdmPhysicalDpiX:
+ val = GetDeviceCaps(d->hdc, LOGPIXELSX);
+ break;
+ case QPaintDevice::PdmPhysicalDpiY:
+ val = GetDeviceCaps(d->hdc, LOGPIXELSY);
+ break;
+ case QPaintDevice::PdmWidthMM:
+ if (d->has_custom_paper_size) {
+ val = qRound(d->paper_size.width()*25.4/72);
+ } else {
+ if (!d->fullPage) {
+ val = GetDeviceCaps(d->hdc, HORZSIZE);
+ } else {
+ float wi = 25.4 * GetDeviceCaps(d->hdc, PHYSICALWIDTH);
+ int logPixelsX = GetDeviceCaps(d->hdc, LOGPIXELSX);
+ if (logPixelsX == 0) {
+ qWarning("QWin32PrintEngine::metric: GetDeviceCaps() failed, "
+ "might be a driver problem");
+ logPixelsX = 600; // Reasonable default
+ }
+ val = qRound(wi / logPixelsX);
+ }
+ }
+ if (d->pageMarginsSet)
+ val -= (d->previousDialogMargins.left() +
+ d->previousDialogMargins.width()) / 100.0;
+ break;
+ case QPaintDevice::PdmHeightMM:
+ if (d->has_custom_paper_size) {
+ val = qRound(d->paper_size.height()*25.4/72);
+ } else {
+ if (!d->fullPage) {
+ val = GetDeviceCaps(d->hdc, VERTSIZE);
+ } else {
+ float hi = 25.4 * GetDeviceCaps(d->hdc, PHYSICALHEIGHT);
+ int logPixelsY = GetDeviceCaps(d->hdc, LOGPIXELSY);
+ if (logPixelsY == 0) {
+ qWarning("QWin32PrintEngine::metric: GetDeviceCaps() failed, "
+ "might be a driver problem");
+ logPixelsY = 600; // Reasonable default
+ }
+ val = qRound(hi / logPixelsY);
+ }
+ }
+ if (d->pageMarginsSet)
+ val -= (d->previousDialogMargins.top() +
+ d->previousDialogMargins.height()) / 100.0;
+ break;
+ case QPaintDevice::PdmNumColors:
+ {
+ int bpp = GetDeviceCaps(d->hdc, BITSPIXEL);
+ if(bpp==32)
+ val = INT_MAX;
+ else if(bpp<=8)
+ val = GetDeviceCaps(d->hdc, NUMCOLORS);
+ else
+ val = 1 << (bpp * GetDeviceCaps(d->hdc, PLANES));
+ }
+ break;
+ case QPaintDevice::PdmDepth:
+ val = GetDeviceCaps(d->hdc, PLANES);
+ break;
+ default:
+ qWarning("QPrinter::metric: Invalid metric command");
+ return 0;
+ }
+ return val;
+}
+
+void QWin32PrintEngine::updateState(const QPaintEngineState &state)
+{
+ Q_D(QWin32PrintEngine);
+
+ QAlphaPaintEngine::updateState(state);
+ if (!continueCall())
+ return;
+
+ if (state.state() & DirtyTransform) {
+ updateMatrix(state.transform());
+ }
+
+ if (state.state() & DirtyPen) {
+ d->pen = state.pen();
+ d->has_pen = d->pen.style() != Qt::NoPen && d->pen.isSolid();
+ }
+
+ if (state.state() & DirtyBrush) {
+ QBrush brush = state.brush();
+ d->has_brush = brush.style() == Qt::SolidPattern;
+ d->brush_color = brush.color();
+ }
+
+ if (state.state() & DirtyClipEnabled) {
+ if (state.isClipEnabled())
+ updateClipPath(painter()->clipPath(), Qt::ReplaceClip);
+ else
+ updateClipPath(QPainterPath(), Qt::NoClip);
+ }
+
+ if (state.state() & DirtyClipPath) {
+ updateClipPath(state.clipPath(), state.clipOperation());
+ }
+
+ if (state.state() & DirtyClipRegion) {
+ QRegion clipRegion = state.clipRegion();
+ QPainterPath clipPath = qt_regionToPath(clipRegion);
+ updateClipPath(clipPath, state.clipOperation());
+ }
+}
+
+void QWin32PrintEngine::updateClipPath(const QPainterPath &clipPath, Qt::ClipOperation op)
+{
+ Q_D(QWin32PrintEngine);
+
+ bool doclip = true;
+ if (op == Qt::NoClip) {
+ SelectClipRgn(d->hdc, 0);
+ doclip = false;
+ }
+
+ if (doclip) {
+ QPainterPath xformed = clipPath * d->matrix;
+
+ if (xformed.isEmpty()) {
+ QRegion empty(-0x1000000, -0x1000000, 1, 1);
+ SelectClipRgn(d->hdc, empty.handle());
+ } else {
+ d->composeGdiPath(xformed);
+ const int ops[] = {
+ -1, // Qt::NoClip, covered above
+ RGN_COPY, // Qt::ReplaceClip
+ RGN_AND, // Qt::IntersectClip
+ RGN_OR // Qt::UniteClip
+ };
+ Q_ASSERT(op > 0 && unsigned(op) <= sizeof(ops) / sizeof(int));
+ SelectClipPath(d->hdc, ops[op]);
+ }
+ }
+
+ QPainterPath aclip = qt_regionToPath(alphaClipping());
+ if (!aclip.isEmpty()) {
+ QTransform tx(d->stretch_x, 0, 0, d->stretch_y, d->origin_x, d->origin_y);
+ d->composeGdiPath(tx.map(aclip));
+ SelectClipPath(d->hdc, RGN_DIFF);
+ }
+}
+
+void QWin32PrintEngine::updateMatrix(const QTransform &m)
+{
+ Q_D(QWin32PrintEngine);
+
+ QTransform stretch(d->stretch_x, 0, 0, d->stretch_y, d->origin_x, d->origin_y);
+ d->painterMatrix = m;
+ d->matrix = d->painterMatrix * stretch;
+ d->txop = d->matrix.type();
+ d->complex_xform = (d->txop > QTransform::TxScale);
+}
+
+void QWin32PrintEngine::drawPixmap(const QRectF &targetRect,
+ const QPixmap &originalPixmap,
+ const QRectF &sourceRect)
+{
+ Q_D(QWin32PrintEngine);
+
+ QAlphaPaintEngine::drawPixmap(targetRect, originalPixmap, sourceRect);
+ if (!continueCall())
+ return;
+
+ const int tileSize = 2048;
+
+ QRectF r = targetRect;
+ QRectF sr = sourceRect;
+
+ QPixmap pixmap = originalPixmap;
+ if (sr.size() != pixmap.size()) {
+ pixmap = pixmap.copy(sr.toRect());
+ }
+
+ qreal scaleX = 1.0f;
+ qreal scaleY = 1.0f;
+
+ QTransform scaleMatrix = QTransform::fromScale(r.width() / pixmap.width(), r.height() / pixmap.height());
+ QTransform adapted = QPixmap::trueMatrix(d->painterMatrix * scaleMatrix,
+ pixmap.width(), pixmap.height());
+
+ qreal xform_offset_x = adapted.dx();
+ qreal xform_offset_y = adapted.dy();
+
+ if (d->complex_xform) {
+ pixmap = pixmap.transformed(adapted);
+ scaleX = d->stretch_x;
+ scaleY = d->stretch_y;
+ } else {
+ scaleX = d->stretch_x * (r.width() / pixmap.width()) * d->painterMatrix.m11();
+ scaleY = d->stretch_y * (r.height() / pixmap.height()) * d->painterMatrix.m22();
+ }
+
+ QPointF topLeft = r.topLeft() * d->painterMatrix;
+ int tx = int(topLeft.x() * d->stretch_x + d->origin_x);
+ int ty = int(topLeft.y() * d->stretch_y + d->origin_y);
+ int tw = qAbs(int(pixmap.width() * scaleX));
+ int th = qAbs(int(pixmap.height() * scaleY));
+
+ xform_offset_x *= d->stretch_x;
+ xform_offset_y *= d->stretch_y;
+
+ int dc_state = SaveDC(d->hdc);
+
+ int tilesw = pixmap.width() / tileSize;
+ int tilesh = pixmap.height() / tileSize;
+ ++tilesw;
+ ++tilesh;
+
+ int txinc = tileSize*scaleX;
+ int tyinc = tileSize*scaleY;
+
+ for (int y = 0; y < tilesh; ++y) {
+ int tposy = ty + (y * tyinc);
+ int imgh = tileSize;
+ int height = tyinc;
+ if (y == (tilesh - 1)) {
+ imgh = pixmap.height() - (y * tileSize);
+ height = (th - (y * tyinc));
+ }
+ for (int x = 0; x < tilesw; ++x) {
+ int tposx = tx + (x * txinc);
+ int imgw = tileSize;
+ int width = txinc;
+ if (x == (tilesw - 1)) {
+ imgw = pixmap.width() - (x * tileSize);
+ width = (tw - (x * txinc));
+ }
+
+ QPixmap p = pixmap.copy(tileSize * x, tileSize * y, imgw, imgh);
+ HBITMAP hbitmap = p.toWinHBITMAP(QPixmap::NoAlpha);
+ HDC display_dc = GetDC(0);
+ HDC hbitmap_hdc = CreateCompatibleDC(display_dc);
+ HGDIOBJ null_bitmap = SelectObject(hbitmap_hdc, hbitmap);
+
+ ReleaseDC(0, display_dc);
+
+ if (!StretchBlt(d->hdc, qRound(tposx - xform_offset_x), qRound(tposy - xform_offset_y), width, height,
+ hbitmap_hdc, 0, 0, p.width(), p.height(), SRCCOPY))
+ qErrnoWarning("QWin32PrintEngine::drawPixmap, StretchBlt failed");
+
+ SelectObject(hbitmap_hdc, null_bitmap);
+ DeleteObject(hbitmap);
+ DeleteDC(hbitmap_hdc);
+ }
+ }
+
+ RestoreDC(d->hdc, dc_state);
+}
+
+
+void QWin32PrintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &pos)
+{
+ Q_D(QWin32PrintEngine);
+
+ QAlphaPaintEngine::drawTiledPixmap(r, pm, pos);
+ if (!continueCall())
+ return;
+
+ if (d->complex_xform || !pos.isNull()) {
+ QPaintEngine::drawTiledPixmap(r, pm, pos);
+ } else {
+ int dc_state = SaveDC(d->hdc);
+
+ HDC display_dc = GetDC(0);
+ HBITMAP hbitmap = pm.toWinHBITMAP(QPixmap::NoAlpha);
+ HDC hbitmap_hdc = CreateCompatibleDC(display_dc);
+ HGDIOBJ null_bitmap = SelectObject(hbitmap_hdc, hbitmap);
+
+ ReleaseDC(0, display_dc);
+
+ QRectF trect = d->painterMatrix.mapRect(r);
+ int tx = int(trect.left() * d->stretch_x + d->origin_x);
+ int ty = int(trect.top() * d->stretch_y + d->origin_y);
+
+ int xtiles = int(trect.width() / pm.width()) + 1;
+ int ytiles = int(trect.height() / pm.height()) + 1;
+ int xinc = int(pm.width() * d->stretch_x);
+ int yinc = int(pm.height() * d->stretch_y);
+
+ for (int y = 0; y < ytiles; ++y) {
+ int ity = ty + (yinc * y);
+ int ith = pm.height();
+ if (y == (ytiles - 1)) {
+ ith = int(trect.height() - (pm.height() * y));
+ }
+
+ for (int x = 0; x < xtiles; ++x) {
+ int itx = tx + (xinc * x);
+ int itw = pm.width();
+ if (x == (xtiles - 1)) {
+ itw = int(trect.width() - (pm.width() * x));
+ }
+
+ if (!StretchBlt(d->hdc, itx, ity, int(itw * d->stretch_x), int(ith * d->stretch_y),
+ hbitmap_hdc, 0, 0, itw, ith, SRCCOPY))
+ qErrnoWarning("QWin32PrintEngine::drawPixmap, StretchBlt failed");
+
+ }
+ }
+
+ SelectObject(hbitmap_hdc, null_bitmap);
+ DeleteObject(hbitmap);
+ DeleteDC(hbitmap_hdc);
+
+ RestoreDC(d->hdc, dc_state);
+ }
+}
+
+
+void QWin32PrintEnginePrivate::composeGdiPath(const QPainterPath &path)
+{
+ if (!BeginPath(hdc))
+ qErrnoWarning("QWin32PrintEnginePrivate::drawPath: BeginPath failed");
+
+ // Drawing the subpaths
+ int start = -1;
+ for (int i=0; i<path.elementCount(); ++i) {
+ const QPainterPath::Element &elm = path.elementAt(i);
+ switch (elm.type) {
+ case QPainterPath::MoveToElement:
+ if (start >= 0
+ && path.elementAt(start).x == path.elementAt(i-1).x
+ && path.elementAt(start).y == path.elementAt(i-1).y)
+ CloseFigure(hdc);
+ start = i;
+ MoveToEx(hdc, qRound(elm.x), qRound(elm.y), 0);
+ break;
+ case QPainterPath::LineToElement:
+ LineTo(hdc, qRound(elm.x), qRound(elm.y));
+ break;
+ case QPainterPath::CurveToElement: {
+ POINT pts[3] = {
+ { qRound(elm.x), qRound(elm.y) },
+ { qRound(path.elementAt(i+1).x), qRound(path.elementAt(i+1).y) },
+ { qRound(path.elementAt(i+2).x), qRound(path.elementAt(i+2).y) }
+ };
+ i+=2;
+ PolyBezierTo(hdc, pts, 3);
+ break;
+ }
+ default:
+ qFatal("QWin32PaintEngine::drawPath: Unhandled type: %d", elm.type);
+ }
+ }
+
+ if (start >= 0
+ && path.elementAt(start).x == path.elementAt(path.elementCount()-1).x
+ && path.elementAt(start).y == path.elementAt(path.elementCount()-1).y)
+ CloseFigure(hdc);
+
+ if (!EndPath(hdc))
+ qErrnoWarning("QWin32PaintEngine::drawPath: EndPath failed");
+
+ SetPolyFillMode(hdc, path.fillRule() == Qt::WindingFill ? WINDING : ALTERNATE);
+}
+
+
+void QWin32PrintEnginePrivate::fillPath_dev(const QPainterPath &path, const QColor &color)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << " --- QWin32PrintEnginePrivate::fillPath() bound:" << path.boundingRect() << color;
+#endif
+
+ composeGdiPath(path);
+
+ HBRUSH brush = CreateSolidBrush(RGB(color.red(), color.green(), color.blue()));
+ HGDIOBJ old_brush = SelectObject(hdc, brush);
+ FillPath(hdc);
+ DeleteObject(SelectObject(hdc, old_brush));
+}
+
+void QWin32PrintEnginePrivate::strokePath_dev(const QPainterPath &path, const QColor &color, qreal penWidth)
+{
+ composeGdiPath(path);
+ LOGBRUSH brush;
+ brush.lbStyle = BS_SOLID;
+ brush.lbColor = RGB(color.red(), color.green(), color.blue());
+ DWORD capStyle = PS_ENDCAP_SQUARE;
+ DWORD joinStyle = PS_JOIN_BEVEL;
+ if (pen.capStyle() == Qt::FlatCap)
+ capStyle = PS_ENDCAP_FLAT;
+ else if (pen.capStyle() == Qt::RoundCap)
+ capStyle = PS_ENDCAP_ROUND;
+
+ if (pen.joinStyle() == Qt::MiterJoin)
+ joinStyle = PS_JOIN_MITER;
+ else if (pen.joinStyle() == Qt::RoundJoin)
+ joinStyle = PS_JOIN_ROUND;
+
+ HPEN pen = ExtCreatePen(((penWidth == 0) ? PS_COSMETIC : PS_GEOMETRIC)
+ | PS_SOLID | capStyle | joinStyle,
+ (penWidth == 0) ? 1 : penWidth, &brush, 0, 0);
+
+ HGDIOBJ old_pen = SelectObject(hdc, pen);
+ StrokePath(hdc);
+ DeleteObject(SelectObject(hdc, old_pen));
+}
+
+
+void QWin32PrintEnginePrivate::fillPath(const QPainterPath &path, const QColor &color)
+{
+ fillPath_dev(path * matrix, color);
+}
+
+void QWin32PrintEnginePrivate::strokePath(const QPainterPath &path, const QColor &color)
+{
+ QPainterPathStroker stroker;
+ if (pen.style() == Qt::CustomDashLine) {
+ stroker.setDashPattern(pen.dashPattern());
+ stroker.setDashOffset(pen.dashOffset());
+ } else {
+ stroker.setDashPattern(pen.style());
+ }
+ stroker.setCapStyle(pen.capStyle());
+ stroker.setJoinStyle(pen.joinStyle());
+ stroker.setMiterLimit(pen.miterLimit());
+
+ QPainterPath stroke;
+ qreal width = pen.widthF();
+ if (pen.style() == Qt::SolidLine && (pen.isCosmetic() || matrix.type() < QTransform::TxScale)) {
+ strokePath_dev(path * matrix, color, width);
+ } else {
+ stroker.setWidth(width);
+ if (pen.isCosmetic()) {
+ stroke = stroker.createStroke(path * matrix);
+ } else {
+ stroke = stroker.createStroke(path) * painterMatrix;
+ QTransform stretch(stretch_x, 0, 0, stretch_y, origin_x, origin_y);
+ stroke = stroke * stretch;
+ }
+
+ if (stroke.isEmpty())
+ return;
+
+ fillPath_dev(stroke, color);
+ }
+}
+
+
+void QWin32PrintEngine::drawPath(const QPainterPath &path)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << " - QWin32PrintEngine::drawPath(), bounds: " << path.boundingRect();
+#endif
+
+ Q_D(QWin32PrintEngine);
+
+ QAlphaPaintEngine::drawPath(path);
+ if (!continueCall())
+ return;
+
+ if (d->has_brush)
+ d->fillPath(path, d->brush_color);
+
+ if (d->has_pen)
+ d->strokePath(path, d->pen.color());
+}
+
+
+void QWin32PrintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
+{
+#ifdef QT_DEBUG_DRAW
+ qDebug() << " - QWin32PrintEngine::drawPolygon(), pointCount: " << pointCount;
+#endif
+
+ QAlphaPaintEngine::drawPolygon(points, pointCount, mode);
+ if (!continueCall())
+ return;
+
+ Q_ASSERT(pointCount > 1);
+
+ QPainterPath path(points[0]);
+
+ for (int i=1; i<pointCount; ++i) {
+ path.lineTo(points[i]);
+ }
+
+ Q_D(QWin32PrintEngine);
+
+ bool has_brush = d->has_brush;
+
+ if (mode == PolylineMode)
+ d->has_brush = false; // No brush for polylines
+ else
+ path.closeSubpath(); // polygons are should always be closed.
+
+ drawPath(path);
+ d->has_brush = has_brush;
+}
+
+void QWin32PrintEnginePrivate::queryDefault()
+{
+ /* Read the default printer name, driver and port with the intuitive function
+ * Strings "windows" and "device" are specified in the MSDN under EnumPrinters()
+ */
+ QString noPrinters(QLatin1String("qt_no_printers"));
+ wchar_t buffer[256];
+ GetProfileString(L"windows", L"device",
+ reinterpret_cast<const wchar_t *>(noPrinters.utf16()),
+ buffer, 256);
+ QString output = QString::fromWCharArray(buffer);
+ if (output.isEmpty() || output == noPrinters) // no printers
+ return;
+
+ QStringList info = output.split(QLatin1Char(','));
+ int infoSize = info.size();
+ if (infoSize > 0) {
+ if (name.isEmpty())
+ name = info.at(0);
+ if (program.isEmpty() && infoSize > 1)
+ program = info.at(1);
+ if (port.isEmpty() && infoSize > 2)
+ port = info.at(2);
+ }
+}
+
+QWin32PrintEnginePrivate::~QWin32PrintEnginePrivate()
+{
+ if (hdc)
+ release();
+}
+
+void QWin32PrintEnginePrivate::initialize()
+{
+ if (hdc)
+ release();
+ Q_ASSERT(!hPrinter);
+ Q_ASSERT(!hdc);
+ Q_ASSERT(!devMode);
+ Q_ASSERT(!pInfo);
+
+ if (name.isEmpty())
+ return;
+
+ txop = QTransform::TxNone;
+
+ bool ok = OpenPrinter((LPWSTR)name.utf16(), (LPHANDLE)&hPrinter, 0);
+ if (!ok) {
+ qErrnoWarning("QWin32PrintEngine::initialize: OpenPrinter failed");
+ return;
+ }
+
+ // Fetch the PRINTER_INFO_2 with DEVMODE data containing the
+ // printer settings.
+ DWORD infoSize, numBytes;
+ GetPrinter(hPrinter, 2, NULL, 0, &infoSize);
+ hMem = GlobalAlloc(GHND, infoSize);
+ pInfo = (PRINTER_INFO_2*) GlobalLock(hMem);
+ ok = GetPrinter(hPrinter, 2, (LPBYTE)pInfo, infoSize, &numBytes);
+
+ if (!ok) {
+ qErrnoWarning("QWin32PrintEngine::initialize: GetPrinter failed");
+ GlobalUnlock(pInfo);
+ GlobalFree(hMem);
+ ClosePrinter(hPrinter);
+ pInfo = 0;
+ hMem = 0;
+ hPrinter = 0;
+ return;
+ }
+
+ devMode = pInfo->pDevMode;
+ hdc = CreateDC(reinterpret_cast<const wchar_t *>(program.utf16()),
+ reinterpret_cast<const wchar_t *>(name.utf16()), 0, devMode);
+
+ Q_ASSERT(hPrinter);
+ Q_ASSERT(pInfo);
+
+ if (devMode) {
+ num_copies = devMode->dmCopies;
+ }
+
+ initHDC();
+
+#ifdef QT_DEBUG_DRAW
+ qDebug() << "QWin32PrintEngine::initialize()" << endl
+ << " - paperRect" << devPaperRect << endl
+ << " - pageRect" << devPageRect << endl
+ << " - stretch_x" << stretch_x << endl
+ << " - stretch_y" << stretch_y << endl
+ << " - origin_x" << origin_x << endl
+ << " - origin_y" << origin_y << endl;
+#endif
+}
+
+void QWin32PrintEnginePrivate::initHDC()
+{
+ Q_ASSERT(hdc);
+
+ HDC display_dc = GetDC(0);
+ dpi_x = GetDeviceCaps(hdc, LOGPIXELSX);
+ dpi_y = GetDeviceCaps(hdc, LOGPIXELSY);
+ dpi_display = GetDeviceCaps(display_dc, LOGPIXELSY);
+ ReleaseDC(0, display_dc);
+ if (dpi_display == 0) {
+ qWarning("QWin32PrintEngine::metric: GetDeviceCaps() failed, "
+ "might be a driver problem");
+ dpi_display = 96; // Reasonable default
+ }
+
+ switch(mode) {
+ case QPrinter::ScreenResolution:
+ resolution = dpi_display;
+ stretch_x = dpi_x / double(dpi_display);
+ stretch_y = dpi_y / double(dpi_display);
+ break;
+ case QPrinter::PrinterResolution:
+ case QPrinter::HighResolution:
+ resolution = dpi_y;
+ stretch_x = 1;
+ stretch_y = 1;
+ break;
+ default:
+ break;
+ }
+
+ initDevRects();
+}
+
+void QWin32PrintEnginePrivate::initDevRects()
+{
+ devPaperRect = QRect(0, 0,
+ GetDeviceCaps(hdc, PHYSICALWIDTH),
+ GetDeviceCaps(hdc, PHYSICALHEIGHT));
+ devPhysicalPageRect = QRect(GetDeviceCaps(hdc, PHYSICALOFFSETX),
+ GetDeviceCaps(hdc, PHYSICALOFFSETY),
+ GetDeviceCaps(hdc, HORZRES),
+ GetDeviceCaps(hdc, VERTRES));
+ if (!pageMarginsSet)
+ devPageRect = devPhysicalPageRect;
+ else
+ devPageRect = devPaperRect.adjusted(qRound(mmToInches(previousDialogMargins.left() / 100.0) * dpi_x),
+ qRound(mmToInches(previousDialogMargins.top() / 100.0) * dpi_y),
+ -qRound(mmToInches(previousDialogMargins.width() / 100.0) * dpi_x),
+ -qRound(mmToInches(previousDialogMargins.height() / 100.0) * dpi_y));
+ updateOrigin();
+}
+
+void QWin32PrintEnginePrivate::setPageMargins(int marginLeft, int marginTop, int marginRight, int marginBottom)
+{
+ pageMarginsSet = true;
+ previousDialogMargins = QRect(marginLeft, marginTop, marginRight, marginBottom);
+
+ devPageRect = devPaperRect.adjusted(qRound(mmToInches(marginLeft / 100.0) * dpi_x),
+ qRound(mmToInches(marginTop / 100.0) * dpi_y),
+ - qRound(mmToInches(marginRight / 100.0) * dpi_x),
+ - qRound(mmToInches(marginBottom / 100.0) * dpi_y));
+ updateOrigin();
+}
+
+QRect QWin32PrintEnginePrivate::getPageMargins() const
+{
+ if (pageMarginsSet)
+ return previousDialogMargins;
+ else
+ return QRect(qRound(inchesToMM(devPhysicalPageRect.left()) * 100.0 / dpi_x),
+ qRound(inchesToMM(devPhysicalPageRect.top()) * 100.0 / dpi_y),
+ qRound(inchesToMM(devPaperRect.right() - devPhysicalPageRect.right()) * 100.0 / dpi_x),
+ qRound(inchesToMM(devPaperRect.bottom() - devPhysicalPageRect.bottom()) * 100.0 / dpi_y));
+}
+
+void QWin32PrintEnginePrivate::release()
+{
+ if (hdc == 0)
+ return;
+
+ if (globalDevMode) { // Devmode comes from print dialog
+ GlobalUnlock(globalDevMode);
+ } else { // Devmode comes from initialize...
+ // devMode is a part of the same memory block as pInfo so one free is enough...
+ GlobalUnlock(hMem);
+ GlobalFree(hMem);
+ }
+ if (hPrinter)
+ ClosePrinter(hPrinter);
+ DeleteDC(hdc);
+
+ hdc = 0;
+ hPrinter = 0;
+ pInfo = 0;
+ hMem = 0;
+ devMode = 0;
+}
+
+QList<QVariant> QWin32PrintEnginePrivate::queryResolutions() const
+{
+ // Read the supported resolutions of the printer.
+ QList<QVariant> list;
+
+ DWORD numRes = DeviceCapabilities(reinterpret_cast<const wchar_t *>(name.utf16()),
+ reinterpret_cast<const wchar_t *>(port.utf16()),
+ DC_ENUMRESOLUTIONS, 0, 0);
+ if (numRes == (DWORD)-1)
+ return list;
+
+ LONG *enumRes = (LONG*)malloc(numRes * 2 * sizeof(LONG));
+ DWORD errRes = DeviceCapabilities(reinterpret_cast<const wchar_t *>(name.utf16()),
+ reinterpret_cast<const wchar_t *>(port.utf16()),
+ DC_ENUMRESOLUTIONS, (LPWSTR)enumRes, 0);
+
+ if (errRes == (DWORD)-1) {
+ qErrnoWarning("QWin32PrintEngine::queryResolutions: DeviceCapabilities failed");
+ return list;
+ }
+
+ for (uint i=0; i<numRes; ++i)
+ list.append(int(enumRes[i * 2]));
+
+ return list;
+}
+
+void QWin32PrintEnginePrivate::doReinit()
+{
+ if (state == QPrinter::Active) {
+ reinit = true;
+ } else {
+ resetDC();
+ initDevRects();
+ reinit = false;
+ }
+}
+
+void QWin32PrintEnginePrivate::updateOrigin()
+{
+ if (fullPage) {
+ // subtract physical margins to make (0,0) absolute top corner of paper
+ // then add user defined margins
+ origin_x = -devPhysicalPageRect.x();
+ origin_y = -devPhysicalPageRect.y();
+ if (pageMarginsSet) {
+ origin_x += devPageRect.left();
+ origin_y += devPageRect.top();
+ }
+ } else {
+ origin_x = 0;
+ origin_y = 0;
+ if (pageMarginsSet) {
+ origin_x = devPageRect.left() - devPhysicalPageRect.x();
+ origin_y = devPageRect.top() - devPhysicalPageRect.y();
+ }
+ }
+}
+
+void QWin32PrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &value)
+{
+ Q_D(QWin32PrintEngine);
+ switch (key) {
+ case PPK_CollateCopies:
+ {
+ if (!d->devMode)
+ break;
+ d->devMode->dmCollate = value.toBool() ? DMCOLLATE_TRUE : DMCOLLATE_FALSE;
+ d->doReinit();
+ }
+ break;
+
+ case PPK_ColorMode:
+ {
+ if (!d->devMode)
+ break;
+ d->devMode->dmColor = (value.toInt() == QPrinter::Color) ? DMCOLOR_COLOR : DMCOLOR_MONOCHROME;
+ d->doReinit();
+ }
+ break;
+
+ case PPK_Creator:
+
+ break;
+
+ case PPK_DocumentName:
+ if (isActive()) {
+ qWarning("QWin32PrintEngine: Cannot change document name while printing is active");
+ return;
+ }
+ d->docName = value.toString();
+ break;
+
+ case PPK_FullPage:
+ d->fullPage = value.toBool();
+ d->updateOrigin();
+ break;
+
+ case PPK_CopyCount: // fallthrough
+ case PPK_NumberOfCopies:
+ if (!d->devMode)
+ break;
+ d->num_copies = value.toInt();
+ d->devMode->dmCopies = d->num_copies;
+ d->doReinit();
+ break;
+
+ case PPK_Orientation:
+ {
+ if (!d->devMode)
+ break;
+ int orientation = value.toInt() == QPrinter::Landscape ? DMORIENT_LANDSCAPE : DMORIENT_PORTRAIT;
+ int old_orientation = d->devMode->dmOrientation;
+ d->devMode->dmOrientation = orientation;
+ if (d->has_custom_paper_size && old_orientation != orientation)
+ d->paper_size = QSizeF(d->paper_size.height(), d->paper_size.width());
+ d->doReinit();
+ }
+ break;
+
+ case PPK_OutputFileName:
+ if (isActive()) {
+ qWarning("QWin32PrintEngine: Cannot change filename while printing");
+ } else {
+ d->fileName = value.toString();
+ d->printToFile = !value.toString().isEmpty();
+ }
+ break;
+
+ case PPK_PaperSize:
+ if (!d->devMode)
+ break;
+ d->devMode->dmPaperSize = mapPaperSizeDevmode(QPrinter::PaperSize(value.toInt()));
+ d->has_custom_paper_size = (QPrinter::PaperSize(value.toInt()) == QPrinter::Custom);
+ d->doReinit();
+ break;
+
+ case PPK_PaperSource:
+ {
+ if (!d->devMode)
+ break;
+ int dmMapped = DMBIN_AUTO;
+
+ QList<QVariant> v = property(PPK_PaperSources).toList();
+ if (v.contains(value))
+ dmMapped = mapPaperSourceDevmode(QPrinter::PaperSource(value.toInt()));
+
+ d->devMode->dmDefaultSource = dmMapped;
+ d->doReinit();
+ }
+ break;
+
+ case PPK_PrinterName:
+ d->name = value.toString();
+ if(d->name.isEmpty())
+ d->queryDefault();
+ d->initialize();
+ break;
+
+ case PPK_Resolution:
+ {
+ d->resolution = value.toInt();
+
+ d->stretch_x = d->dpi_x / double(d->resolution);
+ d->stretch_y = d->dpi_y / double(d->resolution);
+ }
+ break;
+
+ case PPK_SelectionOption:
+
+ break;
+
+ case PPK_SupportedResolutions:
+
+ break;
+
+
+ case PPK_WindowsPageSize:
+ if (!d->devMode)
+ break;
+ d->has_custom_paper_size = false;
+ d->devMode->dmPaperSize = value.toInt();
+ d->doReinit();
+ break;
+
+ case PPK_CustomPaperSize:
+ {
+ d->has_custom_paper_size = true;
+ d->paper_size = value.toSizeF();
+ if (!d->devMode)
+ break;
+ int orientation = d->devMode->dmOrientation;
+ DWORD needed = 0;
+ DWORD returned = 0;
+ if (!EnumForms(d->hPrinter, 1, 0, 0, &needed, &returned)) {
+ BYTE *forms = (BYTE *) malloc(needed);
+ if (EnumForms(d->hPrinter, 1, forms, needed, &needed, &returned)) {
+ for (DWORD i=0; i< returned; ++i) {
+ FORM_INFO_1 *formArray = reinterpret_cast<FORM_INFO_1 *>(forms);
+ // the form sizes are specified in 1000th of a mm,
+ // convert the size to Points
+ QSizeF size((formArray[i].Size.cx * 72/25.4)/1000.0,
+ (formArray[i].Size.cy * 72/25.4)/1000.0);
+ if (qAbs(d->paper_size.width() - size.width()) <= 2
+ && qAbs(d->paper_size.height() - size.height()) <= 2)
+ {
+ d->devMode->dmPaperSize = i + 1;
+ break;
+ }
+ }
+ }
+ free(forms);
+ }
+ if (orientation != DMORIENT_PORTRAIT)
+ d->paper_size = QSizeF(d->paper_size.height(), d->paper_size.width());
+ break;
+ }
+
+ case PPK_PageMargins:
+ {
+ QList<QVariant> margins(value.toList());
+ Q_ASSERT(margins.size() == 4);
+ int left, top, right, bottom;
+ // specified in 1/100 mm
+ left = (margins.at(0).toReal()*25.4/72.0) * 100;
+ top = (margins.at(1).toReal()*25.4/72.0) * 100;
+ right = (margins.at(2).toReal()*25.4/72.0) * 100;
+ bottom = (margins.at(3).toReal()*25.4/72.0) * 100;
+ d->setPageMargins(left, top, right, bottom);
+ break;
+ }
+ default:
+ // Do nothing
+ break;
+ }
+}
+
+QVariant QWin32PrintEngine::property(PrintEnginePropertyKey key) const
+{
+ Q_D(const QWin32PrintEngine);
+ QVariant value;
+ switch (key) {
+
+ case PPK_CollateCopies:
+ value = false;
+ break;
+
+ case PPK_ColorMode:
+ {
+ if (!d->devMode) {
+ value = QPrinter::Color;
+ } else {
+ value = (d->devMode->dmColor == DMCOLOR_COLOR) ? QPrinter::Color : QPrinter::GrayScale;
+ }
+ }
+ break;
+
+ case PPK_DocumentName:
+ value = d->docName;
+ break;
+
+ case PPK_FullPage:
+ value = d->fullPage;
+ break;
+
+ case PPK_CopyCount:
+ value = d->num_copies;
+ break;
+
+ case PPK_SupportsMultipleCopies:
+ value = true;
+ break;
+
+ case PPK_NumberOfCopies:
+ value = 1;
+ break;
+
+ case PPK_Orientation:
+ {
+ if (!d->devMode) {
+ value = QPrinter::Portrait;
+ } else {
+ value = (d->devMode->dmOrientation == DMORIENT_LANDSCAPE) ? QPrinter::Landscape : QPrinter::Portrait;
+ }
+ }
+ break;
+
+ case PPK_OutputFileName:
+ value = d->fileName;
+ break;
+
+ case PPK_PageRect:
+ if (d->has_custom_paper_size) {
+ QRect rect(0, 0,
+ qRound(d->paper_size.width() * d->resolution / 72.0),
+ qRound(d->paper_size.height() * d->resolution / 72.0));
+ if (d->pageMarginsSet) {
+ rect = rect.adjusted(qRound(mmToInches(d->previousDialogMargins.left()/100.0) * d->resolution),
+ qRound(mmToInches(d->previousDialogMargins.top()/100.0) * d->resolution),
+ -qRound(mmToInches(d->previousDialogMargins.width()/100.0) * d->resolution),
+ -qRound(mmToInches(d->previousDialogMargins.height()/100.0) * d->resolution));
+ }
+ value = rect;
+ } else {
+ value = QTransform(1/d->stretch_x, 0, 0, 1/d->stretch_y, 0, 0)
+ .mapRect(d->fullPage ? d->devPhysicalPageRect : d->devPageRect);
+ }
+ break;
+
+ case PPK_PaperSize:
+ if (d->has_custom_paper_size) {
+ value = QPrinter::Custom;
+ } else {
+ if (!d->devMode) {
+ value = QPrinter::A4;
+ } else {
+ value = mapDevmodePaperSize(d->devMode->dmPaperSize);
+ }
+ }
+ break;
+
+ case PPK_PaperRect:
+ if (d->has_custom_paper_size) {
+ value = QRect(0, 0,
+ qRound(d->paper_size.width() * d->resolution / 72.0),
+ qRound(d->paper_size.height() * d->resolution / 72.0));
+ } else {
+ value = QTransform(1/d->stretch_x, 0, 0, 1/d->stretch_y, 0, 0).mapRect(d->devPaperRect);
+ }
+ break;
+
+ case PPK_PaperSource:
+ if (!d->devMode) {
+ value = QPrinter::Auto;
+ } else {
+ value = mapDevmodePaperSource(d->devMode->dmDefaultSource);
+ }
+ break;
+
+ case PPK_PrinterName:
+ value = d->name;
+ break;
+
+ case PPK_Resolution:
+ if (d->resolution || !d->name.isEmpty())
+ value = d->resolution;
+ break;
+
+ case PPK_SupportedResolutions:
+ value = d->queryResolutions();
+ break;
+
+ case PPK_WindowsPageSize:
+ if (!d->devMode) {
+ value = -1;
+ } else {
+ value = d->devMode->dmPaperSize;
+ }
+ break;
+
+ case PPK_PaperSources:
+ {
+ int available = DeviceCapabilities((const wchar_t *)d->name.utf16(),
+ (const wchar_t *)d->port.utf16(), DC_BINS, 0, d->devMode);
+
+ if (available <= 0)
+ break;
+
+ wchar_t *data = new wchar_t[available];
+ int count = DeviceCapabilities((const wchar_t *)d->name.utf16(),
+ (const wchar_t *)d->port.utf16(), DC_BINS, data, d->devMode);
+
+ QList<QVariant> out;
+ for (int i=0; i<count; ++i) {
+ QPrinter::PaperSource src = mapDevmodePaperSource(data[i]);
+ if (src != -1)
+ out << (int) src;
+ }
+ value = out;
+
+ delete [] data;
+ }
+ break;
+
+ case PPK_CustomPaperSize:
+ value = d->paper_size;
+ break;
+
+ case PPK_PageMargins:
+ {
+ QList<QVariant> margins;
+ QRect pageMargins(d->getPageMargins());
+
+ // specified in 1/100 mm
+ margins << (mmToInches(pageMargins.left()/100.0) * 72)
+ << (mmToInches(pageMargins.top()/100.0) * 72)
+ << (mmToInches(pageMargins.width()/100.0) * 72)
+ << (mmToInches(pageMargins.height()/100.0) * 72);
+ value = margins;
+ break;
+ }
+ default:
+ // Do nothing
+ break;
+ }
+ return value;
+}
+
+QPrinter::PrinterState QWin32PrintEngine::printerState() const
+{
+ return d_func()->state;
+}
+
+HDC QWin32PrintEngine::getDC() const
+{
+ return d_func()->hdc;
+}
+
+void QWin32PrintEngine::releaseDC(HDC) const
+{
+
+}
+
+HGLOBAL *QWin32PrintEnginePrivate::createDevNames()
+{
+ int size = sizeof(DEVNAMES)
+ + program.length() * 2 + 2
+ + name.length() * 2 + 2
+ + port.length() * 2 + 2;
+ HGLOBAL *hGlobal = (HGLOBAL *) GlobalAlloc(GMEM_MOVEABLE, size);
+ DEVNAMES *dn = (DEVNAMES*) GlobalLock(hGlobal);
+
+ dn->wDriverOffset = sizeof(DEVNAMES) / sizeof(wchar_t);
+ dn->wDeviceOffset = dn->wDriverOffset + program.length() + 1;
+ dn->wOutputOffset = dn->wDeviceOffset + name.length() + 1;
+
+ memcpy((ushort*)dn + dn->wDriverOffset, program.utf16(), program.length() * 2 + 2);
+ memcpy((ushort*)dn + dn->wDeviceOffset, name.utf16(), name.length() * 2 + 2);
+ memcpy((ushort*)dn + dn->wOutputOffset, port.utf16(), port.length() * 2 + 2);
+ dn->wDefault = 0;
+
+ GlobalUnlock(hGlobal);
+
+// printf("QPrintDialogWinPrivate::createDevNames()\n"
+// " -> wDriverOffset: %d\n"
+// " -> wDeviceOffset: %d\n"
+// " -> wOutputOffset: %d\n",
+// dn->wDriverOffset,
+// dn->wDeviceOffset,
+// dn->wOutputOffset);
+
+// printf("QPrintDialogWinPrivate::createDevNames(): %s, %s, %s\n",
+// QString::fromWCharArray((wchar_t*)(dn) + dn->wDriverOffset).latin1(),
+// QString::fromWCharArray((wchar_t*)(dn) + dn->wDeviceOffset).latin1(),
+// QString::fromWCharArray((wchar_t*)(dn) + dn->wOutputOffset).latin1());
+
+ return hGlobal;
+}
+
+void QWin32PrintEnginePrivate::readDevnames(HGLOBAL globalDevnames)
+{
+ if (globalDevnames) {
+ DEVNAMES *dn = (DEVNAMES*) GlobalLock(globalDevnames);
+ name = QString::fromWCharArray((wchar_t*)(dn) + dn->wDeviceOffset);
+ port = QString::fromWCharArray((wchar_t*)(dn) + dn->wOutputOffset);
+ program = QString::fromWCharArray((wchar_t*)(dn) + dn->wDriverOffset);
+ GlobalUnlock(globalDevnames);
+ }
+}
+
+void QWin32PrintEnginePrivate::readDevmode(HGLOBAL globalDevmode)
+{
+ if (globalDevmode) {
+ DEVMODE *dm = (DEVMODE*) GlobalLock(globalDevmode);
+ release();
+ globalDevMode = globalDevmode;
+ devMode = dm;
+ hdc = CreateDC(reinterpret_cast<const wchar_t *>(program.utf16()),
+ reinterpret_cast<const wchar_t *>(name.utf16()), 0, dm);
+
+ num_copies = devMode->dmCopies;
+ if (!OpenPrinter((wchar_t*)name.utf16(), &hPrinter, 0))
+ qWarning("QPrinter: OpenPrinter() failed after reading DEVMODE.");
+ }
+
+ if (hdc)
+ initHDC();
+}
+
+static void draw_text_item_win(const QPointF &pos, const QTextItemInt &ti, HDC hdc,
+ bool convertToText, const QTransform &xform, const QPointF &topLeft)
+{
+ QFontEngine *fe = ti.fontEngine;
+ QPointF baseline_pos = xform.inverted().map(xform.map(pos) - topLeft);
+
+ SetTextAlign(hdc, TA_BASELINE);
+ SetBkMode(hdc, TRANSPARENT);
+
+ bool has_kerning = ti.f && ti.f->kerning();
+ QFontEngineWin *winfe = (fe->type() == QFontEngine::Win) ? static_cast<QFontEngineWin *>(fe) : 0;
+
+ HFONT hfont;
+ bool ttf = false;
+
+ if (winfe) {
+ hfont = winfe->hfont;
+ ttf = winfe->ttf;
+ } else {
+ hfont = (HFONT)GetStockObject(ANSI_VAR_FONT);
+ }
+
+ HGDIOBJ old_font = SelectObject(hdc, hfont);
+ unsigned int options = (ttf && !convertToText) ? ETO_GLYPH_INDEX : 0;
+ wchar_t *convertedGlyphs = (wchar_t *)ti.chars;
+ QGlyphLayout glyphs = ti.glyphs;
+
+ bool fast = !has_kerning && !(ti.flags & QTextItem::RightToLeft);
+ for (int i = 0; fast && i < glyphs.numGlyphs; i++) {
+ if (glyphs.offsets[i].x != 0 || glyphs.offsets[i].y != 0 || glyphs.justifications[i].space_18d6 != 0
+ || glyphs.attributes[i].dontPrint) {
+ fast = false;
+ break;
+ }
+ }
+
+#if !defined(Q_OS_WINCE)
+ // Scale, rotate and translate here.
+ XFORM win_xform;
+ win_xform.eM11 = xform.m11();
+ win_xform.eM12 = xform.m12();
+ win_xform.eM21 = xform.m21();
+ win_xform.eM22 = xform.m22();
+ win_xform.eDx = xform.dx();
+ win_xform.eDy = xform.dy();
+
+ SetGraphicsMode(hdc, GM_ADVANCED);
+ SetWorldTransform(hdc, &win_xform);
+#endif
+
+ if (fast) {
+ // fast path
+ QVarLengthArray<wchar_t> g(glyphs.numGlyphs);
+ for (int i = 0; i < glyphs.numGlyphs; ++i)
+ g[i] = glyphs.glyphs[i];
+ ExtTextOut(hdc,
+ qRound(baseline_pos.x() + glyphs.offsets[0].x.toReal()),
+ qRound(baseline_pos.y() + glyphs.offsets[0].y.toReal()),
+ options, 0, convertToText ? convertedGlyphs : g.data(), glyphs.numGlyphs, 0);
+ } else {
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> _glyphs;
+
+ QTransform matrix = QTransform::fromTranslate(baseline_pos.x(), baseline_pos.y());
+ ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags,
+ _glyphs, positions);
+ if (_glyphs.size() == 0) {
+ SelectObject(hdc, old_font);
+ return;
+ }
+
+ convertToText = convertToText && glyphs.numGlyphs == _glyphs.size();
+ bool outputEntireItem = _glyphs.size() > 0;
+
+ if (outputEntireItem) {
+ options |= ETO_PDY;
+ QVarLengthArray<INT> glyphDistances(_glyphs.size() * 2);
+ QVarLengthArray<wchar_t> g(_glyphs.size());
+ for (int i=0; i<_glyphs.size() - 1; ++i) {
+ glyphDistances[i * 2] = qRound(positions[i + 1].x) - qRound(positions[i].x);
+ glyphDistances[i * 2 + 1] = qRound(positions[i + 1].y) - qRound(positions[i].y);
+ g[i] = _glyphs[i];
+ }
+ glyphDistances[(_glyphs.size() - 1) * 2] = 0;
+ glyphDistances[(_glyphs.size() - 1) * 2 + 1] = 0;
+ g[_glyphs.size() - 1] = _glyphs[_glyphs.size() - 1];
+ ExtTextOut(hdc, qRound(positions[0].x), qRound(positions[0].y), options, 0,
+ convertToText ? convertedGlyphs : g.data(), _glyphs.size(),
+ glyphDistances.data());
+ } else {
+ int i = 0;
+ while(i < _glyphs.size()) {
+ wchar_t g = _glyphs[i];
+
+ ExtTextOut(hdc, qRound(positions[i].x),
+ qRound(positions[i].y), options, 0,
+ convertToText ? convertedGlyphs + i : &g, 1, 0);
+ ++i;
+ }
+ }
+ }
+
+#if !defined(Q_OS_WINCE)
+ win_xform.eM11 = win_xform.eM22 = 1.0;
+ win_xform.eM12 = win_xform.eM21 = win_xform.eDx = win_xform.eDy = 0.0;
+ SetWorldTransform(hdc, &win_xform);
+#endif
+
+ SelectObject(hdc, old_font);
+}
+
+
+void QWin32PrintEnginePrivate::updateCustomPaperSize()
+{
+ uint paperSize = devMode->dmPaperSize;
+ if (paperSize > 0 && mapDevmodePaperSize(paperSize) == QPrinter::Custom) {
+ has_custom_paper_size = true;
+ DWORD needed = 0;
+ DWORD returned = 0;
+ if (!EnumForms(hPrinter, 1, 0, 0, &needed, &returned)) {
+ BYTE *forms = (BYTE *) malloc(needed);
+ if (EnumForms(hPrinter, 1, forms, needed, &needed, &returned)) {
+ if (paperSize <= returned) {
+ FORM_INFO_1 *formArray = (FORM_INFO_1 *) forms;
+ int width = formArray[paperSize - 1].Size.cx; // 1/1000 of a mm
+ int height = formArray[paperSize - 1].Size.cy; // 1/1000 of a mm
+ paper_size = QSizeF((width * 72 /25.4) / 1000.0, (height * 72 / 25.4) / 1000.0);
+ } else {
+ has_custom_paper_size = false;
+ }
+ }
+ free(forms);
+ }
+ } else {
+ has_custom_paper_size = false;
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
diff --git a/src/gui/painting/qprintengine_win_p.h b/src/gui/painting/qprintengine_win_p.h
new file mode 100644
index 0000000000..b4d0670e7b
--- /dev/null
+++ b/src/gui/painting/qprintengine_win_p.h
@@ -0,0 +1,261 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPRINTENGINE_WIN_P_H
+#define QPRINTENGINE_WIN_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef QT_NO_PRINTER
+
+#include "QtGui/qprinter.h"
+#include "QtGui/qprintengine.h"
+#include "QtGui/qpaintengine.h"
+#include "QtCore/qt_windows.h"
+#include "private/qpaintengine_alpha_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QWin32PrintEnginePrivate;
+class QPrinterPrivate;
+class QPainterState;
+
+class QWin32PrintEngine : public QAlphaPaintEngine, public QPrintEngine
+{
+ Q_DECLARE_PRIVATE(QWin32PrintEngine)
+public:
+ QWin32PrintEngine(QPrinter::PrinterMode mode);
+
+ // override QWin32PaintEngine
+ bool begin(QPaintDevice *dev);
+ bool end();
+
+ void updateState(const QPaintEngineState &state);
+
+ void updateMatrix(const QTransform &matrix);
+ void updateClipPath(const QPainterPath &clip, Qt::ClipOperation op);
+
+ void drawPath(const QPainterPath &path);
+ void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode);
+ void drawTextItem(const QPointF &p, const QTextItem &textItem);
+
+ void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
+ void drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &p);
+ void setProperty(PrintEnginePropertyKey key, const QVariant &value);
+ QVariant property(PrintEnginePropertyKey key) const;
+
+ bool newPage();
+ bool abort();
+ int metric(QPaintDevice::PaintDeviceMetric) const;
+
+ QPrinter::PrinterState printerState() const;
+
+ QPaintEngine::Type type() const { return Windows; }
+
+ HDC getDC() const;
+ void releaseDC(HDC) const;
+
+ HDC getPrinterDC() const { return getDC(); }
+ void releasePrinterDC(HDC dc) const { releaseDC(dc); }
+
+private:
+ friend class QPrintDialog;
+ friend class QPageSetupDialog;
+};
+
+class QWin32PrintEnginePrivate : public QAlphaPaintEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QWin32PrintEngine)
+public:
+ QWin32PrintEnginePrivate() :
+ hPrinter(0),
+ globalDevMode(0),
+ devMode(0),
+ pInfo(0),
+ hdc(0),
+ mode(QPrinter::ScreenResolution),
+ state(QPrinter::Idle),
+ resolution(0),
+ pageMarginsSet(false),
+ num_copies(1),
+ printToFile(false),
+ fullPage(false),
+ reinit(false),
+ has_custom_paper_size(false)
+ {
+ }
+
+ ~QWin32PrintEnginePrivate();
+
+
+ /* Reads the default printer name and its driver (printerProgram) into
+ the engines private data. */
+ void queryDefault();
+
+ /* Initializes the printer data based on the current printer name. This
+ function creates a DEVMODE struct, HDC and a printer handle. If these
+ structures are already in use, they are freed using release
+ */
+ void initialize();
+
+ /* Initializes data in the print engine whenever the HDC has been renewed
+ */
+ void initHDC();
+
+ /* Releases all the handles the printer currently holds, HDC, DEVMODE,
+ etc and resets the corresponding members to 0. */
+ void release();
+
+ /* Queries the resolutions for the current printer, and returns them
+ in a list. */
+ QList<QVariant> queryResolutions() const;
+
+ /* Resets the DC with changes in devmode. If the printer is active
+ this function only sets the reinit variable to true so it
+ is handled in the next begin or newpage. */
+ void doReinit();
+
+ /* Used by print/page setup dialogs */
+ HGLOBAL *createDevNames();
+
+ void readDevmode(HGLOBAL globalDevmode);
+ void readDevnames(HGLOBAL globalDevnames);
+
+ inline bool resetDC() {
+ hdc = ResetDC(hdc, devMode);
+ return hdc != 0;
+ }
+
+ void strokePath(const QPainterPath &path, const QColor &color);
+ void fillPath(const QPainterPath &path, const QColor &color);
+
+ void composeGdiPath(const QPainterPath &path);
+ void fillPath_dev(const QPainterPath &path, const QColor &color);
+ void strokePath_dev(const QPainterPath &path, const QColor &color, qreal width);
+
+ void updateOrigin();
+
+ void initDevRects();
+ void setPageMargins(int margin_left, int margin_top, int margin_right, int margin_bottom);
+ QRect getPageMargins() const;
+ void updateCustomPaperSize();
+
+ // Windows GDI printer references.
+ HANDLE hPrinter;
+
+ HGLOBAL globalDevMode;
+ DEVMODE *devMode;
+ PRINTER_INFO_2 *pInfo;
+ HGLOBAL hMem;
+
+ HDC hdc;
+
+ QPrinter::PrinterMode mode;
+
+ // Printer info
+ QString name;
+ QString program;
+ QString port;
+
+ // Document info
+ QString docName;
+ QString fileName;
+
+ QPrinter::PrinterState state;
+ int resolution;
+
+ // This QRect is used to store the exact values
+ // entered into the PageSetup Dialog because those are
+ // entered in mm but are since converted to device coordinates.
+ // If they were to be converted back when displaying the dialog
+ // again, there would be inaccuracies so when the user entered 10
+ // it may show up as 9.99 the next time the dialog is opened.
+ // We don't want that confusion.
+ QRect previousDialogMargins;
+
+ bool pageMarginsSet;
+ QRect devPageRect;
+ QRect devPhysicalPageRect;
+ QRect devPaperRect;
+ qreal stretch_x;
+ qreal stretch_y;
+ int origin_x;
+ int origin_y;
+
+ int dpi_x;
+ int dpi_y;
+ int dpi_display;
+ int num_copies;
+
+ uint printToFile : 1;
+ uint fullPage : 1;
+ uint reinit : 1;
+
+ uint complex_xform : 1;
+ uint has_pen : 1;
+ uint has_brush : 1;
+ uint has_custom_paper_size : 1;
+
+ uint txop;
+
+ QColor brush_color;
+ QPen pen;
+ QColor pen_color;
+ QSizeF paper_size;
+
+ QTransform painterMatrix;
+ QTransform matrix;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
+
+#endif // QPRINTENGINE_WIN_P_H
diff --git a/src/gui/painting/qprinter.cpp b/src/gui/painting/qprinter.cpp
new file mode 100644
index 0000000000..5111bf4e2d
--- /dev/null
+++ b/src/gui/painting/qprinter.cpp
@@ -0,0 +1,2453 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qprinter_p.h"
+#include "qprinter.h"
+#include "qprintengine.h"
+#include "qprinterinfo.h"
+#include "qlist.h"
+#include <qpagesetupdialog.h>
+#include <qapplication.h>
+#include <qfileinfo.h>
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+#include "private/qcups_p.h"
+#endif
+
+#ifndef QT_NO_PRINTER
+
+#if defined (Q_WS_WIN)
+#include <private/qprintengine_win_p.h>
+#elif defined (Q_WS_MAC)
+#include <private/qprintengine_mac_p.h>
+#elif defined (QTOPIA_PRINTENGINE)
+#include <private/qprintengine_qws_p.h>
+#endif
+#include <private/qprintengine_ps_p.h>
+
+#if defined(Q_WS_X11)
+#include <private/qt_x11_p.h>
+#endif
+
+#ifndef QT_NO_PDF
+#include "qprintengine_pdf_p.h"
+#endif
+
+#include <qpicture.h>
+#include <private/qpaintengine_preview_p.h>
+
+#if defined(QT3_SUPPORT)
+# include "qprintdialog.h"
+#endif // QT3_SUPPORT
+
+QT_BEGIN_NAMESPACE
+
+#define ABORT_IF_ACTIVE(location) \
+ if (d->printEngine->printerState() == QPrinter::Active) { \
+ qWarning("%s: Cannot be changed while printer is active", location); \
+ return; \
+ }
+
+// NB! This table needs to be in sync with QPrinter::PaperSize
+static const float qt_paperSizes[][2] = {
+ {210, 297}, // A4
+ {176, 250}, // B5
+ {215.9f, 279.4f}, // Letter
+ {215.9f, 355.6f}, // Legal
+ {190.5f, 254}, // Executive
+ {841, 1189}, // A0
+ {594, 841}, // A1
+ {420, 594}, // A2
+ {297, 420}, // A3
+ {148, 210}, // A5
+ {105, 148}, // A6
+ {74, 105}, // A7
+ {52, 74}, // A8
+ {37, 52}, // A8
+ {1000, 1414}, // B0
+ {707, 1000}, // B1
+ {31, 44}, // B10
+ {500, 707}, // B2
+ {353, 500}, // B3
+ {250, 353}, // B4
+ {125, 176}, // B6
+ {88, 125}, // B7
+ {62, 88}, // B8
+ {33, 62}, // B9
+ {163, 229}, // C5E
+ {105, 241}, // US Common
+ {110, 220}, // DLE
+ {210, 330}, // Folio
+ {431.8f, 279.4f}, // Ledger
+ {279.4f, 431.8f} // Tabloid
+};
+
+/// return the multiplier of converting from the unit value to postscript-points.
+double qt_multiplierForUnit(QPrinter::Unit unit, int resolution)
+{
+ switch(unit) {
+ case QPrinter::Millimeter:
+ return 2.83464566929;
+ case QPrinter::Point:
+ return 1.0;
+ case QPrinter::Inch:
+ return 72.0;
+ case QPrinter::Pica:
+ return 12;
+ case QPrinter::Didot:
+ return 1.065826771;
+ case QPrinter::Cicero:
+ return 12.789921252;
+ case QPrinter::DevicePixel:
+ return 72.0/resolution;
+ }
+ return 1.0;
+}
+
+// not static: it's needed in qpagesetupdialog_unix.cpp
+QSizeF qt_printerPaperSize(QPrinter::Orientation orientation,
+ QPrinter::PaperSize paperSize,
+ QPrinter::Unit unit,
+ int resolution)
+{
+ int width_index = 0;
+ int height_index = 1;
+ if (orientation == QPrinter::Landscape) {
+ width_index = 1;
+ height_index = 0;
+ }
+ const qreal multiplier = qt_multiplierForUnit(unit, resolution);
+ return QSizeF((qt_paperSizes[paperSize][width_index] * 72 / 25.4) / multiplier,
+ (qt_paperSizes[paperSize][height_index] * 72 / 25.4) / multiplier);
+}
+
+void QPrinterPrivate::createDefaultEngines()
+{
+ QPrinter::OutputFormat realOutputFormat = outputFormat;
+#if !defined (QTOPIA_PRINTENGINE)
+#if defined (Q_OS_UNIX) && ! defined (Q_WS_MAC)
+ if(outputFormat == QPrinter::NativeFormat) {
+ realOutputFormat = QPrinter::PostScriptFormat;
+ }
+#endif
+#endif
+
+ switch (realOutputFormat) {
+ case QPrinter::NativeFormat: {
+#if defined (Q_WS_WIN)
+ QWin32PrintEngine *winEngine = new QWin32PrintEngine(printerMode);
+ paintEngine = winEngine;
+ printEngine = winEngine;
+#elif defined (Q_WS_MAC)
+ QMacPrintEngine *macEngine = new QMacPrintEngine(printerMode);
+ paintEngine = macEngine;
+ printEngine = macEngine;
+#elif defined (QTOPIA_PRINTENGINE)
+ QtopiaPrintEngine *qwsEngine = new QtopiaPrintEngine(printerMode);
+ paintEngine = qwsEngine;
+ printEngine = qwsEngine;
+#elif defined (Q_OS_UNIX)
+ Q_ASSERT(false);
+#endif
+ }
+ break;
+ case QPrinter::PdfFormat: {
+ QPdfEngine *pdfEngine = new QPdfEngine(printerMode);
+ paintEngine = pdfEngine;
+ printEngine = pdfEngine;
+ }
+ break;
+ case QPrinter::PostScriptFormat: {
+ QPSPrintEngine *psEngine = new QPSPrintEngine(printerMode);
+ paintEngine = psEngine;
+ printEngine = psEngine;
+ }
+ break;
+ }
+ use_default_engine = true;
+ had_default_engines = true;
+}
+
+#ifndef QT_NO_PRINTPREVIEWWIDGET
+QList<const QPicture *> QPrinterPrivate::previewPages() const
+{
+ if (previewEngine)
+ return previewEngine->pages();
+ return QList<const QPicture *>();
+}
+
+void QPrinterPrivate::setPreviewMode(bool enable)
+{
+ Q_Q(QPrinter);
+ if (enable) {
+ if (!previewEngine)
+ previewEngine = new QPreviewPaintEngine;
+ had_default_engines = use_default_engine;
+ use_default_engine = false;
+ realPrintEngine = printEngine;
+ realPaintEngine = paintEngine;
+ q->setEngines(previewEngine, previewEngine);
+ previewEngine->setProxyEngines(realPrintEngine, realPaintEngine);
+ } else {
+ q->setEngines(realPrintEngine, realPaintEngine);
+ use_default_engine = had_default_engines;
+ }
+}
+#endif // QT_NO_PRINTPREVIEWWIDGET
+
+void QPrinterPrivate::addToManualSetList(QPrintEngine::PrintEnginePropertyKey key)
+{
+ for (int c = 0; c < manualSetList.size(); ++c) {
+ if (manualSetList[c] == key) return;
+ }
+ manualSetList.append(key);
+}
+
+
+/*!
+ \class QPrinter
+ \reentrant
+
+ \brief The QPrinter class is a paint device that paints on a printer.
+
+ \ingroup printing
+
+
+ This device represents a series of pages of printed output, and is
+ used in almost exactly the same way as other paint devices such as
+ QWidget and QPixmap.
+ A set of additional functions are provided to manage device-specific
+ features, such as orientation and resolution, and to step through
+ the pages in a document as it is generated.
+
+ When printing directly to a printer on Windows or Mac OS X, QPrinter uses
+ the built-in printer drivers. On X11, QPrinter uses the
+ \l{Common Unix Printing System (CUPS)} or the standard Unix \l lpr utility
+ to send PostScript or PDF output to the printer. As an alternative,
+ the printProgram() function can be used to specify the command or utility
+ to use instead of the system default.
+
+ Note that setting parameters like paper size and resolution on an
+ invalid printer is undefined. You can use QPrinter::isValid() to
+ verify this before changing any parameters.
+
+ QPrinter supports a number of parameters, most of which can be
+ changed by the end user through a \l{QPrintDialog}{print dialog}. In
+ general, QPrinter passes these functions onto the underlying QPrintEngine.
+
+ The most important parameters are:
+ \list
+ \i setOrientation() tells QPrinter which page orientation to use.
+ \i setPaperSize() tells QPrinter what paper size to expect from the
+ printer.
+ \i setResolution() tells QPrinter what resolution you wish the
+ printer to provide, in dots per inch (DPI).
+ \i setFullPage() tells QPrinter whether you want to deal with the
+ full page or just with the part the printer can draw on.
+ \i setCopyCount() tells QPrinter how many copies of the document
+ it should print.
+ \endlist
+
+ Many of these functions can only be called before the actual printing
+ begins (i.e., before QPainter::begin() is called). This usually makes
+ sense because, for example, it's not possible to change the number of
+ copies when you are halfway through printing. There are also some
+ settings that the user sets (through the printer dialog) and that
+ applications are expected to obey. See QAbstractPrintDialog's
+ documentation for more details.
+
+ When QPainter::begin() is called, the QPrinter it operates on is prepared for
+ a new page, enabling the QPainter to be used immediately to paint the first
+ page in a document. Once the first page has been painted, newPage() can be
+ called to request a new blank page to paint on, or QPainter::end() can be
+ called to finish printing. The second page and all following pages are
+ prepared using a call to newPage() before they are painted.
+
+ The first page in a document does not need to be preceded by a call to
+ newPage(). You only need to calling newPage() after QPainter::begin() if you
+ need to insert a blank page at the beginning of a printed document.
+ Similarly, calling newPage() after the last page in a document is painted will
+ result in a trailing blank page appended to the end of the printed document.
+
+ If you want to abort the print job, abort() will try its best to
+ stop printing. It may cancel the entire job or just part of it.
+
+ Since QPrinter can print to any QPrintEngine subclass, it is possible to
+ extend printing support to cover new types of printing subsystem by
+ subclassing QPrintEngine and reimplementing its interface.
+
+ \sa QPrintDialog, {Printing with Qt}
+*/
+
+/*!
+ \enum QPrinter::PrinterState
+
+ \value Idle
+ \value Active
+ \value Aborted
+ \value Error
+*/
+
+/*!
+ \enum QPrinter::PrinterMode
+
+ This enum describes the mode the printer should work in. It
+ basically presets a certain resolution and working mode.
+
+ \value ScreenResolution Sets the resolution of the print device to
+ the screen resolution. This has the big advantage that the results
+ obtained when painting on the printer will match more or less
+ exactly the visible output on the screen. It is the easiest to
+ use, as font metrics on the screen and on the printer are the
+ same. This is the default value. ScreenResolution will produce a
+ lower quality output than HighResolution and should only be used
+ for drafts.
+
+ \value PrinterResolution This value is deprecated. Is is
+ equivalent to ScreenResolution on Unix and HighResolution on
+ Windows and Mac. Due do the difference between ScreenResolution
+ and HighResolution, use of this value may lead to non-portable
+ printer code.
+
+ \value HighResolution On Windows, sets the printer resolution to that
+ defined for the printer in use. For PostScript printing, sets the
+ resolution of the PostScript driver to 1200 dpi.
+
+ \note When rendering text on a QPrinter device, it is important
+ to realize that the size of text, when specified in points, is
+ independent of the resolution specified for the device itself.
+ Therefore, it may be useful to specify the font size in pixels
+ when combining text with graphics to ensure that their relative
+ sizes are what you expect.
+*/
+
+/*!
+ \enum QPrinter::Orientation
+
+ This enum type (not to be confused with \c Orientation) is used
+ to specify each page's orientation.
+
+ \value Portrait the page's height is greater than its width.
+
+ \value Landscape the page's width is greater than its height.
+
+ This type interacts with \l QPrinter::PaperSize and
+ QPrinter::setFullPage() to determine the final size of the page
+ available to the application.
+*/
+
+
+/*!
+ \enum QPrinter::PrintRange
+
+ Used to specify the print range selection option.
+
+ \value AllPages All pages should be printed.
+ \value Selection Only the selection should be printed.
+ \value PageRange The specified page range should be printed.
+ \value CurrentPage Only the current page should be printed.
+
+ \sa QAbstractPrintDialog::PrintRange
+*/
+
+/*!
+ \enum QPrinter::PrinterOption
+ \compat
+
+ Use QAbstractPrintDialog::PrintDialogOption instead.
+
+ \value PrintToFile
+ \value PrintSelection
+ \value PrintPageRange
+*/
+
+/*!
+ \enum QPrinter::PageSize
+
+ \obsolete
+ Use QPrinter::PaperSize instead.
+
+ \value A0 841 x 1189 mm
+ \value A1 594 x 841 mm
+ \value A2 420 x 594 mm
+ \value A3 297 x 420 mm
+ \value A4 210 x 297 mm, 8.26 x 11.69 inches
+ \value A5 148 x 210 mm
+ \value A6 105 x 148 mm
+ \value A7 74 x 105 mm
+ \value A8 52 x 74 mm
+ \value A9 37 x 52 mm
+ \value B0 1030 x 1456 mm
+ \value B1 728 x 1030 mm
+ \value B10 32 x 45 mm
+ \value B2 515 x 728 mm
+ \value B3 364 x 515 mm
+ \value B4 257 x 364 mm
+ \value B5 182 x 257 mm, 7.17 x 10.13 inches
+ \value B6 128 x 182 mm
+ \value B7 91 x 128 mm
+ \value B8 64 x 91 mm
+ \value B9 45 x 64 mm
+ \value C5E 163 x 229 mm
+ \value Comm10E 105 x 241 mm, U.S. Common 10 Envelope
+ \value DLE 110 x 220 mm
+ \value Executive 7.5 x 10 inches, 191 x 254 mm
+ \value Folio 210 x 330 mm
+ \value Ledger 432 x 279 mm
+ \value Legal 8.5 x 14 inches, 216 x 356 mm
+ \value Letter 8.5 x 11 inches, 216 x 279 mm
+ \value Tabloid 279 x 432 mm
+ \value Custom Unknown, or a user defined size.
+
+ \omitvalue NPageSize
+ */
+
+/*!
+ \enum QPrinter::PaperSize
+ \since 4.4
+
+ This enum type specifies what paper size QPrinter should use.
+ QPrinter does not check that the paper size is available; it just
+ uses this information, together with QPrinter::Orientation and
+ QPrinter::setFullPage(), to determine the printable area.
+
+ The defined sizes (with setFullPage(true)) are:
+
+ \value A0 841 x 1189 mm
+ \value A1 594 x 841 mm
+ \value A2 420 x 594 mm
+ \value A3 297 x 420 mm
+ \value A4 210 x 297 mm, 8.26 x 11.69 inches
+ \value A5 148 x 210 mm
+ \value A6 105 x 148 mm
+ \value A7 74 x 105 mm
+ \value A8 52 x 74 mm
+ \value A9 37 x 52 mm
+ \value B0 1000 x 1414 mm
+ \value B1 707 x 1000 mm
+ \value B2 500 x 707 mm
+ \value B3 353 x 500 mm
+ \value B4 250 x 353 mm
+ \value B5 176 x 250 mm, 6.93 x 9.84 inches
+ \value B6 125 x 176 mm
+ \value B7 88 x 125 mm
+ \value B8 62 x 88 mm
+ \value B9 33 x 62 mm
+ \value B10 31 x 44 mm
+ \value C5E 163 x 229 mm
+ \value Comm10E 105 x 241 mm, U.S. Common 10 Envelope
+ \value DLE 110 x 220 mm
+ \value Executive 7.5 x 10 inches, 190.5 x 254 mm
+ \value Folio 210 x 330 mm
+ \value Ledger 431.8 x 279.4 mm
+ \value Legal 8.5 x 14 inches, 215.9 x 355.6 mm
+ \value Letter 8.5 x 11 inches, 215.9 x 279.4 mm
+ \value Tabloid 279.4 x 431.8 mm
+ \value Custom Unknown, or a user defined size.
+
+ With setFullPage(false) (the default), the metrics will be a bit
+ smaller; how much depends on the printer in use.
+
+ \omitvalue NPageSize
+ \omitvalue NPaperSize
+*/
+
+
+/*!
+ \enum QPrinter::PageOrder
+
+ This enum type is used by QPrinter to tell the application program
+ how to print.
+
+ \value FirstPageFirst the lowest-numbered page should be printed
+ first.
+
+ \value LastPageFirst the highest-numbered page should be printed
+ first.
+*/
+
+/*!
+ \enum QPrinter::ColorMode
+
+ This enum type is used to indicate whether QPrinter should print
+ in color or not.
+
+ \value Color print in color if available, otherwise in grayscale.
+
+ \value GrayScale print in grayscale, even on color printers.
+*/
+
+/*!
+ \enum QPrinter::PaperSource
+
+ This enum type specifies what paper source QPrinter is to use.
+ QPrinter does not check that the paper source is available; it
+ just uses this information to try and set the paper source.
+ Whether it will set the paper source depends on whether the
+ printer has that particular source.
+
+ \warning This is currently only implemented for Windows.
+
+ \value Auto
+ \value Cassette
+ \value Envelope
+ \value EnvelopeManual
+ \value FormSource
+ \value LargeCapacity
+ \value LargeFormat
+ \value Lower
+ \value MaxPageSource
+ \value Middle
+ \value Manual
+ \value OnlyOne
+ \value Tractor
+ \value SmallFormat
+*/
+
+/*!
+ \enum QPrinter::Unit
+ \since 4.4
+
+ This enum type is used to specify the measurement unit for page and
+ paper sizes.
+
+ \value Millimeter
+ \value Point
+ \value Inch
+ \value Pica
+ \value Didot
+ \value Cicero
+ \value DevicePixel
+
+ Note the difference between Point and DevicePixel. The Point unit is
+ defined to be 1/72th of an inch, while the DevicePixel unit is
+ resolution dependant and is based on the actual pixels, or dots, on
+ the printer.
+*/
+
+
+/*
+ \enum QPrinter::PrintRange
+
+ This enum is used to specify which print range the application
+ should use to print.
+
+ \value AllPages All the pages should be printed.
+ \value Selection Only the selection should be printed.
+ \value PageRange Print according to the from page and to page options.
+ \value CurrentPage Only the current page should be printed.
+
+ \sa setPrintRange(), printRange()
+*/
+
+/*
+ \enum QPrinter::PrinterOption
+
+ This enum describes various printer options that appear in the
+ printer setup dialog. It is used to enable and disable these
+ options in the setup dialog.
+
+ \value PrintToFile Describes if print to file should be enabled.
+ \value PrintSelection Describes if printing selections should be enabled.
+ \value PrintPageRange Describes if printing page ranges (from, to) should
+ be enabled
+ \value PrintCurrentPage if Print Current Page option should be enabled
+
+ \sa setOptionEnabled(), isOptionEnabled()
+*/
+
+/*!
+ Creates a new printer object with the given \a mode.
+*/
+QPrinter::QPrinter(PrinterMode mode)
+ : QPaintDevice(),
+ d_ptr(new QPrinterPrivate(this))
+{
+ init(mode);
+ QPrinterInfo defPrn(QPrinterInfo::defaultPrinter());
+ if (!defPrn.isNull()) {
+ setPrinterName(defPrn.printerName());
+ } else if (QPrinterInfo::availablePrinters().isEmpty()
+ && d_ptr->paintEngine->type() != QPaintEngine::Windows
+ && d_ptr->paintEngine->type() != QPaintEngine::MacPrinter) {
+ setOutputFormat(QPrinter::PdfFormat);
+ }
+}
+
+/*!
+ \since 4.4
+
+ Creates a new printer object with the given \a printer and \a mode.
+*/
+QPrinter::QPrinter(const QPrinterInfo& printer, PrinterMode mode)
+ : QPaintDevice(),
+ d_ptr(new QPrinterPrivate(this))
+{
+ init(mode);
+ setPrinterName(printer.printerName());
+}
+
+void QPrinter::init(PrinterMode mode)
+{
+#if !defined(Q_WS_X11)
+ if (!qApp) {
+#else
+ if (!qApp || !X11) {
+#endif
+ qFatal("QPrinter: Must construct a QApplication before a QPaintDevice");
+ return;
+ }
+ Q_D(QPrinter);
+
+ d->printerMode = mode;
+ d->outputFormat = QPrinter::NativeFormat;
+ d->createDefaultEngines();
+
+#ifndef QT_NO_PRINTPREVIEWWIDGET
+ d->previewEngine = 0;
+#endif
+ d->realPrintEngine = 0;
+ d->realPaintEngine = 0;
+
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ if (QCUPSSupport::cupsVersion() >= 10200 && QCUPSSupport().currentPPD()) {
+ setOutputFormat(QPrinter::PdfFormat);
+ d->outputFormat = QPrinter::NativeFormat;
+ }
+#endif
+}
+
+/*!
+ This function is used by subclasses of QPrinter to specify custom
+ print and paint engines (\a printEngine and \a paintEngine,
+ respectively).
+
+ QPrinter does not take ownership of the engines, so you need to
+ manage these engine instances yourself.
+
+ Note that changing the engines will reset the printer state and
+ all its properties.
+
+ \sa printEngine() paintEngine() setOutputFormat()
+
+ \since 4.1
+*/
+void QPrinter::setEngines(QPrintEngine *printEngine, QPaintEngine *paintEngine)
+{
+ Q_D(QPrinter);
+
+ if (d->use_default_engine)
+ delete d->printEngine;
+
+ d->printEngine = printEngine;
+ d->paintEngine = paintEngine;
+ d->use_default_engine = false;
+}
+
+/*!
+ Destroys the printer object and frees any allocated resources. If
+ the printer is destroyed while a print job is in progress this may
+ or may not affect the print job.
+*/
+QPrinter::~QPrinter()
+{
+ Q_D(QPrinter);
+ if (d->use_default_engine)
+ delete d->printEngine;
+#ifndef QT_NO_PRINTPREVIEWWIDGET
+ delete d->previewEngine;
+#endif
+}
+
+/*!
+ \enum QPrinter::OutputFormat
+
+ The OutputFormat enum is used to describe the format QPrinter should
+ use for printing.
+
+ \value NativeFormat QPrinter will print output using a method defined
+ by the platform it is running on. This mode is the default when printing
+ directly to a printer.
+
+ \value PdfFormat QPrinter will generate its output as a searchable PDF file.
+ This mode is the default when printing to a file.
+
+ \value PostScriptFormat QPrinter will generate its output as in the PostScript format.
+ (This feature was introduced in Qt 4.2.)
+
+ \sa outputFormat(), setOutputFormat(), setOutputFileName()
+*/
+
+/*!
+ \since 4.1
+
+ Sets the output format for this printer to \a format.
+*/
+void QPrinter::setOutputFormat(OutputFormat format)
+{
+
+#ifndef QT_NO_PDF
+ Q_D(QPrinter);
+ if (d->validPrinter && d->outputFormat == format)
+ return;
+ d->outputFormat = format;
+
+ QPrintEngine *oldPrintEngine = d->printEngine;
+ const bool def_engine = d->use_default_engine;
+ d->printEngine = 0;
+
+ d->createDefaultEngines();
+
+ if (oldPrintEngine) {
+ for (int i = 0; i < d->manualSetList.size(); ++i) {
+ QPrintEngine::PrintEnginePropertyKey key = d->manualSetList[i];
+ QVariant prop;
+ // PPK_NumberOfCopies need special treatmeant since it in most cases
+ // will return 1, disregarding the actual value that was set
+ if (key == QPrintEngine::PPK_NumberOfCopies)
+ prop = QVariant(copyCount());
+ else
+ prop = oldPrintEngine->property(key);
+ if (prop.isValid())
+ d->printEngine->setProperty(key, prop);
+ }
+ }
+
+ if (def_engine)
+ delete oldPrintEngine;
+
+ if (d->outputFormat == QPrinter::PdfFormat || d->outputFormat == QPrinter::PostScriptFormat)
+ d->validPrinter = true;
+#else
+ Q_UNUSED(format);
+#endif
+}
+
+/*!
+ \since 4.1
+
+ Returns the output format for this printer.
+*/
+QPrinter::OutputFormat QPrinter::outputFormat() const
+{
+ Q_D(const QPrinter);
+ return d->outputFormat;
+}
+
+
+
+/*! \internal
+*/
+int QPrinter::devType() const
+{
+ return QInternal::Printer;
+}
+
+/*!
+ Returns the printer name. This value is initially set to the name
+ of the default printer.
+
+ \sa setPrinterName()
+*/
+QString QPrinter::printerName() const
+{
+ Q_D(const QPrinter);
+ return d->printEngine->property(QPrintEngine::PPK_PrinterName).toString();
+}
+
+/*!
+ Sets the printer name to \a name.
+
+ \sa printerName(), isValid()
+*/
+void QPrinter::setPrinterName(const QString &name)
+{
+ Q_D(QPrinter);
+ ABORT_IF_ACTIVE("QPrinter::setPrinterName");
+
+#if defined(Q_OS_UNIX) && !defined(QT_NO_CUPS)
+ if(d->use_default_engine
+ && d->outputFormat == QPrinter::NativeFormat) {
+ if (QCUPSSupport::cupsVersion() >= 10200
+ && QCUPSSupport::printerHasPPD(name.toLocal8Bit().constData()))
+ setOutputFormat(QPrinter::PdfFormat);
+ else
+ setOutputFormat(QPrinter::PostScriptFormat);
+ d->outputFormat = QPrinter::NativeFormat;
+ }
+#endif
+
+ QList<QPrinterInfo> prnList = QPrinterInfo::availablePrinters();
+ if (name.isEmpty()) {
+ d->validPrinter = d->outputFormat == QPrinter::PdfFormat || d->outputFormat == QPrinter::PostScriptFormat;
+ } else {
+ d->validPrinter = false;
+ for (int i = 0; i < prnList.size(); ++i) {
+ if (prnList[i].printerName() == name) {
+ d->validPrinter = true;
+ break;
+ }
+ }
+ }
+
+ d->printEngine->setProperty(QPrintEngine::PPK_PrinterName, name);
+ d->addToManualSetList(QPrintEngine::PPK_PrinterName);
+}
+
+
+/*!
+ \since 4.4
+
+ Returns true if the printer currently selected is a valid printer
+ in the system, or a pure PDF/PostScript printer; otherwise returns false.
+
+ To detect other failures check the output of QPainter::begin() or QPrinter::newPage().
+
+ \snippet doc/src/snippets/printing-qprinter/errors.cpp 0
+
+ \sa setPrinterName()
+*/
+bool QPrinter::isValid() const
+{
+ Q_D(const QPrinter);
+#if defined(Q_WS_X11)
+ if (!qApp || !X11) {
+ return false;
+ }
+#endif
+ return d->validPrinter;
+}
+
+
+/*!
+ \fn bool QPrinter::outputToFile() const
+
+ Returns true if the output should be written to a file, or false
+ if the output should be sent directly to the printer. The default
+ setting is false.
+
+ \sa setOutputToFile(), setOutputFileName()
+*/
+
+
+/*!
+ \fn void QPrinter::setOutputToFile(bool enable)
+
+ Specifies whether the output should be written to a file or sent
+ directly to the printer.
+
+ Will output to a file if \a enable is true, or will output
+ directly to the printer if \a enable is false.
+
+ \sa outputToFile(), setOutputFileName()
+*/
+
+
+/*!
+ \fn QString QPrinter::outputFileName() const
+
+ Returns the name of the output file. By default, this is an empty string
+ (indicating that the printer shouldn't print to file).
+
+ \sa QPrintEngine::PrintEnginePropertyKey
+
+*/
+
+QString QPrinter::outputFileName() const
+{
+ Q_D(const QPrinter);
+ return d->printEngine->property(QPrintEngine::PPK_OutputFileName).toString();
+}
+
+/*!
+ Sets the name of the output file to \a fileName.
+
+ Setting a null or empty name (0 or "") disables printing to a file.
+ Setting a non-empty name enables printing to a file.
+
+ This can change the value of outputFormat(). If the file name has the
+ suffix ".ps" then PostScript is automatically selected as output format.
+ If the file name has the ".pdf" suffix PDF is generated. If the file name
+ has a suffix other than ".ps" and ".pdf", the output format used is the
+ one set with setOutputFormat().
+
+ QPrinter uses Qt's cross-platform PostScript or PDF print engines
+ respectively. If you can produce this format natively, for example
+ Mac OS X can generate PDF's from its print engine, set the output format
+ back to NativeFormat.
+
+ \sa outputFileName() setOutputToFile() setOutputFormat()
+*/
+
+void QPrinter::setOutputFileName(const QString &fileName)
+{
+ Q_D(QPrinter);
+ ABORT_IF_ACTIVE("QPrinter::setOutputFileName");
+
+ QFileInfo fi(fileName);
+ if (!fi.suffix().compare(QLatin1String("ps"), Qt::CaseInsensitive))
+ setOutputFormat(QPrinter::PostScriptFormat);
+ else if (!fi.suffix().compare(QLatin1String("pdf"), Qt::CaseInsensitive))
+ setOutputFormat(QPrinter::PdfFormat);
+ else if (fileName.isEmpty())
+ setOutputFormat(QPrinter::NativeFormat);
+
+ d->printEngine->setProperty(QPrintEngine::PPK_OutputFileName, fileName);
+ d->addToManualSetList(QPrintEngine::PPK_OutputFileName);
+}
+
+
+/*!
+ Returns the name of the program that sends the print output to the
+ printer.
+
+ The default is to return an empty string; meaning that QPrinter will try to
+ be smart in a system-dependent way. On X11 only, you can set it to something
+ different to use a specific print program. On the other platforms, this
+ returns an empty string.
+
+ \sa setPrintProgram(), setPrinterSelectionOption()
+*/
+QString QPrinter::printProgram() const
+{
+ Q_D(const QPrinter);
+ return d->printEngine->property(QPrintEngine::PPK_PrinterProgram).toString();
+}
+
+
+/*!
+ Sets the name of the program that should do the print job to \a
+ printProg.
+
+ On X11, this function sets the program to call with the PostScript
+ output. On other platforms, it has no effect.
+
+ \sa printProgram()
+*/
+void QPrinter::setPrintProgram(const QString &printProg)
+{
+ Q_D(QPrinter);
+ ABORT_IF_ACTIVE("QPrinter::setPrintProgram");
+ d->printEngine->setProperty(QPrintEngine::PPK_PrinterProgram, printProg);
+ d->addToManualSetList(QPrintEngine::PPK_PrinterProgram);
+}
+
+
+/*!
+ Returns the document name.
+
+ \sa setDocName(), QPrintEngine::PrintEnginePropertyKey
+*/
+QString QPrinter::docName() const
+{
+ Q_D(const QPrinter);
+ return d->printEngine->property(QPrintEngine::PPK_DocumentName).toString();
+}
+
+
+/*!
+ Sets the document name to \a name.
+
+ On X11, the document name is for example used as the default
+ output filename in QPrintDialog. Note that the document name does
+ not affect the file name if the printer is printing to a file.
+ Use the setOutputFile() function for this.
+
+ \sa docName(), QPrintEngine::PrintEnginePropertyKey
+*/
+void QPrinter::setDocName(const QString &name)
+{
+ Q_D(QPrinter);
+ ABORT_IF_ACTIVE("QPrinter::setDocName");
+ d->printEngine->setProperty(QPrintEngine::PPK_DocumentName, name);
+ d->addToManualSetList(QPrintEngine::PPK_DocumentName);
+}
+
+
+/*!
+ Returns the name of the application that created the document.
+
+ \sa setCreator()
+*/
+QString QPrinter::creator() const
+{
+ Q_D(const QPrinter);
+ return d->printEngine->property(QPrintEngine::PPK_Creator).toString();
+}
+
+
+/*!
+ Sets the name of the application that created the document to \a
+ creator.
+
+ This function is only applicable to the X11 version of Qt. If no
+ creator name is specified, the creator will be set to "Qt"
+ followed by some version number.
+
+ \sa creator()
+*/
+void QPrinter::setCreator(const QString &creator)
+{
+ Q_D(QPrinter);
+ ABORT_IF_ACTIVE("QPrinter::setCreator");
+ d->printEngine->setProperty(QPrintEngine::PPK_Creator, creator);
+ d->addToManualSetList(QPrintEngine::PPK_Creator);
+}
+
+
+/*!
+ Returns the orientation setting. This is driver-dependent, but is usually
+ QPrinter::Portrait.
+
+ \sa setOrientation()
+*/
+QPrinter::Orientation QPrinter::orientation() const
+{
+ Q_D(const QPrinter);
+ return QPrinter::Orientation(d->printEngine->property(QPrintEngine::PPK_Orientation).toInt());
+}
+
+
+/*!
+ Sets the print orientation to \a orientation.
+
+ The orientation can be either QPrinter::Portrait or
+ QPrinter::Landscape.
+
+ The printer driver reads this setting and prints using the
+ specified orientation.
+
+ On Windows, this option can be changed while printing and will
+ take effect from the next call to newPage().
+
+ On Mac OS X, changing the orientation during a print job has no effect.
+
+ \sa orientation()
+*/
+
+void QPrinter::setOrientation(Orientation orientation)
+{
+ Q_D(QPrinter);
+ d->printEngine->setProperty(QPrintEngine::PPK_Orientation, orientation);
+ d->addToManualSetList(QPrintEngine::PPK_Orientation);
+}
+
+
+/*!
+ \since 4.4
+ Returns the printer paper size. The default value is driver-dependent.
+
+ \sa setPaperSize() pageRect() paperRect()
+*/
+
+QPrinter::PaperSize QPrinter::paperSize() const
+{
+ Q_D(const QPrinter);
+ return QPrinter::PaperSize(d->printEngine->property(QPrintEngine::PPK_PaperSize).toInt());
+}
+
+/*!
+ \since 4.4
+
+ Sets the printer paper size to \a newPaperSize if that size is
+ supported. The result is undefined if \a newPaperSize is not
+ supported.
+
+ The default paper size is driver-dependent.
+
+ This function is useful mostly for setting a default value that
+ the user can override in the print dialog.
+
+ \sa paperSize() PaperSize setFullPage() setResolution() pageRect() paperRect()
+*/
+void QPrinter::setPaperSize(PaperSize newPaperSize)
+{
+ Q_D(QPrinter);
+ if (d->paintEngine->type() != QPaintEngine::Pdf)
+ ABORT_IF_ACTIVE("QPrinter::setPaperSize");
+ if (newPaperSize < 0 || newPaperSize >= NPaperSize) {
+ qWarning("QPrinter::setPaperSize: Illegal paper size %d", newPaperSize);
+ return;
+ }
+ d->printEngine->setProperty(QPrintEngine::PPK_PaperSize, newPaperSize);
+ d->addToManualSetList(QPrintEngine::PPK_PaperSize);
+ d->hasUserSetPageSize = true;
+}
+
+/*!
+ \obsolete
+
+ Returns the printer page size. The default value is driver-dependent.
+
+ Use paperSize() instead.
+*/
+QPrinter::PageSize QPrinter::pageSize() const
+{
+ return paperSize();
+}
+
+
+/*!
+ \obsolete
+
+ Sets the printer page size based on \a newPageSize.
+
+ Use setPaperSize() instead.
+*/
+
+void QPrinter::setPageSize(PageSize newPageSize)
+{
+ setPaperSize(newPageSize);
+}
+
+/*!
+ \since 4.4
+
+ Sets the paper size based on \a paperSize in \a unit.
+
+ \sa paperSize()
+*/
+
+void QPrinter::setPaperSize(const QSizeF &paperSize, QPrinter::Unit unit)
+{
+ Q_D(QPrinter);
+ if (d->paintEngine->type() != QPaintEngine::Pdf)
+ ABORT_IF_ACTIVE("QPrinter::setPaperSize");
+ const qreal multiplier = qt_multiplierForUnit(unit, resolution());
+ QSizeF size(paperSize.width() * multiplier, paperSize.height() * multiplier);
+ d->printEngine->setProperty(QPrintEngine::PPK_CustomPaperSize, size);
+ d->addToManualSetList(QPrintEngine::PPK_CustomPaperSize);
+ d->hasUserSetPageSize = true;
+}
+
+/*!
+ \since 4.4
+
+ Returns the paper size in \a unit.
+
+ \sa setPaperSize()
+*/
+
+QSizeF QPrinter::paperSize(Unit unit) const
+{
+ Q_D(const QPrinter);
+ int res = resolution();
+ const qreal multiplier = qt_multiplierForUnit(unit, res);
+ PaperSize paperType = paperSize();
+ if (paperType == Custom) {
+ QSizeF size = d->printEngine->property(QPrintEngine::PPK_CustomPaperSize).toSizeF();
+ return QSizeF(size.width() / multiplier, size.height() / multiplier);
+ }
+ else {
+ return qt_printerPaperSize(orientation(), paperType, unit, res);
+ }
+}
+
+/*!
+ Sets the page order to \a pageOrder.
+
+ The page order can be QPrinter::FirstPageFirst or
+ QPrinter::LastPageFirst. The application is responsible for
+ reading the page order and printing accordingly.
+
+ This function is mostly useful for setting a default value that
+ the user can override in the print dialog.
+
+ This function is only supported under X11.
+*/
+
+void QPrinter::setPageOrder(PageOrder pageOrder)
+{
+ Q_D(QPrinter);
+ ABORT_IF_ACTIVE("QPrinter::setPageOrder");
+ d->printEngine->setProperty(QPrintEngine::PPK_PageOrder, pageOrder);
+ d->addToManualSetList(QPrintEngine::PPK_PageOrder);
+}
+
+
+/*!
+ Returns the current page order.
+
+ The default page order is \c FirstPageFirst.
+*/
+
+QPrinter::PageOrder QPrinter::pageOrder() const
+{
+ Q_D(const QPrinter);
+ return QPrinter::PageOrder(d->printEngine->property(QPrintEngine::PPK_PageOrder).toInt());
+}
+
+
+/*!
+ Sets the printer's color mode to \a newColorMode, which can be
+ either \c Color or \c GrayScale.
+
+ \sa colorMode()
+*/
+
+void QPrinter::setColorMode(ColorMode newColorMode)
+{
+ Q_D(QPrinter);
+ ABORT_IF_ACTIVE("QPrinter::setColorMode");
+ d->printEngine->setProperty(QPrintEngine::PPK_ColorMode, newColorMode);
+ d->addToManualSetList(QPrintEngine::PPK_ColorMode);
+}
+
+
+/*!
+ Returns the current color mode.
+
+ \sa setColorMode()
+*/
+QPrinter::ColorMode QPrinter::colorMode() const
+{
+ Q_D(const QPrinter);
+ return QPrinter::ColorMode(d->printEngine->property(QPrintEngine::PPK_ColorMode).toInt());
+}
+
+
+/*!
+ \obsolete
+ Returns the number of copies to be printed. The default value is 1.
+
+ On Windows, Mac OS X and X11 systems that support CUPS, this will always
+ return 1 as these operating systems can internally handle the number
+ of copies.
+
+ On X11, this value will return the number of times the application is
+ required to print in order to match the number specified in the printer setup
+ dialog. This has been done since some printer drivers are not capable of
+ buffering up the copies and in those cases the application must make an
+ explicit call to the print code for each copy.
+
+ Use copyCount() in conjunction with supportsMultipleCopies() instead.
+
+ \sa setNumCopies(), actualNumCopies()
+*/
+
+int QPrinter::numCopies() const
+{
+ Q_D(const QPrinter);
+ return d->printEngine->property(QPrintEngine::PPK_NumberOfCopies).toInt();
+}
+
+
+/*!
+ \obsolete
+ \since 4.6
+
+ Returns the number of copies that will be printed. The default
+ value is 1.
+
+ This function always returns the actual value specified in the print
+ dialog or using setNumCopies().
+
+ Use copyCount() instead.
+
+ \sa setNumCopies(), numCopies()
+*/
+int QPrinter::actualNumCopies() const
+{
+ return copyCount();
+}
+
+
+
+/*!
+ \obsolete
+ Sets the number of copies to be printed to \a numCopies.
+
+ The printer driver reads this setting and prints the specified
+ number of copies.
+
+ Use setCopyCount() instead.
+
+ \sa numCopies()
+*/
+
+void QPrinter::setNumCopies(int numCopies)
+{
+ Q_D(QPrinter);
+ ABORT_IF_ACTIVE("QPrinter::setNumCopies");
+ d->printEngine->setProperty(QPrintEngine::PPK_NumberOfCopies, numCopies);
+ d->addToManualSetList(QPrintEngine::PPK_NumberOfCopies);
+}
+
+/*!
+ \since 4.7
+
+ Sets the number of copies to be printed to \a count.
+
+ The printer driver reads this setting and prints the specified number of
+ copies.
+
+ \sa copyCount(), supportsMultipleCopies()
+*/
+
+void QPrinter::setCopyCount(int count)
+{
+ Q_D(QPrinter);
+ ABORT_IF_ACTIVE("QPrinter::setCopyCount;");
+ d->printEngine->setProperty(QPrintEngine::PPK_CopyCount, count);
+ d->addToManualSetList(QPrintEngine::PPK_CopyCount);
+}
+
+/*!
+ \since 4.7
+
+ Returns the number of copies that will be printed. The default value is 1.
+
+ \sa setCopyCount(), supportsMultipleCopies()
+*/
+
+int QPrinter::copyCount() const
+{
+ Q_D(const QPrinter);
+ return d->printEngine->property(QPrintEngine::PPK_CopyCount).toInt();
+}
+
+/*!
+ \since 4.7
+
+ Returns true if the printer supports printing multiple copies of the same
+ document in one job; otherwise false is returned.
+
+ On most systems this function will return true. However, on X11 systems
+ that do not support CUPS, this function will return false. That means the
+ application has to handle the number of copies by printing the same
+ document the required number of times.
+
+ \sa setCopyCount(), copyCount()
+*/
+
+bool QPrinter::supportsMultipleCopies() const
+{
+ Q_D(const QPrinter);
+ return d->printEngine->property(QPrintEngine::PPK_SupportsMultipleCopies).toBool();
+}
+
+/*!
+ \since 4.1
+
+ Returns true if collation is turned on when multiple copies is selected.
+ Returns false if it is turned off when multiple copies is selected.
+ When collating is turned off the printing of each individual page will be repeated
+ the numCopies() amount before the next page is started. With collating turned on
+ all pages are printed before the next copy of those pages is started.
+
+ \sa setCollateCopies()
+*/
+bool QPrinter::collateCopies() const
+{
+ Q_D(const QPrinter);
+ return d->printEngine->property(QPrintEngine::PPK_CollateCopies).toBool();
+}
+
+
+/*!
+ \since 4.1
+
+ Sets the default value for collation checkbox when the print
+ dialog appears. If \a collate is true, it will enable
+ setCollateCopiesEnabled(). The default value is false. This value
+ will be changed by what the user presses in the print dialog.
+
+ \sa collateCopies()
+*/
+void QPrinter::setCollateCopies(bool collate)
+{
+ Q_D(QPrinter);
+ ABORT_IF_ACTIVE("QPrinter::setCollateCopies");
+ d->printEngine->setProperty(QPrintEngine::PPK_CollateCopies, collate);
+ d->addToManualSetList(QPrintEngine::PPK_CollateCopies);
+}
+
+
+
+/*!
+ If \a fp is true, enables support for painting over the entire page;
+ otherwise restricts painting to the printable area reported by the
+ device.
+
+ By default, full page printing is disabled. In this case, the origin
+ of the QPrinter's coordinate system coincides with the top-left
+ corner of the printable area.
+
+ If full page printing is enabled, the origin of the QPrinter's
+ coordinate system coincides with the top-left corner of the paper
+ itself. In this case, the
+ \l{QPaintDevice::PaintDeviceMetric}{device metrics} will report
+ the exact same dimensions as indicated by \l{PaperSize}. It may not
+ be possible to print on the entire physical page because of the
+ printer's margins, so the application must account for the margins
+ itself.
+
+ \sa fullPage(), setPaperSize(), width(), height(), {Printing with Qt}
+*/
+
+void QPrinter::setFullPage(bool fp)
+{
+ Q_D(QPrinter);
+ d->printEngine->setProperty(QPrintEngine::PPK_FullPage, fp);
+ d->addToManualSetList(QPrintEngine::PPK_FullPage);
+}
+
+
+/*!
+ Returns true if the origin of the printer's coordinate system is
+ at the corner of the page and false if it is at the edge of the
+ printable area.
+
+ See setFullPage() for details and caveats.
+
+ \sa setFullPage() PaperSize
+*/
+
+bool QPrinter::fullPage() const
+{
+ Q_D(const QPrinter);
+ return d->printEngine->property(QPrintEngine::PPK_FullPage).toBool();
+}
+
+
+/*!
+ Requests that the printer prints at \a dpi or as near to \a dpi as
+ possible.
+
+ This setting affects the coordinate system as returned by, for
+ example QPainter::viewport().
+
+ This function must be called before QPainter::begin() to have an effect on
+ all platforms.
+
+ \sa resolution() setPaperSize()
+*/
+
+void QPrinter::setResolution(int dpi)
+{
+ Q_D(QPrinter);
+ ABORT_IF_ACTIVE("QPrinter::setResolution");
+ d->printEngine->setProperty(QPrintEngine::PPK_Resolution, dpi);
+ d->addToManualSetList(QPrintEngine::PPK_Resolution);
+}
+
+
+/*!
+ Returns the current assumed resolution of the printer, as set by
+ setResolution() or by the printer driver.
+
+ \sa setResolution()
+*/
+
+int QPrinter::resolution() const
+{
+ Q_D(const QPrinter);
+ return d->printEngine->property(QPrintEngine::PPK_Resolution).toInt();
+}
+
+/*!
+ Sets the paper source setting to \a source.
+
+ Windows only: This option can be changed while printing and will
+ take effect from the next call to newPage()
+
+ \sa paperSource()
+*/
+
+void QPrinter::setPaperSource(PaperSource source)
+{
+ Q_D(QPrinter);
+ d->printEngine->setProperty(QPrintEngine::PPK_PaperSource, source);
+ d->addToManualSetList(QPrintEngine::PPK_PaperSource);
+}
+
+/*!
+ Returns the printer's paper source. This is \c Manual or a printer
+ tray or paper cassette.
+*/
+QPrinter::PaperSource QPrinter::paperSource() const
+{
+ Q_D(const QPrinter);
+ return QPrinter::PaperSource(d->printEngine->property(QPrintEngine::PPK_PaperSource).toInt());
+}
+
+
+/*!
+ \since 4.1
+
+ Enabled or disables font embedding depending on \a enable.
+
+ Currently this option is only supported on X11.
+
+ \sa fontEmbeddingEnabled()
+*/
+void QPrinter::setFontEmbeddingEnabled(bool enable)
+{
+ Q_D(QPrinter);
+ d->printEngine->setProperty(QPrintEngine::PPK_FontEmbedding, enable);
+ d->addToManualSetList(QPrintEngine::PPK_FontEmbedding);
+}
+
+/*!
+ \since 4.1
+
+ Returns true if font embedding is enabled.
+
+ Currently this option is only supported on X11.
+
+ \sa setFontEmbeddingEnabled()
+*/
+bool QPrinter::fontEmbeddingEnabled() const
+{
+ Q_D(const QPrinter);
+ return d->printEngine->property(QPrintEngine::PPK_FontEmbedding).toBool();
+}
+
+/*!
+ \enum QPrinter::DuplexMode
+ \since 4.4
+
+ This enum is used to indicate whether printing will occur on one or both sides
+ of each sheet of paper (simplex or duplex printing).
+
+ \value DuplexNone Single sided (simplex) printing only.
+ \value DuplexAuto The printer's default setting is used to determine whether
+ duplex printing is used.
+ \value DuplexLongSide Both sides of each sheet of paper are used for printing.
+ The paper is turned over its longest edge before the second
+ side is printed
+ \value DuplexShortSide Both sides of each sheet of paper are used for printing.
+ The paper is turned over its shortest edge before the second
+ side is printed
+*/
+
+/*!
+ \since 4.2
+
+ Enables double sided printing if \a doubleSided is true; otherwise disables it.
+
+ Currently this option is only supported on X11.
+*/
+void QPrinter::setDoubleSidedPrinting(bool doubleSided)
+{
+ setDuplex(doubleSided ? DuplexAuto : DuplexNone);
+}
+
+
+/*!
+ \since 4.2
+
+ Returns true if double side printing is enabled.
+
+ Currently this option is only supported on X11.
+*/
+bool QPrinter::doubleSidedPrinting() const
+{
+ return duplex() != DuplexNone;
+}
+
+/*!
+ \since 4.4
+
+ Enables double sided printing based on the \a duplex mode.
+
+ Currently this option is only supported on X11.
+*/
+void QPrinter::setDuplex(DuplexMode duplex)
+{
+ Q_D(QPrinter);
+ d->printEngine->setProperty(QPrintEngine::PPK_Duplex, duplex);
+ d->addToManualSetList(QPrintEngine::PPK_Duplex);
+}
+
+/*!
+ \since 4.4
+
+ Returns the current duplex mode.
+
+ Currently this option is only supported on X11.
+*/
+QPrinter::DuplexMode QPrinter::duplex() const
+{
+ Q_D(const QPrinter);
+ return static_cast <DuplexMode> (d->printEngine->property(QPrintEngine::PPK_Duplex).toInt());
+}
+
+/*!
+ \since 4.4
+
+ Returns the page's rectangle in \a unit; this is usually smaller
+ than the paperRect() since the page normally has margins between
+ its borders and the paper.
+
+ \sa paperSize()
+*/
+QRectF QPrinter::pageRect(Unit unit) const
+{
+ Q_D(const QPrinter);
+ int res = resolution();
+ const qreal multiplier = qt_multiplierForUnit(unit, res);
+ // the page rect is in device pixels
+ QRect devRect(d->printEngine->property(QPrintEngine::PPK_PageRect).toRect());
+ if (unit == DevicePixel)
+ return devRect;
+ QRectF diRect(devRect.x()*72.0/res,
+ devRect.y()*72.0/res,
+ devRect.width()*72.0/res,
+ devRect.height()*72.0/res);
+ return QRectF(diRect.x()/multiplier, diRect.y()/multiplier,
+ diRect.width()/multiplier, diRect.height()/multiplier);
+}
+
+
+/*!
+ \since 4.4
+
+ Returns the paper's rectangle in \a unit; this is usually larger
+ than the pageRect().
+
+ \sa pageRect()
+*/
+QRectF QPrinter::paperRect(Unit unit) const
+{
+ Q_D(const QPrinter);
+ int res = resolution();
+ const qreal multiplier = qt_multiplierForUnit(unit, resolution());
+ // the page rect is in device pixels
+ QRect devRect(d->printEngine->property(QPrintEngine::PPK_PaperRect).toRect());
+ if (unit == DevicePixel)
+ return devRect;
+ QRectF diRect(devRect.x()*72.0/res,
+ devRect.y()*72.0/res,
+ devRect.width()*72.0/res,
+ devRect.height()*72.0/res);
+ return QRectF(diRect.x()/multiplier, diRect.y()/multiplier,
+ diRect.width()/multiplier, diRect.height()/multiplier);
+}
+
+/*!
+ Returns the page's rectangle; this is usually smaller than the
+ paperRect() since the page normally has margins between its
+ borders and the paper.
+
+ The unit of the returned rectangle is DevicePixel.
+
+ \sa paperSize()
+*/
+QRect QPrinter::pageRect() const
+{
+ Q_D(const QPrinter);
+ return d->printEngine->property(QPrintEngine::PPK_PageRect).toRect();
+}
+
+/*!
+ Returns the paper's rectangle; this is usually larger than the
+ pageRect().
+
+ The unit of the returned rectangle is DevicePixel.
+
+ \sa pageRect()
+*/
+QRect QPrinter::paperRect() const
+{
+ Q_D(const QPrinter);
+ return d->printEngine->property(QPrintEngine::PPK_PaperRect).toRect();
+}
+
+
+/*!
+ \since 4.4
+
+ This function sets the \a left, \a top, \a right and \a bottom
+ page margins for this printer. The unit of the margins are
+ specified with the \a unit parameter.
+*/
+void QPrinter::setPageMargins(qreal left, qreal top, qreal right, qreal bottom, QPrinter::Unit unit)
+{
+ Q_D(QPrinter);
+ const qreal multiplier = qt_multiplierForUnit(unit, resolution());
+ QList<QVariant> margins;
+ margins << (left * multiplier) << (top * multiplier)
+ << (right * multiplier) << (bottom * multiplier);
+ d->printEngine->setProperty(QPrintEngine::PPK_PageMargins, margins);
+ d->addToManualSetList(QPrintEngine::PPK_PageMargins);
+ d->hasCustomPageMargins = true;
+}
+
+/*!
+ \since 4.4
+
+ Returns the page margins for this printer in \a left, \a top, \a
+ right, \a bottom. The unit of the returned margins are specified
+ with the \a unit parameter.
+*/
+void QPrinter::getPageMargins(qreal *left, qreal *top, qreal *right, qreal *bottom, QPrinter::Unit unit) const
+{
+ Q_D(const QPrinter);
+ Q_ASSERT(left && top && right && bottom);
+ const qreal multiplier = qt_multiplierForUnit(unit, resolution());
+ QList<QVariant> margins(d->printEngine->property(QPrintEngine::PPK_PageMargins).toList());
+ *left = margins.at(0).toReal() / multiplier;
+ *top = margins.at(1).toReal() / multiplier;
+ *right = margins.at(2).toReal() / multiplier;
+ *bottom = margins.at(3).toReal() / multiplier;
+}
+
+/*!
+ \internal
+
+ Returns the metric for the given \a id.
+*/
+int QPrinter::metric(PaintDeviceMetric id) const
+{
+ Q_D(const QPrinter);
+ return d->printEngine->metric(id);
+}
+
+/*!
+ Returns the paint engine used by the printer.
+*/
+QPaintEngine *QPrinter::paintEngine() const
+{
+ Q_D(const QPrinter);
+ return d->paintEngine;
+}
+
+/*!
+ \since 4.1
+
+ Returns the print engine used by the printer.
+*/
+QPrintEngine *QPrinter::printEngine() const
+{
+ Q_D(const QPrinter);
+ return d->printEngine;
+}
+
+#if defined (Q_WS_WIN)
+/*!
+ Sets the page size to be used by the printer under Windows to \a
+ pageSize.
+
+ \warning This function is not portable so you may prefer to use
+ setPaperSize() instead.
+
+ \sa winPageSize()
+*/
+void QPrinter::setWinPageSize(int pageSize)
+{
+ Q_D(QPrinter);
+ ABORT_IF_ACTIVE("QPrinter::setWinPageSize");
+ d->printEngine->setProperty(QPrintEngine::PPK_WindowsPageSize, pageSize);
+ d->addToManualSetList(QPrintEngine::PPK_WindowsPageSize);
+}
+
+/*!
+ Returns the page size used by the printer under Windows.
+
+ \warning This function is not portable so you may prefer to use
+ paperSize() instead.
+
+ \sa setWinPageSize()
+*/
+int QPrinter::winPageSize() const
+{
+ Q_D(const QPrinter);
+ return d->printEngine->property(QPrintEngine::PPK_WindowsPageSize).toInt();
+}
+#endif // Q_WS_WIN
+
+/*!
+ Returns a list of the resolutions (a list of dots-per-inch
+ integers) that the printer says it supports.
+
+ For X11 where all printing is directly to postscript, this
+ function will always return a one item list containing only the
+ postscript resolution, i.e., 72 (72 dpi -- but see PrinterMode).
+*/
+QList<int> QPrinter::supportedResolutions() const
+{
+ Q_D(const QPrinter);
+ QList<QVariant> varlist
+ = d->printEngine->property(QPrintEngine::PPK_SupportedResolutions).toList();
+ QList<int> intlist;
+ for (int i=0; i<varlist.size(); ++i)
+ intlist << varlist.at(i).toInt();
+ return intlist;
+}
+
+/*!
+ Tells the printer to eject the current page and to continue
+ printing on a new page. Returns true if this was successful;
+ otherwise returns false.
+
+ Calling newPage() on an inactive QPrinter object will always
+ fail.
+*/
+bool QPrinter::newPage()
+{
+ Q_D(QPrinter);
+ if (d->printEngine->printerState() != QPrinter::Active)
+ return false;
+ return d->printEngine->newPage();
+}
+
+/*!
+ Aborts the current print run. Returns true if the print run was
+ successfully aborted and printerState() will return QPrinter::Aborted; otherwise
+ returns false.
+
+ It is not always possible to abort a print job. For example,
+ all the data has gone to the printer but the printer cannot or
+ will not cancel the job when asked to.
+*/
+bool QPrinter::abort()
+{
+ Q_D(QPrinter);
+ return d->printEngine->abort();
+}
+
+/*!
+ Returns the current state of the printer. This may not always be
+ accurate (for example if the printer doesn't have the capability
+ of reporting its state to the operating system).
+*/
+QPrinter::PrinterState QPrinter::printerState() const
+{
+ Q_D(const QPrinter);
+ return d->printEngine->printerState();
+}
+
+
+/*! \fn void QPrinter::margins(uint *top, uint *left, uint *bottom, uint *right) const
+
+ Sets *\a top, *\a left, *\a bottom, *\a right to be the top,
+ left, bottom, and right margins.
+
+ This function has been superseded by paperRect() and pageRect().
+ Use paperRect().top() - pageRect().top() for the top margin,
+ paperRect().left() - pageRect().left() for the left margin,
+ paperRect().bottom() - pageRect().bottom() for the bottom margin,
+ and papaerRect().right() - pageRect().right() for the right
+ margin.
+
+ \oldcode
+ uint rightMargin;
+ uint bottomMargin;
+ printer->margins(0, 0, &bottomMargin, &rightMargin);
+ \newcode
+ int rightMargin = printer->paperRect().right() - printer->pageRect().right();
+ int bottomMargin = printer->paperRect().bottom() - printer->pageRect().bottom();
+ \endcode
+*/
+
+/*! \fn QSize QPrinter::margins() const
+
+ \overload
+
+ Returns a QSize containing the left margin and the top margin.
+
+ This function has been superseded by paperRect() and pageRect().
+ Use paperRect().left() - pageRect().left() for the left margin,
+ and paperRect().top() - pageRect().top() for the top margin.
+
+ \oldcode
+ QSize margins = printer->margins();
+ int leftMargin = margins.width();
+ int topMargin = margins.height();
+ \newcode
+ int leftMargin = printer->paperRect().left() - printer->pageRect().left();
+ int topMargin = printer->paperRect().top() - printer->pageRect().top();
+ \endcode
+*/
+
+/*! \fn bool QPrinter::aborted()
+
+ Use printerState() == QPrinter::Aborted instead.
+*/
+
+#ifdef Q_WS_WIN
+/*!
+ \internal
+*/
+HDC QPrinter::getDC() const
+{
+ Q_D(const QPrinter);
+ return d->printEngine->getPrinterDC();
+}
+
+/*!
+ \internal
+*/
+void QPrinter::releaseDC(HDC hdc) const
+{
+ Q_D(const QPrinter);
+ d->printEngine->releasePrinterDC(hdc);
+}
+
+/*!
+ Returns the supported paper sizes for this printer.
+
+ The values will be either a value that matches an entry in the
+ QPrinter::PaperSource enum or a driver spesific value. The driver
+ spesific values are greater than the constant DMBIN_USER declared
+ in wingdi.h.
+
+ \warning This function is only available in windows.
+*/
+
+QList<QPrinter::PaperSource> QPrinter::supportedPaperSources() const
+{
+ Q_D(const QPrinter);
+ QVariant v = d->printEngine->property(QPrintEngine::PPK_PaperSources);
+
+ QList<QVariant> variant_list = v.toList();
+ QList<QPrinter::PaperSource> int_list;
+ for (int i=0; i<variant_list.size(); ++i)
+ int_list << (QPrinter::PaperSource) variant_list.at(i).toInt();
+
+ return int_list;
+}
+
+#endif
+
+/*!
+ \fn QString QPrinter::printerSelectionOption() const
+
+ Returns the printer options selection string. This is useful only
+ if the print command has been explicitly set.
+
+ The default value (an empty string) implies that the printer should
+ be selected in a system-dependent manner.
+
+ Any other value implies that the given value should be used.
+
+ \warning This function is not available on Windows.
+
+ \sa setPrinterSelectionOption()
+*/
+
+/*!
+ \fn void QPrinter::setPrinterSelectionOption(const QString &option)
+
+ Sets the printer to use \a option to select the printer. \a option
+ is null by default (which implies that Qt should be smart enough
+ to guess correctly), but it can be set to other values to use a
+ specific printer selection option.
+
+ If the printer selection option is changed while the printer is
+ active, the current print job may or may not be affected.
+
+ \warning This function is not available on Windows.
+
+ \sa printerSelectionOption()
+*/
+
+#ifndef Q_WS_WIN
+QString QPrinter::printerSelectionOption() const
+{
+ Q_D(const QPrinter);
+ return d->printEngine->property(QPrintEngine::PPK_SelectionOption).toString();
+}
+
+void QPrinter::setPrinterSelectionOption(const QString &option)
+{
+ Q_D(QPrinter);
+ d->printEngine->setProperty(QPrintEngine::PPK_SelectionOption, option);
+ d->addToManualSetList(QPrintEngine::PPK_SelectionOption);
+}
+#endif
+
+/*!
+ \since 4.1
+ \fn int QPrinter::fromPage() const
+
+ Returns the number of the first page in a range of pages to be printed
+ (the "from page" setting). Pages in a document are numbered according to
+ the convention that the first page is page 1.
+
+ By default, this function returns a special value of 0, meaning that
+ the "from page" setting is unset.
+
+ \note If fromPage() and toPage() both return 0, this indicates that
+ \e{the whole document will be printed}.
+
+ \sa setFromTo(), toPage()
+*/
+
+int QPrinter::fromPage() const
+{
+ Q_D(const QPrinter);
+ return d->fromPage;
+}
+
+/*!
+ \since 4.1
+
+ Returns the number of the last page in a range of pages to be printed
+ (the "to page" setting). Pages in a document are numbered according to
+ the convention that the first page is page 1.
+
+ By default, this function returns a special value of 0, meaning that
+ the "to page" setting is unset.
+
+ \note If fromPage() and toPage() both return 0, this indicates that
+ \e{the whole document will be printed}.
+
+ The programmer is responsible for reading this setting and
+ printing accordingly.
+
+ \sa setFromTo(), fromPage()
+*/
+
+int QPrinter::toPage() const
+{
+ Q_D(const QPrinter);
+ return d->toPage;
+}
+
+/*!
+ \since 4.1
+
+ Sets the range of pages to be printed to cover the pages with numbers
+ specified by \a from and \a to, where \a from corresponds to the first
+ page in the range and \a to corresponds to the last.
+
+ \note Pages in a document are numbered according to the convention that
+ the first page is page 1. However, if \a from and \a to are both set to 0,
+ the \e{whole document will be printed}.
+
+ This function is mostly used to set a default value that the user can
+ override in the print dialog when you call setup().
+
+ \sa fromPage(), toPage()
+*/
+
+void QPrinter::setFromTo(int from, int to)
+{
+ Q_D(QPrinter);
+ if (from > to) {
+ qWarning() << "QPrinter::setFromTo: 'from' must be less than or equal to 'to'";
+ from = to;
+ }
+ d->fromPage = from;
+ d->toPage = to;
+
+ if (d->minPage == 0 && d->maxPage == 0) {
+ d->minPage = 1;
+ d->maxPage = to;
+ d->options |= QAbstractPrintDialog::PrintPageRange;
+ }
+}
+
+/*!
+ \since 4.1
+
+ Sets the print range option in to be \a range.
+*/
+void QPrinter::setPrintRange( PrintRange range )
+{
+ Q_D(QPrinter);
+ d->printRange = QAbstractPrintDialog::PrintRange(range);
+}
+
+/*!
+ \since 4.1
+
+ Returns the page range of the QPrinter. After the print setup
+ dialog has been opened, this function returns the value selected
+ by the user.
+
+ \sa setPrintRange()
+*/
+QPrinter::PrintRange QPrinter::printRange() const
+{
+ Q_D(const QPrinter);
+ return PrintRange(d->printRange);
+}
+
+#if defined(QT3_SUPPORT)
+
+void QPrinter::setOutputToFile(bool f)
+{
+ if (f) {
+ if (outputFileName().isEmpty())
+ setOutputFileName(QLatin1String("untitled_printer_document"));
+ } else {
+ setOutputFileName(QString());
+ }
+}
+
+bool qt_compat_QPrinter_printSetup(QPrinter *printer, QPrinterPrivate *pd, QWidget *parent)
+{
+ Q_UNUSED(pd);
+ QPrintDialog dlg(printer, parent);
+ return dlg.exec() != 0;
+}
+
+
+#ifdef Q_WS_MAC
+bool qt_compat_QPrinter_pageSetup(QPrinter *p, QWidget *parent)
+{
+ QPageSetupDialog psd(p, parent);
+ return psd.exec() != 0;
+}
+
+/*!
+ Executes a page setup dialog so that the user can configure the type of
+ page used for printing. Returns true if the contents of the dialog are
+ accepted; returns false if the dialog is canceled.
+*/
+bool QPrinter::pageSetup(QWidget *parent)
+{
+ return qt_compat_QPrinter_pageSetup(this, parent);
+}
+
+/*!
+ Executes a print setup dialog so that the user can configure the printing
+ process. Returns true if the contents of the dialog are accepted; returns
+ false if the dialog is canceled.
+*/
+bool QPrinter::printSetup(QWidget *parent)
+{
+ Q_D(QPrinter);
+ return qt_compat_QPrinter_printSetup(this, d, parent);
+}
+#endif // Q_WS_MAC
+
+/*!
+ Use QPrintDialog instead.
+
+ \oldcode
+ if (printer->setup(parent))
+ ...
+ \newcode
+ QPrintDialog dialog(printer, parent);
+ if (dialog.exec())
+ ...
+ \endcode
+*/
+bool QPrinter::setup(QWidget *parent)
+{
+ Q_D(QPrinter);
+ return qt_compat_QPrinter_printSetup(this, d, parent)
+#ifdef Q_WS_MAC
+ && qt_compat_QPrinter_pageSetup(this, parent);
+#endif
+ ;
+}
+
+/*!
+ Use QPrintDialog::minPage() instead.
+*/
+int QPrinter::minPage() const
+{
+ Q_D(const QPrinter);
+ return d->minPage;
+}
+
+/*!
+ Use QPrintDialog::maxPage() instead.
+*/
+int QPrinter::maxPage() const
+{
+ Q_D(const QPrinter);
+ return d->maxPage;
+}
+
+/*!
+ Use QPrintDialog::setMinMax() instead.
+*/
+void QPrinter::setMinMax( int minPage, int maxPage )
+{
+ Q_D(QPrinter);
+ Q_ASSERT_X(minPage <= maxPage, "QPrinter::setMinMax",
+ "'min' must be less than or equal to 'max'");
+ d->minPage = minPage;
+ d->maxPage = maxPage;
+ d->options |= QPrintDialog::PrintPageRange;
+}
+
+/*!
+ Returns true if the printer is set up to collate copies of printed documents;
+ otherwise returns false.
+
+ Use QPrintDialog::isOptionEnabled(QPrintDialog::PrintCollateCopies)
+ instead.
+
+ \sa collateCopies()
+*/
+bool QPrinter::collateCopiesEnabled() const
+{
+ Q_D(const QPrinter);
+ return (d->options & QPrintDialog::PrintCollateCopies);
+}
+
+/*!
+ Use QPrintDialog::setOption(QPrintDialog::PrintCollateCopies)
+ or QPrintDialog::setOptions(QPrintDialog::options()
+ & ~QPrintDialog::PrintCollateCopies) instead, depending on \a
+ enable.
+*/
+void QPrinter::setCollateCopiesEnabled(bool enable)
+{
+ Q_D(QPrinter);
+
+ if (enable)
+ d->options |= QPrintDialog::PrintCollateCopies;
+ else
+ d->options &= ~QPrintDialog::PrintCollateCopies;
+}
+
+/*!
+ Use QPrintDialog instead.
+*/
+void QPrinter::setOptionEnabled( PrinterOption option, bool enable )
+{
+ Q_D(QPrinter);
+ if (enable)
+ d->options |= QPrintDialog::PrintDialogOption(1 << option);
+ else
+ d->options &= ~QPrintDialog::PrintDialogOption(1 << option);
+}
+
+/*!
+ Use QPrintDialog instead.
+*/
+bool QPrinter::isOptionEnabled( PrinterOption option ) const
+{
+ Q_D(const QPrinter);
+ return (d->options & QPrintDialog::PrintDialogOption(option));
+}
+
+#endif // QT3_SUPPORT
+
+/*!
+ \class QPrintEngine
+ \reentrant
+
+ \ingroup printing
+
+ \brief The QPrintEngine class defines an interface for how QPrinter
+ interacts with a given printing subsystem.
+
+ The common case when creating your own print engine is to derive from both
+ QPaintEngine and QPrintEngine. Various properties of a print engine are
+ given with property() and set with setProperty().
+
+ \sa QPaintEngine
+*/
+
+/*!
+ \enum QPrintEngine::PrintEnginePropertyKey
+
+ This enum is used to communicate properties between the print
+ engine and QPrinter. A property may or may not be supported by a
+ given print engine.
+
+ \value PPK_CollateCopies A boolean value indicating whether the
+ printout should be collated or not.
+
+ \value PPK_ColorMode Refers to QPrinter::ColorMode, either color or
+ monochrome.
+
+ \value PPK_Creator A string describing the document's creator.
+
+ \value PPK_Duplex A boolean value indicating whether both sides of
+ the printer paper should be used for the printout.
+
+ \value PPK_DocumentName A string describing the document name in
+ the spooler.
+
+ \value PPK_FontEmbedding A boolean value indicating whether data for
+ the document's fonts should be embedded in the data sent to the
+ printer.
+
+ \value PPK_FullPage A boolean describing if the printer should be
+ full page or not.
+
+ \value PPK_NumberOfCopies Obsolete. An integer specifying the number of
+ copies. Use PPK_CopyCount instead.
+
+ \value PPK_Orientation Specifies a QPrinter::Orientation value.
+
+ \value PPK_OutputFileName The output file name as a string. An
+ empty file name indicates that the printer should not print to a file.
+
+ \value PPK_PageOrder Specifies a QPrinter::PageOrder value.
+
+ \value PPK_PageRect A QRect specifying the page rectangle
+
+ \value PPK_PageSize Obsolete. Use PPK_PaperSize instead.
+
+ \value PPK_PaperRect A QRect specifying the paper rectangle.
+
+ \value PPK_PaperSource Specifies a QPrinter::PaperSource value.
+
+ \value PPK_PaperSources Specifies more than one QPrinter::PaperSource value.
+
+ \value PPK_PaperSize Specifies a QPrinter::PaperSize value.
+
+ \value PPK_PrinterName A string specifying the name of the printer.
+
+ \value PPK_PrinterProgram A string specifying the name of the
+ printer program used for printing,
+
+ \value PPK_Resolution An integer describing the dots per inch for
+ this printer.
+
+ \value PPK_SelectionOption
+
+ \value PPK_SupportedResolutions A list of integer QVariants
+ describing the set of supported resolutions that the printer has.
+
+ \value PPK_SuppressSystemPrintStatus Suppress the built-in dialog for showing
+ printing progress. As of 4.1 this only has effect on Mac OS X where, by default,
+ a status dialog is shown.
+
+ \value PPK_WindowsPageSize An integer specifying a DM_PAPER entry
+ on Windows.
+
+ \value PPK_CustomPaperSize A QSizeF specifying a custom paper size
+ in the QPrinter::Point unit.
+
+ \value PPK_PageMargins A QList<QVariant> containing the left, top,
+ right and bottom margin values.
+
+ \value PPK_CopyCount An integer specifying the number of copies to print.
+
+ \value PPK_SupportsMultipleCopies A boolean value indicating whether or not
+ the printer supports printing multiple copies in one job.
+
+ \value PPK_CustomBase Basis for extension.
+*/
+
+/*!
+ \fn QPrintEngine::~QPrintEngine()
+
+ Destroys the print engine.
+*/
+
+/*!
+ \fn void QPrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &value)
+
+ Sets the print engine's property specified by \a key to the given \a value.
+
+ \sa property()
+*/
+
+/*!
+ \fn void QPrintEngine::property(PrintEnginePropertyKey key) const
+
+ Returns the print engine's property specified by \a key.
+
+ \sa setProperty()
+*/
+
+/*!
+ \fn bool QPrintEngine::newPage()
+
+ Instructs the print engine to start a new page. Returns true if
+ the printer was able to create the new page; otherwise returns false.
+*/
+
+/*!
+ \fn bool QPrintEngine::abort()
+
+ Instructs the print engine to abort the printing process. Returns
+ true if successful; otherwise returns false.
+*/
+
+/*!
+ \fn int QPrintEngine::metric(QPaintDevice::PaintDeviceMetric id) const
+
+ Returns the metric for the given \a id.
+*/
+
+/*!
+ \fn QPrinter::PrinterState QPrintEngine::printerState() const
+
+ Returns the current state of the printer being used by the print engine.
+*/
+
+/*!
+ \fn HDC QPrintEngine::getPrinterDC() const
+ \internal
+*/
+
+/*!
+ \fn void QPrintEngine::releasePrinterDC(HDC) const
+ \internal
+*/
+
+/*
+ Returns the dimensions for the given paper size, \a size, in millimeters.
+*/
+QSizeF qt_paperSizeToQSizeF(QPrinter::PaperSize size)
+{
+ if (size == QPrinter::Custom) return QSizeF(0, 0);
+ return QSizeF(qt_paperSizes[size][0], qt_paperSizes[size][1]);
+}
+
+/*
+ Returns the PaperSize type that matches \a size, where \a size
+ is in millimeters.
+
+ Because dimensions may not always be completely accurate (for
+ example when converting between units), a particular PaperSize
+ will be returned if it matches within -1/+1 millimeters.
+*/
+QPrinter::PaperSize qSizeFTopaperSize(const QSizeF& size)
+{
+ for (int i = 0; i < static_cast<int>(QPrinter::NPaperSize); ++i) {
+ if (qt_paperSizes[i][0] >= size.width() - 1 &&
+ qt_paperSizes[i][0] <= size.width() + 1 &&
+ qt_paperSizes[i][1] >= size.height() - 1 &&
+ qt_paperSizes[i][1] <= size.height() + 1) {
+ return QPrinter::PaperSize(i);
+ }
+ }
+
+ return QPrinter::Custom;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
diff --git a/src/gui/painting/qprinter.h b/src/gui/painting/qprinter.h
new file mode 100644
index 0000000000..6a5d0b7566
--- /dev/null
+++ b/src/gui/painting/qprinter.h
@@ -0,0 +1,337 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPRINTER_H
+#define QPRINTER_H
+
+#include <QtCore/qstring.h>
+#include <QtCore/qscopedpointer.h>
+#include <QtGui/qpaintdevice.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_PRINTER
+
+#if defined(B0)
+#undef B0 // Terminal hang-up. We assume that you do not want that.
+#endif
+
+class QPrinterPrivate;
+class QPaintEngine;
+class QPrintEngine;
+class QPrinterInfo;
+
+class Q_GUI_EXPORT QPrinter : public QPaintDevice
+{
+ Q_DECLARE_PRIVATE(QPrinter)
+public:
+ enum PrinterMode { ScreenResolution, PrinterResolution, HighResolution };
+
+ explicit QPrinter(PrinterMode mode = ScreenResolution);
+ explicit QPrinter(const QPrinterInfo& printer, PrinterMode mode = ScreenResolution);
+ ~QPrinter();
+
+ int devType() const;
+
+ enum Orientation { Portrait, Landscape };
+
+#ifndef Q_QDOC
+ enum PageSize { A4, B5, Letter, Legal, Executive,
+ A0, A1, A2, A3, A5, A6, A7, A8, A9, B0, B1,
+ B10, B2, B3, B4, B6, B7, B8, B9, C5E, Comm10E,
+ DLE, Folio, Ledger, Tabloid, Custom, NPageSize = Custom, NPaperSize = Custom };
+ typedef PageSize PaperSize;
+#else
+ enum PageSize { A4, B5, Letter, Legal, Executive,
+ A0, A1, A2, A3, A5, A6, A7, A8, A9, B0, B1,
+ B10, B2, B3, B4, B6, B7, B8, B9, C5E, Comm10E,
+ DLE, Folio, Ledger, Tabloid, Custom, NPageSize = Custom };
+ enum PaperSize { A4, B5, Letter, Legal, Executive,
+ A0, A1, A2, A3, A5, A6, A7, A8, A9, B0, B1,
+ B10, B2, B3, B4, B6, B7, B8, B9, C5E, Comm10E,
+ DLE, Folio, Ledger, Tabloid, Custom, NPageSize = Custom, NPaperSize = Custom };
+#endif
+
+ enum PageOrder { FirstPageFirst,
+ LastPageFirst };
+
+ enum ColorMode { GrayScale,
+ Color };
+
+ enum PaperSource { OnlyOne,
+ Lower,
+ Middle,
+ Manual,
+ Envelope,
+ EnvelopeManual,
+ Auto,
+ Tractor,
+ SmallFormat,
+ LargeFormat,
+ LargeCapacity,
+ Cassette,
+ FormSource,
+ MaxPageSource
+ };
+
+ enum PrinterState { Idle,
+ Active,
+ Aborted,
+ Error };
+
+ enum OutputFormat { NativeFormat, PdfFormat, PostScriptFormat };
+
+ // ### Qt 5: Merge with QAbstractPrintDialog::PrintRange
+ enum PrintRange { AllPages, Selection, PageRange, CurrentPage };
+
+ enum Unit {
+ Millimeter,
+ Point,
+ Inch,
+ Pica,
+ Didot,
+ Cicero,
+ DevicePixel
+ };
+
+ enum DuplexMode {
+ DuplexNone = 0,
+ DuplexAuto,
+ DuplexLongSide,
+ DuplexShortSide
+ };
+
+#ifdef QT3_SUPPORT
+ enum PrinterOption { PrintToFile, PrintSelection, PrintPageRange };
+#endif // QT3_SUPPORT
+
+ void setOutputFormat(OutputFormat format);
+ OutputFormat outputFormat() const;
+
+ void setPrinterName(const QString &);
+ QString printerName() const;
+
+ bool isValid() const;
+
+ void setOutputFileName(const QString &);
+ QString outputFileName()const;
+
+ void setPrintProgram(const QString &);
+ QString printProgram() const;
+
+ void setDocName(const QString &);
+ QString docName() const;
+
+ void setCreator(const QString &);
+ QString creator() const;
+
+ void setOrientation(Orientation);
+ Orientation orientation() const;
+
+ void setPageSize(PageSize);
+ PageSize pageSize() const;
+
+ void setPaperSize(PaperSize);
+ PaperSize paperSize() const;
+
+ void setPaperSize(const QSizeF &paperSize, Unit unit);
+ QSizeF paperSize(Unit unit) const;
+
+ void setPageOrder(PageOrder);
+ PageOrder pageOrder() const;
+
+ void setResolution(int);
+ int resolution() const;
+
+ void setColorMode(ColorMode);
+ ColorMode colorMode() const;
+
+ void setCollateCopies(bool collate);
+ bool collateCopies() const;
+
+ void setFullPage(bool);
+ bool fullPage() const;
+
+ void setNumCopies(int);
+ int numCopies() const;
+
+ int actualNumCopies() const;
+
+ void setCopyCount(int);
+ int copyCount() const;
+ bool supportsMultipleCopies() const;
+
+ void setPaperSource(PaperSource);
+ PaperSource paperSource() const;
+
+ void setDuplex(DuplexMode duplex);
+ DuplexMode duplex() const;
+
+ QList<int> supportedResolutions() const;
+
+#ifdef Q_WS_WIN
+ QList<PaperSource> supportedPaperSources() const;
+#endif
+
+ void setFontEmbeddingEnabled(bool enable);
+ bool fontEmbeddingEnabled() const;
+
+ void setDoubleSidedPrinting(bool enable);
+ bool doubleSidedPrinting() const;
+
+#ifdef Q_WS_WIN
+ void setWinPageSize(int winPageSize);
+ int winPageSize() const;
+#endif
+
+ QRect paperRect() const;
+ QRect pageRect() const;
+ QRectF paperRect(Unit) const;
+ QRectF pageRect(Unit) const;
+
+#if !defined(Q_WS_WIN) || defined(qdoc)
+ QString printerSelectionOption() const;
+ void setPrinterSelectionOption(const QString &);
+#endif
+
+ bool newPage();
+ bool abort();
+
+ PrinterState printerState() const;
+
+ QPaintEngine *paintEngine() const;
+ QPrintEngine *printEngine() const;
+
+#ifdef Q_WS_WIN
+ HDC getDC() const;
+ void releaseDC(HDC hdc) const;
+#endif
+
+ void setFromTo(int fromPage, int toPage);
+ int fromPage() const;
+ int toPage() const;
+
+ void setPrintRange(PrintRange range);
+ PrintRange printRange() const;
+
+ void setPageMargins(qreal left, qreal top, qreal right, qreal bottom, Unit unit);
+ void getPageMargins(qreal *left, qreal *top, qreal *right, qreal *bottom, Unit unit) const;
+
+#ifdef QT3_SUPPORT
+#ifdef Q_WS_MAC
+ QT3_SUPPORT bool pageSetup(QWidget *parent = 0);
+ QT3_SUPPORT bool printSetup(QWidget *parent = 0);
+#endif
+
+ QT3_SUPPORT bool setup(QWidget *parent = 0);
+
+ QT3_SUPPORT void setMinMax(int minPage, int maxPage);
+ QT3_SUPPORT int minPage() const;
+ QT3_SUPPORT int maxPage() const;
+
+ QT3_SUPPORT void setCollateCopiesEnabled(bool);
+ QT3_SUPPORT bool collateCopiesEnabled() const;
+
+ QT3_SUPPORT void setOptionEnabled(PrinterOption, bool enable);
+ QT3_SUPPORT bool isOptionEnabled(PrinterOption) const;
+
+ inline QT3_SUPPORT QSize margins() const;
+ inline QT3_SUPPORT void margins(uint *top, uint *left, uint *bottom, uint *right) const;
+
+ inline QT3_SUPPORT bool aborted() { return printerState() == Aborted; }
+
+ QT3_SUPPORT void setOutputToFile(bool);
+ inline QT3_SUPPORT bool outputToFile() const { return !outputFileName().isEmpty(); }
+#endif
+
+protected:
+ int metric(PaintDeviceMetric) const;
+ void setEngines(QPrintEngine *printEngine, QPaintEngine *paintEngine);
+
+private:
+ void init(PrinterMode mode);
+
+ Q_DISABLE_COPY(QPrinter)
+
+ QScopedPointer<QPrinterPrivate> d_ptr;
+
+ friend class QPrintDialogPrivate;
+ friend class QAbstractPrintDialog;
+ friend class QAbstractPrintDialogPrivate;
+ friend class QPrintPreviewWidgetPrivate;
+ friend class QTextDocument;
+ friend class QPageSetupWidget;
+};
+
+#ifdef QT3_SUPPORT
+inline QSize QPrinter::margins() const
+{
+ QRect page = pageRect();
+ QRect paper = paperRect();
+ return QSize(page.left() - paper.left(), page.top() - paper.top());
+}
+
+inline void QPrinter::margins(uint *top, uint *left, uint *bottom, uint *right) const
+{
+ QRect page = pageRect();
+ QRect paper = paperRect();
+ if (top)
+ *top = page.top() - paper.top();
+ if (left)
+ *left = page.left() - paper.left();
+ if (bottom)
+ *bottom = paper.bottom() - page.bottom();
+ if (right)
+ *right = paper.right() - page.right();
+}
+#endif
+
+#endif // QT_NO_PRINTER
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPRINTER_H
diff --git a/src/gui/painting/qprinter_p.h b/src/gui/painting/qprinter_p.h
new file mode 100644
index 0000000000..556f0f06c2
--- /dev/null
+++ b/src/gui/painting/qprinter_p.h
@@ -0,0 +1,140 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPRINTER_P_H
+#define QPRINTER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+
+#include "QtCore/qglobal.h"
+
+#ifndef QT_NO_PRINTER
+
+#include "QtGui/qprinter.h"
+#include "QtGui/qprintengine.h"
+#include "QtGui/qprintdialog.h"
+#include "QtCore/qpointer.h"
+
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPrintEngine;
+class QPreviewPaintEngine;
+class QPicture;
+
+class QPrinterPrivate
+{
+ Q_DECLARE_PUBLIC(QPrinter)
+public:
+ QPrinterPrivate(QPrinter *printer)
+ : printEngine(0)
+ , paintEngine(0)
+ , q_ptr(printer)
+ , options(QAbstractPrintDialog::PrintToFile | QAbstractPrintDialog::PrintPageRange |
+ QAbstractPrintDialog::PrintCollateCopies | QAbstractPrintDialog::PrintShowPageSize)
+ , printRange(QAbstractPrintDialog::AllPages)
+ , minPage(1)
+ , maxPage(INT_MAX)
+ , fromPage(0)
+ , toPage(0)
+ , use_default_engine(true)
+ , validPrinter(false)
+ , hasCustomPageMargins(false)
+ , hasUserSetPageSize(false)
+ {
+ }
+
+ ~QPrinterPrivate() {
+
+ }
+
+ void createDefaultEngines();
+#ifndef QT_NO_PRINTPREVIEWWIDGET
+ QList<const QPicture *> previewPages() const;
+ void setPreviewMode(bool);
+#endif
+
+ void addToManualSetList(QPrintEngine::PrintEnginePropertyKey key);
+
+ QPrinter::PrinterMode printerMode;
+ QPrinter::OutputFormat outputFormat;
+ QPrintEngine *printEngine;
+ QPaintEngine *paintEngine;
+
+ QPrintEngine *realPrintEngine;
+ QPaintEngine *realPaintEngine;
+#ifndef QT_NO_PRINTPREVIEWWIDGET
+ QPreviewPaintEngine *previewEngine;
+#endif
+
+ QPrinter *q_ptr;
+
+ QAbstractPrintDialog::PrintDialogOptions options;
+ QAbstractPrintDialog::PrintRange printRange;
+ int minPage, maxPage, fromPage, toPage;
+
+ uint use_default_engine : 1;
+ uint had_default_engines : 1;
+
+ uint validPrinter : 1;
+ uint hasCustomPageMargins : 1;
+ uint hasUserSetPageSize : 1;
+
+ // Used to remember which properties have been manually set by the user.
+ QList<QPrintEngine::PrintEnginePropertyKey> manualSetList;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
+
+#endif // QPRINTER_P_H
diff --git a/src/gui/painting/qprinterinfo.cpp b/src/gui/painting/qprinterinfo.cpp
new file mode 100644
index 0000000000..f680ce1950
--- /dev/null
+++ b/src/gui/painting/qprinterinfo.cpp
@@ -0,0 +1,173 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Free Documentation License
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of this
+** file.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qprinterinfo.h"
+#include "qprinterinfo_p.h"
+
+#ifndef QT_NO_PRINTER
+
+QT_BEGIN_NAMESPACE
+
+QPrinterInfoPrivate QPrinterInfoPrivate::shared_null;
+
+
+/*!
+ \class QPrinterInfo
+
+ \brief The QPrinterInfo class gives access to information about
+ existing printers.
+
+ \ingroup printing
+
+ Use the static functions to generate a list of QPrinterInfo
+ objects. Each QPrinterInfo object in the list represents a single
+ printer and can be queried for name, supported paper sizes, and
+ whether or not it is the default printer.
+
+ \since 4.4
+*/
+
+/*!
+ \fn QList<QPrinterInfo> QPrinterInfo::availablePrinters()
+
+ Returns a list of available printers on the system.
+*/
+
+/*!
+ \fn QPrinterInfo QPrinterInfo::defaultPrinter()
+
+ Returns the default printer on the system.
+
+ The return value should be checked using isNull() before being
+ used, in case there is no default printer.
+
+ \sa isNull()
+*/
+
+/*!
+ Constructs an empty QPrinterInfo object.
+
+ \sa isNull()
+*/
+QPrinterInfo::QPrinterInfo()
+ : d_ptr(&QPrinterInfoPrivate::shared_null)
+{
+}
+
+/*!
+ Constructs a copy of \a other.
+*/
+QPrinterInfo::QPrinterInfo(const QPrinterInfo &other)
+ : d_ptr(new QPrinterInfoPrivate(*other.d_ptr))
+{
+}
+
+/*!
+ Constructs a QPrinterInfo object from \a printer.
+*/
+QPrinterInfo::QPrinterInfo(const QPrinter &printer)
+ : d_ptr(&QPrinterInfoPrivate::shared_null)
+{
+ foreach (const QPrinterInfo &printerInfo, availablePrinters()) {
+ if (printerInfo.printerName() == printer.printerName()) {
+ d_ptr.reset(new QPrinterInfoPrivate(*printerInfo.d_ptr));
+ break;
+ }
+ }
+}
+
+/*!
+ \internal
+*/
+QPrinterInfo::QPrinterInfo(const QString &name)
+ : d_ptr(new QPrinterInfoPrivate(name))
+{
+}
+
+/*!
+ Destroys the QPrinterInfo object. References to the values in the
+ object become invalid.
+*/
+QPrinterInfo::~QPrinterInfo()
+{
+}
+
+/*!
+ Sets the QPrinterInfo object to be equal to \a other.
+*/
+QPrinterInfo &QPrinterInfo::operator=(const QPrinterInfo &other)
+{
+ Q_ASSERT(d_ptr);
+ d_ptr.reset(new QPrinterInfoPrivate(*other.d_ptr));
+ return *this;
+}
+
+/*!
+ Returns the name of the printer.
+
+ \sa QPrinter::setPrinterName()
+*/
+QString QPrinterInfo::printerName() const
+{
+ const Q_D(QPrinterInfo);
+ return d->name;
+}
+
+/*!
+ Returns whether this QPrinterInfo object holds a printer definition.
+
+ An empty QPrinterInfo object could result for example from calling
+ defaultPrinter() when there are no printers on the system.
+*/
+bool QPrinterInfo::isNull() const
+{
+ const Q_D(QPrinterInfo);
+ return d == &QPrinterInfoPrivate::shared_null;
+}
+
+/*!
+ Returns whether this printer is the default printer.
+*/
+bool QPrinterInfo::isDefault() const
+{
+ const Q_D(QPrinterInfo);
+ return d->isDefault;
+}
+
+/*!
+ \fn QList< QPrinter::PaperSize> QPrinterInfo::supportedPaperSizes() const
+ \since 4.4
+
+ Returns a list of supported paper sizes by the printer.
+
+ Not all printer drivers support this query, so the list may be empty.
+ On Mac OS X 10.3, this function always returns an empty list.
+*/
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
diff --git a/src/gui/painting/qprinterinfo.h b/src/gui/painting/qprinterinfo.h
new file mode 100644
index 0000000000..34379613de
--- /dev/null
+++ b/src/gui/painting/qprinterinfo.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPRINTERINFO_H
+#define QPRINTERINFO_H
+
+#include <QtCore/QList>
+
+#include <QtGui/QPrinter>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#ifndef QT_NO_PRINTER
+class QPrinterInfoPrivate;
+class QPrinterInfoPrivateDeleter;
+class Q_GUI_EXPORT QPrinterInfo
+{
+public:
+ QPrinterInfo();
+ QPrinterInfo(const QPrinterInfo &other);
+ QPrinterInfo(const QPrinter &printer);
+ ~QPrinterInfo();
+
+ QPrinterInfo &operator=(const QPrinterInfo &other);
+
+ QString printerName() const;
+ bool isNull() const;
+ bool isDefault() const;
+ QList<QPrinter::PaperSize> supportedPaperSizes() const;
+
+ static QList<QPrinterInfo> availablePrinters();
+ static QPrinterInfo defaultPrinter();
+
+private:
+ QPrinterInfo(const QString &name);
+
+private:
+ Q_DECLARE_PRIVATE(QPrinterInfo)
+ QScopedPointer<QPrinterInfoPrivate, QPrinterInfoPrivateDeleter> d_ptr;
+};
+
+#endif // QT_NO_PRINTER
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPRINTERINFO_H
diff --git a/src/gui/painting/qprinterinfo_mac.cpp b/src/gui/painting/qprinterinfo_mac.cpp
new file mode 100644
index 0000000000..b24ab70267
--- /dev/null
+++ b/src/gui/painting/qprinterinfo_mac.cpp
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qprinterinfo.h"
+#include "qprinterinfo_p.h"
+
+#include "private/qt_mac_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_PRINTER
+
+extern QPrinter::PaperSize qSizeFTopaperSize(const QSizeF &size);
+
+QList<QPrinterInfo> QPrinterInfo::availablePrinters()
+{
+ QList<QPrinterInfo> printers;
+
+ QCFType<CFArrayRef> array;
+ if (PMServerCreatePrinterList(kPMServerLocal, &array) == noErr) {
+ CFIndex count = CFArrayGetCount(array);
+ for (int i = 0; i < count; ++i) {
+ PMPrinter printer = static_cast<PMPrinter>(const_cast<void *>(CFArrayGetValueAtIndex(array, i)));
+ QString printerName = QCFString::toQString(PMPrinterGetName(printer));
+
+ QPrinterInfo printerInfo(printerName);
+ if (PMPrinterIsDefault(printer))
+ printerInfo.d_ptr->isDefault = true;
+ printers.append(printerInfo);
+ }
+ }
+
+ return printers;
+}
+
+QPrinterInfo QPrinterInfo::defaultPrinter()
+{
+ QList<QPrinterInfo> printers = availablePrinters();
+ foreach (const QPrinterInfo &printerInfo, printers) {
+ if (printerInfo.isDefault())
+ return printerInfo;
+ }
+
+ return printers.value(0);
+}
+
+QList<QPrinter::PaperSize> QPrinterInfo::supportedPaperSizes() const
+{
+ const Q_D(QPrinterInfo);
+
+ QList<QPrinter::PaperSize> paperSizes;
+ if (isNull())
+ return paperSizes;
+
+ PMPrinter cfPrn = PMPrinterCreateFromPrinterID(QCFString::toCFStringRef(d->name));
+ if (!cfPrn)
+ return paperSizes;
+
+ CFArrayRef array;
+ if (PMPrinterGetPaperList(cfPrn, &array) != noErr) {
+ PMRelease(cfPrn);
+ return paperSizes;
+ }
+
+ int count = CFArrayGetCount(array);
+ for (int i = 0; i < count; ++i) {
+ PMPaper paper = static_cast<PMPaper>(const_cast<void *>(CFArrayGetValueAtIndex(array, i)));
+ double width, height;
+ if (PMPaperGetWidth(paper, &width) == noErr && PMPaperGetHeight(paper, &height) == noErr) {
+ QSizeF size(width * 0.3527, height * 0.3527);
+ paperSizes.append(qSizeFTopaperSize(size));
+ }
+ }
+
+ PMRelease(cfPrn);
+
+ return paperSizes;
+}
+
+#endif // QT_NO_PRINTER
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qprinterinfo_p.h b/src/gui/painting/qprinterinfo_p.h
new file mode 100644
index 0000000000..fcc1acb5d6
--- /dev/null
+++ b/src/gui/painting/qprinterinfo_p.h
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPRINTERINFO_P_H
+#define QPRINTERINFO_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtCore/qglobal.h"
+
+#ifndef QT_NO_PRINTER
+
+#include "QtCore/qlist.h"
+
+QT_BEGIN_NAMESPACE
+
+class QPrinterInfoPrivate
+{
+public:
+ QPrinterInfoPrivate(const QString& name = QString()) :
+ name(name), isDefault(false)
+#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC) && !defined(Q_OS_SYMBIAN)) || defined(Q_WS_QPA)
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ , cupsPrinterIndex(0), hasPaperSizes(false)
+#endif
+#endif
+ {}
+ ~QPrinterInfoPrivate()
+ {}
+
+ static QPrinterInfoPrivate shared_null;
+
+ QString name;
+ bool isDefault;
+
+#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC) && !defined(Q_OS_SYMBIAN)) || defined(Q_WS_QPA)
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ int cupsPrinterIndex;
+ mutable bool hasPaperSizes;
+ mutable QList<QPrinter::PaperSize> paperSizes;
+#endif
+#endif
+};
+
+
+class QPrinterInfoPrivateDeleter
+{
+public:
+ static inline void cleanup(QPrinterInfoPrivate *d)
+ {
+ if (d != &QPrinterInfoPrivate::shared_null)
+ delete d;
+ }
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_PRINTER
+
+#endif // QPRINTERINFO_P_H
diff --git a/src/gui/painting/qprinterinfo_unix.cpp b/src/gui/painting/qprinterinfo_unix.cpp
new file mode 100644
index 0000000000..38e9590b1d
--- /dev/null
+++ b/src/gui/painting/qprinterinfo_unix.cpp
@@ -0,0 +1,927 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qprinterinfo.h"
+#include "qprinterinfo_p.h"
+
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qdir.h>
+#include <qprintdialog.h>
+#include <qlibrary.h>
+#include <qtextstream.h>
+
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+# include <private/qcups_p.h>
+# include <cups/cups.h>
+# include <private/qpdf_p.h>
+#endif
+
+#include <private/qprinterinfo_unix_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_PRINTER
+
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+// preserver names in ascending order for the binary search
+static const struct NamedPaperSize {
+ const char *const name;
+ QPrinter::PaperSize size;
+} named_sizes_map[QPrinter::NPageSize] = {
+ { "A0", QPrinter::A0 },
+ { "A1", QPrinter::A1 },
+ { "A2", QPrinter::A2 },
+ { "A3", QPrinter::A3 },
+ { "A4", QPrinter::A4 },
+ { "A5", QPrinter::A5 },
+ { "A6", QPrinter::A6 },
+ { "A7", QPrinter::A7 },
+ { "A8", QPrinter::A8 },
+ { "A9", QPrinter::A9 },
+ { "B0", QPrinter::B0 },
+ { "B1", QPrinter::B1 },
+ { "B10", QPrinter::B10 },
+ { "B2", QPrinter::B2 },
+ { "B4", QPrinter::B4 },
+ { "B5", QPrinter::B5 },
+ { "B6", QPrinter::B6 },
+ { "B7", QPrinter::B7 },
+ { "B8", QPrinter::B8 },
+ { "B9", QPrinter::B9 },
+ { "C5E", QPrinter::C5E },
+ { "Comm10E", QPrinter::Comm10E },
+ { "Custom", QPrinter::Custom },
+ { "DLE", QPrinter::DLE },
+ { "Executive", QPrinter::Executive },
+ { "Folio", QPrinter::Folio },
+ { "Ledger", QPrinter::Ledger },
+ { "Legal", QPrinter::Legal },
+ { "Letter", QPrinter::Letter },
+ { "Tabloid", QPrinter::Tabloid }
+};
+
+inline bool operator<(const char *name, const NamedPaperSize &data)
+{ return qstrcmp(name, data.name) < 0; }
+inline bool operator<(const NamedPaperSize &data, const char *name)
+{ return qstrcmp(data.name, name) < 0; }
+
+static inline QPrinter::PaperSize string2PaperSize(const char *name)
+{
+ const NamedPaperSize *r = qBinaryFind(named_sizes_map, named_sizes_map + QPrinter::NPageSize, name);
+ if (r - named_sizes_map != QPrinter::NPageSize)
+ return r->size;
+ return QPrinter::Custom;
+}
+
+static inline const char *paperSize2String(QPrinter::PaperSize size)
+{
+ for (int i = 0; i < QPrinter::NPageSize; ++i) {
+ if (size == named_sizes_map[i].size)
+ return named_sizes_map[i].name;
+ }
+ return 0;
+}
+#endif
+
+void qt_perhapsAddPrinter(QList<QPrinterDescription> *printers, const QString &name,
+ QString host, QString comment,
+ QStringList aliases)
+{
+ for (int i = 0; i < printers->size(); ++i)
+ if (printers->at(i).samePrinter(name))
+ return;
+
+#ifndef QT_NO_PRINTDIALOG
+ if (host.isEmpty())
+ host = QPrintDialog::tr("locally connected");
+#endif
+ printers->append(QPrinterDescription(name.simplified(), host.simplified(), comment.simplified(), aliases));
+}
+
+void qt_parsePrinterDesc(QString printerDesc, QList<QPrinterDescription> *printers)
+{
+ if (printerDesc.length() < 1)
+ return;
+
+ printerDesc = printerDesc.simplified();
+ int i = printerDesc.indexOf(QLatin1Char(':'));
+ QString printerName, printerComment, printerHost;
+ QStringList aliases;
+
+ if (i >= 0) {
+ // have ':' want '|'
+ int j = printerDesc.indexOf(QLatin1Char('|'));
+ if (j > 0 && j < i) {
+ printerName = printerDesc.left(j);
+ aliases = printerDesc.mid(j + 1, i - j - 1).split(QLatin1Char('|'));
+#ifndef QT_NO_PRINTDIALOG
+ // try extracting a comment from the aliases
+ printerComment = QPrintDialog::tr("Aliases: %1")
+ .arg(aliases.join(QLatin1String(", ")));
+#endif
+ } else {
+ printerName = printerDesc.left(i);
+ }
+ // look for lprng pseudo all printers entry
+ i = printerDesc.indexOf(QRegExp(QLatin1String(": *all *=")));
+ if (i >= 0)
+ printerName = QString();
+ // look for signs of this being a remote printer
+ i = printerDesc.indexOf(QRegExp(QLatin1String(": *rm *=")));
+ if (i >= 0) {
+ // point k at the end of remote host name
+ while (printerDesc[i] != QLatin1Char('='))
+ i++;
+ while (printerDesc[i] == QLatin1Char('=') || printerDesc[i].isSpace())
+ i++;
+ j = i;
+ while (j < (int)printerDesc.length() && printerDesc[j] != QLatin1Char(':'))
+ j++;
+
+ // and stuff that into the string
+ printerHost = printerDesc.mid(i, j - i);
+ }
+ }
+ if (printerName.length())
+ qt_perhapsAddPrinter(printers, printerName, printerHost, printerComment,
+ aliases);
+}
+
+int qt_parsePrintcap(QList<QPrinterDescription> *printers, const QString& fileName)
+{
+ QFile printcap(fileName);
+ if (!printcap.open(QIODevice::ReadOnly))
+ return NotFound;
+
+ char *line_ascii = new char[1025];
+ line_ascii[1024] = '\0';
+
+ QString printerDesc;
+ bool atEnd = false;
+
+ while (!atEnd) {
+ if (printcap.atEnd() || printcap.readLine(line_ascii, 1024) <= 0)
+ atEnd = true;
+ QString line = QString::fromLocal8Bit(line_ascii);
+ line = line.trimmed();
+ if (line.length() >= 1 && line[int(line.length()) - 1] == QLatin1Char('\\'))
+ line.chop(1);
+ if (line[0] == QLatin1Char('#')) {
+ if (!atEnd)
+ continue;
+ } else if (line[0] == QLatin1Char('|') || line[0] == QLatin1Char(':')
+ || line.isEmpty()) {
+ printerDesc += line;
+ if (!atEnd)
+ continue;
+ }
+
+ qt_parsePrinterDesc(printerDesc, printers);
+
+ // add the first line of the new printer definition
+ printerDesc = line;
+ }
+ delete[] line_ascii;
+ return Success;
+}
+
+/*!
+ \internal
+
+ Checks $HOME/.printers for a line matching '_default <name>' (where
+ <name> does not contain any white space). The first such match
+ results in <name> being returned.
+ If no lines match then an empty string is returned.
+*/
+QString qt_getDefaultFromHomePrinters()
+{
+ QFile file(QDir::homePath() + QLatin1String("/.printers"));
+ if (!file.open(QIODevice::ReadOnly))
+ return QString();
+ QString all(QLatin1String(file.readAll()));
+ QStringList words = all.split(QRegExp(QLatin1String("\\W+")), QString::SkipEmptyParts);
+ const int i = words.indexOf(QLatin1String("_default"));
+ if (i != -1 && i < words.size() - 1)
+ return words.at(i + 1);
+ return QString();
+}
+
+// solaris, not 2.6
+void qt_parseEtcLpPrinters(QList<QPrinterDescription> *printers)
+{
+ QDir lp(QLatin1String("/etc/lp/printers"));
+ QFileInfoList dirs = lp.entryInfoList();
+ if (dirs.isEmpty())
+ return;
+
+ QString tmp;
+ for (int i = 0; i < dirs.size(); ++i) {
+ QFileInfo printer = dirs.at(i);
+ if (printer.isDir()) {
+ tmp.sprintf("/etc/lp/printers/%s/configuration",
+ printer.fileName().toAscii().data());
+ QFile configuration(tmp);
+ char *line = new char[1025];
+ QString remote(QLatin1String("Remote:"));
+ QString contentType(QLatin1String("Content types:"));
+ QString printerHost;
+ bool canPrintPostscript = false;
+ if (configuration.open(QIODevice::ReadOnly)) {
+ while (!configuration.atEnd() &&
+ configuration.readLine(line, 1024) > 0) {
+ if (QString::fromLatin1(line).startsWith(remote)) {
+ const char *p = line;
+ while (*p != ':')
+ p++;
+ p++;
+ while (isspace((uchar) *p))
+ p++;
+ printerHost = QString::fromLocal8Bit(p);
+ printerHost = printerHost.simplified();
+ } else if (QString::fromLatin1(line).startsWith(contentType)) {
+ char *p = line;
+ while (*p != ':')
+ p++;
+ p++;
+ char *e;
+ while (*p) {
+ while (isspace((uchar) *p))
+ p++;
+ if (*p) {
+ char s;
+ e = p;
+ while (isalnum((uchar) *e))
+ e++;
+ s = *e;
+ *e = '\0';
+ if (!qstrcmp(p, "postscript") ||
+ !qstrcmp(p, "any"))
+ canPrintPostscript = true;
+ *e = s;
+ if (s == ',')
+ e++;
+ p = e;
+ }
+ }
+ }
+ }
+ if (canPrintPostscript)
+ qt_perhapsAddPrinter(printers, printer.fileName(),
+ printerHost, QLatin1String(""));
+ }
+ delete[] line;
+ }
+ }
+}
+
+// solaris 2.6
+char *qt_parsePrintersConf(QList<QPrinterDescription> *printers, bool *found)
+{
+ QFile pc(QLatin1String("/etc/printers.conf"));
+ if (!pc.open(QIODevice::ReadOnly)) {
+ if (found)
+ *found = false;
+ return 0;
+ }
+ if (found)
+ *found = true;
+
+ char *line = new char[1025];
+ line[1024] = '\0';
+
+ QString printerDesc;
+ int lineLength = 0;
+
+ char *defaultPrinter = 0;
+
+ while (!pc.atEnd() &&
+ (lineLength=pc.readLine(line, 1024)) > 0) {
+ if (*line == '#') {
+ *line = '\0';
+ lineLength = 0;
+ }
+ if (lineLength >= 2 && line[lineLength-2] == '\\') {
+ line[lineLength-2] = '\0';
+ printerDesc += QString::fromLocal8Bit(line);
+ } else {
+ printerDesc += QString::fromLocal8Bit(line);
+ printerDesc = printerDesc.simplified();
+ int i = printerDesc.indexOf(QLatin1Char(':'));
+ QString printerName, printerHost, printerComment;
+ QStringList aliases;
+ if (i >= 0) {
+ // have : want |
+ int j = printerDesc.indexOf(QLatin1Char('|'));
+ if (j >= i)
+ j = -1;
+ printerName = printerDesc.mid(0, j < 0 ? i : j);
+ if (printerName == QLatin1String("_default")) {
+ i = printerDesc.indexOf(
+ QRegExp(QLatin1String(": *use *=")));
+ while (printerDesc[i] != QLatin1Char('='))
+ i++;
+ while (printerDesc[i] == QLatin1Char('=') || printerDesc[i].isSpace())
+ i++;
+ j = i;
+ while (j < (int)printerDesc.length() &&
+ printerDesc[j] != QLatin1Char(':') && printerDesc[j] != QLatin1Char(','))
+ j++;
+ // that's our default printer
+ defaultPrinter =
+ qstrdup(printerDesc.mid(i, j-i).toAscii().data());
+ printerName = QString();
+ printerDesc = QString();
+ } else if (printerName == QLatin1String("_all")) {
+ // skip it.. any other cases we want to skip?
+ printerName = QString();
+ printerDesc = QString();
+ }
+
+ if (j > 0) {
+ // try extracting a comment from the aliases
+ aliases = printerDesc.mid(j + 1, i - j - 1).split(QLatin1Char('|'));
+#ifndef QT_NO_PRINTDIALOG
+ printerComment = QPrintDialog::tr("Aliases: %1")
+ .arg(aliases.join(QLatin1String(", ")));
+#endif
+ }
+ // look for signs of this being a remote printer
+ i = printerDesc.indexOf(
+ QRegExp(QLatin1String(": *bsdaddr *=")));
+ if (i >= 0) {
+ // point k at the end of remote host name
+ while (printerDesc[i] != QLatin1Char('='))
+ i++;
+ while (printerDesc[i] == QLatin1Char('=') || printerDesc[i].isSpace())
+ i++;
+ j = i;
+ while (j < (int)printerDesc.length() &&
+ printerDesc[j] != QLatin1Char(':') && printerDesc[j] != QLatin1Char(','))
+ j++;
+ // and stuff that into the string
+ printerHost = printerDesc.mid(i, j-i);
+ // maybe stick the remote printer name into the comment
+ if (printerDesc[j] == QLatin1Char(',')) {
+ i = ++j;
+ while (printerDesc[i].isSpace())
+ i++;
+ j = i;
+ while (j < (int)printerDesc.length() &&
+ printerDesc[j] != QLatin1Char(':') && printerDesc[j] != QLatin1Char(','))
+ j++;
+ if (printerName != printerDesc.mid(i, j-i)) {
+ printerComment =
+ QLatin1String("Remote name: ");
+ printerComment += printerDesc.mid(i, j-i);
+ }
+ }
+ }
+ }
+ if (printerComment == QLatin1String(":"))
+ printerComment = QString(); // for cups
+ if (printerName.length())
+ qt_perhapsAddPrinter(printers, printerName, printerHost,
+ printerComment, aliases);
+ // chop away the line, for processing the next one
+ printerDesc = QString();
+ }
+ }
+ delete[] line;
+ return defaultPrinter;
+}
+
+#ifndef QT_NO_NIS
+
+#if defined(Q_C_CALLBACKS)
+extern "C" {
+#endif
+
+int qt_pd_foreach(int /*status */, char * /*key */, int /*keyLen */,
+ char *val, int valLen, char *data)
+{
+ qt_parsePrinterDesc(QString::fromLatin1(val, valLen), (QList<QPrinterDescription> *)data);
+ return 0;
+}
+
+#if defined(Q_C_CALLBACKS)
+}
+#endif
+
+int qt_retrieveNisPrinters(QList<QPrinterDescription> *printers)
+{
+#ifndef QT_NO_LIBRARY
+ typedef int (*WildCast)(int, char *, int, char *, int, char *);
+ char printersConfByname[] = "printers.conf.byname";
+ char *domain;
+ int err;
+
+ QLibrary lib(QLatin1String("nsl"));
+ typedef int (*ypGetDefaultDomain)(char **);
+ ypGetDefaultDomain _ypGetDefaultDomain = (ypGetDefaultDomain)lib.resolve("yp_get_default_domain");
+ typedef int (*ypAll)(const char *, const char *, const struct ypall_callback *);
+ ypAll _ypAll = (ypAll)lib.resolve("yp_all");
+
+ if (_ypGetDefaultDomain && _ypAll) {
+ err = _ypGetDefaultDomain(&domain);
+ if (err == 0) {
+ ypall_callback cb;
+ // wild cast to support K&R-style system headers
+ (WildCast &) cb.foreach = (WildCast) qt_pd_foreach;
+ cb.data = (char *) printers;
+ err = _ypAll(domain, printersConfByname, &cb);
+ }
+ if (!err)
+ return Success;
+ }
+#endif //QT_NO_LIBRARY
+ return Unavail;
+}
+
+#endif // QT_NO_NIS
+
+char *qt_parseNsswitchPrintersEntry(QList<QPrinterDescription> *printers, char *line)
+{
+#define skipSpaces() \
+ while (line[k] != '\0' && isspace((uchar) line[k])) \
+ k++
+
+ char *defaultPrinter = 0;
+ bool stop = false;
+ int lastStatus = NotFound;
+
+ int k = 8;
+ skipSpaces();
+ if (line[k] != ':')
+ return 0;
+ k++;
+
+ char *cp = strchr(line, '#');
+ if (cp != 0)
+ *cp = '\0';
+
+ while (line[k] != '\0') {
+ if (isspace((uchar) line[k])) {
+ k++;
+ } else if (line[k] == '[') {
+ k++;
+ skipSpaces();
+ while (line[k] != '\0') {
+ char status = tolower(line[k]);
+ char action = '?';
+
+ while (line[k] != '=' && line[k] != ']' && line[k] != '\0')
+ k++;
+ if (line[k] == '=') {
+ k++;
+ skipSpaces();
+ action = tolower(line[k]);
+ while (line[k] != '\0' && !isspace((uchar) line[k]) && line[k] != ']')
+ k++;
+ } else if (line[k] == ']') {
+ k++;
+ break;
+ }
+ skipSpaces();
+
+ if (lastStatus == status)
+ stop = (action == (char) Return);
+ }
+ } else {
+ if (stop)
+ break;
+
+ QByteArray source;
+ while (line[k] != '\0' && !isspace((uchar) line[k]) && line[k] != '[') {
+ source += line[k];
+ k++;
+ }
+
+ if (source == "user") {
+ lastStatus = qt_parsePrintcap(printers,
+ QDir::homePath() + QLatin1String("/.printers"));
+ } else if (source == "files") {
+ bool found;
+ defaultPrinter = qt_parsePrintersConf(printers, &found);
+ if (found)
+ lastStatus = Success;
+#ifndef QT_NO_NIS
+ } else if (source == "nis") {
+ lastStatus = qt_retrieveNisPrinters(printers);
+#endif
+ } else {
+ // nisplus, dns, etc., are not implemented yet
+ lastStatus = NotFound;
+ }
+ stop = (lastStatus == Success);
+ }
+ }
+ return defaultPrinter;
+}
+
+char *qt_parseNsswitchConf(QList<QPrinterDescription> *printers)
+{
+ QFile nc(QLatin1String("/etc/nsswitch.conf"));
+ if (!nc.open(QIODevice::ReadOnly))
+ return 0;
+
+ char *defaultPrinter = 0;
+
+ char *line = new char[1025];
+ line[1024] = '\0';
+
+ while (!nc.atEnd() &&
+ nc.readLine(line, 1024) > 0) {
+ if (qstrncmp(line, "printers", 8) == 0) {
+ defaultPrinter = qt_parseNsswitchPrintersEntry(printers, line);
+ delete[] line;
+ return defaultPrinter;
+ }
+ }
+
+ strcpy(line, "printers: user files nis nisplus xfn");
+ defaultPrinter = qt_parseNsswitchPrintersEntry(printers, line);
+ delete[] line;
+ return defaultPrinter;
+}
+
+// HP-UX
+void qt_parseEtcLpMember(QList<QPrinterDescription> *printers)
+{
+ QDir lp(QLatin1String("/etc/lp/member"));
+ if (!lp.exists())
+ return;
+ QFileInfoList dirs = lp.entryInfoList();
+ if (dirs.isEmpty())
+ return;
+
+#ifdef QT_NO_PRINTDIALOG
+ Q_UNUSED(printers);
+#else
+ QString tmp;
+ for (int i = 0; i < dirs.size(); ++i) {
+ QFileInfo printer = dirs.at(i);
+ // I haven't found any real documentation, so I'm guessing that
+ // since lpstat uses /etc/lp/member rather than one of the
+ // other directories, it's the one to use. I did not find a
+ // decent way to locate aliases and remote printers.
+ if (printer.isFile())
+ qt_perhapsAddPrinter(printers, printer.fileName(),
+ QPrintDialog::tr("unknown"),
+ QLatin1String(""));
+ }
+#endif
+}
+
+// IRIX 6.x
+void qt_parseSpoolInterface(QList<QPrinterDescription> *printers)
+{
+ QDir lp(QLatin1String("/usr/spool/lp/interface"));
+ if (!lp.exists())
+ return;
+ QFileInfoList files = lp.entryInfoList();
+ if(files.isEmpty())
+ return;
+
+ for (int i = 0; i < files.size(); ++i) {
+ QFileInfo printer = files.at(i);
+
+ if (!printer.isFile())
+ continue;
+
+ // parse out some information
+ QFile configFile(printer.filePath());
+ if (!configFile.open(QIODevice::ReadOnly))
+ continue;
+
+ QByteArray line;
+ line.resize(1025);
+ QString namePrinter;
+ QString hostName;
+ QString hostPrinter;
+ QString printerType;
+
+ QString nameKey(QLatin1String("NAME="));
+ QString typeKey(QLatin1String("TYPE="));
+ QString hostKey(QLatin1String("HOSTNAME="));
+ QString hostPrinterKey(QLatin1String("HOSTPRINTER="));
+
+ while (!configFile.atEnd() &&
+ (configFile.readLine(line.data(), 1024)) > 0) {
+ QString uline = QString::fromLocal8Bit(line);
+ if (uline.startsWith(typeKey) ) {
+ printerType = uline.mid(nameKey.length());
+ printerType = printerType.simplified();
+ } else if (uline.startsWith(hostKey)) {
+ hostName = uline.mid(hostKey.length());
+ hostName = hostName.simplified();
+ } else if (uline.startsWith(hostPrinterKey)) {
+ hostPrinter = uline.mid(hostPrinterKey.length());
+ hostPrinter = hostPrinter.simplified();
+ } else if (uline.startsWith(nameKey)) {
+ namePrinter = uline.mid(nameKey.length());
+ namePrinter = namePrinter.simplified();
+ }
+ }
+ configFile.close();
+
+ printerType = printerType.trimmed();
+ if (printerType.indexOf(QLatin1String("postscript"), 0, Qt::CaseInsensitive) < 0)
+ continue;
+
+ int ii = 0;
+ while ((ii = namePrinter.indexOf(QLatin1Char('"'), ii)) >= 0)
+ namePrinter.remove(ii, 1);
+
+ if (hostName.isEmpty() || hostPrinter.isEmpty()) {
+ qt_perhapsAddPrinter(printers, printer.fileName(),
+ QLatin1String(""), namePrinter);
+ } else {
+ QString comment;
+ comment = namePrinter;
+ comment += QLatin1String(" (");
+ comment += hostPrinter;
+ comment += QLatin1Char(')');
+ qt_perhapsAddPrinter(printers, printer.fileName(),
+ hostName, comment);
+ }
+ }
+}
+
+
+// Every unix must have its own. It's a standard. Here is AIX.
+void qt_parseQconfig(QList<QPrinterDescription> *printers)
+{
+ QFile qconfig(QLatin1String("/etc/qconfig"));
+ if (!qconfig.open(QIODevice::ReadOnly))
+ return;
+
+ QTextStream ts(&qconfig);
+ QString line;
+
+ QString stanzaName; // either a queue or a device name
+ bool up = true; // queue up? default true, can be false
+ QString remoteHost; // null if local
+ QString deviceName; // null if remote
+
+ QRegExp newStanza(QLatin1String("^[0-z\\-]*:$"));
+
+ // our basic strategy here is to process each line, detecting new
+ // stanzas. each time we see a new stanza, we check if the
+ // previous stanza was a valid queue for a) a remote printer or b)
+ // a local printer. if it wasn't, we assume that what we see is
+ // the start of the first stanza, or that the previous stanza was
+ // a device stanza, or that there is some syntax error (we don't
+ // report those).
+
+ do {
+ line = ts.readLine();
+ bool indented = line[0].isSpace();
+ line = line.simplified();
+
+ int i = line.indexOf(QLatin1Char('='));
+ if (indented && i != -1) { // line in stanza
+ QString variable = line.left(i).simplified();
+ QString value=line.mid(i+1, line.length()).simplified();
+ if (variable == QLatin1String("device"))
+ deviceName = value;
+ else if (variable == QLatin1String("host"))
+ remoteHost = value;
+ else if (variable == QLatin1String("up"))
+ up = !(value.toLower() == QLatin1String("false"));
+ } else if (line[0] == QLatin1Char('*')) { // comment
+ // nothing to do
+ } else if (ts.atEnd() || // end of file, or beginning of new stanza
+ (!indented && line.contains(newStanza))) {
+ if (up && stanzaName.length() > 0 && stanzaName.length() < 21) {
+ if (remoteHost.length()) // remote printer
+ qt_perhapsAddPrinter(printers, stanzaName, remoteHost,
+ QString());
+ else if (deviceName.length()) // local printer
+ qt_perhapsAddPrinter(printers, stanzaName, QString(),
+ QString());
+ }
+ line.chop(1);
+ if (line.length() >= 1 && line.length() <= 20)
+ stanzaName = line;
+ up = true;
+ remoteHost.clear();
+ deviceName.clear();
+ } else {
+ // syntax error? ignore.
+ }
+ } while (!ts.atEnd());
+}
+
+int qt_getLprPrinters(QList<QPrinterDescription>& printers)
+{
+ QByteArray etcLpDefault;
+ qt_parsePrintcap(&printers, QLatin1String("/etc/printcap"));
+ qt_parseEtcLpMember(&printers);
+ qt_parseSpoolInterface(&printers);
+ qt_parseQconfig(&printers);
+
+ QFileInfo f;
+ f.setFile(QLatin1String("/etc/lp/printers"));
+ if (f.isDir()) {
+ qt_parseEtcLpPrinters(&printers);
+ QFile def(QLatin1String("/etc/lp/default"));
+ if (def.open(QIODevice::ReadOnly)) {
+ etcLpDefault.resize(1025);
+ if (def.readLine(etcLpDefault.data(), 1024) > 0) {
+ QRegExp rx(QLatin1String("^(\\S+)"));
+ if (rx.indexIn(QString::fromLatin1(etcLpDefault)) != -1)
+ etcLpDefault = rx.cap(1).toAscii();
+ }
+ }
+ }
+
+ char *def = 0;
+ f.setFile(QLatin1String("/etc/nsswitch.conf"));
+ if (f.isFile()) {
+ def = qt_parseNsswitchConf(&printers);
+ } else {
+ f.setFile(QLatin1String("/etc/printers.conf"));
+ if (f.isFile())
+ def = qt_parsePrintersConf(&printers);
+ }
+
+ if (def) {
+ etcLpDefault = def;
+ delete [] def;
+ }
+
+ QString homePrintersDefault = qt_getDefaultFromHomePrinters();
+
+ // all printers hopefully known. try to find a good default
+ QString dollarPrinter;
+ {
+ dollarPrinter = QString::fromLocal8Bit(qgetenv("PRINTER"));
+ if (dollarPrinter.isEmpty())
+ dollarPrinter = QString::fromLocal8Bit(qgetenv("LPDEST"));
+ if (dollarPrinter.isEmpty())
+ dollarPrinter = QString::fromLocal8Bit(qgetenv("NPRINTER"));
+ if (dollarPrinter.isEmpty())
+ dollarPrinter = QString::fromLocal8Bit(qgetenv("NGPRINTER"));
+#ifndef QT_NO_PRINTDIALOG
+ if (!dollarPrinter.isEmpty())
+ qt_perhapsAddPrinter(&printers, dollarPrinter,
+ QPrintDialog::tr("unknown"),
+ QLatin1String(""));
+#endif
+ }
+
+ QRegExp ps(QLatin1String("[^a-z]ps(?:[^a-z]|$)"));
+ QRegExp lp(QLatin1String("[^a-z]lp(?:[^a-z]|$)"));
+
+ int quality = 0;
+ int best = 0;
+ for (int i = 0; i < printers.size(); ++i) {
+ QString name = printers.at(i).name;
+ QString comment = printers.at(i).comment;
+ if (quality < 5 && name == dollarPrinter) {
+ best = i;
+ quality = 5;
+ } else if (quality < 4 && !homePrintersDefault.isEmpty() &&
+ name == homePrintersDefault) {
+ best = i;
+ quality = 4;
+ } else if (quality < 3 && !etcLpDefault.isEmpty() &&
+ name == QLatin1String(etcLpDefault)) {
+ best = i;
+ quality = 3;
+ } else if (quality < 2 &&
+ (name == QLatin1String("ps") ||
+ ps.indexIn(comment) != -1)) {
+ best = i;
+ quality = 2;
+ } else if (quality < 1 &&
+ (name == QLatin1String("lp") ||
+ lp.indexIn(comment) > -1)) {
+ best = i;
+ quality = 1;
+ }
+ }
+
+ return best;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+QList<QPrinterInfo> QPrinterInfo::availablePrinters()
+{
+ QList<QPrinterInfo> printers;
+
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ if (QCUPSSupport::isAvailable()) {
+ QCUPSSupport cups;
+ int cupsPrinterCount = cups.availablePrintersCount();
+ const cups_dest_t* cupsPrinters = cups.availablePrinters();
+ for (int i = 0; i < cupsPrinterCount; ++i) {
+ QString printerName(QString::fromLocal8Bit(cupsPrinters[i].name));
+ if (cupsPrinters[i].instance)
+ printerName += QLatin1Char('/') + QString::fromLocal8Bit(cupsPrinters[i].instance);
+
+ QPrinterInfo printerInfo(printerName);
+ if (cupsPrinters[i].is_default)
+ printerInfo.d_ptr->isDefault = true;
+ printerInfo.d_ptr->cupsPrinterIndex = i;
+ printers.append(printerInfo);
+ }
+ } else
+#endif
+ {
+ QList<QPrinterDescription> lprPrinters;
+ int defprn = qt_getLprPrinters(lprPrinters);
+ // populating printer combo
+ foreach (const QPrinterDescription &description, lprPrinters)
+ printers.append(QPrinterInfo(description.name));
+ if (defprn >= 0 && defprn < printers.size())
+ printers[defprn].d_ptr->isDefault = true;
+ }
+
+ return printers;
+}
+
+QPrinterInfo QPrinterInfo::defaultPrinter()
+{
+ QList<QPrinterInfo> printers = availablePrinters();
+ foreach (const QPrinterInfo &printerInfo, printers) {
+ if (printerInfo.isDefault())
+ return printerInfo;
+ }
+
+ return printers.value(0);
+}
+
+QList<QPrinter::PaperSize> QPrinterInfo::supportedPaperSizes() const
+{
+#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY)
+ const Q_D(QPrinterInfo);
+
+ if (isNull())
+ return d->paperSizes;
+
+ if (!d->hasPaperSizes) {
+ d->hasPaperSizes = true;
+
+ if (QCUPSSupport::isAvailable()) {
+ // Find paper sizes from CUPS.
+ QCUPSSupport cups;
+ cups.setCurrentPrinter(d->cupsPrinterIndex);
+ const ppd_option_t* sizes = cups.pageSizes();
+ if (sizes) {
+ for (int j = 0; j < sizes->num_choices; ++j)
+ d->paperSizes.append(string2PaperSize(sizes->choices[j].choice));
+ }
+ }
+ }
+
+ return d->paperSizes;
+#else
+ return QList<QPrinter::PaperSize>();
+#endif
+}
+
+#endif // QT_NO_PRINTER
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qprinterinfo_unix_p.h b/src/gui/painting/qprinterinfo_unix_p.h
new file mode 100644
index 0000000000..c5a62cefcd
--- /dev/null
+++ b/src/gui/painting/qprinterinfo_unix_p.h
@@ -0,0 +1,125 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPRINTERINFO_UNIX_P_H
+#define QPRINTERINFO_UNIX_P_H
+
+#ifndef QT_NO_NIS
+# ifndef BOOL_DEFINED
+# define BOOL_DEFINED
+# endif
+
+# include <sys/types.h>
+# include <rpc/rpc.h>
+# include <rpcsvc/ypclnt.h>
+# include <rpcsvc/yp_prot.h>
+#endif // QT_NO_NIS
+
+#ifdef Success
+# undef Success
+#endif
+
+#include <ctype.h>
+
+QT_BEGIN_NAMESPACE
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef QT_NO_PRINTER
+
+struct QPrinterDescription {
+ QPrinterDescription(const QString &n, const QString &h, const QString &c, const QStringList &a)
+ : name(n), host(h), comment(c), aliases(a) {}
+ QString name;
+ QString host;
+ QString comment;
+ QStringList aliases;
+ bool samePrinter(const QString& printer) const {
+ return name == printer || aliases.contains(printer);
+ }
+};
+
+enum { Success = 's', Unavail = 'u', NotFound = 'n', TryAgain = 't' };
+enum { Continue = 'c', Return = 'r' };
+
+void qt_perhapsAddPrinter(QList<QPrinterDescription> *printers, const QString &name,
+ QString host, QString comment,
+ QStringList aliases = QStringList());
+void qt_parsePrinterDesc(QString printerDesc, QList<QPrinterDescription> *printers);
+
+int qt_parsePrintcap(QList<QPrinterDescription> *printers, const QString& fileName);
+QString qt_getDefaultFromHomePrinters();
+void qt_parseEtcLpPrinters(QList<QPrinterDescription> *printers);
+char *qt_parsePrintersConf(QList<QPrinterDescription> *printers, bool *found = 0);
+
+#ifndef QT_NO_NIS
+#if defined(Q_C_CALLBACKS)
+extern "C" {
+#endif
+int qt_pd_foreach(int /*status */, char * /*key */, int /*keyLen */,
+ char *val, int valLen, char *data);
+
+#if defined(Q_C_CALLBACKS)
+}
+#endif
+int qt_retrieveNisPrinters(QList<QPrinterDescription> *printers);
+#endif // QT_NO_NIS
+char *qt_parseNsswitchPrintersEntry(QList<QPrinterDescription> *printers, char *line);
+char *qt_parseNsswitchConf(QList<QPrinterDescription> *printers);
+void qt_parseEtcLpMember(QList<QPrinterDescription> *printers);
+void qt_parseSpoolInterface(QList<QPrinterDescription> *printers);
+void qt_parseQconfig(QList<QPrinterDescription> *printers);
+int qt_getLprPrinters(QList<QPrinterDescription>& printers);
+
+#endif // QT_NO_PRINTER
+
+QT_END_NAMESPACE
+
+#endif // QPRINTERINFO_UNIX_P_H
diff --git a/src/gui/painting/qprinterinfo_win.cpp b/src/gui/painting/qprinterinfo_win.cpp
new file mode 100644
index 0000000000..2c4014d8dc
--- /dev/null
+++ b/src/gui/painting/qprinterinfo_win.cpp
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qprinterinfo.h"
+#include "qprinterinfo_p.h"
+
+#include <qstringlist.h>
+
+#include <qt_windows.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_PRINTER
+
+extern QPrinter::PaperSize mapDevmodePaperSize(int s);
+
+QList<QPrinterInfo> QPrinterInfo::availablePrinters()
+{
+ QList<QPrinterInfo> printers;
+
+ DWORD needed = 0;
+ DWORD returned = 0;
+ if (!EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 4, 0, 0, &needed, &returned)) {
+ LPBYTE buffer = new BYTE[needed];
+ if (EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 4, buffer, needed, &needed, &returned)) {
+ PPRINTER_INFO_4 infoList = reinterpret_cast<PPRINTER_INFO_4>(buffer);
+ QPrinterInfo defPrn = defaultPrinter();
+ for (uint i = 0; i < returned; ++i) {
+ QString printerName(QString::fromWCharArray(infoList[i].pPrinterName));
+
+ QPrinterInfo printerInfo(printerName);
+ if (printerInfo.printerName() == defPrn.printerName())
+ printerInfo.d_ptr->isDefault = true;
+ printers.append(printerInfo);
+ }
+ }
+ delete [] buffer;
+ }
+
+ return printers;
+}
+
+QPrinterInfo QPrinterInfo::defaultPrinter()
+{
+ QString noPrinters(QLatin1String("qt_no_printers"));
+ wchar_t buffer[256];
+ GetProfileString(L"windows", L"device", (wchar_t*)noPrinters.utf16(), buffer, 256);
+ QString output = QString::fromWCharArray(buffer);
+ if (output != noPrinters) {
+ // Filter out the name of the printer, which should be everything before a comma.
+ QString printerName = output.split(QLatin1Char(',')).value(0);
+ QPrinterInfo printerInfo(printerName);
+ printerInfo.d_ptr->isDefault = true;
+ return printerInfo;
+ }
+
+ return QPrinterInfo();
+}
+
+QList<QPrinter::PaperSize> QPrinterInfo::supportedPaperSizes() const
+{
+ const Q_D(QPrinterInfo);
+
+ QList<QPrinter::PaperSize> paperSizes;
+ if (isNull())
+ return paperSizes;
+
+ DWORD size = DeviceCapabilities(reinterpret_cast<const wchar_t*>(d->name.utf16()),
+ NULL, DC_PAPERS, NULL, NULL);
+ if ((int)size != -1) {
+ wchar_t *papers = new wchar_t[size];
+ size = DeviceCapabilities(reinterpret_cast<const wchar_t*>(d->name.utf16()),
+ NULL, DC_PAPERS, papers, NULL);
+ for (int c = 0; c < (int)size; ++c)
+ paperSizes.append(mapDevmodePaperSize(papers[c]));
+ delete [] papers;
+ }
+
+ return paperSizes;
+}
+
+#endif // QT_NO_PRINTER
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qpsprinter.agl b/src/gui/painting/qpsprinter.agl
new file mode 100644
index 0000000000..137b64c53b
--- /dev/null
+++ b/src/gui/painting/qpsprinter.agl
@@ -0,0 +1,452 @@
+# the next table is derived from a list provided by Adobe on its web
+# server: http://partners.adobe.com/asn/developer/typeforum/glyphlist.txt
+
+# the start of the header comment:
+#
+# Name: Adobe Glyph List
+# Table version: 1.2
+# Date: 22 Oct 1998
+#
+# Description:
+#
+# The Adobe Glyph List (AGL) list relates Unicode values (UVs) to glyph
+# names, and should be used only as described in the document "Unicode and
+# Glyph Names," at
+# http://partners.adobe.com:80/asn/developer/type/unicodegn.html
+#
+# IMPORTANT NOTE:
+# the list contains glyphs in the private use area of unicode.
+# These should get removed when regenerating the glyphlist.
+#
+# also 0 should be mapped to .notdef
+#
+# grep '^[0-9A-F][0-9A-F][0-9A-F][0-9A-F];' < /tmp/glyphlist.txt | sed -e 's/;/, "/' -e 's-;-" }, // -' -e 's/^/ { 0x/' | sort
+#
+0x0000, ".notdef"
+0x0020, "space" # SPACE
+0x0021, "exclam" # EXCLAMATION MARK
+0x0022, "quotedbl" # QUOTATION MARK
+0x0023, "numbersign" # NUMBER SIGN
+0x0024, "dollar" # DOLLAR SIGN
+0x0025, "percent" # PERCENT SIGN
+0x0026, "ampersand" # AMPERSAND
+0x0027, "quotesingle" # APOSTROPHE
+0x0028, "parenleft" # LEFT PARENTHESIS
+0x0029, "parenright" # RIGHT PARENTHESIS
+0x002A, "asterisk" # ASTERISK
+0x002B, "plus" # PLUS SIGN
+0x002C, "comma" # COMMA
+0x002D, "hyphen" # HYPHEN-MINUS
+0x002E, "period" # FULL STOP
+0x002F, "slash" # SOLIDUS
+0x0030, "zero" # DIGIT ZERO
+0x0031, "one" # DIGIT ONE
+0x0032, "two" # DIGIT TWO
+0x0033, "three" # DIGIT THREE
+0x0034, "four" # DIGIT FOUR
+0x0035, "five" # DIGIT FIVE
+0x0036, "six" # DIGIT SIX
+0x0037, "seven" # DIGIT SEVEN
+0x0038, "eight" # DIGIT EIGHT
+0x0039, "nine" # DIGIT NINE
+0x003A, "colon" # COLON
+0x003B, "semicolon" # SEMICOLON
+0x003C, "less" # LESS-THAN SIGN
+0x003D, "equal" # EQUALS SIGN
+0x003E, "greater" # GREATER-THAN SIGN
+0x003F, "question" # QUESTION MARK
+0x0040, "at" # COMMERCIAL AT
+0x0041, "A" # LATIN CAPITAL LETTER A
+0x0042, "B" # LATIN CAPITAL LETTER B
+0x0043, "C" # LATIN CAPITAL LETTER C
+0x0044, "D" # LATIN CAPITAL LETTER D
+0x0045, "E" # LATIN CAPITAL LETTER E
+0x0046, "F" # LATIN CAPITAL LETTER F
+0x0047, "G" # LATIN CAPITAL LETTER G
+0x0048, "H" # LATIN CAPITAL LETTER H
+0x0049, "I" # LATIN CAPITAL LETTER I
+0x004A, "J" # LATIN CAPITAL LETTER J
+0x004B, "K" # LATIN CAPITAL LETTER K
+0x004C, "L" # LATIN CAPITAL LETTER L
+0x004D, "M" # LATIN CAPITAL LETTER M
+0x004E, "N" # LATIN CAPITAL LETTER N
+0x004F, "O" # LATIN CAPITAL LETTER O
+0x0050, "P" # LATIN CAPITAL LETTER P
+0x0051, "Q" # LATIN CAPITAL LETTER Q
+0x0052, "R" # LATIN CAPITAL LETTER R
+0x0053, "S" # LATIN CAPITAL LETTER S
+0x0054, "T" # LATIN CAPITAL LETTER T
+0x0055, "U" # LATIN CAPITAL LETTER U
+0x0056, "V" # LATIN CAPITAL LETTER V
+0x0057, "W" # LATIN CAPITAL LETTER W
+0x0058, "X" # LATIN CAPITAL LETTER X
+0x0059, "Y" # LATIN CAPITAL LETTER Y
+0x005A, "Z" # LATIN CAPITAL LETTER Z
+0x005B, "bracketleft" # LEFT SQUARE BRACKET
+0x005C, "backslash" # REVERSE SOLIDUS
+0x005D, "bracketright" # RIGHT SQUARE BRACKET
+0x005E, "asciicircum" # CIRCUMFLEX ACCENT
+0x005F, "underscore" # LOW LINE
+0x0060, "grave" # GRAVE ACCENT
+0x0061, "a" # LATIN SMALL LETTER A
+0x0062, "b" # LATIN SMALL LETTER B
+0x0063, "c" # LATIN SMALL LETTER C
+0x0064, "d" # LATIN SMALL LETTER D
+0x0065, "e" # LATIN SMALL LETTER E
+0x0066, "f" # LATIN SMALL LETTER F
+0x0067, "g" # LATIN SMALL LETTER G
+0x0068, "h" # LATIN SMALL LETTER H
+0x0069, "i" # LATIN SMALL LETTER I
+0x006A, "j" # LATIN SMALL LETTER J
+0x006B, "k" # LATIN SMALL LETTER K
+0x006C, "l" # LATIN SMALL LETTER L
+0x006D, "m" # LATIN SMALL LETTER M
+0x006E, "n" # LATIN SMALL LETTER N
+0x006F, "o" # LATIN SMALL LETTER O
+0x0070, "p" # LATIN SMALL LETTER P
+0x0071, "q" # LATIN SMALL LETTER Q
+0x0072, "r" # LATIN SMALL LETTER R
+0x0073, "s" # LATIN SMALL LETTER S
+0x0074, "t" # LATIN SMALL LETTER T
+0x0075, "u" # LATIN SMALL LETTER U
+0x0076, "v" # LATIN SMALL LETTER V
+0x0077, "w" # LATIN SMALL LETTER W
+0x0078, "x" # LATIN SMALL LETTER X
+0x0079, "y" # LATIN SMALL LETTER Y
+0x007A, "z" # LATIN SMALL LETTER Z
+0x007B, "braceleft" # LEFT CURLY BRACKET
+0x007C, "bar" # VERTICAL LINE
+0x007D, "braceright" # RIGHT CURLY BRACKET
+0x007E, "asciitilde" # TILDE
+0x00A0, "space" # NO-BREAK SPACE;Duplicate
+0x00A1, "exclamdown" # INVERTED EXCLAMATION MARK
+0x00A2, "cent" # CENT SIGN
+0x00A3, "sterling" # POUND SIGN
+0x00A4, "currency" # CURRENCY SIGN
+0x00A5, "yen" # YEN SIGN
+0x00A6, "brokenbar" # BROKEN BAR
+0x00A7, "section" # SECTION SIGN
+0x00A8, "dieresis" # DIAERESIS
+0x00A9, "copyright" # COPYRIGHT SIGN
+0x00AA, "ordfeminine" # FEMININE ORDINAL INDICATOR
+0x00AB, "guillemotleft" # LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0x00AC, "logicalnot" # NOT SIGN
+0x00AD, "hyphen" # SOFT HYPHEN;Duplicate
+0x00AE, "registered" # REGISTERED SIGN
+0x00AF, "macron" # MACRON
+0x00B0, "degree" # DEGREE SIGN
+0x00B1, "plusminus" # PLUS-MINUS SIGN
+0x00B2, "twosuperior" # SUPERSCRIPT TWO
+0x00B3, "threesuperior" # SUPERSCRIPT THREE
+0x00B4, "acute" # ACUTE ACCENT
+0x00B5, "mu" # MICRO SIGN
+0x00B6, "paragraph" # PILCROW SIGN
+0x00B7, "periodcentered" # MIDDLE DOT
+0x00B8, "cedilla" # CEDILLA
+0x00B9, "onesuperior" # SUPERSCRIPT ONE
+0x00BA, "ordmasculine" # MASCULINE ORDINAL INDICATOR
+0x00BB, "guillemotright" # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0x00BC, "onequarter" # VULGAR FRACTION ONE QUARTER
+0x00BD, "onehalf" # VULGAR FRACTION ONE HALF
+0x00BE, "threequarters" # VULGAR FRACTION THREE QUARTERS
+0x00BF, "questiondown" # INVERTED QUESTION MARK
+0x00C0, "Agrave" # LATIN CAPITAL LETTER A WITH GRAVE
+0x00C1, "Aacute" # LATIN CAPITAL LETTER A WITH ACUTE
+0x00C2, "Acircumflex" # LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+0x00C3, "Atilde" # LATIN CAPITAL LETTER A WITH TILDE
+0x00C4, "Adieresis" # LATIN CAPITAL LETTER A WITH DIAERESIS
+0x00C5, "Aring" # LATIN CAPITAL LETTER A WITH RING ABOVE
+0x00C6, "AE" # LATIN CAPITAL LETTER AE
+0x00C7, "Ccedilla" # LATIN CAPITAL LETTER C WITH CEDILLA
+0x00C8, "Egrave" # LATIN CAPITAL LETTER E WITH GRAVE
+0x00C9, "Eacute" # LATIN CAPITAL LETTER E WITH ACUTE
+0x00CA, "Ecircumflex" # LATIN CAPITAL LETTER E WITH CIRCUMFLEX
+0x00CB, "Edieresis" # LATIN CAPITAL LETTER E WITH DIAERESIS
+0x00CC, "Igrave" # LATIN CAPITAL LETTER I WITH GRAVE
+0x00CD, "Iacute" # LATIN CAPITAL LETTER I WITH ACUTE
+0x00CE, "Icircumflex" # LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+0x00CF, "Idieresis" # LATIN CAPITAL LETTER I WITH DIAERESIS
+0x00D0, "Eth" # LATIN CAPITAL LETTER ETH
+0x00D1, "Ntilde" # LATIN CAPITAL LETTER N WITH TILDE
+0x00D2, "Ograve" # LATIN CAPITAL LETTER O WITH GRAVE
+0x00D3, "Oacute" # LATIN CAPITAL LETTER O WITH ACUTE
+0x00D4, "Ocircumflex" # LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+0x00D5, "Otilde" # LATIN CAPITAL LETTER O WITH TILDE
+0x00D6, "Odieresis" # LATIN CAPITAL LETTER O WITH DIAERESIS
+0x00D7, "multiply" # MULTIPLICATION SIGN
+0x00D8, "Oslash" # LATIN CAPITAL LETTER O WITH STROKE
+0x00D9, "Ugrave" # LATIN CAPITAL LETTER U WITH GRAVE
+0x00DA, "Uacute" # LATIN CAPITAL LETTER U WITH ACUTE
+0x00DB, "Ucircumflex" # LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+0x00DC, "Udieresis" # LATIN CAPITAL LETTER U WITH DIAERESIS
+0x00DD, "Yacute" # LATIN CAPITAL LETTER Y WITH ACUTE
+0x00DE, "Thorn" # LATIN CAPITAL LETTER THORN
+0x00DF, "germandbls" # LATIN SMALL LETTER SHARP S
+0x00E0, "agrave" # LATIN SMALL LETTER A WITH GRAVE
+0x00E1, "aacute" # LATIN SMALL LETTER A WITH ACUTE
+0x00E2, "acircumflex" # LATIN SMALL LETTER A WITH CIRCUMFLEX
+0x00E3, "atilde" # LATIN SMALL LETTER A WITH TILDE
+0x00E4, "adieresis" # LATIN SMALL LETTER A WITH DIAERESIS
+0x00E5, "aring" # LATIN SMALL LETTER A WITH RING ABOVE
+0x00E6, "ae" # LATIN SMALL LETTER AE
+0x00E7, "ccedilla" # LATIN SMALL LETTER C WITH CEDILLA
+0x00E8, "egrave" # LATIN SMALL LETTER E WITH GRAVE
+0x00E9, "eacute" # LATIN SMALL LETTER E WITH ACUTE
+0x00EA, "ecircumflex" # LATIN SMALL LETTER E WITH CIRCUMFLEX
+0x00EB, "edieresis" # LATIN SMALL LETTER E WITH DIAERESIS
+0x00EC, "igrave" # LATIN SMALL LETTER I WITH GRAVE
+0x00ED, "iacute" # LATIN SMALL LETTER I WITH ACUTE
+0x00EE, "icircumflex" # LATIN SMALL LETTER I WITH CIRCUMFLEX
+0x00EF, "idieresis" # LATIN SMALL LETTER I WITH DIAERESIS
+0x00F0, "eth" # LATIN SMALL LETTER ETH
+0x00F1, "ntilde" # LATIN SMALL LETTER N WITH TILDE
+0x00F2, "ograve" # LATIN SMALL LETTER O WITH GRAVE
+0x00F3, "oacute" # LATIN SMALL LETTER O WITH ACUTE
+0x00F4, "ocircumflex" # LATIN SMALL LETTER O WITH CIRCUMFLEX
+0x00F5, "otilde" # LATIN SMALL LETTER O WITH TILDE
+0x00F6, "odieresis" # LATIN SMALL LETTER O WITH DIAERESIS
+0x00F7, "divide" # DIVISION SIGN
+0x00F8, "oslash" # LATIN SMALL LETTER O WITH STROKE
+0x00F9, "ugrave" # LATIN SMALL LETTER U WITH GRAVE
+0x00FA, "uacute" # LATIN SMALL LETTER U WITH ACUTE
+0x00FB, "ucircumflex" # LATIN SMALL LETTER U WITH CIRCUMFLEX
+0x00FC, "udieresis" # LATIN SMALL LETTER U WITH DIAERESIS
+0x00FD, "yacute" # LATIN SMALL LETTER Y WITH ACUTE
+0x00FE, "thorn" # LATIN SMALL LETTER THORN
+0x00FF, "ydieresis" # LATIN SMALL LETTER Y WITH DIAERESIS
+0x0100, "Amacron" # LATIN CAPITAL LETTER A WITH MACRON
+0x0101, "amacron" # LATIN SMALL LETTER A WITH MACRON
+0x0102, "Abreve" # LATIN CAPITAL LETTER A WITH BREVE
+0x0103, "abreve" # LATIN SMALL LETTER A WITH BREVE
+0x0104, "Aogonek" # LATIN CAPITAL LETTER A WITH OGONEK
+0x0105, "aogonek" # LATIN SMALL LETTER A WITH OGONEK
+0x0106, "Cacute" # LATIN CAPITAL LETTER C WITH ACUTE
+0x0107, "cacute" # LATIN SMALL LETTER C WITH ACUTE
+0x0108, "Ccircumflex" # LATIN CAPITAL LETTER C WITH CIRCUMFLEX
+0x0109, "ccircumflex" # LATIN SMALL LETTER C WITH CIRCUMFLEX
+0x010A, "Cdotaccent" # LATIN CAPITAL LETTER C WITH DOT ABOVE
+0x010B, "cdotaccent" # LATIN SMALL LETTER C WITH DOT ABOVE
+0x010C, "Ccaron" # LATIN CAPITAL LETTER C WITH CARON
+0x010D, "ccaron" # LATIN SMALL LETTER C WITH CARON
+0x010E, "Dcaron" # LATIN CAPITAL LETTER D WITH CARON
+0x010F, "dcaron" # LATIN SMALL LETTER D WITH CARON
+0x0110, "Dcroat" # LATIN CAPITAL LETTER D WITH STROKE
+0x0111, "dcroat" # LATIN SMALL LETTER D WITH STROKE
+0x0112, "Emacron" # LATIN CAPITAL LETTER E WITH MACRON
+0x0113, "emacron" # LATIN SMALL LETTER E WITH MACRON
+0x0114, "Ebreve" # LATIN CAPITAL LETTER E WITH BREVE
+0x0115, "ebreve" # LATIN SMALL LETTER E WITH BREVE
+0x0116, "Edotaccent" # LATIN CAPITAL LETTER E WITH DOT ABOVE
+0x0117, "edotaccent" # LATIN SMALL LETTER E WITH DOT ABOVE
+0x0118, "Eogonek" # LATIN CAPITAL LETTER E WITH OGONEK
+0x0119, "eogonek" # LATIN SMALL LETTER E WITH OGONEK
+0x011A, "Ecaron" # LATIN CAPITAL LETTER E WITH CARON
+0x011B, "ecaron" # LATIN SMALL LETTER E WITH CARON
+0x011C, "Gcircumflex" # LATIN CAPITAL LETTER G WITH CIRCUMFLEX
+0x011D, "gcircumflex" # LATIN SMALL LETTER G WITH CIRCUMFLEX
+0x011E, "Gbreve" # LATIN CAPITAL LETTER G WITH BREVE
+0x011F, "gbreve" # LATIN SMALL LETTER G WITH BREVE
+0x0120, "Gdotaccent" # LATIN CAPITAL LETTER G WITH DOT ABOVE
+0x0121, "gdotaccent" # LATIN SMALL LETTER G WITH DOT ABOVE
+0x0122, "Gcommaaccent" # LATIN CAPITAL LETTER G WITH CEDILLA
+0x0123, "gcommaaccent" # LATIN SMALL LETTER G WITH CEDILLA
+0x0124, "Hcircumflex" # LATIN CAPITAL LETTER H WITH CIRCUMFLEX
+0x0125, "hcircumflex" # LATIN SMALL LETTER H WITH CIRCUMFLEX
+0x0126, "Hbar" # LATIN CAPITAL LETTER H WITH STROKE
+0x0127, "hbar" # LATIN SMALL LETTER H WITH STROKE
+0x0128, "Itilde" # LATIN CAPITAL LETTER I WITH TILDE
+0x0129, "itilde" # LATIN SMALL LETTER I WITH TILDE
+0x012A, "Imacron" # LATIN CAPITAL LETTER I WITH MACRON
+0x012B, "imacron" # LATIN SMALL LETTER I WITH MACRON
+0x012C, "Ibreve" # LATIN CAPITAL LETTER I WITH BREVE
+0x012D, "ibreve" # LATIN SMALL LETTER I WITH BREVE
+0x012E, "Iogonek" # LATIN CAPITAL LETTER I WITH OGONEK
+0x012F, "iogonek" # LATIN SMALL LETTER I WITH OGONEK
+0x0130, "Idotaccent" # LATIN CAPITAL LETTER I WITH DOT ABOVE
+0x0131, "dotlessi" # LATIN SMALL LETTER DOTLESS I
+0x0132, "IJ" # LATIN CAPITAL LIGATURE IJ
+0x0133, "ij" # LATIN SMALL LIGATURE IJ
+0x0134, "Jcircumflex" # LATIN CAPITAL LETTER J WITH CIRCUMFLEX
+0x0135, "jcircumflex" # LATIN SMALL LETTER J WITH CIRCUMFLEX
+0x0136, "Kcommaaccent" # LATIN CAPITAL LETTER K WITH CEDILLA
+0x0137, "kcommaaccent" # LATIN SMALL LETTER K WITH CEDILLA
+0x0138, "kgreenlandic" # LATIN SMALL LETTER KRA
+0x0139, "Lacute" # LATIN CAPITAL LETTER L WITH ACUTE
+0x013A, "lacute" # LATIN SMALL LETTER L WITH ACUTE
+0x013B, "Lcommaaccent" # LATIN CAPITAL LETTER L WITH CEDILLA
+0x013C, "lcommaaccent" # LATIN SMALL LETTER L WITH CEDILLA
+0x013D, "Lcaron" # LATIN CAPITAL LETTER L WITH CARON
+0x013E, "lcaron" # LATIN SMALL LETTER L WITH CARON
+0x013F, "Ldot" # LATIN CAPITAL LETTER L WITH MIDDLE DOT
+0x0140, "ldot" # LATIN SMALL LETTER L WITH MIDDLE DOT
+0x0141, "Lslash" # LATIN CAPITAL LETTER L WITH STROKE
+0x0142, "lslash" # LATIN SMALL LETTER L WITH STROKE
+0x0143, "Nacute" # LATIN CAPITAL LETTER N WITH ACUTE
+0x0144, "nacute" # LATIN SMALL LETTER N WITH ACUTE
+0x0145, "Ncommaaccent" # LATIN CAPITAL LETTER N WITH CEDILLA
+0x0146, "ncommaaccent" # LATIN SMALL LETTER N WITH CEDILLA
+0x0147, "Ncaron" # LATIN CAPITAL LETTER N WITH CARON
+0x0148, "ncaron" # LATIN SMALL LETTER N WITH CARON
+0x0149, "napostrophe" # LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
+0x014A, "Eng" # LATIN CAPITAL LETTER ENG
+0x014B, "eng" # LATIN SMALL LETTER ENG
+0x014C, "Omacron" # LATIN CAPITAL LETTER O WITH MACRON
+0x014D, "omacron" # LATIN SMALL LETTER O WITH MACRON
+0x014E, "Obreve" # LATIN CAPITAL LETTER O WITH BREVE
+0x014F, "obreve" # LATIN SMALL LETTER O WITH BREVE
+0x0150, "Ohungarumlaut" # LATIN CAPITAL LETTER O WITH DOUBLE ACUTE
+0x0151, "ohungarumlaut" # LATIN SMALL LETTER O WITH DOUBLE ACUTE
+0x0152, "OE" # LATIN CAPITAL LIGATURE OE
+0x0153, "oe" # LATIN SMALL LIGATURE OE
+0x0154, "Racute" # LATIN CAPITAL LETTER R WITH ACUTE
+0x0155, "racute" # LATIN SMALL LETTER R WITH ACUTE
+0x0156, "Rcommaaccent" # LATIN CAPITAL LETTER R WITH CEDILLA
+0x0157, "rcommaaccent" # LATIN SMALL LETTER R WITH CEDILLA
+0x0158, "Rcaron" # LATIN CAPITAL LETTER R WITH CARON
+0x0159, "rcaron" # LATIN SMALL LETTER R WITH CARON
+0x015A, "Sacute" # LATIN CAPITAL LETTER S WITH ACUTE
+0x015B, "sacute" # LATIN SMALL LETTER S WITH ACUTE
+0x015C, "Scircumflex" # LATIN CAPITAL LETTER S WITH CIRCUMFLEX
+0x015D, "scircumflex" # LATIN SMALL LETTER S WITH CIRCUMFLEX
+0x015E, "Scedilla" # LATIN CAPITAL LETTER S WITH CEDILLA
+0x015F, "scedilla" # LATIN SMALL LETTER S WITH CEDILLA
+0x0160, "Scaron" # LATIN CAPITAL LETTER S WITH CARON
+0x0161, "scaron" # LATIN SMALL LETTER S WITH CARON
+0x0164, "Tcaron" # LATIN CAPITAL LETTER T WITH CARON
+0x0165, "tcaron" # LATIN SMALL LETTER T WITH CARON
+0x0166, "Tbar" # LATIN CAPITAL LETTER T WITH STROKE
+0x0167, "tbar" # LATIN SMALL LETTER T WITH STROKE
+0x0168, "Utilde" # LATIN CAPITAL LETTER U WITH TILDE
+0x0169, "utilde" # LATIN SMALL LETTER U WITH TILDE
+0x016A, "Umacron" # LATIN CAPITAL LETTER U WITH MACRON
+0x016B, "umacron" # LATIN SMALL LETTER U WITH MACRON
+0x016C, "Ubreve" # LATIN CAPITAL LETTER U WITH BREVE
+0x016D, "ubreve" # LATIN SMALL LETTER U WITH BREVE
+0x016E, "Uring" # LATIN CAPITAL LETTER U WITH RING ABOVE
+0x016F, "uring" # LATIN SMALL LETTER U WITH RING ABOVE
+0x0170, "Uhungarumlaut" # LATIN CAPITAL LETTER U WITH DOUBLE ACUTE
+0x0171, "uhungarumlaut" # LATIN SMALL LETTER U WITH DOUBLE ACUTE
+0x0172, "Uogonek" # LATIN CAPITAL LETTER U WITH OGONEK
+0x0173, "uogonek" # LATIN SMALL LETTER U WITH OGONEK
+0x0174, "Wcircumflex" # LATIN CAPITAL LETTER W WITH CIRCUMFLEX
+0x0175, "wcircumflex" # LATIN SMALL LETTER W WITH CIRCUMFLEX
+0x0176, "Ycircumflex" # LATIN CAPITAL LETTER Y WITH CIRCUMFLEX
+0x0177, "ycircumflex" # LATIN SMALL LETTER Y WITH CIRCUMFLEX
+0x0178, "Ydieresis" # LATIN CAPITAL LETTER Y WITH DIAERESIS
+0x0179, "Zacute" # LATIN CAPITAL LETTER Z WITH ACUTE
+0x017A, "zacute" # LATIN SMALL LETTER Z WITH ACUTE
+0x017B, "Zdotaccent" # LATIN CAPITAL LETTER Z WITH DOT ABOVE
+0x017C, "zdotaccent" # LATIN SMALL LETTER Z WITH DOT ABOVE
+0x017D, "Zcaron" # LATIN CAPITAL LETTER Z WITH CARON
+0x017E, "zcaron" # LATIN SMALL LETTER Z WITH CARON
+0x017F, "longs" # LATIN SMALL LETTER LONG S
+0x0192, "florin" # LATIN SMALL LETTER F WITH HOOK
+0x01A0, "Ohorn" # LATIN CAPITAL LETTER O WITH HORN
+0x01A1, "ohorn" # LATIN SMALL LETTER O WITH HORN
+0x01AF, "Uhorn" # LATIN CAPITAL LETTER U WITH HORN
+0x01B0, "uhorn" # LATIN SMALL LETTER U WITH HORN
+0x01E6, "Gcaron" # LATIN CAPITAL LETTER G WITH CARON
+0x01E7, "gcaron" # LATIN SMALL LETTER G WITH CARON
+0x01FA, "Aringacute" # LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE
+0x01FB, "aringacute" # LATIN SMALL LETTER A WITH RING ABOVE AND ACUTE
+0x01FC, "AEacute" # LATIN CAPITAL LETTER AE WITH ACUTE
+0x01FD, "aeacute" # LATIN SMALL LETTER AE WITH ACUTE
+0x01FE, "Oslashacute" # LATIN CAPITAL LETTER O WITH STROKE AND ACUTE
+0x01FF, "oslashacute" # LATIN SMALL LETTER O WITH STROKE AND ACUTE
+0x0218, "Scommaaccent" # LATIN CAPITAL LETTER S WITH COMMA BELOW
+0x0219, "scommaaccent" # LATIN SMALL LETTER S WITH COMMA BELOW
+0x021A, "Tcommaaccent" # LATIN CAPITAL LETTER T WITH COMMA BELOW
+0x021B, "tcommaaccent" # LATIN SMALL LETTER T WITH COMMA BELOW
+0x02BC, "afii57929" # MODIFIER LETTER APOSTROPHE
+0x02BD, "afii64937" # MODIFIER LETTER REVERSED COMMA
+0x02C6, "circumflex" # MODIFIER LETTER CIRCUMFLEX ACCENT
+0x02C7, "caron" # CARON
+0x02D8, "breve" # BREVE
+0x02D9, "dotaccent" # DOT ABOVE
+0x02DA, "ring" # RING ABOVE
+0x02DB, "ogonek" # OGONEK
+0x02DC, "tilde" # SMALL TILDE
+0x02DD, "hungarumlaut" # DOUBLE ACUTE ACCENT
+0x0300, "gravecomb" # COMBINING GRAVE ACCENT
+0x0301, "acutecomb" # COMBINING ACUTE ACCENT
+0x0303, "tildecomb" # COMBINING TILDE
+0x0309, "hookabovecomb" # COMBINING HOOK ABOVE
+0x0323, "dotbelowcomb" # COMBINING DOT BELOW
+0x0384, "tonos" # GREEK TONOS
+0x0385, "dieresistonos" # GREEK DIALYTIKA TONOS
+0x0386, "Alphatonos" # GREEK CAPITAL LETTER ALPHA WITH TONOS
+0x0387, "anoteleia" # GREEK ANO TELEIA
+0x0388, "Epsilontonos" # GREEK CAPITAL LETTER EPSILON WITH TONOS
+0x0389, "Etatonos" # GREEK CAPITAL LETTER ETA WITH TONOS
+0x038A, "Iotatonos" # GREEK CAPITAL LETTER IOTA WITH TONOS
+0x038C, "Omicrontonos" # GREEK CAPITAL LETTER OMICRON WITH TONOS
+0x038E, "Upsilontonos" # GREEK CAPITAL LETTER UPSILON WITH TONOS
+0x038F, "Omegatonos" # GREEK CAPITAL LETTER OMEGA WITH TONOS
+0x0390, "iotadieresistonos" # GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS
+0x0391, "Alpha" # GREEK CAPITAL LETTER ALPHA
+0x0392, "Beta" # GREEK CAPITAL LETTER BETA
+0x0393, "Gamma" # GREEK CAPITAL LETTER GAMMA
+0x0394, "Delta" # GREEK CAPITAL LETTER DELTA
+0x0395, "Epsilon" # GREEK CAPITAL LETTER EPSILON
+0x0396, "Zeta" # GREEK CAPITAL LETTER ZETA
+0x0397, "Eta" # GREEK CAPITAL LETTER ETA
+0x0398, "Theta" # GREEK CAPITAL LETTER THETA
+0x0399, "Iota" # GREEK CAPITAL LETTER IOTA
+0x039A, "Kappa" # GREEK CAPITAL LETTER KAPPA
+0x039B, "Lambda" # GREEK CAPITAL LETTER LAMDA
+0x039C, "Mu" # GREEK CAPITAL LETTER MU
+0x039D, "Nu" # GREEK CAPITAL LETTER NU
+0x039E, "Xi" # GREEK CAPITAL LETTER XI
+0x039F, "Omicron" # GREEK CAPITAL LETTER OMICRON
+0x03A0, "Pi" # GREEK CAPITAL LETTER PI
+0x03A1, "Rho" # GREEK CAPITAL LETTER RHO
+0x03A3, "Sigma" # GREEK CAPITAL LETTER SIGMA
+0x03A4, "Tau" # GREEK CAPITAL LETTER TAU
+0x03A5, "Upsilon" # GREEK CAPITAL LETTER UPSILON
+0x03A6, "Phi" # GREEK CAPITAL LETTER PHI
+0x03A7, "Chi" # GREEK CAPITAL LETTER CHI
+0x03A8, "Psi" # GREEK CAPITAL LETTER PSI
+0x03A9, "Omega" # GREEK CAPITAL LETTER OMEGA
+0x03AA, "Iotadieresis" # GREEK CAPITAL LETTER IOTA WITH DIALYTIKA
+0x03AB, "Upsilondieresis" # GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA
+0x03AC, "alphatonos" # GREEK SMALL LETTER ALPHA WITH TONOS
+0x03AD, "epsilontonos" # GREEK SMALL LETTER EPSILON WITH TONOS
+0x03AE, "etatonos" # GREEK SMALL LETTER ETA WITH TONOS
+0x03AF, "iotatonos" # GREEK SMALL LETTER IOTA WITH TONOS
+0x03B0, "upsilondieresistonos" # GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS
+0x03B1, "alpha" # GREEK SMALL LETTER ALPHA
+0x03B2, "beta" # GREEK SMALL LETTER BETA
+0x03B3, "gamma" # GREEK SMALL LETTER GAMMA
+0x03B4, "delta" # GREEK SMALL LETTER DELTA
+0x03B5, "epsilon" # GREEK SMALL LETTER EPSILON
+0x03B6, "zeta" # GREEK SMALL LETTER ZETA
+0x03B7, "eta" # GREEK SMALL LETTER ETA
+0x03B8, "theta" # GREEK SMALL LETTER THETA
+0x03B9, "iota" # GREEK SMALL LETTER IOTA
+0x03BA, "kappa" # GREEK SMALL LETTER KAPPA
+0x03BB, "lambda" # GREEK SMALL LETTER LAMDA
+0x03BC, "mu" # GREEK SMALL LETTER MU;Duplicate
+0x03BD, "nu" # GREEK SMALL LETTER NU
+0x03BE, "xi" # GREEK SMALL LETTER XI
+0x03BF, "omicron" # GREEK SMALL LETTER OMICRON
+0x03C0, "pi" # GREEK SMALL LETTER PI
+0x03C1, "rho" # GREEK SMALL LETTER RHO
+0x03C2, "sigma1" # GREEK SMALL LETTER FINAL SIGMA
+0x03C3, "sigma" # GREEK SMALL LETTER SIGMA
+0x03C4, "tau" # GREEK SMALL LETTER TAU
+0x03C5, "upsilon" # GREEK SMALL LETTER UPSILON
+0x03C6, "phi" # GREEK SMALL LETTER PHI
+0x03C7, "chi" # GREEK SMALL LETTER CHI
+0x03C8, "psi" # GREEK SMALL LETTER PSI
+0x03C9, "omega" # GREEK SMALL LETTER OMEGA
+0x03CA, "iotadieresis" # GREEK SMALL LETTER IOTA WITH DIALYTIKA
+0x03CB, "upsilondieresis" # GREEK SMALL LETTER UPSILON WITH DIALYTIKA
+0x03CC, "omicrontonos" # GREEK SMALL LETTER OMICRON WITH TONOS
+0x03CD, "upsilontonos" # GREEK SMALL LETTER UPSILON WITH TONOS
+0x03CE, "omegatonos" # GREEK SMALL LETTER OMEGA WITH TONOS
+0x03D1, "theta1" # GREEK THETA SYMBOL
+0x03D2, "Upsilon1" # GREEK UPSILON WITH HOOK SYMBOL
+0x03D5, "phi1" # GREEK PHI SYMBOL
+0x03D6, "omega1" # GREEK PI SYMBOL
+# end of stuff from glyphlist.txt
+0xFFFF, ""
diff --git a/src/gui/painting/qpsprinter.ps b/src/gui/painting/qpsprinter.ps
new file mode 100644
index 0000000000..ef3f42970b
--- /dev/null
+++ b/src/gui/painting/qpsprinter.ps
@@ -0,0 +1,449 @@
+% the postscript header we use for our qpsprinter in uncompressed and commented form.
+% use the makepsheader perl script to generate a compressed version of this header
+% you can then paste into qpsprinter.cpp
+%
+% some compression of the code is done by the makepsheader script, so we don't need to
+% write too criptically here.
+
+/BD {bind def} bind def
+/d2 {dup dup} BD
+/ED {exch def} BD
+/D0 {0 ED} BD
+
+/F {setfont} BD
+/RL {rlineto} BD
+/CM {currentmatrix} BD
+/SM {setmatrix} BD
+/TR {translate} BD
+/SD {setdash} BD
+/SC {aload pop setrgbcolor} BD
+/CR {currentfile read pop} BD
+/i {index} BD
+/scs {setcolorspace} BD
+/DB {dict dup begin} BD
+/DE {end def} BD
+/ie {ifelse} BD
+/gs {gsave} BD
+/gr {grestore} BD
+
+% these use PDF syntax
+/w {setlinewidth} BD
+/d {setdash} BD
+/J {setlinecap} BD
+/j {setlinejoin} BD
+/scn {3 array astore /BCol exch def} BD
+/SCN {3 array astore /PCol exch def} BD
+/cm {6 array astore concat} BD
+
+/m {moveto} BD
+/l {lineto} BD
+/c {curveto} BD
+/h {closepath} BD
+
+/W {clip} BD
+/W* {eoclip} BD
+/n {newpath} BD
+% ENDUNCOMPRESSED: Warning: leave this line in.
+% Everything before this line will be left untouched by the compression
+
+/q {gsave 10 dict begin} BD
+/Q {end grestore} BD
+
+% PDF operators
+/re { % PDF re operator
+ 4 2 roll % w h x y
+ moveto % w h
+ dup % w h h
+ 0 exch rlineto % w h
+ exch 0 rlineto % h
+ 0 exch neg rlineto
+ closepath
+} bind def
+
+/S {
+ gsave
+ PCol SC stroke
+ grestore
+ newpath
+} BD
+
+% PDF text operators
+/BT {gsave 10 dict begin /_m matrix currentmatrix def BCol SC} BD
+/ET {end grestore} BD
+/Tf {
+ /_fs exch def
+ findfont
+ [ _fs 0 0 _fs 0 0 ]
+ makefont
+ setfont
+} BD
+/Tm {6 array astore concat} BD
+/Td {translate} BD
+/Tj {0 0 moveto show} BD
+/BDC {pop pop} BD
+/EMC {} BD
+
+% old operators
+
+/BSt 0 def % brush style
+/WFi false def % winding fill
+
+/BCol [ 1 1 1 ] def % brush color
+/PCol [ 0 0 0 ] def % pen color
+/BDArr [ % Brush dense patterns
+ 0.94
+ 0.88
+ 0.63
+ 0.50
+ 0.37
+ 0.12
+ 0.06
+] def
+
+% -- level3 true/false
+/level3 {
+ /languagelevel where {
+ pop
+ languagelevel 3 ge
+ } { false } ifelse
+} bind def
+
+
+%% image drawing routines
+
+% defines for QCI
+/QCIgray D0 /QCIcolor D0 /QCIindex D0
+
+% this method prints color images if colorimage is available, otherwise
+% converts the string to a grayscale image and uses the reular postscript image
+% operator for printing.
+% Arguments are the same as for the image operator:
+%
+% width height bits/sample matrix datasrc QCI -
+/QCI {
+ /colorimage where {
+ pop
+ false 3 colorimage
+ }{ % the hard way, based on PD code by John Walker <kelvin@autodesk.com>
+ exec /QCIcolor exch def
+ /QCIgray QCIcolor length 3 idiv string def
+ 0 1 QCIcolor length 3 idiv 1 sub
+ { /QCIindex exch def
+ /_x QCIindex 3 mul def
+ QCIgray QCIindex
+ QCIcolor _x get 0.30 mul
+ QCIcolor _x 1 add get 0.59 mul
+ QCIcolor _x 2 add get 0.11 mul
+ add add cvi
+ put
+ } for
+ QCIgray image
+ } ifelse
+} bind def
+
+% general image drawing routine, used from the postscript driver
+%
+% Draws images with and without mask with 1, 8 and 24(rgb) bits depth.
+%
+% width height matrix image 1|8|24 mask|false x y di
+%
+% width and height specify the width/height of the image,
+% matrix a transformation matrix, image a procedure holding the image data
+% (same for mask) and x/y an additional translation.
+%
+% ### should move the translation into the matrix!!!
+/di
+{
+ gsave
+ translate
+ 1 index 1 eq { % bitmap
+ pop pop % get rid of mask and depth
+ false 3 1 roll % width height false matrix image
+ BCol SC
+ imagemask
+ } {
+ dup false ne {
+ % have a mask, see if we can use it
+ level3
+ } {
+ false
+ } ifelse
+
+ {
+ % languagelevel3, we can use image mask and dicts
+
+ % store the image mask
+ /_ma exch def
+ % select colorspace according to 8|24 bit depth and set the decode array /dc
+ 8 eq {
+ /_dc [0 1] def
+ /DeviceGray
+ } {
+ /_dc [0 1 0 1 0 1] def
+ /DeviceRGB
+ } ifelse
+ setcolorspace
+ % the image data
+ /_im exch def
+ % transformation matrix
+ /_mt exch def
+ % width and height
+ /_h exch def
+ /_w exch def
+ % and the combined image dict
+ <<
+ /ImageType 3
+ % the image dict
+ /DataDict <<
+ /ImageType 1
+ /Width _w
+ /Height _h
+ /ImageMatrix _mt
+ /DataSource _im
+ /BitsPerComponent 8
+ /Decode _dc
+ >>
+ % the mask dictionary
+ /MaskDict <<
+ /ImageType 1
+ /Width _w
+ /Height _h
+ /ImageMatrix _mt
+ /DataSource _ma
+ /BitsPerComponent 1
+ /Decode [0 1]
+ >>
+ /InterleaveType 3
+ >>
+ image
+ } {
+ pop % no mask or can't use it, get rid of it
+ 8 % width height image 8|24 8 matrix
+ 4 1 roll
+ 8 eq { % grayscale
+ image
+ } { %color
+ QCI
+ } ifelse
+ } ifelse
+ } ifelse
+ grestore
+} bind def
+
+
+/BF { % brush fill
+ gsave
+ BSt 1 eq % solid brush?
+ {
+ BCol SC
+ WFi { fill } { eofill } ifelse
+ } if
+ BSt 2 ge BSt 8 le and % dense pattern?
+ {
+ BDArr BSt 2 sub get /_sc exch def
+ % the following line scales the brush color according to the pattern. the higher the pattern the lighter the color.
+ BCol
+ {
+ 1. exch sub _sc mul 1. exch sub
+ } forall
+ 3 array astore
+ SC
+ WFi { fill } { eofill } ifelse
+ } if
+ BSt 9 ge BSt 14 le and % brush pattern?
+ {
+ WFi { clip } { eoclip } ifelse
+ pathbbox % left upper right lower
+ 3 index 3 index translate
+ 4 2 roll % right lower left upper
+ 3 2 roll % right left upper lower
+ exch % left right lower upper
+ sub /_h exch def
+ sub /_w exch def
+ BCol SC
+ 0.3 setlinewidth
+ newpath
+ BSt 9 eq BSt 11 eq or % horiz or cross pattern
+ { 0 4 _h
+ { dup 0 exch moveto _w exch lineto } for
+ } if
+ BSt 10 eq BSt 11 eq or % vert or cross pattern
+ { 0 4 _w
+ { dup 0 moveto _h lineto } for
+ } if
+ BSt 12 eq BSt 14 eq or % F-diag or diag cross
+ { _w _h gt
+ { 0 6 _w _h add
+ { dup 0 moveto _h sub _h lineto } for
+ } { 0 6 _w _h add
+ { dup 0 exch moveto _w sub _w exch lineto } for
+ } ifelse
+ } if
+ BSt 13 eq BSt 14 eq or % B-diag or diag cross
+ { _w _h gt
+ { 0 6 _w _h add
+ { dup _h moveto _h sub 0 lineto } for
+ } { 0 6 _w _h add
+ { dup _w exch moveto _w sub 0 exch lineto } for
+ } ifelse
+ } if
+ stroke
+ } if
+ BSt 15 eq
+ {
+ } if
+ BSt 24 eq % TexturePattern
+ {
+ } if
+ grestore
+} bind def
+
+% more PDF operators
+/f { /WFi true def BF newpath } bind def
+/f* { /WFi false def BF newpath } bind def
+/B { /WFi true def BF S newpath } bind def
+/B* { /WFi false def BF S newpath } bind def
+
+%% start of page
+/QI {
+ /C save def
+ pageinit
+ q
+ newpath
+} bind def
+
+%% end of page
+/QP {
+ Q % show page
+ C restore
+ showpage
+} bind def
+
+% merges one key value pair into the page device dict
+%
+% key value SPD -
+/SPD {
+ /setpagedevice where {
+ << 3 1 roll >>
+ setpagedevice
+ } { pop pop } ifelse
+} bind def
+
+
+% font handling
+
+/T1AddMapping { % basefont [glyphname ...] T1AddMapping -
+ 10 dict begin
+ /glyphs exch def
+ /fnt exch def
+ /current fnt /NumGlyphs get def
+ /CMap fnt /CMap get def
+
+ 0 1 glyphs length 1 sub % 0 1 (num glyphs - 1)
+ {
+ glyphs exch get /gn exch def
+
+ current dup % glyph_index glyph_index
+ 256 mod /min exch def % glyph_index
+ 256 idiv /maj exch def % -
+ CMap dup maj get dup % cmap cmap_maj cmap_maj
+ null eq {
+ pop 256 array
+ 0 1 255 {1 index exch /.notdef put} for
+ } if
+ dup % cmap cmap_maj cmap_maj
+ min gn put % cmap cmap_maj
+ maj exch put % -
+
+ /current current 1 add def
+ } for
+
+ fnt /CMap CMap put
+ fnt /NumGlyphs current put
+ end
+} def
+
+/T1AddGlyphs { % basefont [glyphname charstring ...] T1AddGlyphs -
+ 10 dict begin
+ /glyphs exch def
+ /fnt exch def
+ /current fnt /NumGlyphs get def
+ /CMap fnt /CMap get def
+ /CharStrings fnt /CharStrings get def
+
+ 0 1 glyphs length 2 idiv 1 sub % 0 1 (num glyphs - 1)
+ {
+ 2 mul dup
+ glyphs exch get /gn exch def
+ 1 add
+ glyphs exch get /cs exch def
+
+ current dup % glyph_index glyph_index
+ 256 mod /min exch def % glyph_index
+ 256 idiv /maj exch def % -
+ CMap dup maj get dup % cmap cmap_maj cmap_maj
+ null eq {
+ pop 256 array
+ 0 1 255 {1 index exch /.notdef put} for
+ } if
+ dup % cmap cmap_maj cmap_maj
+ min gn put % cmap cmap_maj
+ maj exch put % -
+
+ CharStrings gn cs put
+ /current current 1 add def
+ } for
+
+ fnt /CharStrings CharStrings put
+ fnt /CMap CMap put
+ fnt /NumGlyphs current put
+ end
+} def
+
+
+
+/StringAdd { % string1 string2 stringadd result
+ 1 index length 1 index length add
+ string
+ 3 1 roll
+ 2 index 0 3 index putinterval
+ 2 index 2 index length 2 index putinterval
+ pop pop
+} def
+
+
+/T1Setup { % fontname T1Setup -
+10 dict begin
+ dup /FontName exch def
+ (-Base) StringAdd cvx cvn /Font exch def
+ /MaxPage Font /NumGlyphs get 1 sub 256 idiv def
+
+ /FDepVector MaxPage 1 add array def
+ /Encoding MaxPage 1 add array def
+
+ 0 1 MaxPage {
+ dup Encoding exch dup put
+
+
+ dup /Page exch def
+ FontName (-) StringAdd
+ exch
+ 20 string cvs StringAdd % page fontname
+ cvn
+
+ Font 0 dict copy dup dup /CMap get
+ Page get
+ /Encoding exch put definefont
+ FDepVector exch Page exch put
+ } for
+
+ FontName cvn <<
+ /FontType 0
+ /FMapType 2
+ /FontMatrix[1 0 0 1 0 0]
+ /Encoding Encoding
+ /FDepVector FDepVector
+ >> definefont pop
+ end
+} def
+
diff --git a/src/gui/painting/qrasterdefs_p.h b/src/gui/painting/qrasterdefs_p.h
new file mode 100644
index 0000000000..706064c305
--- /dev/null
+++ b/src/gui/painting/qrasterdefs_p.h
@@ -0,0 +1,1279 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/***************************************************************************/
+/* */
+/* ftimage.h */
+/* */
+/* FreeType glyph image formats and default raster interface */
+/* (specification). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* Note: A `raster' is simply a scan-line converter, used to render */
+ /* QT_FT_Outlines into QT_FT_Bitmaps. */
+ /* */
+ /*************************************************************************/
+
+
+#ifndef __QT_FTIMAGE_H__
+#define __QT_FTIMAGE_H__
+
+/*
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+*/
+
+QT_FT_BEGIN_HEADER
+
+ /*************************************************************************/
+ /* */
+ /* <Section> */
+ /* basic_types */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* <Type> */
+ /* QT_FT_Pos */
+ /* */
+ /* <Description> */
+ /* The type QT_FT_Pos is a 32-bit integer used to store vectorial */
+ /* coordinates. Depending on the context, these can represent */
+ /* distances in integer font units, or 16,16, or 26.6 fixed float */
+ /* pixel coordinates. */
+ /* */
+ typedef signed int QT_FT_Pos;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Struct> */
+ /* QT_FT_Vector */
+ /* */
+ /* <Description> */
+ /* A simple structure used to store a 2D vector; coordinates are of */
+ /* the QT_FT_Pos type. */
+ /* */
+ /* <Fields> */
+ /* x :: The horizontal coordinate. */
+ /* y :: The vertical coordinate. */
+ /* */
+ typedef struct QT_FT_Vector_
+ {
+ QT_FT_Pos x;
+ QT_FT_Pos y;
+
+ } QT_FT_Vector;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Struct> */
+ /* QT_FT_BBox */
+ /* */
+ /* <Description> */
+ /* A structure used to hold an outline's bounding box, i.e., the */
+ /* coordinates of its extrema in the horizontal and vertical */
+ /* directions. */
+ /* */
+ /* <Fields> */
+ /* xMin :: The horizontal minimum (left-most). */
+ /* */
+ /* yMin :: The vertical minimum (bottom-most). */
+ /* */
+ /* xMax :: The horizontal maximum (right-most). */
+ /* */
+ /* yMax :: The vertical maximum (top-most). */
+ /* */
+ typedef struct QT_FT_BBox_
+ {
+ QT_FT_Pos xMin, yMin;
+ QT_FT_Pos xMax, yMax;
+
+ } QT_FT_BBox;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Enum> */
+ /* QT_FT_Pixel_Mode */
+ /* */
+ /* <Description> */
+ /* An enumeration type used to describe the format of pixels in a */
+ /* given bitmap. Note that additional formats may be added in the */
+ /* future. */
+ /* */
+ /* <Values> */
+ /* QT_FT_PIXEL_MODE_NONE :: */
+ /* Value 0 is reserved. */
+ /* */
+ /* QT_FT_PIXEL_MODE_MONO :: */
+ /* A monochrome bitmap, using 1 bit per pixel. Note that pixels */
+ /* are stored in most-significant order (MSB), which means that */
+ /* the left-most pixel in a byte has value 128. */
+ /* */
+ /* QT_FT_PIXEL_MODE_GRAY :: */
+ /* An 8-bit bitmap, generally used to represent anti-aliased glyph */
+ /* images. Each pixel is stored in one byte. Note that the number */
+ /* of value "gray" levels is stored in the `num_bytes' field of */
+ /* the @QT_FT_Bitmap structure (it generally is 256). */
+ /* */
+ /* QT_FT_PIXEL_MODE_GRAY2 :: */
+ /* A 2-bit/pixel bitmap, used to represent embedded anti-aliased */
+ /* bitmaps in font files according to the OpenType specification. */
+ /* We haven't found a single font using this format, however. */
+ /* */
+ /* QT_FT_PIXEL_MODE_GRAY4 :: */
+ /* A 4-bit/pixel bitmap, used to represent embedded anti-aliased */
+ /* bitmaps in font files according to the OpenType specification. */
+ /* We haven't found a single font using this format, however. */
+ /* */
+ /* QT_FT_PIXEL_MODE_LCD :: */
+ /* An 8-bit bitmap, used to represent RGB or BGR decimated glyph */
+ /* images used for display on LCD displays; the bitmap's width is */
+ /* three times wider than the original glyph image. See also */
+ /* @QT_FT_RENDER_MODE_LCD. */
+ /* */
+ /* QT_FT_PIXEL_MODE_LCD_V :: */
+ /* An 8-bit bitmap, used to represent RGB or BGR decimated glyph */
+ /* images used for display on rotated LCD displays; the bitmap's */
+ /* height is three times taller than the original glyph image. */
+ /* See also @QT_FT_RENDER_MODE_LCD_V. */
+ /* */
+ typedef enum QT_FT_Pixel_Mode_
+ {
+ QT_FT_PIXEL_MODE_NONE = 0,
+ QT_FT_PIXEL_MODE_MONO,
+ QT_FT_PIXEL_MODE_GRAY,
+ QT_FT_PIXEL_MODE_GRAY2,
+ QT_FT_PIXEL_MODE_GRAY4,
+ QT_FT_PIXEL_MODE_LCD,
+ QT_FT_PIXEL_MODE_LCD_V,
+
+ QT_FT_PIXEL_MODE_MAX /* do not remove */
+
+ } QT_FT_Pixel_Mode;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Enum> */
+ /* qt_ft_pixel_mode_xxx */
+ /* */
+ /* <Description> */
+ /* A list of deprecated constants. Use the corresponding */
+ /* @QT_FT_Pixel_Mode values instead. */
+ /* */
+ /* <Values> */
+ /* qt_ft_pixel_mode_none :: see @QT_FT_PIXEL_MODE_NONE */
+ /* qt_ft_pixel_mode_mono :: see @QT_FT_PIXEL_MODE_MONO */
+ /* qt_ft_pixel_mode_grays :: see @QT_FT_PIXEL_MODE_GRAY */
+ /* qt_ft_pixel_mode_pal2 :: see @QT_FT_PIXEL_MODE_GRAY2 */
+ /* qt_ft_pixel_mode_pal4 :: see @QT_FT_PIXEL_MODE_GRAY4 */
+ /* */
+#define qt_ft_pixel_mode_none QT_FT_PIXEL_MODE_NONE
+#define qt_ft_pixel_mode_mono QT_FT_PIXEL_MODE_MONO
+#define qt_ft_pixel_mode_grays QT_FT_PIXEL_MODE_GRAY
+#define qt_ft_pixel_mode_pal2 QT_FT_PIXEL_MODE_GRAY2
+#define qt_ft_pixel_mode_pal4 QT_FT_PIXEL_MODE_GRAY4
+
+ /* */
+
+#if 0
+
+ /*************************************************************************/
+ /* */
+ /* <Enum> */
+ /* QT_FT_Palette_Mode */
+ /* */
+ /* <Description> */
+ /* THIS TYPE IS DEPRECATED. DO NOT USE IT! */
+ /* */
+ /* An enumeration type used to describe the format of a bitmap */
+ /* palette, used with qt_ft_pixel_mode_pal4 and qt_ft_pixel_mode_pal8. */
+ /* */
+ /* <Fields> */
+ /* qt_ft_palette_mode_rgb :: The palette is an array of 3-bytes RGB */
+ /* records. */
+ /* */
+ /* qt_ft_palette_mode_rgba :: The palette is an array of 4-bytes RGBA */
+ /* records. */
+ /* */
+ /* <Note> */
+ /* As qt_ft_pixel_mode_pal2, pal4 and pal8 are currently unused by */
+ /* FreeType, these types are not handled by the library itself. */
+ /* */
+ typedef enum QT_FT_Palette_Mode_
+ {
+ qt_ft_palette_mode_rgb = 0,
+ qt_ft_palette_mode_rgba,
+
+ qt_ft_palettte_mode_max /* do not remove */
+
+ } QT_FT_Palette_Mode;
+
+ /* */
+
+#endif
+
+
+ /*************************************************************************/
+ /* */
+ /* <Struct> */
+ /* QT_FT_Bitmap */
+ /* */
+ /* <Description> */
+ /* A structure used to describe a bitmap or pixmap to the raster. */
+ /* Note that we now manage pixmaps of various depths through the */
+ /* `pixel_mode' field. */
+ /* */
+ /* <Fields> */
+ /* rows :: The number of bitmap rows. */
+ /* */
+ /* width :: The number of pixels in bitmap row. */
+ /* */
+ /* pitch :: The pitch's absolute value is the number of bytes */
+ /* taken by one bitmap row, including padding. */
+ /* However, the pitch is positive when the bitmap has */
+ /* a `down' flow, and negative when it has an `up' */
+ /* flow. In all cases, the pitch is an offset to add */
+ /* to a bitmap pointer in order to go down one row. */
+ /* */
+ /* buffer :: A typeless pointer to the bitmap buffer. This */
+ /* value should be aligned on 32-bit boundaries in */
+ /* most cases. */
+ /* */
+ /* num_grays :: This field is only used with */
+ /* `QT_FT_PIXEL_MODE_GRAY'; it gives the number of gray */
+ /* levels used in the bitmap. */
+ /* */
+ /* pixel_mode :: The pixel mode, i.e., how pixel bits are stored. */
+ /* See @QT_FT_Pixel_Mode for possible values. */
+ /* */
+ /* palette_mode :: This field is only used with paletted pixel modes; */
+ /* it indicates how the palette is stored. */
+ /* */
+ /* palette :: A typeless pointer to the bitmap palette; only */
+ /* used for paletted pixel modes. */
+ /* */
+ /* <Note> */
+ /* For now, the only pixel mode supported by FreeType are mono and */
+ /* grays. However, drivers might be added in the future to support */
+ /* more `colorful' options. */
+ /* */
+ /* When using pixel modes pal2, pal4 and pal8 with a void `palette' */
+ /* field, a gray pixmap with respectively 4, 16, and 256 levels of */
+ /* gray is assumed. This, in order to be compatible with some */
+ /* embedded bitmap formats defined in the TrueType specification. */
+ /* */
+ /* Note that no font was found presenting such embedded bitmaps, so */
+ /* this is currently completely unhandled by the library. */
+ /* */
+ typedef struct QT_FT_Bitmap_
+ {
+ int rows;
+ int width;
+ int pitch;
+ unsigned char* buffer;
+ short num_grays;
+ char pixel_mode;
+ char palette_mode;
+ void* palette;
+
+ } QT_FT_Bitmap;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Section> */
+ /* outline_processing */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* <Struct> */
+ /* QT_FT_Outline */
+ /* */
+ /* <Description> */
+ /* This structure is used to describe an outline to the scan-line */
+ /* converter. */
+ /* */
+ /* <Fields> */
+ /* n_contours :: The number of contours in the outline. */
+ /* */
+ /* n_points :: The number of points in the outline. */
+ /* */
+ /* points :: A pointer to an array of `n_points' QT_FT_Vector */
+ /* elements, giving the outline's point coordinates. */
+ /* */
+ /* tags :: A pointer to an array of `n_points' chars, giving */
+ /* each outline point's type. If bit 0 is unset, the */
+ /* point is `off' the curve, i.e. a Bezier control */
+ /* point, while it is `on' when set. */
+ /* */
+ /* Bit 1 is meaningful for `off' points only. If set, */
+ /* it indicates a third-order Bezier arc control point; */
+ /* and a second-order control point if unset. */
+ /* */
+ /* contours :: An array of `n_contours' shorts, giving the end */
+ /* point of each contour within the outline. For */
+ /* example, the first contour is defined by the points */
+ /* `0' to `contours[0]', the second one is defined by */
+ /* the points `contours[0]+1' to `contours[1]', etc. */
+ /* */
+ /* flags :: A set of bit flags used to characterize the outline */
+ /* and give hints to the scan-converter and hinter on */
+ /* how to convert/grid-fit it. See QT_FT_Outline_Flags. */
+ /* */
+ typedef struct QT_FT_Outline_
+ {
+ int n_contours; /* number of contours in glyph */
+ int n_points; /* number of points in the glyph */
+
+ QT_FT_Vector* points; /* the outline's points */
+ char* tags; /* the points flags */
+ int* contours; /* the contour end points */
+
+ int flags; /* outline masks */
+
+ } QT_FT_Outline;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Enum> */
+ /* QT_FT_OUTLINE_FLAGS */
+ /* */
+ /* <Description> */
+ /* A list of bit-field constants use for the flags in an outline's */
+ /* `flags' field. */
+ /* */
+ /* <Values> */
+ /* QT_FT_OUTLINE_NONE :: Value 0 is reserved. */
+ /* */
+ /* QT_FT_OUTLINE_OWNER :: If set, this flag indicates that the */
+ /* outline's field arrays (i.e. */
+ /* `points', `flags' & `contours') are */
+ /* `owned' by the outline object, and */
+ /* should thus be freed when it is */
+ /* destroyed. */
+ /* */
+ /* QT_FT_OUTLINE_EVEN_ODD_FILL :: By default, outlines are filled using */
+ /* the non-zero winding rule. If set to */
+ /* 1, the outline will be filled using */
+ /* the even-odd fill rule (only works */
+ /* with the smooth raster). */
+ /* */
+ /* QT_FT_OUTLINE_REVERSE_FILL :: By default, outside contours of an */
+ /* outline are oriented in clock-wise */
+ /* direction, as defined in the TrueType */
+ /* specification. This flag is set if */
+ /* the outline uses the opposite */
+ /* direction (typically for Type 1 */
+ /* fonts). This flag is ignored by the */
+ /* scan-converter. However, it is very */
+ /* important for the auto-hinter. */
+ /* */
+ /* QT_FT_OUTLINE_IGNORE_DROPOUTS :: By default, the scan converter will */
+ /* try to detect drop-outs in an outline */
+ /* and correct the glyph bitmap to */
+ /* ensure consistent shape continuity. */
+ /* If set, this flag hints the scan-line */
+ /* converter to ignore such cases. */
+ /* */
+ /* QT_FT_OUTLINE_HIGH_PRECISION :: This flag indicates that the */
+ /* scan-line converter should try to */
+ /* convert this outline to bitmaps with */
+ /* the highest possible quality. It is */
+ /* typically set for small character */
+ /* sizes. Note that this is only a */
+ /* hint, that might be completely */
+ /* ignored by a given scan-converter. */
+ /* */
+ /* QT_FT_OUTLINE_SINGLE_PASS :: This flag is set to force a given */
+ /* scan-converter to only use a single */
+ /* pass over the outline to render a */
+ /* bitmap glyph image. Normally, it is */
+ /* set for very large character sizes. */
+ /* It is only a hint, that might be */
+ /* completely ignored by a given */
+ /* scan-converter. */
+ /* */
+#define QT_FT_OUTLINE_NONE 0x0
+#define QT_FT_OUTLINE_OWNER 0x1
+#define QT_FT_OUTLINE_EVEN_ODD_FILL 0x2
+#define QT_FT_OUTLINE_REVERSE_FILL 0x4
+#define QT_FT_OUTLINE_IGNORE_DROPOUTS 0x8
+
+#define QT_FT_OUTLINE_HIGH_PRECISION 0x100
+#define QT_FT_OUTLINE_SINGLE_PASS 0x200
+
+
+ /*************************************************************************
+ *
+ * @enum:
+ * qt_ft_outline_flags
+ *
+ * @description:
+ * These constants are deprecated. Please use the corresponding
+ * @QT_FT_OUTLINE_FLAGS values.
+ *
+ * @values:
+ * qt_ft_outline_none :: See @QT_FT_OUTLINE_NONE.
+ * qt_ft_outline_owner :: See @QT_FT_OUTLINE_OWNER.
+ * qt_ft_outline_even_odd_fill :: See @QT_FT_OUTLINE_EVEN_ODD_FILL.
+ * qt_ft_outline_reverse_fill :: See @QT_FT_OUTLINE_REVERSE_FILL.
+ * qt_ft_outline_ignore_dropouts :: See @QT_FT_OUTLINE_IGNORE_DROPOUTS.
+ * qt_ft_outline_high_precision :: See @QT_FT_OUTLINE_HIGH_PRECISION.
+ * qt_ft_outline_single_pass :: See @QT_FT_OUTLINE_SINGLE_PASS.
+ */
+#define qt_ft_outline_none QT_FT_OUTLINE_NONE
+#define qt_ft_outline_owner QT_FT_OUTLINE_OWNER
+#define qt_ft_outline_even_odd_fill QT_FT_OUTLINE_EVEN_ODD_FILL
+#define qt_ft_outline_reverse_fill QT_FT_OUTLINE_REVERSE_FILL
+#define qt_ft_outline_ignore_dropouts QT_FT_OUTLINE_IGNORE_DROPOUTS
+#define qt_ft_outline_high_precision QT_FT_OUTLINE_HIGH_PRECISION
+#define qt_ft_outline_single_pass QT_FT_OUTLINE_SINGLE_PASS
+
+ /* */
+
+#define QT_FT_CURVE_TAG( flag ) ( flag & 3 )
+
+#define QT_FT_CURVE_TAG_ON 1
+#define QT_FT_CURVE_TAG_CONIC 0
+#define QT_FT_CURVE_TAG_CUBIC 2
+
+#define QT_FT_CURVE_TAG_TOUCH_X 8 /* reserved for the TrueType hinter */
+#define QT_FT_CURVE_TAG_TOUCH_Y 16 /* reserved for the TrueType hinter */
+
+#define QT_FT_CURVE_TAG_TOUCH_BOTH ( QT_FT_CURVE_TAG_TOUCH_X | \
+ QT_FT_CURVE_TAG_TOUCH_Y )
+
+#define QT_FT_Curve_Tag_On QT_FT_CURVE_TAG_ON
+#define QT_FT_Curve_Tag_Conic QT_FT_CURVE_TAG_CONIC
+#define QT_FT_Curve_Tag_Cubic QT_FT_CURVE_TAG_CUBIC
+#define QT_FT_Curve_Tag_Touch_X QT_FT_CURVE_TAG_TOUCH_X
+#define QT_FT_Curve_Tag_Touch_Y QT_FT_CURVE_TAG_TOUCH_Y
+
+ /*************************************************************************/
+ /* */
+ /* <FuncType> */
+ /* QT_FT_Outline_MoveToFunc */
+ /* */
+ /* <Description> */
+ /* A function pointer type used to describe the signature of a `move */
+ /* to' function during outline walking/decomposition. */
+ /* */
+ /* A `move to' is emitted to start a new contour in an outline. */
+ /* */
+ /* <Input> */
+ /* to :: A pointer to the target point of the `move to'. */
+ /* */
+ /* user :: A typeless pointer which is passed from the caller of the */
+ /* decomposition function. */
+ /* */
+ /* <Return> */
+ /* Error code. 0 means success. */
+ /* */
+ typedef int
+ (*QT_FT_Outline_MoveToFunc)( QT_FT_Vector* to,
+ void* user );
+
+#define QT_FT_Outline_MoveTo_Func QT_FT_Outline_MoveToFunc
+
+ /*************************************************************************/
+ /* */
+ /* <FuncType> */
+ /* QT_FT_Outline_LineToFunc */
+ /* */
+ /* <Description> */
+ /* A function pointer type used to describe the signature of a `line */
+ /* to' function during outline walking/decomposition. */
+ /* */
+ /* A `line to' is emitted to indicate a segment in the outline. */
+ /* */
+ /* <Input> */
+ /* to :: A pointer to the target point of the `line to'. */
+ /* */
+ /* user :: A typeless pointer which is passed from the caller of the */
+ /* decomposition function. */
+ /* */
+ /* <Return> */
+ /* Error code. 0 means success. */
+ /* */
+ typedef int
+ (*QT_FT_Outline_LineToFunc)( QT_FT_Vector* to,
+ void* user );
+
+#define QT_FT_Outline_LineTo_Func QT_FT_Outline_LineToFunc
+
+ /*************************************************************************/
+ /* */
+ /* <FuncType> */
+ /* QT_FT_Outline_ConicToFunc */
+ /* */
+ /* <Description> */
+ /* A function pointer type use to describe the signature of a `conic */
+ /* to' function during outline walking/decomposition. */
+ /* */
+ /* A `conic to' is emitted to indicate a second-order Bezier arc in */
+ /* the outline. */
+ /* */
+ /* <Input> */
+ /* control :: An intermediate control point between the last position */
+ /* and the new target in `to'. */
+ /* */
+ /* to :: A pointer to the target end point of the conic arc. */
+ /* */
+ /* user :: A typeless pointer which is passed from the caller of */
+ /* the decomposition function. */
+ /* */
+ /* <Return> */
+ /* Error code. 0 means success. */
+ /* */
+ typedef int
+ (*QT_FT_Outline_ConicToFunc)( QT_FT_Vector* control,
+ QT_FT_Vector* to,
+ void* user );
+
+#define QT_FT_Outline_ConicTo_Func QT_FT_Outline_ConicToFunc
+
+ /*************************************************************************/
+ /* */
+ /* <FuncType> */
+ /* QT_FT_Outline_CubicToFunc */
+ /* */
+ /* <Description> */
+ /* A function pointer type used to describe the signature of a `cubic */
+ /* to' function during outline walking/decomposition. */
+ /* */
+ /* A `cubic to' is emitted to indicate a third-order Bezier arc. */
+ /* */
+ /* <Input> */
+ /* control1 :: A pointer to the first Bezier control point. */
+ /* */
+ /* control2 :: A pointer to the second Bezier control point. */
+ /* */
+ /* to :: A pointer to the target end point. */
+ /* */
+ /* user :: A typeless pointer which is passed from the caller of */
+ /* the decomposition function. */
+ /* */
+ /* <Return> */
+ /* Error code. 0 means success. */
+ /* */
+ typedef int
+ (*QT_FT_Outline_CubicToFunc)( QT_FT_Vector* control1,
+ QT_FT_Vector* control2,
+ QT_FT_Vector* to,
+ void* user );
+
+#define QT_FT_Outline_CubicTo_Func QT_FT_Outline_CubicToFunc
+
+
+ /*************************************************************************/
+ /* */
+ /* <Struct> */
+ /* QT_FT_Outline_Funcs */
+ /* */
+ /* <Description> */
+ /* A structure to hold various function pointers used during outline */
+ /* decomposition in order to emit segments, conic, and cubic Beziers, */
+ /* as well as `move to' and `close to' operations. */
+ /* */
+ /* <Fields> */
+ /* move_to :: The `move to' emitter. */
+ /* */
+ /* line_to :: The segment emitter. */
+ /* */
+ /* conic_to :: The second-order Bezier arc emitter. */
+ /* */
+ /* cubic_to :: The third-order Bezier arc emitter. */
+ /* */
+ /* shift :: The shift that is applied to coordinates before they */
+ /* are sent to the emitter. */
+ /* */
+ /* delta :: The delta that is applied to coordinates before they */
+ /* are sent to the emitter, but after the shift. */
+ /* */
+ /* <Note> */
+ /* The point coordinates sent to the emitters are the transformed */
+ /* version of the original coordinates (this is important for high */
+ /* accuracy during scan-conversion). The transformation is simple: */
+ /* */
+ /* x' = (x << shift) - delta */
+ /* y' = (x << shift) - delta */
+ /* */
+ /* Set the value of `shift' and `delta' to 0 to get the original */
+ /* point coordinates. */
+ /* */
+ typedef struct QT_FT_Outline_Funcs_
+ {
+ QT_FT_Outline_MoveToFunc move_to;
+ QT_FT_Outline_LineToFunc line_to;
+ QT_FT_Outline_ConicToFunc conic_to;
+ QT_FT_Outline_CubicToFunc cubic_to;
+
+ int shift;
+ QT_FT_Pos delta;
+
+ } QT_FT_Outline_Funcs;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Section> */
+ /* basic_types */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* <Macro> */
+ /* QT_FT_IMAGE_TAG */
+ /* */
+ /* <Description> */
+ /* This macro converts four letter tags into an unsigned long. */
+ /* */
+ /* <Note> */
+ /* Since many 16bit compilers don't like 32bit enumerations, you */
+ /* should redefine this macro in case of problems to something like */
+ /* this: */
+ /* */
+ /* #define QT_FT_IMAGE_TAG( value, _x1, _x2, _x3, _x4 ) value */
+ /* */
+ /* to get a simple enumeration without assigning special numbers. */
+ /* */
+#ifndef QT_FT_IMAGE_TAG
+#define QT_FT_IMAGE_TAG( value, _x1, _x2, _x3, _x4 ) \
+ value = ( ( (unsigned long)_x1 << 24 ) | \
+ ( (unsigned long)_x2 << 16 ) | \
+ ( (unsigned long)_x3 << 8 ) | \
+ (unsigned long)_x4 )
+#endif /* QT_FT_IMAGE_TAG */
+
+
+ /*************************************************************************/
+ /* */
+ /* <Enum> */
+ /* QT_FT_Glyph_Format */
+ /* */
+ /* <Description> */
+ /* An enumeration type used to describe the format of a given glyph */
+ /* image. Note that this version of FreeType only supports two image */
+ /* formats, even though future font drivers will be able to register */
+ /* their own format. */
+ /* */
+ /* <Values> */
+ /* QT_FT_GLYPH_FORMAT_NONE :: */
+ /* The value 0 is reserved and does describe a glyph format. */
+ /* */
+ /* QT_FT_GLYPH_FORMAT_COMPOSITE :: */
+ /* The glyph image is a composite of several other images. This */
+ /* format is _only_ used with @QT_FT_LOAD_NO_RECURSE, and is used to */
+ /* report compound glyphs (like accented characters). */
+ /* */
+ /* QT_FT_GLYPH_FORMAT_BITMAP :: */
+ /* The glyph image is a bitmap, and can be described as an */
+ /* @QT_FT_Bitmap. You generally need to access the `bitmap' field of */
+ /* the @QT_FT_GlyphSlotRec structure to read it. */
+ /* */
+ /* QT_FT_GLYPH_FORMAT_OUTLINE :: */
+ /* The glyph image is a vertorial outline made of line segments */
+ /* and Bezier arcs; it can be described as an @QT_FT_Outline; you */
+ /* generally want to access the `outline' field of the */
+ /* @QT_FT_GlyphSlotRec structure to read it. */
+ /* */
+ /* QT_FT_GLYPH_FORMAT_PLOTTER :: */
+ /* The glyph image is a vectorial path with no inside/outside */
+ /* contours. Some Type 1 fonts, like those in the Hershey family, */
+ /* contain glyphs in this format. These are described as */
+ /* @QT_FT_Outline, but FreeType isn't currently capable of rendering */
+ /* them correctly. */
+ /* */
+ typedef enum QT_FT_Glyph_Format_
+ {
+ QT_FT_IMAGE_TAG( QT_FT_GLYPH_FORMAT_NONE, 0, 0, 0, 0 ),
+
+ QT_FT_IMAGE_TAG( QT_FT_GLYPH_FORMAT_COMPOSITE, 'c', 'o', 'm', 'p' ),
+ QT_FT_IMAGE_TAG( QT_FT_GLYPH_FORMAT_BITMAP, 'b', 'i', 't', 's' ),
+ QT_FT_IMAGE_TAG( QT_FT_GLYPH_FORMAT_OUTLINE, 'o', 'u', 't', 'l' ),
+ QT_FT_IMAGE_TAG( QT_FT_GLYPH_FORMAT_PLOTTER, 'p', 'l', 'o', 't' )
+
+ } QT_FT_Glyph_Format;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Enum> */
+ /* qt_ft_glyph_format_xxx */
+ /* */
+ /* <Description> */
+ /* A list of decprecated constants. Use the corresponding */
+ /* @QT_FT_Glyph_Format values instead. */
+ /* */
+ /* <Values> */
+ /* qt_ft_glyph_format_none :: see @QT_FT_GLYPH_FORMAT_NONE */
+ /* qt_ft_glyph_format_composite :: see @QT_FT_GLYPH_FORMAT_COMPOSITE */
+ /* qt_ft_glyph_format_bitmap :: see @QT_FT_GLYPH_FORMAT_BITMAP */
+ /* qt_ft_glyph_format_outline :: see @QT_FT_GLYPH_FORMAT_OUTLINE */
+ /* qt_ft_glyph_format_plotter :: see @QT_FT_GLYPH_FORMAT_PLOTTER */
+ /* */
+#define qt_ft_glyph_format_none QT_FT_GLYPH_FORMAT_NONE
+#define qt_ft_glyph_format_composite QT_FT_GLYPH_FORMAT_COMPOSITE
+#define qt_ft_glyph_format_bitmap QT_FT_GLYPH_FORMAT_BITMAP
+#define qt_ft_glyph_format_outline QT_FT_GLYPH_FORMAT_OUTLINE
+#define qt_ft_glyph_format_plotter QT_FT_GLYPH_FORMAT_PLOTTER
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** R A S T E R D E F I N I T I O N S *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* A raster is a scan converter, in charge of rendering an outline into */
+ /* a a bitmap. This section contains the public API for rasters. */
+ /* */
+ /* Note that in FreeType 2, all rasters are now encapsulated within */
+ /* specific modules called `renderers'. See `freetype/ftrender.h' for */
+ /* more details on renderers. */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* <Section> */
+ /* raster */
+ /* */
+ /* <Title> */
+ /* Scanline converter */
+ /* */
+ /* <Abstract> */
+ /* How vectorial outlines are converted into bitmaps and pixmaps. */
+ /* */
+ /* <Description> */
+ /* This section contains technical definitions. */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* <Type> */
+ /* QT_FT_Raster */
+ /* */
+ /* <Description> */
+ /* A handle (pointer) to a raster object. Each object can be used */
+ /* independently to convert an outline into a bitmap or pixmap. */
+ /* */
+ typedef struct TRaster_ *QT_FT_Raster;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Struct> */
+ /* QT_FT_Span */
+ /* */
+ /* <Description> */
+ /* A structure used to model a single span of gray (or black) pixels */
+ /* when rendering a monochrome or anti-aliased bitmap. */
+ /* */
+ /* <Fields> */
+ /* x :: The span's horizontal start position. */
+ /* */
+ /* len :: The span's length in pixels. */
+ /* */
+ /* coverage :: The span color/coverage, ranging from 0 (background) */
+ /* to 255 (foreground). Only used for anti-aliased */
+ /* rendering. */
+ /* */
+ /* <Note> */
+ /* This structure is used by the span drawing callback type named */
+ /* QT_FT_SpanFunc which takes the y-coordinate of the span as a */
+ /* a parameter. */
+ /* */
+ /* The coverage value is always between 0 and 255, even if the number */
+ /* of gray levels have been set through QT_FT_Set_Gray_Levels(). */
+ /* */
+ typedef struct QT_FT_Span_
+ {
+ short x;
+ unsigned short len;
+ short y;
+ unsigned char coverage;
+ } QT_FT_Span;
+
+
+ /*************************************************************************/
+ /* */
+ /* <FuncType> */
+ /* QT_FT_SpanFunc */
+ /* */
+ /* <Description> */
+ /* A function used as a call-back by the anti-aliased renderer in */
+ /* order to let client applications draw themselves the gray pixel */
+ /* spans on each scan line. */
+ /* */
+ /* <Input> */
+ /* y :: The scanline's y-coordinate. */
+ /* */
+ /* count :: The number of spans to draw on this scanline. */
+ /* */
+ /* spans :: A table of `count' spans to draw on the scanline. */
+ /* */
+ /* user :: User-supplied data that is passed to the callback. */
+ /* */
+ /* <Note> */
+ /* This callback allows client applications to directly render the */
+ /* gray spans of the anti-aliased bitmap to any kind of surfaces. */
+ /* */
+ /* This can be used to write anti-aliased outlines directly to a */
+ /* given background bitmap, and even perform translucency. */
+ /* */
+ /* Note that the `count' field cannot be greater than a fixed value */
+ /* defined by the QT_FT_MAX_GRAY_SPANS configuration macro in */
+ /* ftoption.h. By default, this value is set to 32, which means that */
+ /* if there are more than 32 spans on a given scanline, the callback */
+ /* will be called several times with the same `y' parameter in order */
+ /* to draw all callbacks. */
+ /* */
+ /* Otherwise, the callback is only called once per scan-line, and */
+ /* only for those scanlines that do have `gray' pixels on them. */
+ /* */
+ typedef void
+ (*QT_FT_SpanFunc)(int count,
+ const QT_FT_Span* spans,
+ void* worker);
+
+#define QT_FT_Raster_Span_Func QT_FT_SpanFunc
+
+
+ /*************************************************************************/
+ /* */
+ /* <FuncType> */
+ /* QT_FT_Raster_BitTest_Func */
+ /* */
+ /* <Description> */
+ /* THIS TYPE IS DEPRECATED. DO NOT USE IT. */
+ /* */
+ /* A function used as a call-back by the monochrome scan-converter */
+ /* to test whether a given target pixel is already set to the drawing */
+ /* `color'. These tests are crucial to implement drop-out control */
+ /* per-se the TrueType spec. */
+ /* */
+ /* <Input> */
+ /* y :: The pixel's y-coordinate. */
+ /* */
+ /* x :: The pixel's x-coordinate. */
+ /* */
+ /* user :: User-supplied data that is passed to the callback. */
+ /* */
+ /* <Return> */
+ /* 1 if the pixel is `set', 0 otherwise. */
+ /* */
+ typedef int
+ (*QT_FT_Raster_BitTest_Func)( int y,
+ int x,
+ void* user );
+
+
+ /*************************************************************************/
+ /* */
+ /* <FuncType> */
+ /* QT_FT_Raster_BitSet_Func */
+ /* */
+ /* <Description> */
+ /* THIS TYPE IS DEPRECATED. DO NOT USE IT. */
+ /* */
+ /* A function used as a call-back by the monochrome scan-converter */
+ /* to set an individual target pixel. This is crucial to implement */
+ /* drop-out control according to the TrueType specification. */
+ /* */
+ /* <Input> */
+ /* y :: The pixel's y-coordinate. */
+ /* */
+ /* x :: The pixel's x-coordinate. */
+ /* */
+ /* user :: User-supplied data that is passed to the callback. */
+ /* */
+ /* <Return> */
+ /* 1 if the pixel is `set', 0 otherwise. */
+ /* */
+ typedef void
+ (*QT_FT_Raster_BitSet_Func)( int y,
+ int x,
+ void* user );
+
+
+ /*************************************************************************/
+ /* */
+ /* <Enum> */
+ /* QT_FT_RASTER_FLAG_XXX */
+ /* */
+ /* <Description> */
+ /* A list of bit flag constants as used in the `flags' field of a */
+ /* @QT_FT_Raster_Params structure. */
+ /* */
+ /* <Values> */
+ /* QT_FT_RASTER_FLAG_DEFAULT :: This value is 0. */
+ /* */
+ /* QT_FT_RASTER_FLAG_AA :: This flag is set to indicate that an */
+ /* anti-aliased glyph image should be */
+ /* generated. Otherwise, it will be */
+ /* monochrome (1-bit). */
+ /* */
+ /* QT_FT_RASTER_FLAG_DIRECT :: This flag is set to indicate direct */
+ /* rendering. In this mode, client */
+ /* applications must provide their own span */
+ /* callback. This lets them directly */
+ /* draw or compose over an existing bitmap. */
+ /* If this bit is not set, the target */
+ /* pixmap's buffer _must_ be zeroed before */
+ /* rendering. */
+ /* */
+ /* Note that for now, direct rendering is */
+ /* only possible with anti-aliased glyphs. */
+ /* */
+ /* QT_FT_RASTER_FLAG_CLIP :: This flag is only used in direct */
+ /* rendering mode. If set, the output will */
+ /* be clipped to a box specified in the */
+ /* "clip_box" field of the QT_FT_Raster_Params */
+ /* structure. */
+ /* */
+ /* Note that by default, the glyph bitmap */
+ /* is clipped to the target pixmap, except */
+ /* in direct rendering mode where all spans */
+ /* are generated if no clipping box is set. */
+ /* */
+#define QT_FT_RASTER_FLAG_DEFAULT 0x0
+#define QT_FT_RASTER_FLAG_AA 0x1
+#define QT_FT_RASTER_FLAG_DIRECT 0x2
+#define QT_FT_RASTER_FLAG_CLIP 0x4
+
+ /* deprecated */
+#define qt_ft_raster_flag_default QT_FT_RASTER_FLAG_DEFAULT
+#define qt_ft_raster_flag_aa QT_FT_RASTER_FLAG_AA
+#define qt_ft_raster_flag_direct QT_FT_RASTER_FLAG_DIRECT
+#define qt_ft_raster_flag_clip QT_FT_RASTER_FLAG_CLIP
+
+
+ /*************************************************************************/
+ /* */
+ /* <Struct> */
+ /* QT_FT_Raster_Params */
+ /* */
+ /* <Description> */
+ /* A structure to hold the arguments used by a raster's render */
+ /* function. */
+ /* */
+ /* <Fields> */
+ /* target :: The target bitmap. */
+ /* */
+ /* source :: A pointer to the source glyph image (e.g. an */
+ /* QT_FT_Outline). */
+ /* */
+ /* flags :: The rendering flags. */
+ /* */
+ /* gray_spans :: The gray span drawing callback. */
+ /* */
+ /* black_spans :: The black span drawing callback. */
+ /* */
+ /* bit_test :: The bit test callback. UNIMPLEMENTED! */
+ /* */
+ /* bit_set :: The bit set callback. UNIMPLEMENTED! */
+ /* */
+ /* user :: User-supplied data that is passed to each drawing */
+ /* callback. */
+ /* */
+ /* clip_box :: An optional clipping box. It is only used in */
+ /* direct rendering mode. Note that coordinates here */
+ /* should be expressed in _integer_ pixels (and not in */
+ /* 26.6 fixed-point units). */
+ /* */
+ /* <Note> */
+ /* An anti-aliased glyph bitmap is drawn if the QT_FT_RASTER_FLAG_AA bit */
+ /* flag is set in the `flags' field, otherwise a monochrome bitmap */
+ /* will be generated. */
+ /* */
+ /* If the QT_FT_RASTER_FLAG_DIRECT bit flag is set in `flags', the */
+ /* raster will call the `gray_spans' callback to draw gray pixel */
+ /* spans, in the case of an aa glyph bitmap, it will call */
+ /* `black_spans', and `bit_test' and `bit_set' in the case of a */
+ /* monochrome bitmap. This allows direct composition over a */
+ /* pre-existing bitmap through user-provided callbacks to perform the */
+ /* span drawing/composition. */
+ /* */
+ /* Note that the `bit_test' and `bit_set' callbacks are required when */
+ /* rendering a monochrome bitmap, as they are crucial to implement */
+ /* correct drop-out control as defined in the TrueType specification. */
+ /* */
+ typedef struct QT_FT_Raster_Params_
+ {
+ QT_FT_Bitmap* target;
+ void* source;
+ int flags;
+ QT_FT_SpanFunc gray_spans;
+ QT_FT_SpanFunc black_spans;
+ QT_FT_Raster_BitTest_Func bit_test; /* doesn't work! */
+ QT_FT_Raster_BitSet_Func bit_set; /* doesn't work! */
+ void* user;
+ QT_FT_BBox clip_box;
+ int skip_spans;
+
+ } QT_FT_Raster_Params;
+
+
+ /*************************************************************************/
+ /* */
+ /* <FuncType> */
+ /* QT_FT_Raster_NewFunc */
+ /* */
+ /* <Description> */
+ /* A function used to create a new raster object. */
+ /* */
+ /* <Input> */
+ /* memory :: A handle to the memory allocator. */
+ /* */
+ /* <Output> */
+ /* raster :: A handle to the new raster object. */
+ /* */
+ /* <Return> */
+ /* Error code. 0 means success. */
+ /* */
+ /* <Note> */
+ /* The `memory' parameter is a typeless pointer in order to avoid */
+ /* un-wanted dependencies on the rest of the FreeType code. In */
+ /* practice, it is a QT_FT_Memory, i.e., a handle to the standard */
+ /* FreeType memory allocator. However, this field can be completely */
+ /* ignored by a given raster implementation. */
+ /* */
+ typedef int
+ (*QT_FT_Raster_NewFunc)( QT_FT_Raster* raster );
+
+#define QT_FT_Raster_New_Func QT_FT_Raster_NewFunc
+
+ /*************************************************************************/
+ /* */
+ /* <FuncType> */
+ /* QT_FT_Raster_DoneFunc */
+ /* */
+ /* <Description> */
+ /* A function used to destroy a given raster object. */
+ /* */
+ /* <Input> */
+ /* raster :: A handle to the raster object. */
+ /* */
+ typedef void
+ (*QT_FT_Raster_DoneFunc)( QT_FT_Raster raster );
+
+#define QT_FT_Raster_Done_Func QT_FT_Raster_DoneFunc
+
+ /*************************************************************************/
+ /* */
+ /* <FuncType> */
+ /* QT_FT_Raster_ResetFunc */
+ /* */
+ /* <Description> */
+ /* FreeType provides an area of memory called the `render pool', */
+ /* available to all registered rasters. This pool can be freely used */
+ /* during a given scan-conversion but is shared by all rasters. Its */
+ /* content is thus transient. */
+ /* */
+ /* This function is called each time the render pool changes, or just */
+ /* after a new raster object is created. */
+ /* */
+ /* <Input> */
+ /* raster :: A handle to the new raster object. */
+ /* */
+ /* pool_base :: The address in memory of the render pool. */
+ /* */
+ /* pool_size :: The size in bytes of the render pool. */
+ /* */
+ /* <Note> */
+ /* Rasters can ignore the render pool and rely on dynamic memory */
+ /* allocation if they want to (a handle to the memory allocator is */
+ /* passed to the raster constructor). However, this is not */
+ /* recommended for efficiency purposes. */
+ /* */
+ typedef void
+ (*QT_FT_Raster_ResetFunc)( QT_FT_Raster raster,
+ unsigned char* pool_base,
+ unsigned long pool_size );
+
+#define QT_FT_Raster_Reset_Func QT_FT_Raster_ResetFunc
+
+ /*************************************************************************/
+ /* */
+ /* <FuncType> */
+ /* QT_FT_Raster_SetModeFunc */
+ /* */
+ /* <Description> */
+ /* This function is a generic facility to change modes or attributes */
+ /* in a given raster. This can be used for debugging purposes, or */
+ /* simply to allow implementation-specific `features' in a given */
+ /* raster module. */
+ /* */
+ /* <Input> */
+ /* raster :: A handle to the new raster object. */
+ /* */
+ /* mode :: A 4-byte tag used to name the mode or property. */
+ /* */
+ /* args :: A pointer to the new mode/property to use. */
+ /* */
+ typedef int
+ (*QT_FT_Raster_SetModeFunc)( QT_FT_Raster raster,
+ unsigned long mode,
+ void* args );
+
+#define QT_FT_Raster_Set_Mode_Func QT_FT_Raster_SetModeFunc
+
+ /*************************************************************************/
+ /* */
+ /* <FuncType> */
+ /* QT_FT_Raster_RenderFunc */
+ /* */
+ /* <Description> */
+ /* Invokes a given raster to scan-convert a given glyph image into a */
+ /* target bitmap. */
+ /* */
+ /* <Input> */
+ /* raster :: A handle to the raster object. */
+ /* */
+ /* params :: A pointer to a QT_FT_Raster_Params structure used to store */
+ /* the rendering parameters. */
+ /* */
+ /* <Return> */
+ /* Error code. 0 means success. */
+ /* */
+ /* <Note> */
+ /* The exact format of the source image depends on the raster's glyph */
+ /* format defined in its QT_FT_Raster_Funcs structure. It can be an */
+ /* QT_FT_Outline or anything else in order to support a large array of */
+ /* glyph formats. */
+ /* */
+ /* Note also that the render function can fail and return a */
+ /* QT_FT_Err_Unimplemented_Feature error code if the raster used does */
+ /* not support direct composition. */
+ /* */
+ /* XXX: For now, the standard raster doesn't support direct */
+ /* composition but this should change for the final release (see */
+ /* the files demos/src/ftgrays.c and demos/src/ftgrays2.c for */
+ /* examples of distinct implementations which support direct */
+ /* composition). */
+ /* */
+ typedef int
+ (*QT_FT_Raster_RenderFunc)( QT_FT_Raster raster,
+ QT_FT_Raster_Params* params );
+
+#define QT_FT_Raster_Render_Func QT_FT_Raster_RenderFunc
+
+ /*************************************************************************/
+ /* */
+ /* <Struct> */
+ /* QT_FT_Raster_Funcs */
+ /* */
+ /* <Description> */
+ /* A structure used to describe a given raster class to the library. */
+ /* */
+ /* <Fields> */
+ /* glyph_format :: The supported glyph format for this raster. */
+ /* */
+ /* raster_new :: The raster constructor. */
+ /* */
+ /* raster_reset :: Used to reset the render pool within the raster. */
+ /* */
+ /* raster_render :: A function to render a glyph into a given bitmap. */
+ /* */
+ /* raster_done :: The raster destructor. */
+ /* */
+ typedef struct QT_FT_Raster_Funcs_
+ {
+ QT_FT_Glyph_Format glyph_format;
+ QT_FT_Raster_NewFunc raster_new;
+ QT_FT_Raster_ResetFunc raster_reset;
+ QT_FT_Raster_SetModeFunc raster_set_mode;
+ QT_FT_Raster_RenderFunc raster_render;
+ QT_FT_Raster_DoneFunc raster_done;
+
+ } QT_FT_Raster_Funcs;
+
+
+ /* */
+
+
+QT_FT_END_HEADER
+
+#endif /* __FTIMAGE_H__ */
+
+
+/* END */
diff --git a/src/gui/painting/qrasterizer.cpp b/src/gui/painting/qrasterizer.cpp
new file mode 100644
index 0000000000..75116c2420
--- /dev/null
+++ b/src/gui/painting/qrasterizer.cpp
@@ -0,0 +1,1270 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qrasterizer_p.h"
+
+#include <QPoint>
+#include <QRect>
+
+#include <private/qmath_p.h>
+#include <private/qdatabuffer_p.h>
+#include <private/qdrawhelper_p.h>
+
+QT_BEGIN_NAMESPACE
+
+typedef int Q16Dot16;
+#define Q16Dot16ToFloat(i) ((i)/65536.)
+#define FloatToQ16Dot16(i) (int)((i) * 65536.)
+#define IntToQ16Dot16(i) ((i) << 16)
+#define Q16Dot16ToInt(i) ((i) >> 16)
+#define Q16Dot16Factor 65536
+
+#define Q16Dot16Multiply(x, y) (int)((qlonglong(x) * qlonglong(y)) >> 16)
+#define Q16Dot16FastMultiply(x, y) (((x) * (y)) >> 16)
+
+#define SPAN_BUFFER_SIZE 256
+
+#define COORD_ROUNDING 0 // 0: round up, 1: round down
+#define COORD_OFFSET 0 // 26.6, 32 is half a pixel
+
+static inline QT_FT_Vector PointToVector(const QPointF &p)
+{
+ QT_FT_Vector result = { QT_FT_Pos(p.x() * 64), QT_FT_Pos(p.y() * 64) };
+ return result;
+}
+
+class QSpanBuffer {
+public:
+ QSpanBuffer(ProcessSpans blend, void *data, const QRect &clipRect)
+ : m_spanCount(0)
+ , m_blend(blend)
+ , m_data(data)
+ , m_clipRect(clipRect)
+ {
+ }
+
+ ~QSpanBuffer()
+ {
+ flushSpans();
+ }
+
+ void addSpan(int x, unsigned int len, int y, unsigned char coverage)
+ {
+ if (!coverage || !len)
+ return;
+
+ Q_ASSERT(y >= m_clipRect.top());
+ Q_ASSERT(y <= m_clipRect.bottom());
+ Q_ASSERT(x >= m_clipRect.left());
+ Q_ASSERT(x + int(len) - 1 <= m_clipRect.right());
+
+ m_spans[m_spanCount].x = x;
+ m_spans[m_spanCount].len = len;
+ m_spans[m_spanCount].y = y;
+ m_spans[m_spanCount].coverage = coverage;
+
+ if (++m_spanCount == SPAN_BUFFER_SIZE)
+ flushSpans();
+ }
+
+private:
+ void flushSpans()
+ {
+ m_blend(m_spanCount, m_spans, m_data);
+ m_spanCount = 0;
+ }
+
+ QT_FT_Span m_spans[SPAN_BUFFER_SIZE];
+ int m_spanCount;
+
+ ProcessSpans m_blend;
+ void *m_data;
+
+ QRect m_clipRect;
+};
+
+#define CHUNK_SIZE 64
+class QScanConverter
+{
+public:
+ QScanConverter();
+ ~QScanConverter();
+
+ void begin(int top, int bottom, int left, int right,
+ Qt::FillRule fillRule, QSpanBuffer *spanBuffer);
+ void end();
+
+ void mergeCurve(const QT_FT_Vector &a, const QT_FT_Vector &b,
+ const QT_FT_Vector &c, const QT_FT_Vector &d);
+ void mergeLine(QT_FT_Vector a, QT_FT_Vector b);
+
+ struct Line
+ {
+ Q16Dot16 x;
+ Q16Dot16 delta;
+
+ int top, bottom;
+
+ int winding;
+ };
+
+private:
+ struct Intersection
+ {
+ int x;
+ int winding;
+
+ int left, right;
+ };
+
+ inline bool clip(Q16Dot16 &xFP, int &iTop, int &iBottom, Q16Dot16 slopeFP, Q16Dot16 edgeFP, int winding);
+ inline void mergeIntersection(Intersection *head, const Intersection &isect);
+
+ void prepareChunk();
+
+ void emitNode(const Intersection *node);
+ void emitSpans(int chunk);
+
+ inline void allocate(int size);
+
+ QDataBuffer<Line> m_lines;
+
+ int m_alloc;
+ int m_size;
+
+ int m_top;
+ int m_bottom;
+
+ Q16Dot16 m_leftFP;
+ Q16Dot16 m_rightFP;
+
+ int m_fillRuleMask;
+
+ int m_x;
+ int m_y;
+ int m_winding;
+
+ Intersection *m_intersections;
+
+ QSpanBuffer *m_spanBuffer;
+
+ QDataBuffer<Line *> m_active;
+
+ template <typename T>
+ friend void qScanConvert(QScanConverter &d, T allVertical);
+};
+
+class QRasterizerPrivate
+{
+public:
+ bool antialiased;
+ ProcessSpans blend;
+ void *data;
+ QRect clipRect;
+
+ QScanConverter scanConverter;
+};
+
+QScanConverter::QScanConverter()
+ : m_lines(0)
+ , m_alloc(0)
+ , m_size(0)
+ , m_intersections(0)
+ , m_active(0)
+{
+}
+
+QScanConverter::~QScanConverter()
+{
+ if (m_intersections)
+ free(m_intersections);
+}
+
+void QScanConverter::begin(int top, int bottom, int left, int right,
+ Qt::FillRule fillRule, QSpanBuffer *spanBuffer)
+{
+ m_top = top;
+ m_bottom = bottom;
+ m_leftFP = IntToQ16Dot16(left);
+ m_rightFP = IntToQ16Dot16(right + 1);
+
+ m_lines.reset();
+
+ m_fillRuleMask = fillRule == Qt::WindingFill ? ~0x0 : 0x1;
+ m_spanBuffer = spanBuffer;
+}
+
+void QScanConverter::prepareChunk()
+{
+ m_size = CHUNK_SIZE;
+
+ allocate(CHUNK_SIZE);
+ memset(m_intersections, 0, CHUNK_SIZE * sizeof(Intersection));
+}
+
+void QScanConverter::emitNode(const Intersection *node)
+{
+tail_call:
+ if (node->left)
+ emitNode(node + node->left);
+
+ if (m_winding & m_fillRuleMask)
+ m_spanBuffer->addSpan(m_x, node->x - m_x, m_y, 0xff);
+
+ m_x = node->x;
+ m_winding += node->winding;
+
+ if (node->right) {
+ node += node->right;
+ goto tail_call;
+ }
+}
+
+void QScanConverter::emitSpans(int chunk)
+{
+ for (int dy = 0; dy < CHUNK_SIZE; ++dy) {
+ m_x = 0;
+ m_y = chunk + dy;
+ m_winding = 0;
+
+ emitNode(&m_intersections[dy]);
+ }
+}
+
+// split control points b[0] ... b[3] into
+// left (b[0] ... b[3]) and right (b[3] ... b[6])
+static void split(QT_FT_Vector *b)
+{
+ b[6] = b[3];
+
+ {
+ const QT_FT_Pos temp = (b[1].x + b[2].x)/2;
+
+ b[1].x = (b[0].x + b[1].x)/2;
+ b[5].x = (b[2].x + b[3].x)/2;
+ b[2].x = (b[1].x + temp)/2;
+ b[4].x = (b[5].x + temp)/2;
+ b[3].x = (b[2].x + b[4].x)/2;
+ }
+ {
+ const QT_FT_Pos temp = (b[1].y + b[2].y)/2;
+
+ b[1].y = (b[0].y + b[1].y)/2;
+ b[5].y = (b[2].y + b[3].y)/2;
+ b[2].y = (b[1].y + temp)/2;
+ b[4].y = (b[5].y + temp)/2;
+ b[3].y = (b[2].y + b[4].y)/2;
+ }
+}
+
+static inline bool topOrder(const QScanConverter::Line &a, const QScanConverter::Line &b)
+{
+ return a.top < b.top;
+}
+
+static inline bool xOrder(const QScanConverter::Line *a, const QScanConverter::Line *b)
+{
+ return a->x < b->x;
+}
+
+template <bool B>
+struct QBoolToType
+{
+ inline bool operator()() const
+ {
+ return B;
+ }
+};
+
+// should be a member function but VC6 doesn't support member template functions
+template <typename T>
+void qScanConvert(QScanConverter &d, T allVertical)
+{
+ if (!d.m_lines.size()) {
+ d.m_active.reset();
+ return;
+ }
+ qSort(d.m_lines.data(), d.m_lines.data() + d.m_lines.size(), QT_PREPEND_NAMESPACE(topOrder));
+ int line = 0;
+ for (int y = d.m_lines.first().top; y <= d.m_bottom; ++y) {
+ for (; line < d.m_lines.size() && d.m_lines.at(line).top == y; ++line) {
+ // add node to active list
+ if (allVertical()) {
+ QScanConverter::Line *l = &d.m_lines.at(line);
+ d.m_active.resize(d.m_active.size() + 1);
+ int j;
+ for (j = d.m_active.size() - 2; j >= 0 && QT_PREPEND_NAMESPACE(xOrder)(l, d.m_active.at(j)); --j)
+ d.m_active.at(j+1) = d.m_active.at(j);
+ d.m_active.at(j+1) = l;
+ } else {
+ d.m_active << &d.m_lines.at(line);
+ }
+ }
+
+ int numActive = d.m_active.size();
+ if (!allVertical()) {
+ // use insertion sort instead of qSort, as the active edge list is quite small
+ // and in the average case already sorted
+ for (int i = 1; i < numActive; ++i) {
+ QScanConverter::Line *l = d.m_active.at(i);
+ int j;
+ for (j = i-1; j >= 0 && QT_PREPEND_NAMESPACE(xOrder)(l, d.m_active.at(j)); --j)
+ d.m_active.at(j+1) = d.m_active.at(j);
+ d.m_active.at(j+1) = l;
+ }
+ }
+
+ int x = 0;
+ int winding = 0;
+ for (int i = 0; i < numActive; ++i) {
+ QScanConverter::Line *node = d.m_active.at(i);
+
+ const int current = Q16Dot16ToInt(node->x);
+ if (winding & d.m_fillRuleMask)
+ d.m_spanBuffer->addSpan(x, current - x, y, 0xff);
+
+ x = current;
+ winding += node->winding;
+
+ if (node->bottom == y) {
+ // remove node from active list
+ for (int j = i; j < numActive - 1; ++j)
+ d.m_active.at(j) = d.m_active.at(j+1);
+
+ d.m_active.resize(--numActive);
+ --i;
+ } else if (!allVertical())
+ node->x += node->delta;
+ }
+ }
+ d.m_active.reset();
+}
+
+void QScanConverter::end()
+{
+ if (m_lines.isEmpty())
+ return;
+
+ if (m_lines.size() <= 32) {
+ bool allVertical = true;
+ for (int i = 0; i < m_lines.size(); ++i) {
+ if (m_lines.at(i).delta) {
+ allVertical = false;
+ break;
+ }
+ }
+ if (allVertical)
+ qScanConvert(*this, QBoolToType<true>());
+ else
+ qScanConvert(*this, QBoolToType<false>());
+ } else {
+ for (int chunkTop = m_top; chunkTop <= m_bottom; chunkTop += CHUNK_SIZE) {
+ prepareChunk();
+
+ Intersection isect = { 0, 0, 0, 0 };
+
+ const int chunkBottom = chunkTop + CHUNK_SIZE;
+ for (int i = 0; i < m_lines.size(); ++i) {
+ Line &line = m_lines.at(i);
+
+ if ((line.bottom < chunkTop) || (line.top > chunkBottom))
+ continue;
+
+ const int top = qMax(0, line.top - chunkTop);
+ const int bottom = qMin(CHUNK_SIZE, line.bottom + 1 - chunkTop);
+ allocate(m_size + bottom - top);
+
+ isect.winding = line.winding;
+
+ Intersection *it = m_intersections + top;
+ Intersection *end = m_intersections + bottom;
+
+ if (line.delta) {
+ for (; it != end; ++it) {
+ isect.x = Q16Dot16ToInt(line.x);
+ line.x += line.delta;
+ mergeIntersection(it, isect);
+ }
+ } else {
+ isect.x = Q16Dot16ToInt(line.x);
+ for (; it != end; ++it)
+ mergeIntersection(it, isect);
+ }
+ }
+
+ emitSpans(chunkTop);
+ }
+ }
+
+ if (m_alloc > 1024) {
+ free(m_intersections);
+ m_alloc = 0;
+ m_size = 0;
+ m_intersections = 0;
+ }
+
+ if (m_lines.size() > 1024)
+ m_lines.shrink(1024);
+}
+
+inline void QScanConverter::allocate(int size)
+{
+ if (m_alloc < size) {
+ int newAlloc = qMax(size, 2 * m_alloc);
+ m_intersections = q_check_ptr((Intersection *)realloc(m_intersections, newAlloc * sizeof(Intersection)));
+ m_alloc = newAlloc;
+ }
+}
+
+inline void QScanConverter::mergeIntersection(Intersection *it, const Intersection &isect)
+{
+ Intersection *current = it;
+
+ while (isect.x != current->x) {
+ int &next = isect.x < current->x ? current->left : current->right;
+ if (next)
+ current += next;
+ else {
+ Intersection *last = m_intersections + m_size;
+ next = last - current;
+ *last = isect;
+ ++m_size;
+ return;
+ }
+ }
+
+ current->winding += isect.winding;
+}
+
+void QScanConverter::mergeCurve(const QT_FT_Vector &pa, const QT_FT_Vector &pb,
+ const QT_FT_Vector &pc, const QT_FT_Vector &pd)
+{
+ // make room for 32 splits
+ QT_FT_Vector beziers[4 + 3 * 32];
+
+ QT_FT_Vector *b = beziers;
+
+ b[0] = pa;
+ b[1] = pb;
+ b[2] = pc;
+ b[3] = pd;
+
+ const QT_FT_Pos flatness = 16;
+
+ while (b >= beziers) {
+ QT_FT_Vector delta = { b[3].x - b[0].x, b[3].y - b[0].y };
+ QT_FT_Pos l = qAbs(delta.x) + qAbs(delta.y);
+
+ bool belowThreshold;
+ if (l > 64) {
+ qlonglong d2 = qAbs(qlonglong(b[1].x-b[0].x) * qlonglong(delta.y) -
+ qlonglong(b[1].y-b[0].y) * qlonglong(delta.x));
+ qlonglong d3 = qAbs(qlonglong(b[2].x-b[0].x) * qlonglong(delta.y) -
+ qlonglong(b[2].y-b[0].y) * qlonglong(delta.x));
+
+ qlonglong d = d2 + d3;
+
+ belowThreshold = (d <= qlonglong(flatness) * qlonglong(l));
+ } else {
+ QT_FT_Pos d = qAbs(b[0].x-b[1].x) + qAbs(b[0].y-b[1].y) +
+ qAbs(b[0].x-b[2].x) + qAbs(b[0].y-b[2].y);
+
+ belowThreshold = (d <= flatness);
+ }
+
+ if (belowThreshold || b == beziers + 3 * 32) {
+ mergeLine(b[0], b[3]);
+ b -= 3;
+ continue;
+ }
+
+ split(b);
+ b += 3;
+ }
+}
+
+inline bool QScanConverter::clip(Q16Dot16 &xFP, int &iTop, int &iBottom, Q16Dot16 slopeFP, Q16Dot16 edgeFP, int winding)
+{
+ bool right = edgeFP == m_rightFP;
+
+ if (xFP == edgeFP) {
+ if ((slopeFP > 0) ^ right)
+ return false;
+ else {
+ Line line = { edgeFP, 0, iTop, iBottom, winding };
+ m_lines.add(line);
+ return true;
+ }
+ }
+
+ Q16Dot16 lastFP = xFP + slopeFP * (iBottom - iTop);
+
+ if (lastFP == edgeFP) {
+ if ((slopeFP < 0) ^ right)
+ return false;
+ else {
+ Line line = { edgeFP, 0, iTop, iBottom, winding };
+ m_lines.add(line);
+ return true;
+ }
+ }
+
+ // does line cross edge?
+ if ((lastFP < edgeFP) ^ (xFP < edgeFP)) {
+ Q16Dot16 deltaY = Q16Dot16((edgeFP - xFP) / Q16Dot16ToFloat(slopeFP));
+
+ if ((xFP < edgeFP) ^ right) {
+ // top segment needs to be clipped
+ int iHeight = Q16Dot16ToInt(deltaY + 1);
+ int iMiddle = iTop + iHeight;
+
+ Line line = { edgeFP, 0, iTop, iMiddle, winding };
+ m_lines.add(line);
+
+ if (iMiddle != iBottom) {
+ xFP += slopeFP * (iHeight + 1);
+ iTop = iMiddle + 1;
+ } else
+ return true;
+ } else {
+ // bottom segment needs to be clipped
+ int iHeight = Q16Dot16ToInt(deltaY);
+ int iMiddle = iTop + iHeight;
+
+ if (iMiddle != iBottom) {
+ Line line = { edgeFP, 0, iMiddle + 1, iBottom, winding };
+ m_lines.add(line);
+
+ iBottom = iMiddle;
+ }
+ }
+ return false;
+ } else if ((xFP < edgeFP) ^ right) {
+ Line line = { edgeFP, 0, iTop, iBottom, winding };
+ m_lines.add(line);
+ return true;
+ }
+
+ return false;
+}
+
+void QScanConverter::mergeLine(QT_FT_Vector a, QT_FT_Vector b)
+{
+ int winding = 1;
+
+ if (a.y > b.y) {
+ qSwap(a, b);
+ winding = -1;
+ }
+
+ a.x += COORD_OFFSET;
+ a.y += COORD_OFFSET;
+ b.x += COORD_OFFSET;
+ b.y += COORD_OFFSET;
+
+ int iTop = qMax(m_top, int((a.y + 32 - COORD_ROUNDING) >> 6));
+ int iBottom = qMin(m_bottom, int((b.y - 32 - COORD_ROUNDING) >> 6));
+
+ if (iTop <= iBottom) {
+ Q16Dot16 aFP = Q16Dot16Factor/2 + (a.x << 10) - COORD_ROUNDING;
+
+ if (b.x == a.x) {
+ Line line = { qBound(m_leftFP, aFP, m_rightFP), 0, iTop, iBottom, winding };
+ m_lines.add(line);
+ } else {
+ const qreal slope = (b.x - a.x) / qreal(b.y - a.y);
+
+ const Q16Dot16 slopeFP = FloatToQ16Dot16(slope);
+
+ Q16Dot16 xFP = aFP + Q16Dot16Multiply(slopeFP,
+ IntToQ16Dot16(iTop)
+ + Q16Dot16Factor/2 - (a.y << 10));
+
+ if (clip(xFP, iTop, iBottom, slopeFP, m_leftFP, winding))
+ return;
+
+ if (clip(xFP, iTop, iBottom, slopeFP, m_rightFP, winding))
+ return;
+
+ Q_ASSERT(xFP >= m_leftFP);
+
+ Line line = { xFP, slopeFP, iTop, iBottom, winding };
+ m_lines.add(line);
+ }
+ }
+}
+
+QRasterizer::QRasterizer()
+ : d(new QRasterizerPrivate)
+{
+}
+
+QRasterizer::~QRasterizer()
+{
+ delete d;
+}
+
+void QRasterizer::setAntialiased(bool antialiased)
+{
+ d->antialiased = antialiased;
+}
+
+void QRasterizer::initialize(ProcessSpans blend, void *data)
+{
+ d->blend = blend;
+ d->data = data;
+}
+
+void QRasterizer::setClipRect(const QRect &clipRect)
+{
+ d->clipRect = clipRect;
+}
+
+static Q16Dot16 intersectPixelFP(int x, Q16Dot16 top, Q16Dot16 bottom, Q16Dot16 leftIntersectX, Q16Dot16 rightIntersectX, Q16Dot16 slope, Q16Dot16 invSlope)
+{
+ Q16Dot16 leftX = IntToQ16Dot16(x);
+ Q16Dot16 rightX = IntToQ16Dot16(x) + Q16Dot16Factor;
+
+ Q16Dot16 leftIntersectY, rightIntersectY;
+ if (slope > 0) {
+ leftIntersectY = top + Q16Dot16Multiply(leftX - leftIntersectX, invSlope);
+ rightIntersectY = leftIntersectY + invSlope;
+ } else {
+ leftIntersectY = top + Q16Dot16Multiply(leftX - rightIntersectX, invSlope);
+ rightIntersectY = leftIntersectY + invSlope;
+ }
+
+ if (leftIntersectX >= leftX && rightIntersectX <= rightX) {
+ return Q16Dot16Multiply(bottom - top, leftIntersectX - leftX + ((rightIntersectX - leftIntersectX) >> 1));
+ } else if (leftIntersectX >= rightX) {
+ return bottom - top;
+ } else if (leftIntersectX >= leftX) {
+ if (slope > 0) {
+ return (bottom - top) - Q16Dot16FastMultiply((rightX - leftIntersectX) >> 1, rightIntersectY - top);
+ } else {
+ return (bottom - top) - Q16Dot16FastMultiply((rightX - leftIntersectX) >> 1, bottom - rightIntersectY);
+ }
+ } else if (rightIntersectX <= leftX) {
+ return 0;
+ } else if (rightIntersectX <= rightX) {
+ if (slope > 0) {
+ return Q16Dot16FastMultiply((rightIntersectX - leftX) >> 1, bottom - leftIntersectY);
+ } else {
+ return Q16Dot16FastMultiply((rightIntersectX - leftX) >> 1, leftIntersectY - top);
+ }
+ } else {
+ if (slope > 0) {
+ return (bottom - rightIntersectY) + ((rightIntersectY - leftIntersectY) >> 1);
+ } else {
+ return (rightIntersectY - top) + ((leftIntersectY - rightIntersectY) >> 1);
+ }
+ }
+}
+
+static inline bool q26Dot6Compare(qreal p1, qreal p2)
+{
+ return int((p2 - p1) * 64.) == 0;
+}
+
+static inline qreal qFloorF(qreal v)
+{
+#ifdef QT_USE_MATH_H_FLOATS
+ if (sizeof(qreal) == sizeof(float))
+ return floorf(v);
+ else
+#endif
+ return floor(v);
+}
+
+static inline QPointF snapTo26Dot6Grid(const QPointF &p)
+{
+ return QPointF(qFloorF(p.x() * 64) * (1 / qreal(64)),
+ qFloorF(p.y() * 64) * (1 / qreal(64)));
+}
+
+void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width, bool squareCap)
+{
+ if (a == b || width == 0 || d->clipRect.isEmpty())
+ return;
+
+ Q_ASSERT(width > 0.0);
+
+ QPointF pa = a;
+ QPointF pb = b;
+
+ if (squareCap) {
+ QPointF delta = pb - pa;
+ pa -= (0.5f * width) * delta;
+ pb += (0.5f * width) * delta;
+ }
+
+ QPointF offs = QPointF(qAbs(b.y() - a.y()), qAbs(b.x() - a.x())) * width * 0.5;
+ const QRectF clip(d->clipRect.topLeft() - offs, d->clipRect.bottomRight() + QPoint(1, 1) + offs);
+
+ if (!clip.contains(pa) || !clip.contains(pb)) {
+ qreal t1 = 0;
+ qreal t2 = 1;
+
+ const qreal o[2] = { pa.x(), pa.y() };
+ const qreal d[2] = { pb.x() - pa.x(), pb.y() - pa.y() };
+
+ const qreal low[2] = { clip.left(), clip.top() };
+ const qreal high[2] = { clip.right(), clip.bottom() };
+
+ for (int i = 0; i < 2; ++i) {
+ if (d[i] == 0) {
+ if (o[i] <= low[i] || o[i] >= high[i])
+ return;
+ continue;
+ }
+ const qreal d_inv = 1 / d[i];
+ qreal t_low = (low[i] - o[i]) * d_inv;
+ qreal t_high = (high[i] - o[i]) * d_inv;
+ if (t_low > t_high)
+ qSwap(t_low, t_high);
+ if (t1 < t_low)
+ t1 = t_low;
+ if (t2 > t_high)
+ t2 = t_high;
+ if (t1 >= t2)
+ return;
+ }
+
+ QPointF npa = pa + (pb - pa) * t1;
+ QPointF npb = pa + (pb - pa) * t2;
+
+ pa = npa;
+ pb = npb;
+ }
+
+ if (!d->antialiased) {
+ pa.rx() += (COORD_OFFSET - COORD_ROUNDING)/64.;
+ pa.ry() += (COORD_OFFSET - COORD_ROUNDING)/64.;
+ pb.rx() += (COORD_OFFSET - COORD_ROUNDING)/64.;
+ pb.ry() += (COORD_OFFSET - COORD_ROUNDING)/64.;
+ }
+
+ {
+ // old delta
+ const QPointF d0 = a - b;
+ const qreal w0 = d0.x() * d0.x() + d0.y() * d0.y();
+
+ // new delta
+ const QPointF d = pa - pb;
+ const qreal w = d.x() * d.x() + d.y() * d.y();
+
+ if (w == 0)
+ return;
+
+ // adjust width which is given relative to |b - a|
+ width *= sqrt(w0 / w);
+ }
+
+ QSpanBuffer buffer(d->blend, d->data, d->clipRect);
+
+ if (q26Dot6Compare(pa.y(), pb.y())) {
+ const qreal x = (pa.x() + pb.x()) * 0.5f;
+ const qreal dx = qAbs(pb.x() - pa.x()) * 0.5f;
+
+ const qreal y = pa.y();
+ const qreal dy = width * dx;
+
+ pa = QPointF(x, y - dy);
+ pb = QPointF(x, y + dy);
+
+ width = 1 / width;
+ }
+
+ if (q26Dot6Compare(pa.x(), pb.x())) {
+ if (pa.y() > pb.y())
+ qSwap(pa, pb);
+
+ const qreal dy = pb.y() - pa.y();
+ const qreal halfWidth = 0.5f * width * dy;
+
+ qreal left = pa.x() - halfWidth;
+ qreal right = pa.x() + halfWidth;
+
+ left = qBound(qreal(d->clipRect.left()), left, qreal(d->clipRect.right() + 1));
+ right = qBound(qreal(d->clipRect.left()), right, qreal(d->clipRect.right() + 1));
+
+ pa.ry() = qBound(qreal(d->clipRect.top()), pa.y(), qreal(d->clipRect.bottom() + 1));
+ pb.ry() = qBound(qreal(d->clipRect.top()), pb.y(), qreal(d->clipRect.bottom() + 1));
+
+ if (q26Dot6Compare(left, right) || q26Dot6Compare(pa.y(), pb.y()))
+ return;
+
+ if (d->antialiased) {
+ const Q16Dot16 iLeft = int(left);
+ const Q16Dot16 iRight = int(right);
+ const Q16Dot16 leftWidth = IntToQ16Dot16(iLeft + 1)
+ - FloatToQ16Dot16(left);
+ const Q16Dot16 rightWidth = FloatToQ16Dot16(right)
+ - IntToQ16Dot16(iRight);
+
+ Q16Dot16 coverage[3];
+ int x[3];
+ int len[3];
+
+ int n = 1;
+ if (iLeft == iRight) {
+ coverage[0] = (leftWidth + rightWidth) * 255;
+ x[0] = iLeft;
+ len[0] = 1;
+ } else {
+ coverage[0] = leftWidth * 255;
+ x[0] = iLeft;
+ len[0] = 1;
+ if (leftWidth == Q16Dot16Factor) {
+ len[0] = iRight - iLeft;
+ } else if (iRight - iLeft > 1) {
+ coverage[1] = IntToQ16Dot16(255);
+ x[1] = iLeft + 1;
+ len[1] = iRight - iLeft - 1;
+ ++n;
+ }
+ if (rightWidth) {
+ coverage[n] = rightWidth * 255;
+ x[n] = iRight;
+ len[n] = 1;
+ ++n;
+ }
+ }
+
+ const Q16Dot16 iTopFP = IntToQ16Dot16(int(pa.y()));
+ const Q16Dot16 iBottomFP = IntToQ16Dot16(int(pb.y()));
+ const Q16Dot16 yPa = FloatToQ16Dot16(pa.y());
+ const Q16Dot16 yPb = FloatToQ16Dot16(pb.y());
+ for (Q16Dot16 yFP = iTopFP; yFP <= iBottomFP; yFP += Q16Dot16Factor) {
+ const Q16Dot16 rowHeight = qMin(yFP + Q16Dot16Factor, yPb)
+ - qMax(yFP, yPa);
+ const int y = Q16Dot16ToInt(yFP);
+ for (int i = 0; i < n; ++i) {
+ buffer.addSpan(x[i], len[i], y,
+ Q16Dot16ToInt(Q16Dot16Multiply(rowHeight, coverage[i])));
+ }
+ }
+ } else { // aliased
+ int iTop = int(pa.y() + 0.5f);
+ int iBottom = pb.y() < 0.5f ? -1 : int(pb.y() - 0.5f);
+ int iLeft = int(left + 0.5f);
+ int iRight = right < 0.5f ? -1 : int(right - 0.5f);
+
+ int iWidth = iRight - iLeft + 1;
+ for (int y = iTop; y <= iBottom; ++y)
+ buffer.addSpan(iLeft, iWidth, y, 255);
+ }
+ } else {
+ if (pa.y() > pb.y())
+ qSwap(pa, pb);
+
+ QPointF delta = pb - pa;
+ delta *= 0.5f * width;
+ const QPointF perp(delta.y(), -delta.x());
+
+ QPointF top;
+ QPointF left;
+ QPointF right;
+ QPointF bottom;
+
+ if (pa.x() < pb.x()) {
+ top = pa + perp;
+ left = pa - perp;
+ right = pb + perp;
+ bottom = pb - perp;
+ } else {
+ top = pa - perp;
+ left = pb - perp;
+ right = pa + perp;
+ bottom = pb + perp;
+ }
+
+ top = snapTo26Dot6Grid(top);
+ bottom = snapTo26Dot6Grid(bottom);
+ left = snapTo26Dot6Grid(left);
+ right = snapTo26Dot6Grid(right);
+
+ const qreal topBound = qBound(qreal(d->clipRect.top()), top.y(), qreal(d->clipRect.bottom()));
+ const qreal bottomBound = qBound(qreal(d->clipRect.top()), bottom.y(), qreal(d->clipRect.bottom()));
+
+ const QPointF topLeftEdge = left - top;
+ const QPointF topRightEdge = right - top;
+ const QPointF bottomLeftEdge = bottom - left;
+ const QPointF bottomRightEdge = bottom - right;
+
+ const qreal topLeftSlope = topLeftEdge.x() / topLeftEdge.y();
+ const qreal bottomLeftSlope = bottomLeftEdge.x() / bottomLeftEdge.y();
+
+ const qreal topRightSlope = topRightEdge.x() / topRightEdge.y();
+ const qreal bottomRightSlope = bottomRightEdge.x() / bottomRightEdge.y();
+
+ const Q16Dot16 topLeftSlopeFP = FloatToQ16Dot16(topLeftSlope);
+ const Q16Dot16 topRightSlopeFP = FloatToQ16Dot16(topRightSlope);
+
+ const Q16Dot16 bottomLeftSlopeFP = FloatToQ16Dot16(bottomLeftSlope);
+ const Q16Dot16 bottomRightSlopeFP = FloatToQ16Dot16(bottomRightSlope);
+
+ const Q16Dot16 invTopLeftSlopeFP = FloatToQ16Dot16(1 / topLeftSlope);
+ const Q16Dot16 invTopRightSlopeFP = FloatToQ16Dot16(1 / topRightSlope);
+
+ const Q16Dot16 invBottomLeftSlopeFP = FloatToQ16Dot16(1 / bottomLeftSlope);
+ const Q16Dot16 invBottomRightSlopeFP = FloatToQ16Dot16(1 / bottomRightSlope);
+
+ if (d->antialiased) {
+ const Q16Dot16 iTopFP = IntToQ16Dot16(int(topBound));
+ const Q16Dot16 iLeftFP = IntToQ16Dot16(int(left.y()));
+ const Q16Dot16 iRightFP = IntToQ16Dot16(int(right.y()));
+ const Q16Dot16 iBottomFP = IntToQ16Dot16(int(bottomBound));
+
+ Q16Dot16 leftIntersectAf = FloatToQ16Dot16(top.x() + (int(topBound) - top.y()) * topLeftSlope);
+ Q16Dot16 rightIntersectAf = FloatToQ16Dot16(top.x() + (int(topBound) - top.y()) * topRightSlope);
+ Q16Dot16 leftIntersectBf = 0;
+ Q16Dot16 rightIntersectBf = 0;
+
+ if (iLeftFP < iTopFP)
+ leftIntersectBf = FloatToQ16Dot16(left.x() + (int(topBound) - left.y()) * bottomLeftSlope);
+
+ if (iRightFP < iTopFP)
+ rightIntersectBf = FloatToQ16Dot16(right.x() + (int(topBound) - right.y()) * bottomRightSlope);
+
+ Q16Dot16 rowTop, rowBottomLeft, rowBottomRight, rowTopLeft, rowTopRight, rowBottom;
+ Q16Dot16 topLeftIntersectAf, topLeftIntersectBf, topRightIntersectAf, topRightIntersectBf;
+ Q16Dot16 bottomLeftIntersectAf, bottomLeftIntersectBf, bottomRightIntersectAf, bottomRightIntersectBf;
+
+ int leftMin, leftMax, rightMin, rightMax;
+
+ const Q16Dot16 yTopFP = FloatToQ16Dot16(top.y());
+ const Q16Dot16 yLeftFP = FloatToQ16Dot16(left.y());
+ const Q16Dot16 yRightFP = FloatToQ16Dot16(right.y());
+ const Q16Dot16 yBottomFP = FloatToQ16Dot16(bottom.y());
+
+ rowTop = qMax(iTopFP, yTopFP);
+ topLeftIntersectAf = leftIntersectAf +
+ Q16Dot16Multiply(topLeftSlopeFP, rowTop - iTopFP);
+ topRightIntersectAf = rightIntersectAf +
+ Q16Dot16Multiply(topRightSlopeFP, rowTop - iTopFP);
+
+ Q16Dot16 yFP = iTopFP;
+ while (yFP <= iBottomFP) {
+ rowBottomLeft = qMin(yFP + Q16Dot16Factor, yLeftFP);
+ rowBottomRight = qMin(yFP + Q16Dot16Factor, yRightFP);
+ rowTopLeft = qMax(yFP, yLeftFP);
+ rowTopRight = qMax(yFP, yRightFP);
+ rowBottom = qMin(yFP + Q16Dot16Factor, yBottomFP);
+
+ if (yFP == iLeftFP) {
+ const int y = Q16Dot16ToInt(yFP);
+ leftIntersectBf = FloatToQ16Dot16(left.x() + (y - left.y()) * bottomLeftSlope);
+ topLeftIntersectBf = leftIntersectBf + Q16Dot16Multiply(bottomLeftSlopeFP, rowTopLeft - yFP);
+ bottomLeftIntersectAf = leftIntersectAf + Q16Dot16Multiply(topLeftSlopeFP, rowBottomLeft - yFP);
+ } else {
+ topLeftIntersectBf = leftIntersectBf;
+ bottomLeftIntersectAf = leftIntersectAf + topLeftSlopeFP;
+ }
+
+ if (yFP == iRightFP) {
+ const int y = Q16Dot16ToInt(yFP);
+ rightIntersectBf = FloatToQ16Dot16(right.x() + (y - right.y()) * bottomRightSlope);
+ topRightIntersectBf = rightIntersectBf + Q16Dot16Multiply(bottomRightSlopeFP, rowTopRight - yFP);
+ bottomRightIntersectAf = rightIntersectAf + Q16Dot16Multiply(topRightSlopeFP, rowBottomRight - yFP);
+ } else {
+ topRightIntersectBf = rightIntersectBf;
+ bottomRightIntersectAf = rightIntersectAf + topRightSlopeFP;
+ }
+
+ if (yFP == iBottomFP) {
+ bottomLeftIntersectBf = leftIntersectBf + Q16Dot16Multiply(bottomLeftSlopeFP, rowBottom - yFP);
+ bottomRightIntersectBf = rightIntersectBf + Q16Dot16Multiply(bottomRightSlopeFP, rowBottom - yFP);
+ } else {
+ bottomLeftIntersectBf = leftIntersectBf + bottomLeftSlopeFP;
+ bottomRightIntersectBf = rightIntersectBf + bottomRightSlopeFP;
+ }
+
+ if (yFP < iLeftFP) {
+ leftMin = Q16Dot16ToInt(bottomLeftIntersectAf);
+ leftMax = Q16Dot16ToInt(topLeftIntersectAf);
+ } else if (yFP == iLeftFP) {
+ leftMin = Q16Dot16ToInt(qMax(bottomLeftIntersectAf, topLeftIntersectBf));
+ leftMax = Q16Dot16ToInt(qMax(topLeftIntersectAf, bottomLeftIntersectBf));
+ } else {
+ leftMin = Q16Dot16ToInt(topLeftIntersectBf);
+ leftMax = Q16Dot16ToInt(bottomLeftIntersectBf);
+ }
+
+ leftMin = qBound(d->clipRect.left(), leftMin, d->clipRect.right());
+ leftMax = qBound(d->clipRect.left(), leftMax, d->clipRect.right());
+
+ if (yFP < iRightFP) {
+ rightMin = Q16Dot16ToInt(topRightIntersectAf);
+ rightMax = Q16Dot16ToInt(bottomRightIntersectAf);
+ } else if (yFP == iRightFP) {
+ rightMin = Q16Dot16ToInt(qMin(topRightIntersectAf, bottomRightIntersectBf));
+ rightMax = Q16Dot16ToInt(qMin(bottomRightIntersectAf, topRightIntersectBf));
+ } else {
+ rightMin = Q16Dot16ToInt(bottomRightIntersectBf);
+ rightMax = Q16Dot16ToInt(topRightIntersectBf);
+ }
+
+ rightMin = qBound(d->clipRect.left(), rightMin, d->clipRect.right());
+ rightMax = qBound(d->clipRect.left(), rightMax, d->clipRect.right());
+
+ if (leftMax > rightMax)
+ leftMax = rightMax;
+ if (rightMin < leftMin)
+ rightMin = leftMin;
+
+ Q16Dot16 rowHeight = rowBottom - rowTop;
+
+ int x = leftMin;
+ while (x <= leftMax) {
+ Q16Dot16 excluded = 0;
+
+ if (yFP <= iLeftFP)
+ excluded += intersectPixelFP(x, rowTop, rowBottomLeft,
+ bottomLeftIntersectAf, topLeftIntersectAf,
+ topLeftSlopeFP, invTopLeftSlopeFP);
+ if (yFP >= iLeftFP)
+ excluded += intersectPixelFP(x, rowTopLeft, rowBottom,
+ topLeftIntersectBf, bottomLeftIntersectBf,
+ bottomLeftSlopeFP, invBottomLeftSlopeFP);
+
+ if (x >= rightMin) {
+ if (yFP <= iRightFP)
+ excluded += (rowBottomRight - rowTop) - intersectPixelFP(x, rowTop, rowBottomRight,
+ topRightIntersectAf, bottomRightIntersectAf,
+ topRightSlopeFP, invTopRightSlopeFP);
+ if (yFP >= iRightFP)
+ excluded += (rowBottom - rowTopRight) - intersectPixelFP(x, rowTopRight, rowBottom,
+ bottomRightIntersectBf, topRightIntersectBf,
+ bottomRightSlopeFP, invBottomRightSlopeFP);
+ }
+
+ Q16Dot16 coverage = rowHeight - excluded;
+ buffer.addSpan(x, 1, Q16Dot16ToInt(yFP),
+ Q16Dot16ToInt(255 * coverage));
+ ++x;
+ }
+ if (x < rightMin) {
+ buffer.addSpan(x, rightMin - x, Q16Dot16ToInt(yFP),
+ Q16Dot16ToInt(255 * rowHeight));
+ x = rightMin;
+ }
+ while (x <= rightMax) {
+ Q16Dot16 excluded = 0;
+ if (yFP <= iRightFP)
+ excluded += (rowBottomRight - rowTop) - intersectPixelFP(x, rowTop, rowBottomRight,
+ topRightIntersectAf, bottomRightIntersectAf,
+ topRightSlopeFP, invTopRightSlopeFP);
+ if (yFP >= iRightFP)
+ excluded += (rowBottom - rowTopRight) - intersectPixelFP(x, rowTopRight, rowBottom,
+ bottomRightIntersectBf, topRightIntersectBf,
+ bottomRightSlopeFP, invBottomRightSlopeFP);
+
+ Q16Dot16 coverage = rowHeight - excluded;
+ buffer.addSpan(x, 1, Q16Dot16ToInt(yFP),
+ Q16Dot16ToInt(255 * coverage));
+ ++x;
+ }
+
+ leftIntersectAf += topLeftSlopeFP;
+ leftIntersectBf += bottomLeftSlopeFP;
+ rightIntersectAf += topRightSlopeFP;
+ rightIntersectBf += bottomRightSlopeFP;
+ topLeftIntersectAf = leftIntersectAf;
+ topRightIntersectAf = rightIntersectAf;
+
+ yFP += Q16Dot16Factor;
+ rowTop = yFP;
+ }
+ } else { // aliased
+ int iTop = int(top.y() + 0.5f);
+ int iLeft = left.y() < 0.5f ? -1 : int(left.y() - 0.5f);
+ int iRight = right.y() < 0.5f ? -1 : int(right.y() - 0.5f);
+ int iBottom = bottom.y() < 0.5f? -1 : int(bottom.y() - 0.5f);
+ int iMiddle = qMin(iLeft, iRight);
+
+ Q16Dot16 leftIntersectAf = FloatToQ16Dot16(top.x() + 0.5f + (iTop + 0.5f - top.y()) * topLeftSlope);
+ Q16Dot16 leftIntersectBf = FloatToQ16Dot16(left.x() + 0.5f + (iLeft + 1.5f - left.y()) * bottomLeftSlope);
+ Q16Dot16 rightIntersectAf = FloatToQ16Dot16(top.x() - 0.5f + (iTop + 0.5f - top.y()) * topRightSlope);
+ Q16Dot16 rightIntersectBf = FloatToQ16Dot16(right.x() - 0.5f + (iRight + 1.5f - right.y()) * bottomRightSlope);
+
+ int ny;
+ int y = iTop;
+#define DO_SEGMENT(next, li, ri, ls, rs) \
+ ny = qMin(next + 1, d->clipRect.top()); \
+ if (y < ny) { \
+ li += ls * (ny - y); \
+ ri += rs * (ny - y); \
+ y = ny; \
+ } \
+ if (next > d->clipRect.bottom()) \
+ next = d->clipRect.bottom(); \
+ for (; y <= next; ++y) { \
+ const int x1 = qMax(Q16Dot16ToInt(li), d->clipRect.left()); \
+ const int x2 = qMin(Q16Dot16ToInt(ri), d->clipRect.right()); \
+ if (x2 >= x1) \
+ buffer.addSpan(x1, x2 - x1 + 1, y, 255); \
+ li += ls; \
+ ri += rs; \
+ }
+
+ DO_SEGMENT(iMiddle, leftIntersectAf, rightIntersectAf, topLeftSlopeFP, topRightSlopeFP)
+ DO_SEGMENT(iRight, leftIntersectBf, rightIntersectAf, bottomLeftSlopeFP, topRightSlopeFP)
+ DO_SEGMENT(iLeft, leftIntersectAf, rightIntersectBf, topLeftSlopeFP, bottomRightSlopeFP);
+ DO_SEGMENT(iBottom, leftIntersectBf, rightIntersectBf, bottomLeftSlopeFP, bottomRightSlopeFP);
+#undef DO_SEGMENT
+ }
+ }
+}
+
+void QRasterizer::rasterize(const QT_FT_Outline *outline, Qt::FillRule fillRule)
+{
+ if (outline->n_points < 3 || outline->n_contours == 0)
+ return;
+
+ const QT_FT_Vector *points = outline->points;
+
+ QSpanBuffer buffer(d->blend, d->data, d->clipRect);
+
+ // ### QT_FT_Outline already has a bounding rect which is
+ // ### precomputed at this point, so we should probably just be
+ // ### using that instead...
+ QT_FT_Pos min_y = points[0].y, max_y = points[0].y;
+ for (int i = 1; i < outline->n_points; ++i) {
+ const QT_FT_Vector &p = points[i];
+ min_y = qMin(p.y, min_y);
+ max_y = qMax(p.y, max_y);
+ }
+
+ int iTopBound = qMax(d->clipRect.top(), int((min_y + 32 + COORD_OFFSET - COORD_ROUNDING) >> 6));
+ int iBottomBound = qMin(d->clipRect.bottom(), int((max_y - 32 + COORD_OFFSET - COORD_ROUNDING) >> 6));
+
+ if (iTopBound > iBottomBound)
+ return;
+
+ d->scanConverter.begin(iTopBound, iBottomBound, d->clipRect.left(), d->clipRect.right(), fillRule, &buffer);
+
+ int first = 0;
+ for (int i = 0; i < outline->n_contours; ++i) {
+ const int last = outline->contours[i];
+ for (int j = first; j < last; ++j) {
+ if (outline->tags[j+1] == QT_FT_CURVE_TAG_CUBIC) {
+ Q_ASSERT(outline->tags[j+2] == QT_FT_CURVE_TAG_CUBIC);
+ d->scanConverter.mergeCurve(points[j], points[j+1], points[j+2], points[j+3]);
+ j += 2;
+ } else {
+ d->scanConverter.mergeLine(points[j], points[j+1]);
+ }
+ }
+
+ first = last + 1;
+ }
+
+ d->scanConverter.end();
+}
+
+void QRasterizer::rasterize(const QPainterPath &path, Qt::FillRule fillRule)
+{
+ if (path.isEmpty())
+ return;
+
+ QSpanBuffer buffer(d->blend, d->data, d->clipRect);
+
+ QRectF bounds = path.controlPointRect();
+
+ int iTopBound = qMax(d->clipRect.top(), int(bounds.top() + 0.5 + (COORD_OFFSET - COORD_ROUNDING)/64.));
+ int iBottomBound = qMin(d->clipRect.bottom(), int(bounds.bottom() - 0.5 + (COORD_OFFSET - COORD_ROUNDING)/64.));
+
+ if (iTopBound > iBottomBound)
+ return;
+
+ d->scanConverter.begin(iTopBound, iBottomBound, d->clipRect.left(), d->clipRect.right(), fillRule, &buffer);
+
+ int subpathStart = 0;
+ QT_FT_Vector last = { 0, 0 };
+ for (int i = 0; i < path.elementCount(); ++i) {
+ switch (path.elementAt(i).type) {
+ case QPainterPath::LineToElement:
+ {
+ QT_FT_Vector p1 = last;
+ QT_FT_Vector p2 = PointToVector(path.elementAt(i));
+ d->scanConverter.mergeLine(p1, p2);
+ last = p2;
+ break;
+ }
+ case QPainterPath::MoveToElement:
+ {
+ if (i != 0) {
+ QT_FT_Vector first = PointToVector(path.elementAt(subpathStart));
+ // close previous subpath
+ if (first.x != last.x || first.y != last.y)
+ d->scanConverter.mergeLine(last, first);
+ }
+ subpathStart = i;
+ last = PointToVector(path.elementAt(i));
+ break;
+ }
+ case QPainterPath::CurveToElement:
+ {
+ QT_FT_Vector p1 = last;
+ QT_FT_Vector p2 = PointToVector(path.elementAt(i));
+ QT_FT_Vector p3 = PointToVector(path.elementAt(++i));
+ QT_FT_Vector p4 = PointToVector(path.elementAt(++i));
+ d->scanConverter.mergeCurve(p1, p2, p3, p4);
+ last = p4;
+ break;
+ }
+ default:
+ Q_ASSERT(false);
+ break;
+ }
+ }
+
+ QT_FT_Vector first = PointToVector(path.elementAt(subpathStart));
+
+ // close path
+ if (first.x != last.x || first.y != last.y)
+ d->scanConverter.mergeLine(last, first);
+
+ d->scanConverter.end();
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qrasterizer_p.h b/src/gui/painting/qrasterizer_p.h
new file mode 100644
index 0000000000..d640600172
--- /dev/null
+++ b/src/gui/painting/qrasterizer_p.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRASTERIZER_P_H
+#define QRASTERIZER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtCore/qglobal.h"
+#include "QtGui/qpainter.h"
+
+#include <private/qdrawhelper_p.h>
+#include <private/qrasterdefs_p.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QSpanData;
+class QRasterBuffer;
+class QRasterizerPrivate;
+
+class
+#ifdef Q_WS_QWS
+Q_GUI_EXPORT
+#endif
+QRasterizer
+{
+public:
+ QRasterizer();
+ ~QRasterizer();
+
+ void setAntialiased(bool antialiased);
+ void setClipRect(const QRect &clipRect);
+
+ void initialize(ProcessSpans blend, void *data);
+
+ void rasterize(const QT_FT_Outline *outline, Qt::FillRule fillRule);
+ void rasterize(const QPainterPath &path, Qt::FillRule fillRule);
+
+ // width should be in units of |a-b|
+ void rasterizeLine(const QPointF &a, const QPointF &b, qreal width, bool squareCap = false);
+
+private:
+ QRasterizerPrivate *d;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/painting/qregion.cpp b/src/gui/painting/qregion.cpp
new file mode 100644
index 0000000000..d713346902
--- /dev/null
+++ b/src/gui/painting/qregion.cpp
@@ -0,0 +1,4365 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qregion.h"
+#include "qpainterpath.h"
+#include "qpolygon.h"
+#include "qbuffer.h"
+#include "qdatastream.h"
+#include "qvariant.h"
+#include "qvarlengtharray.h"
+
+#include <qdebug.h>
+
+#if defined(Q_OS_UNIX) || defined(Q_WS_WIN)
+#include "qimage.h"
+#include "qbitmap.h"
+#include <stdlib.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QRegion
+ \brief The QRegion class specifies a clip region for a painter.
+
+ \ingroup painting
+ \ingroup shared
+
+ QRegion is used with QPainter::setClipRegion() to limit the paint
+ area to what needs to be painted. There is also a QWidget::repaint()
+ function that takes a QRegion parameter. QRegion is the best tool for
+ minimizing the amount of screen area to be updated by a repaint.
+
+ This class is not suitable for constructing shapes for rendering, especially
+ as outlines. Use QPainterPath to create paths and shapes for use with
+ QPainter.
+
+ QRegion is an \l{implicitly shared} class.
+
+ \section1 Creating and Using Regions
+
+ A region can be created from a rectangle, an ellipse, a polygon or
+ a bitmap. Complex regions may be created by combining simple
+ regions using united(), intersected(), subtracted(), or xored() (exclusive
+ or). You can move a region using translate().
+
+ You can test whether a region isEmpty() or if it
+ contains() a QPoint or QRect. The bounding rectangle can be found
+ with boundingRect().
+
+ The function rects() gives a decomposition of the region into
+ rectangles.
+
+ Example of using complex regions:
+ \snippet doc/src/snippets/code/src_gui_painting_qregion.cpp 0
+
+ \section1 Additional License Information
+
+ On Embedded Linux, Windows CE and X11 platforms, parts of this class rely on
+ code obtained under the following licenses:
+
+ \legalese
+ Copyright (c) 1987 X Consortium
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+ Except as contained in this notice, the name of the X Consortium shall not be
+ used in advertising or otherwise to promote the sale, use or other dealings
+ in this Software without prior written authorization from the X Consortium.
+ \endlegalese
+
+ \br
+
+ \legalese
+ Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
+
+ All Rights Reserved
+
+ Permission to use, copy, modify, and distribute this software and its
+ documentation for any purpose and without fee is hereby granted,
+ provided that the above copyright notice appear in all copies and that
+ both that copyright notice and this permission notice appear in
+ supporting documentation, and that the name of Digital not be
+ used in advertising or publicity pertaining to distribution of the
+ software without specific, written prior permission.
+
+ DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+ DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ SOFTWARE.
+ \endlegalese
+
+ \sa QPainter::setClipRegion(), QPainter::setClipRect(), QPainterPath
+*/
+
+
+/*!
+ \enum QRegion::RegionType
+
+ Specifies the shape of the region to be created.
+
+ \value Rectangle the region covers the entire rectangle.
+ \value Ellipse the region is an ellipse inside the rectangle.
+*/
+
+/*!
+ \fn void QRegion::translate(const QPoint &point)
+
+ \overload
+
+ Translates the region \a{point}\e{.x()} along the x axis and
+ \a{point}\e{.y()} along the y axis, relative to the current
+ position. Positive values move the region to the right and down.
+
+ Translates to the given \a point.
+*/
+
+/*!
+ \fn Handle QRegion::handle() const
+
+ Returns a platform-specific region handle. The \c Handle type is
+ \c HRGN on Windows, \c Region on X11, and \c RgnHandle on Mac OS
+ X. On \l{Qt for Embedded Linux} it is \c {void *}.
+
+ \warning This function is not portable.
+*/
+
+/*****************************************************************************
+ QRegion member functions
+ *****************************************************************************/
+
+/*!
+ \fn QRegion::QRegion()
+
+ Constructs an empty region.
+
+ \sa isEmpty()
+*/
+
+/*!
+ \fn QRegion::QRegion(const QRect &r, RegionType t)
+ \overload
+
+ Create a region based on the rectange \a r with region type \a t.
+
+ If the rectangle is invalid a null region will be created.
+
+ \sa QRegion::RegionType
+*/
+
+/*!
+ \fn QRegion::QRegion(const QPolygon &a, Qt::FillRule fillRule)
+
+ Constructs a polygon region from the point array \a a with the fill rule
+ specified by \a fillRule.
+
+ If \a fillRule is \l{Qt::WindingFill}, the polygon region is defined
+ using the winding algorithm; if it is \l{Qt::OddEvenFill}, the odd-even fill
+ algorithm is used.
+
+ \warning This constructor can be used to create complex regions that will
+ slow down painting when used.
+*/
+
+/*!
+ \fn QRegion::QRegion(const QRegion &r)
+
+ Constructs a new region which is equal to region \a r.
+*/
+
+/*!
+ \fn QRegion::QRegion(const QBitmap &bm)
+
+ Constructs a region from the bitmap \a bm.
+
+ The resulting region consists of the pixels in bitmap \a bm that
+ are Qt::color1, as if each pixel was a 1 by 1 rectangle.
+
+ This constructor may create complex regions that will slow down
+ painting when used. Note that drawing masked pixmaps can be done
+ much faster using QPixmap::setMask().
+*/
+
+/*!
+ Constructs a rectangular or elliptic region.
+
+ If \a t is \c Rectangle, the region is the filled rectangle (\a x,
+ \a y, \a w, \a h). If \a t is \c Ellipse, the region is the filled
+ ellipse with center at (\a x + \a w / 2, \a y + \a h / 2) and size
+ (\a w ,\a h).
+*/
+QRegion::QRegion(int x, int y, int w, int h, RegionType t)
+{
+ QRegion tmp(QRect(x, y, w, h), t);
+ tmp.d->ref.ref();
+ d = tmp.d;
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Use the constructor tha takes a Qt::FillRule as the second
+ argument instead.
+*/
+QRegion::QRegion(const QPolygon &pa, bool winding)
+{
+ new (this) QRegion(pa, winding ? Qt::WindingFill : Qt::OddEvenFill);
+}
+#endif
+
+/*!
+ \fn QRegion::~QRegion()
+ \internal
+
+ Destroys the region.
+*/
+
+void QRegion::detach()
+{
+ if (d->ref != 1)
+ *this = copy();
+#if defined(Q_WS_X11)
+ else if (d->xrectangles) {
+ free(d->xrectangles);
+ d->xrectangles = 0;
+ }
+#endif
+}
+
+// duplicates in qregion_win.cpp and qregion_wce.cpp
+#define QRGN_SETRECT 1 // region stream commands
+#define QRGN_SETELLIPSE 2 // (these are internal)
+#define QRGN_SETPTARRAY_ALT 3
+#define QRGN_SETPTARRAY_WIND 4
+#define QRGN_TRANSLATE 5
+#define QRGN_OR 6
+#define QRGN_AND 7
+#define QRGN_SUB 8
+#define QRGN_XOR 9
+#define QRGN_RECTS 10
+
+
+#ifndef QT_NO_DATASTREAM
+
+/*
+ Executes region commands in the internal buffer and rebuilds the
+ original region.
+
+ We do this when we read a region from the data stream.
+
+ If \a ver is non-0, uses the format version \a ver on reading the
+ byte array.
+*/
+void QRegion::exec(const QByteArray &buffer, int ver, QDataStream::ByteOrder byteOrder)
+{
+ QByteArray copy = buffer;
+ QDataStream s(&copy, QIODevice::ReadOnly);
+ if (ver)
+ s.setVersion(ver);
+ s.setByteOrder(byteOrder);
+ QRegion rgn;
+#ifndef QT_NO_DEBUG
+ int test_cnt = 0;
+#endif
+ while (!s.atEnd()) {
+ qint32 id;
+ if (s.version() == 1) {
+ int id_int;
+ s >> id_int;
+ id = id_int;
+ } else {
+ s >> id;
+ }
+#ifndef QT_NO_DEBUG
+ if (test_cnt > 0 && id != QRGN_TRANSLATE)
+ qWarning("QRegion::exec: Internal error");
+ test_cnt++;
+#endif
+ if (id == QRGN_SETRECT || id == QRGN_SETELLIPSE) {
+ QRect r;
+ s >> r;
+ rgn = QRegion(r, id == QRGN_SETRECT ? Rectangle : Ellipse);
+ } else if (id == QRGN_SETPTARRAY_ALT || id == QRGN_SETPTARRAY_WIND) {
+ QPolygon a;
+ s >> a;
+ rgn = QRegion(a, id == QRGN_SETPTARRAY_WIND ? Qt::WindingFill : Qt::OddEvenFill);
+ } else if (id == QRGN_TRANSLATE) {
+ QPoint p;
+ s >> p;
+ rgn.translate(p.x(), p.y());
+ } else if (id >= QRGN_OR && id <= QRGN_XOR) {
+ QByteArray bop1, bop2;
+ QRegion r1, r2;
+ s >> bop1;
+ r1.exec(bop1);
+ s >> bop2;
+ r2.exec(bop2);
+
+ switch (id) {
+ case QRGN_OR:
+ rgn = r1.united(r2);
+ break;
+ case QRGN_AND:
+ rgn = r1.intersected(r2);
+ break;
+ case QRGN_SUB:
+ rgn = r1.subtracted(r2);
+ break;
+ case QRGN_XOR:
+ rgn = r1.xored(r2);
+ break;
+ }
+ } else if (id == QRGN_RECTS) {
+ // (This is the only form used in Qt 2.0)
+ quint32 n;
+ s >> n;
+ QRect r;
+ for (int i=0; i<(int)n; i++) {
+ s >> r;
+ rgn = rgn.united(QRegion(r));
+ }
+ }
+ }
+ *this = rgn;
+}
+
+
+/*****************************************************************************
+ QRegion stream functions
+ *****************************************************************************/
+
+/*!
+ \fn QRegion &QRegion::operator=(const QRegion &r)
+
+ Assigns \a r to this region and returns a reference to the region.
+*/
+
+/*!
+ \fn void QRegion::swap(QRegion &other)
+ \since 4.8
+
+ Swaps region \a other with this region. This operation is very
+ fast and never fails.
+*/
+
+/*!
+ \relates QRegion
+
+ Writes the region \a r to the stream \a s and returns a reference
+ to the stream.
+
+ \sa \link datastreamformat.html Format of the QDataStream operators \endlink
+*/
+
+QDataStream &operator<<(QDataStream &s, const QRegion &r)
+{
+ QVector<QRect> a = r.rects();
+ if (a.isEmpty()) {
+ s << (quint32)0;
+ } else {
+ if (s.version() == 1) {
+ int i;
+ for (i = a.size() - 1; i > 0; --i) {
+ s << (quint32)(12 + i * 24);
+ s << (int)QRGN_OR;
+ }
+ for (i = 0; i < a.size(); ++i) {
+ s << (quint32)(4+8) << (int)QRGN_SETRECT << a[i];
+ }
+ } else {
+ s << (quint32)(4 + 4 + 16 * a.size()); // 16: storage size of QRect
+ s << (qint32)QRGN_RECTS;
+ s << a;
+ }
+ }
+ return s;
+}
+
+/*!
+ \relates QRegion
+
+ Reads a region from the stream \a s into \a r and returns a
+ reference to the stream.
+
+ \sa \link datastreamformat.html Format of the QDataStream operators \endlink
+*/
+
+QDataStream &operator>>(QDataStream &s, QRegion &r)
+{
+ QByteArray b;
+ s >> b;
+ r.exec(b, s.version(), s.byteOrder());
+ return s;
+}
+#endif //QT_NO_DATASTREAM
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug s, const QRegion &r)
+{
+ QVector<QRect> rects = r.rects();
+ s.nospace() << "QRegion(size=" << rects.size() << "), "
+ << "bounds = " << r.boundingRect() << '\n';
+ for (int i=0; i<rects.size(); ++i)
+ s << "- " << i << rects.at(i) << '\n';
+ return s;
+}
+#endif
+
+
+// These are not inline - they can be implemented better on some platforms
+// (eg. Windows at least provides 3-variable operations). For now, simple.
+
+
+/*!
+ Applies the united() function to this region and \a r. \c r1|r2 is
+ equivalent to \c r1.united(r2).
+
+ \sa united(), operator+()
+*/
+const QRegion QRegion::operator|(const QRegion &r) const
+ { return united(r); }
+
+/*!
+ Applies the united() function to this region and \a r. \c r1+r2 is
+ equivalent to \c r1.united(r2).
+
+ \sa united(), operator|()
+*/
+const QRegion QRegion::operator+(const QRegion &r) const
+ { return united(r); }
+
+/*!
+ \overload
+ \since 4.4
+ */
+const QRegion QRegion::operator+(const QRect &r) const
+ { return united(r); }
+
+/*!
+ Applies the intersected() function to this region and \a r. \c r1&r2
+ is equivalent to \c r1.intersected(r2).
+
+ \sa intersected()
+*/
+const QRegion QRegion::operator&(const QRegion &r) const
+ { return intersected(r); }
+
+/*!
+ \overload
+ \since 4.4
+ */
+const QRegion QRegion::operator&(const QRect &r) const
+{
+ return intersected(r);
+}
+
+/*!
+ Applies the subtracted() function to this region and \a r. \c r1-r2
+ is equivalent to \c r1.subtracted(r2).
+
+ \sa subtracted()
+*/
+const QRegion QRegion::operator-(const QRegion &r) const
+ { return subtracted(r); }
+
+/*!
+ Applies the xored() function to this region and \a r. \c r1^r2 is
+ equivalent to \c r1.xored(r2).
+
+ \sa xored()
+*/
+const QRegion QRegion::operator^(const QRegion &r) const
+ { return xored(r); }
+
+/*!
+ Applies the united() function to this region and \a r and assigns
+ the result to this region. \c r1|=r2 is equivalent to \c
+ {r1 = r1.united(r2)}.
+
+ \sa united()
+*/
+QRegion& QRegion::operator|=(const QRegion &r)
+ { return *this = *this | r; }
+
+/*!
+ \fn QRegion& QRegion::operator+=(const QRect &rect)
+
+ Returns a region that is the union of this region with the specified \a rect.
+
+ \sa united()
+*/
+/*!
+ \fn QRegion& QRegion::operator+=(const QRegion &r)
+
+ Applies the united() function to this region and \a r and assigns
+ the result to this region. \c r1+=r2 is equivalent to \c
+ {r1 = r1.united(r2)}.
+
+ \sa intersected()
+*/
+#if !defined (Q_OS_UNIX) && !defined (Q_WS_WIN)
+QRegion& QRegion::operator+=(const QRect &r)
+{
+ return operator+=(QRegion(r));
+}
+#endif
+
+/*!
+ \fn QRegion& QRegion::operator&=(const QRegion &r)
+
+ Applies the intersected() function to this region and \a r and
+ assigns the result to this region. \c r1&=r2 is equivalent to \c
+ r1 = r1.intersected(r2).
+
+ \sa intersected()
+*/
+QRegion& QRegion::operator&=(const QRegion &r)
+ { return *this = *this & r; }
+
+/*!
+ \overload
+ \since 4.4
+ */
+#if defined (Q_OS_UNIX) || defined (Q_WS_WIN)
+QRegion& QRegion::operator&=(const QRect &r)
+{
+ return *this = *this & r;
+}
+#else
+QRegion& QRegion::operator&=(const QRect &r)
+{
+ return *this &= (QRegion(r));
+}
+#endif
+
+/*!
+ \fn QRegion& QRegion::operator-=(const QRegion &r)
+
+ Applies the subtracted() function to this region and \a r and
+ assigns the result to this region. \c r1-=r2 is equivalent to \c
+ {r1 = r1.subtracted(r2)}.
+
+ \sa subtracted()
+*/
+QRegion& QRegion::operator-=(const QRegion &r)
+ { return *this = *this - r; }
+
+/*!
+ Applies the xored() function to this region and \a r and
+ assigns the result to this region. \c r1^=r2 is equivalent to \c
+ {r1 = r1.xored(r2)}.
+
+ \sa xored()
+*/
+QRegion& QRegion::operator^=(const QRegion &r)
+ { return *this = *this ^ r; }
+
+/*!
+ \fn bool QRegion::operator!=(const QRegion &other) const
+
+ Returns true if this region is different from the \a other region;
+ otherwise returns false.
+*/
+
+/*!
+ Returns the region as a QVariant
+*/
+QRegion::operator QVariant() const
+{
+ return QVariant(QVariant::Region, this);
+}
+
+/*!
+ \fn bool QRegion::operator==(const QRegion &r) const
+
+ Returns true if the region is equal to \a r; otherwise returns
+ false.
+*/
+
+/*!
+ \fn bool QRegion::isNull() const
+
+ Use isEmpty() instead.
+*/
+
+
+/*!
+ \fn void QRegion::translate(int dx, int dy)
+
+ Translates (moves) the region \a dx along the X axis and \a dy
+ along the Y axis.
+*/
+
+/*!
+ \fn QRegion QRegion::translated(const QPoint &p) const
+ \overload
+ \since 4.1
+
+ Returns a copy of the regtion that is translated \a{p}\e{.x()}
+ along the x axis and \a{p}\e{.y()} along the y axis, relative to
+ the current position. Positive values move the rectangle to the
+ right and down.
+
+ \sa translate()
+*/
+
+/*!
+ \since 4.1
+
+ Returns a copy of the region that is translated \a dx along the
+ x axis and \a dy along the y axis, relative to the current
+ position. Positive values move the region to the right and
+ down.
+
+ \sa translate()
+*/
+
+QRegion
+QRegion::translated(int dx, int dy) const
+{
+ QRegion ret(*this);
+ ret.translate(dx, dy);
+ return ret;
+}
+
+
+inline bool rect_intersects(const QRect &r1, const QRect &r2)
+{
+ return (r1.right() >= r2.left() && r1.left() <= r2.right() &&
+ r1.bottom() >= r2.top() && r1.top() <= r2.bottom());
+}
+
+/*!
+ \since 4.2
+
+ Returns true if this region intersects with \a region, otherwise
+ returns false.
+*/
+bool QRegion::intersects(const QRegion &region) const
+{
+ if (isEmpty() || region.isEmpty())
+ return false;
+
+ if (!rect_intersects(boundingRect(), region.boundingRect()))
+ return false;
+ if (rectCount() == 1 && region.rectCount() == 1)
+ return true;
+
+ const QVector<QRect> myRects = rects();
+ const QVector<QRect> otherRects = region.rects();
+
+ for (QVector<QRect>::const_iterator i1 = myRects.constBegin(); i1 < myRects.constEnd(); ++i1)
+ for (QVector<QRect>::const_iterator i2 = otherRects.constBegin(); i2 < otherRects.constEnd(); ++i2)
+ if (rect_intersects(*i1, *i2))
+ return true;
+ return false;
+}
+
+/*!
+ \fn bool QRegion::intersects(const QRect &rect) const
+ \since 4.2
+
+ Returns true if this region intersects with \a rect, otherwise
+ returns false.
+*/
+
+
+#if !defined (Q_OS_UNIX) && !defined (Q_WS_WIN)
+/*!
+ \overload
+ \since 4.4
+*/
+QRegion QRegion::intersect(const QRect &r) const
+{
+ return intersect(QRegion(r));
+}
+#endif
+
+/*!
+ \obsolete
+ \fn int QRegion::numRects() const
+ \since 4.4
+
+ Returns the number of rectangles that will be returned in rects().
+*/
+
+/*!
+ \fn int QRegion::rectCount() const
+ \since 4.6
+
+ Returns the number of rectangles that will be returned in rects().
+*/
+
+/*!
+ \fn bool QRegion::isEmpty() const
+
+ Returns true if the region is empty; otherwise returns false. An
+ empty region is a region that contains no points.
+
+ Example:
+ \snippet doc/src/snippets/code/src_gui_painting_qregion_unix.cpp 0
+*/
+
+/*!
+ \fn bool QRegion::contains(const QPoint &p) const
+
+ Returns true if the region contains the point \a p; otherwise
+ returns false.
+*/
+
+/*!
+ \fn bool QRegion::contains(const QRect &r) const
+ \overload
+
+ Returns true if the region overlaps the rectangle \a r; otherwise
+ returns false.
+*/
+
+/*!
+ \fn QRegion QRegion::unite(const QRegion &r) const
+ \obsolete
+
+ Use united(\a r) instead.
+*/
+
+/*!
+ \fn QRegion QRegion::unite(const QRect &rect) const
+ \since 4.4
+ \obsolete
+
+ Use united(\a rect) instead.
+*/
+
+/*!
+ \fn QRegion QRegion::united(const QRect &rect) const
+ \since 4.4
+
+ Returns a region which is the union of this region and the given \a rect.
+
+ \sa intersected(), subtracted(), xored()
+*/
+
+/*!
+ \fn QRegion QRegion::united(const QRegion &r) const
+ \since 4.2
+
+ Returns a region which is the union of this region and \a r.
+
+ \img runion.png Region Union
+
+ The figure shows the union of two elliptical regions.
+
+ \sa intersected(), subtracted(), xored()
+*/
+
+/*!
+ \fn QRegion QRegion::intersect(const QRegion &r) const
+ \obsolete
+
+ Use intersected(\a r) instead.
+*/
+
+/*!
+ \fn QRegion QRegion::intersect(const QRect &rect) const
+ \since 4.4
+ \obsolete
+
+ Use intersected(\a rect) instead.
+*/
+
+/*!
+ \fn QRegion QRegion::intersected(const QRect &rect) const
+ \since 4.4
+
+ Returns a region which is the intersection of this region and the given \a rect.
+
+ \sa subtracted(), united(), xored()
+*/
+
+/*!
+ \fn QRegion QRegion::intersected(const QRegion &r) const
+ \since 4.2
+
+ Returns a region which is the intersection of this region and \a r.
+
+ \img rintersect.png Region Intersection
+
+ The figure shows the intersection of two elliptical regions.
+
+ \sa subtracted(), united(), xored()
+*/
+
+/*!
+ \fn QRegion QRegion::subtract(const QRegion &r) const
+ \obsolete
+
+ Use subtracted(\a r) instead.
+*/
+
+/*!
+ \fn QRegion QRegion::subtracted(const QRegion &r) const
+ \since 4.2
+
+ Returns a region which is \a r subtracted from this region.
+
+ \img rsubtract.png Region Subtraction
+
+ The figure shows the result when the ellipse on the right is
+ subtracted from the ellipse on the left (\c {left - right}).
+
+ \sa intersected(), united(), xored()
+*/
+
+/*!
+ \fn QRegion QRegion::eor(const QRegion &r) const
+ \obsolete
+
+ Use xored(\a r) instead.
+*/
+
+/*!
+ \fn QRegion QRegion::xored(const QRegion &r) const
+ \since 4.2
+
+ Returns a region which is the exclusive or (XOR) of this region
+ and \a r.
+
+ \img rxor.png Region XORed
+
+ The figure shows the exclusive or of two elliptical regions.
+
+ \sa intersected(), united(), subtracted()
+*/
+
+/*!
+ \fn QRect QRegion::boundingRect() const
+
+ Returns the bounding rectangle of this region. An empty region
+ gives a rectangle that is QRect::isNull().
+*/
+
+/*!
+ \fn QVector<QRect> QRegion::rects() const
+
+ Returns an array of non-overlapping rectangles that make up the
+ region.
+
+ The union of all the rectangles is equal to the original region.
+*/
+
+/*!
+ \fn void QRegion::setRects(const QRect *rects, int number)
+
+ Sets the region using the array of rectangles specified by \a rects and
+ \a number.
+ The rectangles \e must be optimally Y-X sorted and follow these restrictions:
+
+ \list
+ \o The rectangles must not intersect.
+ \o All rectangles with a given top coordinate must have the same height.
+ \o No two rectangles may abut horizontally (they should be combined
+ into a single wider rectangle in that case).
+ \o The rectangles must be sorted in ascending order, with Y as the major
+ sort key and X as the minor sort key.
+ \endlist
+ \omit
+ Only some platforms have these restrictions (Qt for Embedded Linux, X11 and Mac OS X).
+ \endomit
+*/
+
+namespace {
+
+struct Segment
+{
+ Segment() {}
+ Segment(const QPoint &p)
+ : added(false)
+ , point(p)
+ {
+ }
+
+ int left() const
+ {
+ return qMin(point.x(), next->point.x());
+ }
+
+ int right() const
+ {
+ return qMax(point.x(), next->point.x());
+ }
+
+ bool overlaps(const Segment &other) const
+ {
+ return left() < other.right() && other.left() < right();
+ }
+
+ void connect(Segment &other)
+ {
+ next = &other;
+ other.prev = this;
+
+ horizontal = (point.y() == other.point.y());
+ }
+
+ void merge(Segment &other)
+ {
+ if (right() <= other.right()) {
+ QPoint p = other.point;
+ Segment *oprev = other.prev;
+
+ other.point = point;
+ other.prev = prev;
+ prev->next = &other;
+
+ point = p;
+ prev = oprev;
+ oprev->next = this;
+ } else {
+ Segment *onext = other.next;
+ other.next = next;
+ next->prev = &other;
+
+ next = onext;
+ next->prev = this;
+ }
+ }
+
+ int horizontal : 1;
+ int added : 1;
+
+ QPoint point;
+ Segment *prev;
+ Segment *next;
+};
+
+void mergeSegments(Segment *a, int na, Segment *b, int nb)
+{
+ int i = 0;
+ int j = 0;
+
+ while (i != na && j != nb) {
+ Segment &sa = a[i];
+ Segment &sb = b[j];
+ const int ra = sa.right();
+ const int rb = sb.right();
+ if (sa.overlaps(sb))
+ sa.merge(sb);
+ i += (rb >= ra);
+ j += (ra >= rb);
+ }
+}
+
+void addSegmentsToPath(Segment *segment, QPainterPath &path)
+{
+ Segment *current = segment;
+ path.moveTo(current->point);
+
+ current->added = true;
+
+ Segment *last = current;
+ current = current->next;
+ while (current != segment) {
+ if (current->horizontal != last->horizontal)
+ path.lineTo(current->point);
+ current->added = true;
+ last = current;
+ current = current->next;
+ }
+}
+
+}
+
+Q_AUTOTEST_EXPORT QPainterPath qt_regionToPath(const QRegion &region)
+{
+ QPainterPath result;
+ if (region.rectCount() == 1) {
+ result.addRect(region.boundingRect());
+ return result;
+ }
+
+ const QVector<QRect> rects = region.rects();
+
+ QVarLengthArray<Segment> segments;
+ segments.resize(4 * rects.size());
+
+ const QRect *rect = rects.constData();
+ const QRect *end = rect + rects.size();
+
+ int lastRowSegmentCount = 0;
+ Segment *lastRowSegments = 0;
+
+ int lastSegment = 0;
+ int lastY = 0;
+ while (rect != end) {
+ const int y = rect[0].y();
+ int count = 0;
+ while (&rect[count] != end && rect[count].y() == y)
+ ++count;
+
+ for (int i = 0; i < count; ++i) {
+ int offset = lastSegment + i;
+ segments[offset] = Segment(rect[i].topLeft());
+ segments[offset += count] = Segment(rect[i].topRight() + QPoint(1, 0));
+ segments[offset += count] = Segment(rect[i].bottomRight() + QPoint(1, 1));
+ segments[offset += count] = Segment(rect[i].bottomLeft() + QPoint(0, 1));
+
+ offset = lastSegment + i;
+ for (int j = 0; j < 4; ++j)
+ segments[offset + j * count].connect(segments[offset + ((j + 1) % 4) * count]);
+ }
+
+ if (lastRowSegments && lastY == y)
+ mergeSegments(lastRowSegments, lastRowSegmentCount, &segments[lastSegment], count);
+
+ lastRowSegments = &segments[lastSegment + 2 * count];
+ lastRowSegmentCount = count;
+ lastSegment += 4 * count;
+ lastY = y + rect[0].height();
+ rect += count;
+ }
+
+ for (int i = 0; i < lastSegment; ++i) {
+ Segment *segment = &segments[i];
+ if (!segment->added)
+ addSegmentsToPath(segment, result);
+ }
+
+ return result;
+}
+
+#if defined(Q_OS_UNIX) || defined(Q_WS_WIN)
+
+//#define QT_REGION_DEBUG
+/*
+ * clip region
+ */
+
+struct QRegionPrivate {
+ int numRects;
+ QVector<QRect> rects;
+ QRect extents;
+ QRect innerRect;
+ int innerArea;
+
+ inline QRegionPrivate() : numRects(0), innerArea(-1) {}
+ inline QRegionPrivate(const QRect &r) {
+ numRects = 1;
+ extents = r;
+ innerRect = r;
+ innerArea = r.width() * r.height();
+ }
+
+ inline QRegionPrivate(const QRegionPrivate &r) {
+ rects = r.rects;
+ numRects = r.numRects;
+ extents = r.extents;
+ innerRect = r.innerRect;
+ innerArea = r.innerArea;
+ }
+
+ inline QRegionPrivate &operator=(const QRegionPrivate &r) {
+ rects = r.rects;
+ numRects = r.numRects;
+ extents = r.extents;
+ innerRect = r.innerRect;
+ innerArea = r.innerArea;
+ return *this;
+ }
+
+ void intersect(const QRect &r);
+
+ /*
+ * Returns true if r is guaranteed to be fully contained in this region.
+ * A false return value does not guarantee the opposite.
+ */
+ inline bool contains(const QRegionPrivate &r) const {
+ return contains(r.extents);
+ }
+
+ inline bool contains(const QRect &r2) const {
+ const QRect &r1 = innerRect;
+ return r2.left() >= r1.left() && r2.right() <= r1.right()
+ && r2.top() >= r1.top() && r2.bottom() <= r1.bottom();
+ }
+
+ /*
+ * Returns true if this region is guaranteed to be fully contained in r.
+ */
+ inline bool within(const QRect &r1) const {
+ const QRect &r2 = extents;
+ return r2.left() >= r1.left() && r2.right() <= r1.right()
+ && r2.top() >= r1.top() && r2.bottom() <= r1.bottom();
+ }
+
+ inline void updateInnerRect(const QRect &rect) {
+ const int area = rect.width() * rect.height();
+ if (area > innerArea) {
+ innerArea = area;
+ innerRect = rect;
+ }
+ }
+
+ inline void vectorize() {
+ if (numRects == 1) {
+ if (!rects.size())
+ rects.resize(1);
+ rects[0] = extents;
+ }
+ }
+
+ inline void append(const QRect *r);
+ void append(const QRegionPrivate *r);
+ void prepend(const QRect *r);
+ void prepend(const QRegionPrivate *r);
+ inline bool canAppend(const QRect *r) const;
+ inline bool canAppend(const QRegionPrivate *r) const;
+ inline bool canPrepend(const QRect *r) const;
+ inline bool canPrepend(const QRegionPrivate *r) const;
+
+ inline bool mergeFromRight(QRect *left, const QRect *right);
+ inline bool mergeFromLeft(QRect *left, const QRect *right);
+ inline bool mergeFromBelow(QRect *top, const QRect *bottom,
+ const QRect *nextToTop,
+ const QRect *nextToBottom);
+ inline bool mergeFromAbove(QRect *bottom, const QRect *top,
+ const QRect *nextToBottom,
+ const QRect *nextToTop);
+
+#ifdef QT_REGION_DEBUG
+ void selfTest() const;
+#endif
+};
+
+static inline bool isEmptyHelper(const QRegionPrivate *preg)
+{
+ return !preg || preg->numRects == 0;
+}
+
+static inline bool canMergeFromRight(const QRect *left, const QRect *right)
+{
+ return (right->top() == left->top()
+ && right->bottom() == left->bottom()
+ && right->left() <= (left->right() + 1));
+}
+
+static inline bool canMergeFromLeft(const QRect *right, const QRect *left)
+{
+ return canMergeFromRight(left, right);
+}
+
+bool QRegionPrivate::mergeFromRight(QRect *left, const QRect *right)
+{
+ if (canMergeFromRight(left, right)) {
+ left->setRight(right->right());
+ updateInnerRect(*left);
+ return true;
+ }
+ return false;
+}
+
+bool QRegionPrivate::mergeFromLeft(QRect *right, const QRect *left)
+{
+ if (canMergeFromLeft(right, left)) {
+ right->setLeft(left->left());
+ updateInnerRect(*right);
+ return true;
+ }
+ return false;
+}
+
+static inline bool canMergeFromBelow(const QRect *top, const QRect *bottom,
+ const QRect *nextToTop,
+ const QRect *nextToBottom)
+{
+ if (nextToTop && nextToTop->y() == top->y())
+ return false;
+ if (nextToBottom && nextToBottom->y() == bottom->y())
+ return false;
+
+ return ((top->bottom() >= (bottom->top() - 1))
+ && top->left() == bottom->left()
+ && top->right() == bottom->right());
+}
+
+bool QRegionPrivate::mergeFromBelow(QRect *top, const QRect *bottom,
+ const QRect *nextToTop,
+ const QRect *nextToBottom)
+{
+ if (canMergeFromBelow(top, bottom, nextToTop, nextToBottom)) {
+ top->setBottom(bottom->bottom());
+ updateInnerRect(*top);
+ return true;
+ }
+ return false;
+}
+
+bool QRegionPrivate::mergeFromAbove(QRect *bottom, const QRect *top,
+ const QRect *nextToBottom,
+ const QRect *nextToTop)
+{
+ if (canMergeFromBelow(top, bottom, nextToTop, nextToBottom)) {
+ bottom->setTop(top->top());
+ updateInnerRect(*bottom);
+ return true;
+ }
+ return false;
+}
+
+static inline QRect qt_rect_intersect_normalized(const QRect &r1,
+ const QRect &r2)
+{
+ QRect r;
+ r.setLeft(qMax(r1.left(), r2.left()));
+ r.setRight(qMin(r1.right(), r2.right()));
+ r.setTop(qMax(r1.top(), r2.top()));
+ r.setBottom(qMin(r1.bottom(), r2.bottom()));
+ return r;
+}
+
+void QRegionPrivate::intersect(const QRect &rect)
+{
+ Q_ASSERT(extents.intersects(rect));
+ Q_ASSERT(numRects > 1);
+
+#ifdef QT_REGION_DEBUG
+ selfTest();
+#endif
+
+ const QRect r = rect.normalized();
+ extents = QRect();
+ innerRect = QRect();
+ innerArea = -1;
+
+ QRect *dest = rects.data();
+ const QRect *src = dest;
+ int n = numRects;
+ numRects = 0;
+ while (n--) {
+ *dest = qt_rect_intersect_normalized(*src++, r);
+ if (dest->isEmpty())
+ continue;
+
+ if (numRects == 0) {
+ extents = *dest;
+ } else {
+ extents.setLeft(qMin(extents.left(), dest->left()));
+ // hw: extents.top() will never change after initialization
+ //extents.setTop(qMin(extents.top(), dest->top()));
+ extents.setRight(qMax(extents.right(), dest->right()));
+ extents.setBottom(qMax(extents.bottom(), dest->bottom()));
+
+ const QRect *nextToLast = (numRects > 1 ? dest - 2 : 0);
+
+ // mergeFromBelow inlined and optimized
+ if (canMergeFromBelow(dest - 1, dest, nextToLast, 0)) {
+ if (!n || src->y() != dest->y() || src->left() > r.right()) {
+ QRect *prev = dest - 1;
+ prev->setBottom(dest->bottom());
+ updateInnerRect(*prev);
+ continue;
+ }
+ }
+ }
+ updateInnerRect(*dest);
+ ++dest;
+ ++numRects;
+ }
+#ifdef QT_REGION_DEBUG
+ selfTest();
+#endif
+}
+
+void QRegionPrivate::append(const QRect *r)
+{
+ Q_ASSERT(!r->isEmpty());
+
+ QRect *myLast = (numRects == 1 ? &extents : rects.data() + (numRects - 1));
+ if (mergeFromRight(myLast, r)) {
+ if (numRects > 1) {
+ const QRect *nextToTop = (numRects > 2 ? myLast - 2 : 0);
+ if (mergeFromBelow(myLast - 1, myLast, nextToTop, 0))
+ --numRects;
+ }
+ } else if (mergeFromBelow(myLast, r, (numRects > 1 ? myLast - 1 : 0), 0)) {
+ // nothing
+ } else {
+ vectorize();
+ ++numRects;
+ updateInnerRect(*r);
+ if (rects.size() < numRects)
+ rects.resize(numRects);
+ rects[numRects - 1] = *r;
+ }
+ extents.setCoords(qMin(extents.left(), r->left()),
+ qMin(extents.top(), r->top()),
+ qMax(extents.right(), r->right()),
+ qMax(extents.bottom(), r->bottom()));
+
+#ifdef QT_REGION_DEBUG
+ selfTest();
+#endif
+}
+
+void QRegionPrivate::append(const QRegionPrivate *r)
+{
+ Q_ASSERT(!isEmptyHelper(r));
+
+ if (r->numRects == 1) {
+ append(&r->extents);
+ return;
+ }
+
+ vectorize();
+
+ QRect *destRect = rects.data() + numRects;
+ const QRect *srcRect = r->rects.constData();
+ int numAppend = r->numRects;
+
+ // try merging
+ {
+ const QRect *rFirst = srcRect;
+ QRect *myLast = destRect - 1;
+ const QRect *nextToLast = (numRects > 1 ? myLast - 1 : 0);
+ if (mergeFromRight(myLast, rFirst)) {
+ ++srcRect;
+ --numAppend;
+ const QRect *rNextToFirst = (numAppend > 1 ? rFirst + 2 : 0);
+ if (mergeFromBelow(myLast, rFirst + 1, nextToLast, rNextToFirst)) {
+ ++srcRect;
+ --numAppend;
+ }
+ if (numRects > 1) {
+ nextToLast = (numRects > 2 ? myLast - 2 : 0);
+ rNextToFirst = (numAppend > 0 ? srcRect : 0);
+ if (mergeFromBelow(myLast - 1, myLast, nextToLast, rNextToFirst)) {
+ --destRect;
+ --numRects;
+ }
+ }
+ } else if (mergeFromBelow(myLast, rFirst, nextToLast, rFirst + 1)) {
+ ++srcRect;
+ --numAppend;
+ }
+ }
+
+ // append rectangles
+ if (numAppend > 0) {
+ const int newNumRects = numRects + numAppend;
+ if (newNumRects > rects.size()) {
+ rects.resize(newNumRects);
+ destRect = rects.data() + numRects;
+ }
+ memcpy(destRect, srcRect, numAppend * sizeof(QRect));
+
+ numRects = newNumRects;
+ }
+
+ // update inner rectangle
+ if (innerArea < r->innerArea) {
+ innerArea = r->innerArea;
+ innerRect = r->innerRect;
+ }
+
+ // update extents
+ destRect = &extents;
+ srcRect = &r->extents;
+ extents.setCoords(qMin(destRect->left(), srcRect->left()),
+ qMin(destRect->top(), srcRect->top()),
+ qMax(destRect->right(), srcRect->right()),
+ qMax(destRect->bottom(), srcRect->bottom()));
+
+#ifdef QT_REGION_DEBUG
+ selfTest();
+#endif
+}
+
+void QRegionPrivate::prepend(const QRegionPrivate *r)
+{
+ Q_ASSERT(!isEmptyHelper(r));
+
+ if (r->numRects == 1) {
+ prepend(&r->extents);
+ return;
+ }
+
+ vectorize();
+
+ int numPrepend = r->numRects;
+ int numSkip = 0;
+
+ // try merging
+ {
+ QRect *myFirst = rects.data();
+ const QRect *nextToFirst = (numRects > 1 ? myFirst + 1 : 0);
+ const QRect *rLast = r->rects.constData() + r->numRects - 1;
+ const QRect *rNextToLast = (r->numRects > 1 ? rLast - 1 : 0);
+ if (mergeFromLeft(myFirst, rLast)) {
+ --numPrepend;
+ --rLast;
+ rNextToLast = (numPrepend > 1 ? rLast - 1 : 0);
+ if (mergeFromAbove(myFirst, rLast, nextToFirst, rNextToLast)) {
+ --numPrepend;
+ --rLast;
+ }
+ if (numRects > 1) {
+ nextToFirst = (numRects > 2? myFirst + 2 : 0);
+ rNextToLast = (numPrepend > 0 ? rLast : 0);
+ if (mergeFromAbove(myFirst + 1, myFirst, nextToFirst, rNextToLast)) {
+ --numRects;
+ ++numSkip;
+ }
+ }
+ } else if (mergeFromAbove(myFirst, rLast, nextToFirst, rNextToLast)) {
+ --numPrepend;
+ }
+ }
+
+ if (numPrepend > 0) {
+ const int newNumRects = numRects + numPrepend;
+ if (newNumRects > rects.size())
+ rects.resize(newNumRects);
+
+ // move existing rectangles
+ memmove(rects.data() + numPrepend, rects.constData() + numSkip,
+ numRects * sizeof(QRect));
+
+ // prepend new rectangles
+ memcpy(rects.data(), r->rects.constData(), numPrepend * sizeof(QRect));
+
+ numRects = newNumRects;
+ }
+
+ // update inner rectangle
+ if (innerArea < r->innerArea) {
+ innerArea = r->innerArea;
+ innerRect = r->innerRect;
+ }
+
+ // update extents
+ extents.setCoords(qMin(extents.left(), r->extents.left()),
+ qMin(extents.top(), r->extents.top()),
+ qMax(extents.right(), r->extents.right()),
+ qMax(extents.bottom(), r->extents.bottom()));
+
+#ifdef QT_REGION_DEBUG
+ selfTest();
+#endif
+}
+
+void QRegionPrivate::prepend(const QRect *r)
+{
+ Q_ASSERT(!r->isEmpty());
+
+ QRect *myFirst = (numRects == 1 ? &extents : rects.data());
+ if (mergeFromLeft(myFirst, r)) {
+ if (numRects > 1) {
+ const QRect *nextToFirst = (numRects > 2 ? myFirst + 2 : 0);
+ if (mergeFromAbove(myFirst + 1, myFirst, nextToFirst, 0)) {
+ --numRects;
+ memmove(rects.data(), rects.constData() + 1,
+ numRects * sizeof(QRect));
+ }
+ }
+ } else if (mergeFromAbove(myFirst, r, (numRects > 1 ? myFirst + 1 : 0), 0)) {
+ // nothing
+ } else {
+ vectorize();
+ ++numRects;
+ updateInnerRect(*r);
+ rects.prepend(*r);
+ }
+ extents.setCoords(qMin(extents.left(), r->left()),
+ qMin(extents.top(), r->top()),
+ qMax(extents.right(), r->right()),
+ qMax(extents.bottom(), r->bottom()));
+
+#ifdef QT_REGION_DEBUG
+ selfTest();
+#endif
+}
+
+bool QRegionPrivate::canAppend(const QRect *r) const
+{
+ Q_ASSERT(!r->isEmpty());
+
+ const QRect *myLast = (numRects == 1) ? &extents : (rects.constData() + (numRects - 1));
+ if (r->top() > myLast->bottom())
+ return true;
+ if (r->top() == myLast->top()
+ && r->height() == myLast->height()
+ && r->left() > myLast->right())
+ {
+ return true;
+ }
+
+ return false;
+}
+
+bool QRegionPrivate::canAppend(const QRegionPrivate *r) const
+{
+ return canAppend(r->numRects == 1 ? &r->extents : r->rects.constData());
+}
+
+bool QRegionPrivate::canPrepend(const QRect *r) const
+{
+ Q_ASSERT(!r->isEmpty());
+
+ const QRect *myFirst = (numRects == 1) ? &extents : rects.constData();
+ if (r->bottom() < myFirst->top()) // not overlapping
+ return true;
+ if (r->top() == myFirst->top()
+ && r->height() == myFirst->height()
+ && r->right() < myFirst->left())
+ {
+ return true;
+ }
+
+ return false;
+}
+
+bool QRegionPrivate::canPrepend(const QRegionPrivate *r) const
+{
+ return canPrepend(r->numRects == 1 ? &r->extents : r->rects.constData() + r->numRects - 1);
+}
+
+#ifdef QT_REGION_DEBUG
+void QRegionPrivate::selfTest() const
+{
+ if (numRects == 0) {
+ Q_ASSERT(extents.isEmpty());
+ Q_ASSERT(innerRect.isEmpty());
+ return;
+ }
+
+ Q_ASSERT(innerArea == (innerRect.width() * innerRect.height()));
+
+ if (numRects == 1) {
+ Q_ASSERT(innerRect == extents);
+ Q_ASSERT(!innerRect.isEmpty());
+ return;
+ }
+
+ for (int i = 0; i < numRects; ++i) {
+ const QRect r = rects.at(i);
+ if ((r.width() * r.height()) > innerArea)
+ qDebug() << "selfTest(): innerRect" << innerRect << '<' << r;
+ }
+
+ QRect r = rects.first();
+ for (int i = 1; i < numRects; ++i) {
+ const QRect r2 = rects.at(i);
+ Q_ASSERT(!r2.isEmpty());
+ if (r2.y() == r.y()) {
+ Q_ASSERT(r.bottom() == r2.bottom());
+ Q_ASSERT(r.right() < (r2.left() + 1));
+ } else {
+ Q_ASSERT(r2.y() >= r.bottom());
+ }
+ r = r2;
+ }
+}
+#endif // QT_REGION_DEBUG
+
+#if defined(Q_WS_X11)
+QT_BEGIN_INCLUDE_NAMESPACE
+# include "qregion_x11.cpp"
+QT_END_INCLUDE_NAMESPACE
+#elif defined(Q_WS_MAC)
+QT_BEGIN_INCLUDE_NAMESPACE
+# include "qregion_mac.cpp"
+QT_END_INCLUDE_NAMESPACE
+#elif defined(Q_WS_WIN)
+QT_BEGIN_INCLUDE_NAMESPACE
+# include "qregion_win.cpp"
+QT_END_INCLUDE_NAMESPACE
+#elif defined(Q_WS_QWS) || defined(Q_WS_QPA)
+static QRegionPrivate qrp;
+QRegion::QRegionData QRegion::shared_empty = {Q_BASIC_ATOMIC_INITIALIZER(1), &qrp};
+#endif
+
+typedef void (*OverlapFunc)(register QRegionPrivate &dest, register const QRect *r1, const QRect *r1End,
+ register const QRect *r2, const QRect *r2End, register int y1, register int y2);
+typedef void (*NonOverlapFunc)(register QRegionPrivate &dest, register const QRect *r, const QRect *rEnd,
+ register int y1, register int y2);
+
+static bool EqualRegion(const QRegionPrivate *r1, const QRegionPrivate *r2);
+static void UnionRegion(const QRegionPrivate *reg1, const QRegionPrivate *reg2, QRegionPrivate &dest);
+static void miRegionOp(register QRegionPrivate &dest, const QRegionPrivate *reg1, const QRegionPrivate *reg2,
+ OverlapFunc overlapFunc, NonOverlapFunc nonOverlap1Func,
+ NonOverlapFunc nonOverlap2Func);
+
+#define RectangleOut 0
+#define RectangleIn 1
+#define RectanglePart 2
+#define EvenOddRule 0
+#define WindingRule 1
+
+// START OF region.h extract
+/* $XConsortium: region.h,v 11.14 94/04/17 20:22:20 rws Exp $ */
+/************************************************************************
+
+Copyright (c) 1987 X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+
+
+Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Digital not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+************************************************************************/
+
+#ifndef _XREGION_H
+#define _XREGION_H
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <limits.h>
+QT_END_INCLUDE_NAMESPACE
+
+/* 1 if two BOXes overlap.
+ * 0 if two BOXes do not overlap.
+ * Remember, x2 and y2 are not in the region
+ */
+#define EXTENTCHECK(r1, r2) \
+ ((r1)->right() >= (r2)->left() && \
+ (r1)->left() <= (r2)->right() && \
+ (r1)->bottom() >= (r2)->top() && \
+ (r1)->top() <= (r2)->bottom())
+
+/*
+ * update region extents
+ */
+#define EXTENTS(r,idRect){\
+ if((r)->left() < (idRect)->extents.left())\
+ (idRect)->extents.setLeft((r)->left());\
+ if((r)->top() < (idRect)->extents.top())\
+ (idRect)->extents.setTop((r)->top());\
+ if((r)->right() > (idRect)->extents.right())\
+ (idRect)->extents.setRight((r)->right());\
+ if((r)->bottom() > (idRect)->extents.bottom())\
+ (idRect)->extents.setBottom((r)->bottom());\
+ }
+
+/*
+ * Check to see if there is enough memory in the present region.
+ */
+#define MEMCHECK(dest, rect, firstrect){\
+ if ((dest).numRects >= ((dest).rects.size()-1)){\
+ firstrect.resize(firstrect.size() * 2); \
+ (rect) = (firstrect).data() + (dest).numRects;\
+ }\
+ }
+
+
+/*
+ * number of points to buffer before sending them off
+ * to scanlines(): Must be an even number
+ */
+#define NUMPTSTOBUFFER 200
+
+/*
+ * used to allocate buffers for points and link
+ * the buffers together
+ */
+typedef struct _POINTBLOCK {
+ int data[NUMPTSTOBUFFER * sizeof(QPoint)];
+ QPoint *pts;
+ struct _POINTBLOCK *next;
+} POINTBLOCK;
+
+#endif
+// END OF region.h extract
+
+// START OF Region.c extract
+/* $XConsortium: Region.c /main/30 1996/10/22 14:21:24 kaleb $ */
+/************************************************************************
+
+Copyright (c) 1987, 1988 X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+
+
+Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Digital not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+************************************************************************/
+/*
+ * The functions in this file implement the Region abstraction, similar to one
+ * used in the X11 sample server. A Region is simply an area, as the name
+ * implies, and is implemented as a "y-x-banded" array of rectangles. To
+ * explain: Each Region is made up of a certain number of rectangles sorted
+ * by y coordinate first, and then by x coordinate.
+ *
+ * Furthermore, the rectangles are banded such that every rectangle with a
+ * given upper-left y coordinate (y1) will have the same lower-right y
+ * coordinate (y2) and vice versa. If a rectangle has scanlines in a band, it
+ * will span the entire vertical distance of the band. This means that some
+ * areas that could be merged into a taller rectangle will be represented as
+ * several shorter rectangles to account for shorter rectangles to its left
+ * or right but within its "vertical scope".
+ *
+ * An added constraint on the rectangles is that they must cover as much
+ * horizontal area as possible. E.g. no two rectangles in a band are allowed
+ * to touch.
+ *
+ * Whenever possible, bands will be merged together to cover a greater vertical
+ * distance (and thus reduce the number of rectangles). Two bands can be merged
+ * only if the bottom of one touches the top of the other and they have
+ * rectangles in the same places (of the same width, of course). This maintains
+ * the y-x-banding that's so nice to have...
+ */
+/* $XFree86: xc/lib/X11/Region.c,v 1.1.1.2.2.2 1998/10/04 15:22:50 hohndel Exp $ */
+
+static void UnionRectWithRegion(register const QRect *rect, const QRegionPrivate *source,
+ QRegionPrivate &dest)
+{
+ if (rect->isEmpty())
+ return;
+
+ Q_ASSERT(EqualRegion(source, &dest));
+
+ if (dest.numRects == 0) {
+ dest = QRegionPrivate(*rect);
+ } else if (dest.canAppend(rect)) {
+ dest.append(rect);
+ } else {
+ QRegionPrivate p(*rect);
+ UnionRegion(&p, source, dest);
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * miSetExtents --
+ * Reset the extents and innerRect of a region to what they should be.
+ * Called by miSubtract and miIntersect b/c they can't figure it out
+ * along the way or do so easily, as miUnion can.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The region's 'extents' and 'innerRect' structure is overwritten.
+ *
+ *-----------------------------------------------------------------------
+ */
+static void miSetExtents(QRegionPrivate &dest)
+{
+ register const QRect *pBox,
+ *pBoxEnd;
+ register QRect *pExtents;
+
+ dest.innerRect.setCoords(0, 0, -1, -1);
+ dest.innerArea = -1;
+ if (dest.numRects == 0) {
+ dest.extents.setCoords(0, 0, -1, -1);
+ return;
+ }
+
+ pExtents = &dest.extents;
+ if (dest.rects.isEmpty())
+ pBox = &dest.extents;
+ else
+ pBox = dest.rects.constData();
+ pBoxEnd = pBox + dest.numRects - 1;
+
+ /*
+ * Since pBox is the first rectangle in the region, it must have the
+ * smallest y1 and since pBoxEnd is the last rectangle in the region,
+ * it must have the largest y2, because of banding. Initialize x1 and
+ * x2 from pBox and pBoxEnd, resp., as good things to initialize them
+ * to...
+ */
+ pExtents->setLeft(pBox->left());
+ pExtents->setTop(pBox->top());
+ pExtents->setRight(pBoxEnd->right());
+ pExtents->setBottom(pBoxEnd->bottom());
+
+ Q_ASSERT(pExtents->top() <= pExtents->bottom());
+ while (pBox <= pBoxEnd) {
+ if (pBox->left() < pExtents->left())
+ pExtents->setLeft(pBox->left());
+ if (pBox->right() > pExtents->right())
+ pExtents->setRight(pBox->right());
+ dest.updateInnerRect(*pBox);
+ ++pBox;
+ }
+ Q_ASSERT(pExtents->left() <= pExtents->right());
+}
+
+/* TranslateRegion(pRegion, x, y)
+ translates in place
+ added by raymond
+*/
+
+static void OffsetRegion(register QRegionPrivate &region, register int x, register int y)
+{
+ if (region.rects.size()) {
+ register QRect *pbox = region.rects.data();
+ register int nbox = region.numRects;
+
+ while (nbox--) {
+ pbox->translate(x, y);
+ ++pbox;
+ }
+ }
+ region.extents.translate(x, y);
+ region.innerRect.translate(x, y);
+}
+
+/*======================================================================
+ * Region Intersection
+ *====================================================================*/
+/*-
+ *-----------------------------------------------------------------------
+ * miIntersectO --
+ * Handle an overlapping band for miIntersect.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Rectangles may be added to the region.
+ *
+ *-----------------------------------------------------------------------
+ */
+static void miIntersectO(register QRegionPrivate &dest, register const QRect *r1, const QRect *r1End,
+ register const QRect *r2, const QRect *r2End, int y1, int y2)
+{
+ register int x1;
+ register int x2;
+ register QRect *pNextRect;
+
+ pNextRect = dest.rects.data() + dest.numRects;
+
+ while (r1 != r1End && r2 != r2End) {
+ x1 = qMax(r1->left(), r2->left());
+ x2 = qMin(r1->right(), r2->right());
+
+ /*
+ * If there's any overlap between the two rectangles, add that
+ * overlap to the new region.
+ * There's no need to check for subsumption because the only way
+ * such a need could arise is if some region has two rectangles
+ * right next to each other. Since that should never happen...
+ */
+ if (x1 <= x2) {
+ Q_ASSERT(y1 <= y2);
+ MEMCHECK(dest, pNextRect, dest.rects)
+ pNextRect->setCoords(x1, y1, x2, y2);
+ ++dest.numRects;
+ ++pNextRect;
+ }
+
+ /*
+ * Need to advance the pointers. Shift the one that extends
+ * to the right the least, since the other still has a chance to
+ * overlap with that region's next rectangle, if you see what I mean.
+ */
+ if (r1->right() < r2->right()) {
+ ++r1;
+ } else if (r2->right() < r1->right()) {
+ ++r2;
+ } else {
+ ++r1;
+ ++r2;
+ }
+ }
+}
+
+/*======================================================================
+ * Generic Region Operator
+ *====================================================================*/
+
+/*-
+ *-----------------------------------------------------------------------
+ * miCoalesce --
+ * Attempt to merge the boxes in the current band with those in the
+ * previous one. Used only by miRegionOp.
+ *
+ * Results:
+ * The new index for the previous band.
+ *
+ * Side Effects:
+ * If coalescing takes place:
+ * - rectangles in the previous band will have their y2 fields
+ * altered.
+ * - dest.numRects will be decreased.
+ *
+ *-----------------------------------------------------------------------
+ */
+static int miCoalesce(register QRegionPrivate &dest, int prevStart, int curStart)
+{
+ register QRect *pPrevBox; /* Current box in previous band */
+ register QRect *pCurBox; /* Current box in current band */
+ register QRect *pRegEnd; /* End of region */
+ int curNumRects; /* Number of rectangles in current band */
+ int prevNumRects; /* Number of rectangles in previous band */
+ int bandY1; /* Y1 coordinate for current band */
+ QRect *rData = dest.rects.data();
+
+ pRegEnd = rData + dest.numRects;
+
+ pPrevBox = rData + prevStart;
+ prevNumRects = curStart - prevStart;
+
+ /*
+ * Figure out how many rectangles are in the current band. Have to do
+ * this because multiple bands could have been added in miRegionOp
+ * at the end when one region has been exhausted.
+ */
+ pCurBox = rData + curStart;
+ bandY1 = pCurBox->top();
+ for (curNumRects = 0; pCurBox != pRegEnd && pCurBox->top() == bandY1; ++curNumRects) {
+ ++pCurBox;
+ }
+
+ if (pCurBox != pRegEnd) {
+ /*
+ * If more than one band was added, we have to find the start
+ * of the last band added so the next coalescing job can start
+ * at the right place... (given when multiple bands are added,
+ * this may be pointless -- see above).
+ */
+ --pRegEnd;
+ while ((pRegEnd - 1)->top() == pRegEnd->top())
+ --pRegEnd;
+ curStart = pRegEnd - rData;
+ pRegEnd = rData + dest.numRects;
+ }
+
+ if (curNumRects == prevNumRects && curNumRects != 0) {
+ pCurBox -= curNumRects;
+ /*
+ * The bands may only be coalesced if the bottom of the previous
+ * matches the top scanline of the current.
+ */
+ if (pPrevBox->bottom() == pCurBox->top() - 1) {
+ /*
+ * Make sure the bands have boxes in the same places. This
+ * assumes that boxes have been added in such a way that they
+ * cover the most area possible. I.e. two boxes in a band must
+ * have some horizontal space between them.
+ */
+ do {
+ if (pPrevBox->left() != pCurBox->left() || pPrevBox->right() != pCurBox->right()) {
+ // The bands don't line up so they can't be coalesced.
+ return curStart;
+ }
+ ++pPrevBox;
+ ++pCurBox;
+ --prevNumRects;
+ } while (prevNumRects != 0);
+
+ dest.numRects -= curNumRects;
+ pCurBox -= curNumRects;
+ pPrevBox -= curNumRects;
+
+ /*
+ * The bands may be merged, so set the bottom y of each box
+ * in the previous band to that of the corresponding box in
+ * the current band.
+ */
+ do {
+ pPrevBox->setBottom(pCurBox->bottom());
+ dest.updateInnerRect(*pPrevBox);
+ ++pPrevBox;
+ ++pCurBox;
+ curNumRects -= 1;
+ } while (curNumRects != 0);
+
+ /*
+ * If only one band was added to the region, we have to backup
+ * curStart to the start of the previous band.
+ *
+ * If more than one band was added to the region, copy the
+ * other bands down. The assumption here is that the other bands
+ * came from the same region as the current one and no further
+ * coalescing can be done on them since it's all been done
+ * already... curStart is already in the right place.
+ */
+ if (pCurBox == pRegEnd) {
+ curStart = prevStart;
+ } else {
+ do {
+ *pPrevBox++ = *pCurBox++;
+ dest.updateInnerRect(*pPrevBox);
+ } while (pCurBox != pRegEnd);
+ }
+ }
+ }
+ return curStart;
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * miRegionOp --
+ * Apply an operation to two regions. Called by miUnion, miInverse,
+ * miSubtract, miIntersect...
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The new region is overwritten.
+ *
+ * Notes:
+ * The idea behind this function is to view the two regions as sets.
+ * Together they cover a rectangle of area that this function divides
+ * into horizontal bands where points are covered only by one region
+ * or by both. For the first case, the nonOverlapFunc is called with
+ * each the band and the band's upper and lower extents. For the
+ * second, the overlapFunc is called to process the entire band. It
+ * is responsible for clipping the rectangles in the band, though
+ * this function provides the boundaries.
+ * At the end of each band, the new region is coalesced, if possible,
+ * to reduce the number of rectangles in the region.
+ *
+ *-----------------------------------------------------------------------
+ */
+static void miRegionOp(register QRegionPrivate &dest,
+ const QRegionPrivate *reg1, const QRegionPrivate *reg2,
+ OverlapFunc overlapFunc, NonOverlapFunc nonOverlap1Func,
+ NonOverlapFunc nonOverlap2Func)
+{
+ register const QRect *r1; // Pointer into first region
+ register const QRect *r2; // Pointer into 2d region
+ const QRect *r1End; // End of 1st region
+ const QRect *r2End; // End of 2d region
+ register int ybot; // Bottom of intersection
+ register int ytop; // Top of intersection
+ int prevBand; // Index of start of previous band in dest
+ int curBand; // Index of start of current band in dest
+ register const QRect *r1BandEnd; // End of current band in r1
+ register const QRect *r2BandEnd; // End of current band in r2
+ int top; // Top of non-overlapping band
+ int bot; // Bottom of non-overlapping band
+
+ /*
+ * Initialization:
+ * set r1, r2, r1End and r2End appropriately, preserve the important
+ * parts of the destination region until the end in case it's one of
+ * the two source regions, then mark the "new" region empty, allocating
+ * another array of rectangles for it to use.
+ */
+ if (reg1->numRects == 1)
+ r1 = &reg1->extents;
+ else
+ r1 = reg1->rects.constData();
+ if (reg2->numRects == 1)
+ r2 = &reg2->extents;
+ else
+ r2 = reg2->rects.constData();
+
+ r1End = r1 + reg1->numRects;
+ r2End = r2 + reg2->numRects;
+
+ dest.vectorize();
+
+ QVector<QRect> oldRects = dest.rects;
+
+ dest.numRects = 0;
+
+ /*
+ * Allocate a reasonable number of rectangles for the new region. The idea
+ * is to allocate enough so the individual functions don't need to
+ * reallocate and copy the array, which is time consuming, yet we don't
+ * have to worry about using too much memory. I hope to be able to
+ * nuke the realloc() at the end of this function eventually.
+ */
+ dest.rects.resize(qMax(reg1->numRects,reg2->numRects) * 2);
+
+ /*
+ * Initialize ybot and ytop.
+ * In the upcoming loop, ybot and ytop serve different functions depending
+ * on whether the band being handled is an overlapping or non-overlapping
+ * band.
+ * In the case of a non-overlapping band (only one of the regions
+ * has points in the band), ybot is the bottom of the most recent
+ * intersection and thus clips the top of the rectangles in that band.
+ * ytop is the top of the next intersection between the two regions and
+ * serves to clip the bottom of the rectangles in the current band.
+ * For an overlapping band (where the two regions intersect), ytop clips
+ * the top of the rectangles of both regions and ybot clips the bottoms.
+ */
+ if (reg1->extents.top() < reg2->extents.top())
+ ybot = reg1->extents.top() - 1;
+ else
+ ybot = reg2->extents.top() - 1;
+
+ /*
+ * prevBand serves to mark the start of the previous band so rectangles
+ * can be coalesced into larger rectangles. qv. miCoalesce, above.
+ * In the beginning, there is no previous band, so prevBand == curBand
+ * (curBand is set later on, of course, but the first band will always
+ * start at index 0). prevBand and curBand must be indices because of
+ * the possible expansion, and resultant moving, of the new region's
+ * array of rectangles.
+ */
+ prevBand = 0;
+
+ do {
+ curBand = dest.numRects;
+
+ /*
+ * This algorithm proceeds one source-band (as opposed to a
+ * destination band, which is determined by where the two regions
+ * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the
+ * rectangle after the last one in the current band for their
+ * respective regions.
+ */
+ r1BandEnd = r1;
+ while (r1BandEnd != r1End && r1BandEnd->top() == r1->top())
+ ++r1BandEnd;
+
+ r2BandEnd = r2;
+ while (r2BandEnd != r2End && r2BandEnd->top() == r2->top())
+ ++r2BandEnd;
+
+ /*
+ * First handle the band that doesn't intersect, if any.
+ *
+ * Note that attention is restricted to one band in the
+ * non-intersecting region at once, so if a region has n
+ * bands between the current position and the next place it overlaps
+ * the other, this entire loop will be passed through n times.
+ */
+ if (r1->top() < r2->top()) {
+ top = qMax(r1->top(), ybot + 1);
+ bot = qMin(r1->bottom(), r2->top() - 1);
+
+ if (nonOverlap1Func != 0 && bot >= top)
+ (*nonOverlap1Func)(dest, r1, r1BandEnd, top, bot);
+ ytop = r2->top();
+ } else if (r2->top() < r1->top()) {
+ top = qMax(r2->top(), ybot + 1);
+ bot = qMin(r2->bottom(), r1->top() - 1);
+
+ if (nonOverlap2Func != 0 && bot >= top)
+ (*nonOverlap2Func)(dest, r2, r2BandEnd, top, bot);
+ ytop = r1->top();
+ } else {
+ ytop = r1->top();
+ }
+
+ /*
+ * If any rectangles got added to the region, try and coalesce them
+ * with rectangles from the previous band. Note we could just do
+ * this test in miCoalesce, but some machines incur a not
+ * inconsiderable cost for function calls, so...
+ */
+ if (dest.numRects != curBand)
+ prevBand = miCoalesce(dest, prevBand, curBand);
+
+ /*
+ * Now see if we've hit an intersecting band. The two bands only
+ * intersect if ybot >= ytop
+ */
+ ybot = qMin(r1->bottom(), r2->bottom());
+ curBand = dest.numRects;
+ if (ybot >= ytop)
+ (*overlapFunc)(dest, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot);
+
+ if (dest.numRects != curBand)
+ prevBand = miCoalesce(dest, prevBand, curBand);
+
+ /*
+ * If we've finished with a band (y2 == ybot) we skip forward
+ * in the region to the next band.
+ */
+ if (r1->bottom() == ybot)
+ r1 = r1BandEnd;
+ if (r2->bottom() == ybot)
+ r2 = r2BandEnd;
+ } while (r1 != r1End && r2 != r2End);
+
+ /*
+ * Deal with whichever region still has rectangles left.
+ */
+ curBand = dest.numRects;
+ if (r1 != r1End) {
+ if (nonOverlap1Func != 0) {
+ do {
+ r1BandEnd = r1;
+ while (r1BandEnd < r1End && r1BandEnd->top() == r1->top())
+ ++r1BandEnd;
+ (*nonOverlap1Func)(dest, r1, r1BandEnd, qMax(r1->top(), ybot + 1), r1->bottom());
+ r1 = r1BandEnd;
+ } while (r1 != r1End);
+ }
+ } else if ((r2 != r2End) && (nonOverlap2Func != 0)) {
+ do {
+ r2BandEnd = r2;
+ while (r2BandEnd < r2End && r2BandEnd->top() == r2->top())
+ ++r2BandEnd;
+ (*nonOverlap2Func)(dest, r2, r2BandEnd, qMax(r2->top(), ybot + 1), r2->bottom());
+ r2 = r2BandEnd;
+ } while (r2 != r2End);
+ }
+
+ if (dest.numRects != curBand)
+ (void)miCoalesce(dest, prevBand, curBand);
+
+ /*
+ * A bit of cleanup. To keep regions from growing without bound,
+ * we shrink the array of rectangles to match the new number of
+ * rectangles in the region.
+ *
+ * Only do this stuff if the number of rectangles allocated is more than
+ * twice the number of rectangles in the region (a simple optimization).
+ */
+ if (qMax(4, dest.numRects) < (dest.rects.size() >> 1))
+ dest.rects.resize(dest.numRects);
+}
+
+/*======================================================================
+ * Region Union
+ *====================================================================*/
+
+/*-
+ *-----------------------------------------------------------------------
+ * miUnionNonO --
+ * Handle a non-overlapping band for the union operation. Just
+ * Adds the rectangles into the region. Doesn't have to check for
+ * subsumption or anything.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * dest.numRects is incremented and the final rectangles overwritten
+ * with the rectangles we're passed.
+ *
+ *-----------------------------------------------------------------------
+ */
+
+static void miUnionNonO(register QRegionPrivate &dest, register const QRect *r, const QRect *rEnd,
+ register int y1, register int y2)
+{
+ register QRect *pNextRect;
+
+ pNextRect = dest.rects.data() + dest.numRects;
+
+ Q_ASSERT(y1 <= y2);
+
+ while (r != rEnd) {
+ Q_ASSERT(r->left() <= r->right());
+ MEMCHECK(dest, pNextRect, dest.rects)
+ pNextRect->setCoords(r->left(), y1, r->right(), y2);
+ dest.numRects++;
+ ++pNextRect;
+ ++r;
+ }
+}
+
+
+/*-
+ *-----------------------------------------------------------------------
+ * miUnionO --
+ * Handle an overlapping band for the union operation. Picks the
+ * left-most rectangle each time and merges it into the region.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Rectangles are overwritten in dest.rects and dest.numRects will
+ * be changed.
+ *
+ *-----------------------------------------------------------------------
+ */
+
+static void miUnionO(register QRegionPrivate &dest, register const QRect *r1, const QRect *r1End,
+ register const QRect *r2, const QRect *r2End, register int y1, register int y2)
+{
+ register QRect *pNextRect;
+
+ pNextRect = dest.rects.data() + dest.numRects;
+
+#define MERGERECT(r) \
+ if ((dest.numRects != 0) && \
+ (pNextRect[-1].top() == y1) && \
+ (pNextRect[-1].bottom() == y2) && \
+ (pNextRect[-1].right() >= r->left()-1)) { \
+ if (pNextRect[-1].right() < r->right()) { \
+ pNextRect[-1].setRight(r->right()); \
+ dest.updateInnerRect(pNextRect[-1]); \
+ Q_ASSERT(pNextRect[-1].left() <= pNextRect[-1].right()); \
+ } \
+ } else { \
+ MEMCHECK(dest, pNextRect, dest.rects) \
+ pNextRect->setCoords(r->left(), y1, r->right(), y2); \
+ dest.updateInnerRect(*pNextRect); \
+ dest.numRects++; \
+ pNextRect++; \
+ } \
+ r++;
+
+ Q_ASSERT(y1 <= y2);
+ while (r1 != r1End && r2 != r2End) {
+ if (r1->left() < r2->left()) {
+ MERGERECT(r1)
+ } else {
+ MERGERECT(r2)
+ }
+ }
+
+ if (r1 != r1End) {
+ do {
+ MERGERECT(r1)
+ } while (r1 != r1End);
+ } else {
+ while (r2 != r2End) {
+ MERGERECT(r2)
+ }
+ }
+}
+
+static void UnionRegion(const QRegionPrivate *reg1, const QRegionPrivate *reg2, QRegionPrivate &dest)
+{
+ Q_ASSERT(!isEmptyHelper(reg1) && !isEmptyHelper(reg2));
+ Q_ASSERT(!reg1->contains(*reg2));
+ Q_ASSERT(!reg2->contains(*reg1));
+ Q_ASSERT(!EqualRegion(reg1, reg2));
+ Q_ASSERT(!reg1->canAppend(reg2));
+ Q_ASSERT(!reg2->canAppend(reg1));
+
+ if (reg1->innerArea > reg2->innerArea) {
+ dest.innerArea = reg1->innerArea;
+ dest.innerRect = reg1->innerRect;
+ } else {
+ dest.innerArea = reg2->innerArea;
+ dest.innerRect = reg2->innerRect;
+ }
+ miRegionOp(dest, reg1, reg2, miUnionO, miUnionNonO, miUnionNonO);
+
+ dest.extents.setCoords(qMin(reg1->extents.left(), reg2->extents.left()),
+ qMin(reg1->extents.top(), reg2->extents.top()),
+ qMax(reg1->extents.right(), reg2->extents.right()),
+ qMax(reg1->extents.bottom(), reg2->extents.bottom()));
+}
+
+/*======================================================================
+ * Region Subtraction
+ *====================================================================*/
+
+/*-
+ *-----------------------------------------------------------------------
+ * miSubtractNonO --
+ * Deal with non-overlapping band for subtraction. Any parts from
+ * region 2 we discard. Anything from region 1 we add to the region.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * dest may be affected.
+ *
+ *-----------------------------------------------------------------------
+ */
+
+static void miSubtractNonO1(register QRegionPrivate &dest, register const QRect *r,
+ const QRect *rEnd, register int y1, register int y2)
+{
+ register QRect *pNextRect;
+
+ pNextRect = dest.rects.data() + dest.numRects;
+
+ Q_ASSERT(y1<=y2);
+
+ while (r != rEnd) {
+ Q_ASSERT(r->left() <= r->right());
+ MEMCHECK(dest, pNextRect, dest.rects)
+ pNextRect->setCoords(r->left(), y1, r->right(), y2);
+ ++dest.numRects;
+ ++pNextRect;
+ ++r;
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * miSubtractO --
+ * Overlapping band subtraction. x1 is the left-most point not yet
+ * checked.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * dest may have rectangles added to it.
+ *
+ *-----------------------------------------------------------------------
+ */
+
+static void miSubtractO(register QRegionPrivate &dest, register const QRect *r1, const QRect *r1End,
+ register const QRect *r2, const QRect *r2End, register int y1, register int y2)
+{
+ register QRect *pNextRect;
+ register int x1;
+
+ x1 = r1->left();
+
+ Q_ASSERT(y1 <= y2);
+ pNextRect = dest.rects.data() + dest.numRects;
+
+ while (r1 != r1End && r2 != r2End) {
+ if (r2->right() < x1) {
+ /*
+ * Subtrahend missed the boat: go to next subtrahend.
+ */
+ ++r2;
+ } else if (r2->left() <= x1) {
+ /*
+ * Subtrahend precedes minuend: nuke left edge of minuend.
+ */
+ x1 = r2->right() + 1;
+ if (x1 > r1->right()) {
+ /*
+ * Minuend completely covered: advance to next minuend and
+ * reset left fence to edge of new minuend.
+ */
+ ++r1;
+ if (r1 != r1End)
+ x1 = r1->left();
+ } else {
+ // Subtrahend now used up since it doesn't extend beyond minuend
+ ++r2;
+ }
+ } else if (r2->left() <= r1->right()) {
+ /*
+ * Left part of subtrahend covers part of minuend: add uncovered
+ * part of minuend to region and skip to next subtrahend.
+ */
+ Q_ASSERT(x1 < r2->left());
+ MEMCHECK(dest, pNextRect, dest.rects)
+ pNextRect->setCoords(x1, y1, r2->left() - 1, y2);
+ ++dest.numRects;
+ ++pNextRect;
+
+ x1 = r2->right() + 1;
+ if (x1 > r1->right()) {
+ /*
+ * Minuend used up: advance to new...
+ */
+ ++r1;
+ if (r1 != r1End)
+ x1 = r1->left();
+ } else {
+ // Subtrahend used up
+ ++r2;
+ }
+ } else {
+ /*
+ * Minuend used up: add any remaining piece before advancing.
+ */
+ if (r1->right() >= x1) {
+ MEMCHECK(dest, pNextRect, dest.rects)
+ pNextRect->setCoords(x1, y1, r1->right(), y2);
+ ++dest.numRects;
+ ++pNextRect;
+ }
+ ++r1;
+ if (r1 != r1End)
+ x1 = r1->left();
+ }
+ }
+
+ /*
+ * Add remaining minuend rectangles to region.
+ */
+ while (r1 != r1End) {
+ Q_ASSERT(x1 <= r1->right());
+ MEMCHECK(dest, pNextRect, dest.rects)
+ pNextRect->setCoords(x1, y1, r1->right(), y2);
+ ++dest.numRects;
+ ++pNextRect;
+
+ ++r1;
+ if (r1 != r1End)
+ x1 = r1->left();
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * miSubtract --
+ * Subtract regS from regM and leave the result in regD.
+ * S stands for subtrahend, M for minuend and D for difference.
+ *
+ * Side Effects:
+ * regD is overwritten.
+ *
+ *-----------------------------------------------------------------------
+ */
+
+static void SubtractRegion(QRegionPrivate *regM, QRegionPrivate *regS,
+ register QRegionPrivate &dest)
+{
+ Q_ASSERT(!isEmptyHelper(regM));
+ Q_ASSERT(!isEmptyHelper(regS));
+ Q_ASSERT(EXTENTCHECK(&regM->extents, &regS->extents));
+ Q_ASSERT(!regS->contains(*regM));
+ Q_ASSERT(!EqualRegion(regM, regS));
+
+ miRegionOp(dest, regM, regS, miSubtractO, miSubtractNonO1, 0);
+
+ /*
+ * Can't alter dest's extents before we call miRegionOp because
+ * it might be one of the source regions and miRegionOp depends
+ * on the extents of those regions being the unaltered. Besides, this
+ * way there's no checking against rectangles that will be nuked
+ * due to coalescing, so we have to examine fewer rectangles.
+ */
+ miSetExtents(dest);
+}
+
+static void XorRegion(QRegionPrivate *sra, QRegionPrivate *srb, QRegionPrivate &dest)
+{
+ Q_ASSERT(!isEmptyHelper(sra) && !isEmptyHelper(srb));
+ Q_ASSERT(EXTENTCHECK(&sra->extents, &srb->extents));
+ Q_ASSERT(!EqualRegion(sra, srb));
+
+ QRegionPrivate tra, trb;
+
+ if (!srb->contains(*sra))
+ SubtractRegion(sra, srb, tra);
+ if (!sra->contains(*srb))
+ SubtractRegion(srb, sra, trb);
+
+ Q_ASSERT(isEmptyHelper(&trb) || !tra.contains(trb));
+ Q_ASSERT(isEmptyHelper(&tra) || !trb.contains(tra));
+
+ if (isEmptyHelper(&tra)) {
+ dest = trb;
+ } else if (isEmptyHelper(&trb)) {
+ dest = tra;
+ } else if (tra.canAppend(&trb)) {
+ dest = tra;
+ dest.append(&trb);
+ } else if (trb.canAppend(&tra)) {
+ dest = trb;
+ dest.append(&tra);
+ } else {
+ UnionRegion(&tra, &trb, dest);
+ }
+}
+
+/*
+ * Check to see if two regions are equal
+ */
+static bool EqualRegion(const QRegionPrivate *r1, const QRegionPrivate *r2)
+{
+ if (r1->numRects != r2->numRects) {
+ return false;
+ } else if (r1->numRects == 0) {
+ return true;
+ } else if (r1->extents != r2->extents) {
+ return false;
+ } else if (r1->numRects == 1 && r2->numRects == 1) {
+ return true; // equality tested in previous if-statement
+ } else {
+ const QRect *rr1 = (r1->numRects == 1) ? &r1->extents : r1->rects.constData();
+ const QRect *rr2 = (r2->numRects == 1) ? &r2->extents : r2->rects.constData();
+ for (int i = 0; i < r1->numRects; ++i, ++rr1, ++rr2) {
+ if (*rr1 != *rr2)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool PointInRegion(QRegionPrivate *pRegion, int x, int y)
+{
+ int i;
+
+ if (isEmptyHelper(pRegion))
+ return false;
+ if (!pRegion->extents.contains(x, y))
+ return false;
+ if (pRegion->numRects == 1)
+ return pRegion->extents.contains(x, y);
+ if (pRegion->innerRect.contains(x, y))
+ return true;
+ for (i = 0; i < pRegion->numRects; ++i) {
+ if (pRegion->rects[i].contains(x, y))
+ return true;
+ }
+ return false;
+}
+
+static bool RectInRegion(register QRegionPrivate *region, int rx, int ry, uint rwidth, uint rheight)
+{
+ register const QRect *pbox;
+ register const QRect *pboxEnd;
+ QRect rect(rx, ry, rwidth, rheight);
+ register QRect *prect = &rect;
+ int partIn, partOut;
+
+ if (!region || region->numRects == 0 || !EXTENTCHECK(&region->extents, prect))
+ return RectangleOut;
+
+ partOut = false;
+ partIn = false;
+
+ /* can stop when both partOut and partIn are true, or we reach prect->y2 */
+ pbox = (region->numRects == 1) ? &region->extents : region->rects.constData();
+ pboxEnd = pbox + region->numRects;
+ for (; pbox < pboxEnd; ++pbox) {
+ if (pbox->bottom() < ry)
+ continue;
+
+ if (pbox->top() > ry) {
+ partOut = true;
+ if (partIn || pbox->top() > prect->bottom())
+ break;
+ ry = pbox->top();
+ }
+
+ if (pbox->right() < rx)
+ continue; /* not far enough over yet */
+
+ if (pbox->left() > rx) {
+ partOut = true; /* missed part of rectangle to left */
+ if (partIn)
+ break;
+ }
+
+ if (pbox->left() <= prect->right()) {
+ partIn = true; /* definitely overlap */
+ if (partOut)
+ break;
+ }
+
+ if (pbox->right() >= prect->right()) {
+ ry = pbox->bottom() + 1; /* finished with this band */
+ if (ry > prect->bottom())
+ break;
+ rx = prect->left(); /* reset x out to left again */
+ } else {
+ /*
+ * Because boxes in a band are maximal width, if the first box
+ * to overlap the rectangle doesn't completely cover it in that
+ * band, the rectangle must be partially out, since some of it
+ * will be uncovered in that band. partIn will have been set true
+ * by now...
+ */
+ break;
+ }
+ }
+ return partIn ? ((ry <= prect->bottom()) ? RectanglePart : RectangleIn) : RectangleOut;
+}
+// END OF Region.c extract
+// START OF poly.h extract
+/* $XConsortium: poly.h,v 1.4 94/04/17 20:22:19 rws Exp $ */
+/************************************************************************
+
+Copyright (c) 1987 X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+
+
+Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Digital not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+************************************************************************/
+
+/*
+ * This file contains a few macros to help track
+ * the edge of a filled object. The object is assumed
+ * to be filled in scanline order, and thus the
+ * algorithm used is an extension of Bresenham's line
+ * drawing algorithm which assumes that y is always the
+ * major axis.
+ * Since these pieces of code are the same for any filled shape,
+ * it is more convenient to gather the library in one
+ * place, but since these pieces of code are also in
+ * the inner loops of output primitives, procedure call
+ * overhead is out of the question.
+ * See the author for a derivation if needed.
+ */
+
+
+/*
+ * In scan converting polygons, we want to choose those pixels
+ * which are inside the polygon. Thus, we add .5 to the starting
+ * x coordinate for both left and right edges. Now we choose the
+ * first pixel which is inside the pgon for the left edge and the
+ * first pixel which is outside the pgon for the right edge.
+ * Draw the left pixel, but not the right.
+ *
+ * How to add .5 to the starting x coordinate:
+ * If the edge is moving to the right, then subtract dy from the
+ * error term from the general form of the algorithm.
+ * If the edge is moving to the left, then add dy to the error term.
+ *
+ * The reason for the difference between edges moving to the left
+ * and edges moving to the right is simple: If an edge is moving
+ * to the right, then we want the algorithm to flip immediately.
+ * If it is moving to the left, then we don't want it to flip until
+ * we traverse an entire pixel.
+ */
+#define BRESINITPGON(dy, x1, x2, xStart, d, m, m1, incr1, incr2) { \
+ int dx; /* local storage */ \
+\
+ /* \
+ * if the edge is horizontal, then it is ignored \
+ * and assumed not to be processed. Otherwise, do this stuff. \
+ */ \
+ if ((dy) != 0) { \
+ xStart = (x1); \
+ dx = (x2) - xStart; \
+ if (dx < 0) { \
+ m = dx / (dy); \
+ m1 = m - 1; \
+ incr1 = -2 * dx + 2 * (dy) * m1; \
+ incr2 = -2 * dx + 2 * (dy) * m; \
+ d = 2 * m * (dy) - 2 * dx - 2 * (dy); \
+ } else { \
+ m = dx / (dy); \
+ m1 = m + 1; \
+ incr1 = 2 * dx - 2 * (dy) * m1; \
+ incr2 = 2 * dx - 2 * (dy) * m; \
+ d = -2 * m * (dy) + 2 * dx; \
+ } \
+ } \
+}
+
+#define BRESINCRPGON(d, minval, m, m1, incr1, incr2) { \
+ if (m1 > 0) { \
+ if (d > 0) { \
+ minval += m1; \
+ d += incr1; \
+ } \
+ else { \
+ minval += m; \
+ d += incr2; \
+ } \
+ } else {\
+ if (d >= 0) { \
+ minval += m1; \
+ d += incr1; \
+ } \
+ else { \
+ minval += m; \
+ d += incr2; \
+ } \
+ } \
+}
+
+
+/*
+ * This structure contains all of the information needed
+ * to run the bresenham algorithm.
+ * The variables may be hardcoded into the declarations
+ * instead of using this structure to make use of
+ * register declarations.
+ */
+typedef struct {
+ int minor_axis; /* minor axis */
+ int d; /* decision variable */
+ int m, m1; /* slope and slope+1 */
+ int incr1, incr2; /* error increments */
+} BRESINFO;
+
+
+#define BRESINITPGONSTRUCT(dmaj, min1, min2, bres) \
+ BRESINITPGON(dmaj, min1, min2, bres.minor_axis, bres.d, \
+ bres.m, bres.m1, bres.incr1, bres.incr2)
+
+#define BRESINCRPGONSTRUCT(bres) \
+ BRESINCRPGON(bres.d, bres.minor_axis, bres.m, bres.m1, bres.incr1, bres.incr2)
+
+
+
+/*
+ * These are the data structures needed to scan
+ * convert regions. Two different scan conversion
+ * methods are available -- the even-odd method, and
+ * the winding number method.
+ * The even-odd rule states that a point is inside
+ * the polygon if a ray drawn from that point in any
+ * direction will pass through an odd number of
+ * path segments.
+ * By the winding number rule, a point is decided
+ * to be inside the polygon if a ray drawn from that
+ * point in any direction passes through a different
+ * number of clockwise and counter-clockwise path
+ * segments.
+ *
+ * These data structures are adapted somewhat from
+ * the algorithm in (Foley/Van Dam) for scan converting
+ * polygons.
+ * The basic algorithm is to start at the top (smallest y)
+ * of the polygon, stepping down to the bottom of
+ * the polygon by incrementing the y coordinate. We
+ * keep a list of edges which the current scanline crosses,
+ * sorted by x. This list is called the Active Edge Table (AET)
+ * As we change the y-coordinate, we update each entry in
+ * in the active edge table to reflect the edges new xcoord.
+ * This list must be sorted at each scanline in case
+ * two edges intersect.
+ * We also keep a data structure known as the Edge Table (ET),
+ * which keeps track of all the edges which the current
+ * scanline has not yet reached. The ET is basically a
+ * list of ScanLineList structures containing a list of
+ * edges which are entered at a given scanline. There is one
+ * ScanLineList per scanline at which an edge is entered.
+ * When we enter a new edge, we move it from the ET to the AET.
+ *
+ * From the AET, we can implement the even-odd rule as in
+ * (Foley/Van Dam).
+ * The winding number rule is a little trickier. We also
+ * keep the EdgeTableEntries in the AET linked by the
+ * nextWETE (winding EdgeTableEntry) link. This allows
+ * the edges to be linked just as before for updating
+ * purposes, but only uses the edges linked by the nextWETE
+ * link as edges representing spans of the polygon to
+ * drawn (as with the even-odd rule).
+ */
+
+/*
+ * for the winding number rule
+ */
+#define CLOCKWISE 1
+#define COUNTERCLOCKWISE -1
+
+typedef struct _EdgeTableEntry {
+ int ymax; /* ycoord at which we exit this edge. */
+ BRESINFO bres; /* Bresenham info to run the edge */
+ struct _EdgeTableEntry *next; /* next in the list */
+ struct _EdgeTableEntry *back; /* for insertion sort */
+ struct _EdgeTableEntry *nextWETE; /* for winding num rule */
+ int ClockWise; /* flag for winding number rule */
+} EdgeTableEntry;
+
+
+typedef struct _ScanLineList{
+ int scanline; /* the scanline represented */
+ EdgeTableEntry *edgelist; /* header node */
+ struct _ScanLineList *next; /* next in the list */
+} ScanLineList;
+
+
+typedef struct {
+ int ymax; /* ymax for the polygon */
+ int ymin; /* ymin for the polygon */
+ ScanLineList scanlines; /* header node */
+} EdgeTable;
+
+
+/*
+ * Here is a struct to help with storage allocation
+ * so we can allocate a big chunk at a time, and then take
+ * pieces from this heap when we need to.
+ */
+#define SLLSPERBLOCK 25
+
+typedef struct _ScanLineListBlock {
+ ScanLineList SLLs[SLLSPERBLOCK];
+ struct _ScanLineListBlock *next;
+} ScanLineListBlock;
+
+
+
+/*
+ *
+ * a few macros for the inner loops of the fill code where
+ * performance considerations don't allow a procedure call.
+ *
+ * Evaluate the given edge at the given scanline.
+ * If the edge has expired, then we leave it and fix up
+ * the active edge table; otherwise, we increment the
+ * x value to be ready for the next scanline.
+ * The winding number rule is in effect, so we must notify
+ * the caller when the edge has been removed so he
+ * can reorder the Winding Active Edge Table.
+ */
+#define EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET) { \
+ if (pAET->ymax == y) { /* leaving this edge */ \
+ pPrevAET->next = pAET->next; \
+ pAET = pPrevAET->next; \
+ fixWAET = 1; \
+ if (pAET) \
+ pAET->back = pPrevAET; \
+ } \
+ else { \
+ BRESINCRPGONSTRUCT(pAET->bres) \
+ pPrevAET = pAET; \
+ pAET = pAET->next; \
+ } \
+}
+
+
+/*
+ * Evaluate the given edge at the given scanline.
+ * If the edge has expired, then we leave it and fix up
+ * the active edge table; otherwise, we increment the
+ * x value to be ready for the next scanline.
+ * The even-odd rule is in effect.
+ */
+#define EVALUATEEDGEEVENODD(pAET, pPrevAET, y) { \
+ if (pAET->ymax == y) { /* leaving this edge */ \
+ pPrevAET->next = pAET->next; \
+ pAET = pPrevAET->next; \
+ if (pAET) \
+ pAET->back = pPrevAET; \
+ } \
+ else { \
+ BRESINCRPGONSTRUCT(pAET->bres) \
+ pPrevAET = pAET; \
+ pAET = pAET->next; \
+ } \
+}
+// END OF poly.h extract
+// START OF PolyReg.c extract
+/* $XConsortium: PolyReg.c,v 11.23 94/11/17 21:59:37 converse Exp $ */
+/************************************************************************
+
+Copyright (c) 1987 X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+
+
+Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Digital not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+************************************************************************/
+/* $XFree86: xc/lib/X11/PolyReg.c,v 1.1.1.2.8.2 1998/10/04 15:22:49 hohndel Exp $ */
+
+#define LARGE_COORDINATE INT_MAX
+#define SMALL_COORDINATE INT_MIN
+
+/*
+ * InsertEdgeInET
+ *
+ * Insert the given edge into the edge table.
+ * First we must find the correct bucket in the
+ * Edge table, then find the right slot in the
+ * bucket. Finally, we can insert it.
+ *
+ */
+static void InsertEdgeInET(EdgeTable *ET, EdgeTableEntry *ETE, int scanline,
+ ScanLineListBlock **SLLBlock, int *iSLLBlock)
+{
+ register EdgeTableEntry *start, *prev;
+ register ScanLineList *pSLL, *pPrevSLL;
+ ScanLineListBlock *tmpSLLBlock;
+
+ /*
+ * find the right bucket to put the edge into
+ */
+ pPrevSLL = &ET->scanlines;
+ pSLL = pPrevSLL->next;
+ while (pSLL && (pSLL->scanline < scanline)) {
+ pPrevSLL = pSLL;
+ pSLL = pSLL->next;
+ }
+
+ /*
+ * reassign pSLL (pointer to ScanLineList) if necessary
+ */
+ if ((!pSLL) || (pSLL->scanline > scanline)) {
+ if (*iSLLBlock > SLLSPERBLOCK-1)
+ {
+ tmpSLLBlock =
+ (ScanLineListBlock *)malloc(sizeof(ScanLineListBlock));
+ Q_CHECK_PTR(tmpSLLBlock);
+ (*SLLBlock)->next = tmpSLLBlock;
+ tmpSLLBlock->next = (ScanLineListBlock *)NULL;
+ *SLLBlock = tmpSLLBlock;
+ *iSLLBlock = 0;
+ }
+ pSLL = &((*SLLBlock)->SLLs[(*iSLLBlock)++]);
+
+ pSLL->next = pPrevSLL->next;
+ pSLL->edgelist = (EdgeTableEntry *)NULL;
+ pPrevSLL->next = pSLL;
+ }
+ pSLL->scanline = scanline;
+
+ /*
+ * now insert the edge in the right bucket
+ */
+ prev = 0;
+ start = pSLL->edgelist;
+ while (start && (start->bres.minor_axis < ETE->bres.minor_axis)) {
+ prev = start;
+ start = start->next;
+ }
+ ETE->next = start;
+
+ if (prev)
+ prev->next = ETE;
+ else
+ pSLL->edgelist = ETE;
+}
+
+/*
+ * CreateEdgeTable
+ *
+ * This routine creates the edge table for
+ * scan converting polygons.
+ * The Edge Table (ET) looks like:
+ *
+ * EdgeTable
+ * --------
+ * | ymax | ScanLineLists
+ * |scanline|-->------------>-------------->...
+ * -------- |scanline| |scanline|
+ * |edgelist| |edgelist|
+ * --------- ---------
+ * | |
+ * | |
+ * V V
+ * list of ETEs list of ETEs
+ *
+ * where ETE is an EdgeTableEntry data structure,
+ * and there is one ScanLineList per scanline at
+ * which an edge is initially entered.
+ *
+ */
+
+static void CreateETandAET(register int count, register const QPoint *pts,
+ EdgeTable *ET, EdgeTableEntry *AET, register EdgeTableEntry *pETEs,
+ ScanLineListBlock *pSLLBlock)
+{
+ register const QPoint *top,
+ *bottom,
+ *PrevPt,
+ *CurrPt;
+ int iSLLBlock = 0;
+ int dy;
+
+ if (count < 2)
+ return;
+
+ /*
+ * initialize the Active Edge Table
+ */
+ AET->next = 0;
+ AET->back = 0;
+ AET->nextWETE = 0;
+ AET->bres.minor_axis = SMALL_COORDINATE;
+
+ /*
+ * initialize the Edge Table.
+ */
+ ET->scanlines.next = 0;
+ ET->ymax = SMALL_COORDINATE;
+ ET->ymin = LARGE_COORDINATE;
+ pSLLBlock->next = 0;
+
+ PrevPt = &pts[count - 1];
+
+ /*
+ * for each vertex in the array of points.
+ * In this loop we are dealing with two vertices at
+ * a time -- these make up one edge of the polygon.
+ */
+ while (count--) {
+ CurrPt = pts++;
+
+ /*
+ * find out which point is above and which is below.
+ */
+ if (PrevPt->y() > CurrPt->y()) {
+ bottom = PrevPt;
+ top = CurrPt;
+ pETEs->ClockWise = 0;
+ } else {
+ bottom = CurrPt;
+ top = PrevPt;
+ pETEs->ClockWise = 1;
+ }
+
+ /*
+ * don't add horizontal edges to the Edge table.
+ */
+ if (bottom->y() != top->y()) {
+ pETEs->ymax = bottom->y() - 1; /* -1 so we don't get last scanline */
+
+ /*
+ * initialize integer edge algorithm
+ */
+ dy = bottom->y() - top->y();
+ BRESINITPGONSTRUCT(dy, top->x(), bottom->x(), pETEs->bres)
+
+ InsertEdgeInET(ET, pETEs, top->y(), &pSLLBlock, &iSLLBlock);
+
+ if (PrevPt->y() > ET->ymax)
+ ET->ymax = PrevPt->y();
+ if (PrevPt->y() < ET->ymin)
+ ET->ymin = PrevPt->y();
+ ++pETEs;
+ }
+
+ PrevPt = CurrPt;
+ }
+}
+
+/*
+ * loadAET
+ *
+ * This routine moves EdgeTableEntries from the
+ * EdgeTable into the Active Edge Table,
+ * leaving them sorted by smaller x coordinate.
+ *
+ */
+
+static void loadAET(register EdgeTableEntry *AET, register EdgeTableEntry *ETEs)
+{
+ register EdgeTableEntry *pPrevAET;
+ register EdgeTableEntry *tmp;
+
+ pPrevAET = AET;
+ AET = AET->next;
+ while (ETEs) {
+ while (AET && AET->bres.minor_axis < ETEs->bres.minor_axis) {
+ pPrevAET = AET;
+ AET = AET->next;
+ }
+ tmp = ETEs->next;
+ ETEs->next = AET;
+ if (AET)
+ AET->back = ETEs;
+ ETEs->back = pPrevAET;
+ pPrevAET->next = ETEs;
+ pPrevAET = ETEs;
+
+ ETEs = tmp;
+ }
+}
+
+/*
+ * computeWAET
+ *
+ * This routine links the AET by the
+ * nextWETE (winding EdgeTableEntry) link for
+ * use by the winding number rule. The final
+ * Active Edge Table (AET) might look something
+ * like:
+ *
+ * AET
+ * ---------- --------- ---------
+ * |ymax | |ymax | |ymax |
+ * | ... | |... | |... |
+ * |next |->|next |->|next |->...
+ * |nextWETE| |nextWETE| |nextWETE|
+ * --------- --------- ^--------
+ * | | |
+ * V-------------------> V---> ...
+ *
+ */
+static void computeWAET(register EdgeTableEntry *AET)
+{
+ register EdgeTableEntry *pWETE;
+ register int inside = 1;
+ register int isInside = 0;
+
+ AET->nextWETE = 0;
+ pWETE = AET;
+ AET = AET->next;
+ while (AET) {
+ if (AET->ClockWise)
+ ++isInside;
+ else
+ --isInside;
+
+ if ((!inside && !isInside) || (inside && isInside)) {
+ pWETE->nextWETE = AET;
+ pWETE = AET;
+ inside = !inside;
+ }
+ AET = AET->next;
+ }
+ pWETE->nextWETE = 0;
+}
+
+/*
+ * InsertionSort
+ *
+ * Just a simple insertion sort using
+ * pointers and back pointers to sort the Active
+ * Edge Table.
+ *
+ */
+
+static int InsertionSort(register EdgeTableEntry *AET)
+{
+ register EdgeTableEntry *pETEchase;
+ register EdgeTableEntry *pETEinsert;
+ register EdgeTableEntry *pETEchaseBackTMP;
+ register int changed = 0;
+
+ AET = AET->next;
+ while (AET) {
+ pETEinsert = AET;
+ pETEchase = AET;
+ while (pETEchase->back->bres.minor_axis > AET->bres.minor_axis)
+ pETEchase = pETEchase->back;
+
+ AET = AET->next;
+ if (pETEchase != pETEinsert) {
+ pETEchaseBackTMP = pETEchase->back;
+ pETEinsert->back->next = AET;
+ if (AET)
+ AET->back = pETEinsert->back;
+ pETEinsert->next = pETEchase;
+ pETEchase->back->next = pETEinsert;
+ pETEchase->back = pETEinsert;
+ pETEinsert->back = pETEchaseBackTMP;
+ changed = 1;
+ }
+ }
+ return changed;
+}
+
+/*
+ * Clean up our act.
+ */
+static void FreeStorage(register ScanLineListBlock *pSLLBlock)
+{
+ register ScanLineListBlock *tmpSLLBlock;
+
+ while (pSLLBlock) {
+ tmpSLLBlock = pSLLBlock->next;
+ free(pSLLBlock);
+ pSLLBlock = tmpSLLBlock;
+ }
+}
+
+struct QRegionSpan {
+ QRegionSpan() {}
+ QRegionSpan(int x1_, int x2_) : x1(x1_), x2(x2_) {}
+
+ int x1;
+ int x2;
+ int width() const { return x2 - x1; }
+};
+
+Q_DECLARE_TYPEINFO(QRegionSpan, Q_PRIMITIVE_TYPE);
+
+static inline void flushRow(const QRegionSpan *spans, int y, int numSpans, QRegionPrivate *reg, int *lastRow, int *extendTo, bool *needsExtend)
+{
+ QRect *regRects = reg->rects.data() + *lastRow;
+ bool canExtend = reg->rects.size() - *lastRow == numSpans
+ && !(*needsExtend && *extendTo + 1 != y)
+ && (*needsExtend || regRects[0].y() + regRects[0].height() == y);
+
+ for (int i = 0; i < numSpans && canExtend; ++i) {
+ if (regRects[i].x() != spans[i].x1 || regRects[i].right() != spans[i].x2 - 1)
+ canExtend = false;
+ }
+
+ if (canExtend) {
+ *extendTo = y;
+ *needsExtend = true;
+ } else {
+ if (*needsExtend) {
+ for (int i = 0; i < reg->rects.size() - *lastRow; ++i)
+ regRects[i].setBottom(*extendTo);
+ }
+
+ *lastRow = reg->rects.size();
+ reg->rects.reserve(*lastRow + numSpans);
+ for (int i = 0; i < numSpans; ++i)
+ reg->rects << QRect(spans[i].x1, y, spans[i].width(), 1);
+
+ if (spans[0].x1 < reg->extents.left())
+ reg->extents.setLeft(spans[0].x1);
+
+ if (spans[numSpans-1].x2 - 1 > reg->extents.right())
+ reg->extents.setRight(spans[numSpans-1].x2 - 1);
+
+ *needsExtend = false;
+ }
+}
+
+/*
+ * Create an array of rectangles from a list of points.
+ * If indeed these things (POINTS, RECTS) are the same,
+ * then this proc is still needed, because it allocates
+ * storage for the array, which was allocated on the
+ * stack by the calling procedure.
+ *
+ */
+static void PtsToRegion(register int numFullPtBlocks, register int iCurPtBlock,
+ POINTBLOCK *FirstPtBlock, QRegionPrivate *reg)
+{
+ int lastRow = 0;
+ int extendTo = 0;
+ bool needsExtend = false;
+ QVarLengthArray<QRegionSpan> row;
+ int rowSize = 0;
+
+ reg->extents.setLeft(INT_MAX);
+ reg->extents.setRight(INT_MIN);
+ reg->innerArea = -1;
+
+ POINTBLOCK *CurPtBlock = FirstPtBlock;
+ for (; numFullPtBlocks >= 0; --numFullPtBlocks) {
+ /* the loop uses 2 points per iteration */
+ int i = NUMPTSTOBUFFER >> 1;
+ if (!numFullPtBlocks)
+ i = iCurPtBlock >> 1;
+ if(i) {
+ row.resize(qMax(row.size(), rowSize + i));
+ for (QPoint *pts = CurPtBlock->pts; i--; pts += 2) {
+ const int width = pts[1].x() - pts[0].x();
+ if (width) {
+ if (rowSize && row[rowSize-1].x2 == pts[0].x())
+ row[rowSize-1].x2 = pts[1].x();
+ else
+ row[rowSize++] = QRegionSpan(pts[0].x(), pts[1].x());
+ }
+
+ if (rowSize) {
+ QPoint *next = i ? &pts[2] : (numFullPtBlocks ? CurPtBlock->next->pts : 0);
+
+ if (!next || next->y() != pts[0].y()) {
+ flushRow(row.data(), pts[0].y(), rowSize, reg, &lastRow, &extendTo, &needsExtend);
+ rowSize = 0;
+ }
+ }
+ }
+ }
+ CurPtBlock = CurPtBlock->next;
+ }
+
+ if (needsExtend) {
+ for (int i = lastRow; i < reg->rects.size(); ++i)
+ reg->rects[i].setBottom(extendTo);
+ }
+
+ reg->numRects = reg->rects.size();
+
+ if (reg->numRects) {
+ reg->extents.setTop(reg->rects[0].top());
+ reg->extents.setBottom(reg->rects[lastRow].bottom());
+
+ for (int i = 0; i < reg->rects.size(); ++i)
+ reg->updateInnerRect(reg->rects[i]);
+ } else {
+ reg->extents.setCoords(0, 0, 0, 0);
+ }
+}
+
+/*
+ * polytoregion
+ *
+ * Scan converts a polygon by returning a run-length
+ * encoding of the resultant bitmap -- the run-length
+ * encoding is in the form of an array of rectangles.
+ *
+ * Can return 0 in case of errors.
+ */
+static QRegionPrivate *PolygonRegion(const QPoint *Pts, int Count, int rule)
+ //Point *Pts; /* the pts */
+ //int Count; /* number of pts */
+ //int rule; /* winding rule */
+{
+ QRegionPrivate *region;
+ register EdgeTableEntry *pAET; /* Active Edge Table */
+ register int y; /* current scanline */
+ register int iPts = 0; /* number of pts in buffer */
+ register EdgeTableEntry *pWETE; /* Winding Edge Table Entry*/
+ register ScanLineList *pSLL; /* current scanLineList */
+ register QPoint *pts; /* output buffer */
+ EdgeTableEntry *pPrevAET; /* ptr to previous AET */
+ EdgeTable ET; /* header node for ET */
+ EdgeTableEntry AET; /* header node for AET */
+ EdgeTableEntry *pETEs; /* EdgeTableEntries pool */
+ ScanLineListBlock SLLBlock; /* header for scanlinelist */
+ int fixWAET = false;
+ POINTBLOCK FirstPtBlock, *curPtBlock; /* PtBlock buffers */
+ FirstPtBlock.pts = reinterpret_cast<QPoint *>(FirstPtBlock.data);
+ POINTBLOCK *tmpPtBlock;
+ int numFullPtBlocks = 0;
+
+ if (!(region = new QRegionPrivate))
+ return 0;
+
+ /* special case a rectangle */
+ if (((Count == 4) ||
+ ((Count == 5) && (Pts[4].x() == Pts[0].x()) && (Pts[4].y() == Pts[0].y())))
+ && (((Pts[0].y() == Pts[1].y()) && (Pts[1].x() == Pts[2].x()) && (Pts[2].y() == Pts[3].y())
+ && (Pts[3].x() == Pts[0].x())) || ((Pts[0].x() == Pts[1].x())
+ && (Pts[1].y() == Pts[2].y()) && (Pts[2].x() == Pts[3].x())
+ && (Pts[3].y() == Pts[0].y())))) {
+ int x = qMin(Pts[0].x(), Pts[2].x());
+ region->extents.setLeft(x);
+ int y = qMin(Pts[0].y(), Pts[2].y());
+ region->extents.setTop(y);
+ region->extents.setWidth(qMax(Pts[0].x(), Pts[2].x()) - x);
+ region->extents.setHeight(qMax(Pts[0].y(), Pts[2].y()) - y);
+ if ((region->extents.left() <= region->extents.right()) &&
+ (region->extents.top() <= region->extents.bottom())) {
+ region->numRects = 1;
+ region->innerRect = region->extents;
+ region->innerArea = region->innerRect.width() * region->innerRect.height();
+ }
+ return region;
+ }
+
+ if (!(pETEs = static_cast<EdgeTableEntry *>(malloc(sizeof(EdgeTableEntry) * Count))))
+ return 0;
+
+ region->vectorize();
+
+ pts = FirstPtBlock.pts;
+ CreateETandAET(Count, Pts, &ET, &AET, pETEs, &SLLBlock);
+
+ pSLL = ET.scanlines.next;
+ curPtBlock = &FirstPtBlock;
+
+ // sanity check that the region won't become too big...
+ if (ET.ymax - ET.ymin > 100000) {
+ // clean up region ptr
+#ifndef QT_NO_DEBUG
+ qWarning("QRegion: creating region from big polygon failed...!");
+#endif
+ delete region;
+ return 0;
+ }
+
+
+ QT_TRY {
+ if (rule == EvenOddRule) {
+ /*
+ * for each scanline
+ */
+ for (y = ET.ymin; y < ET.ymax; ++y) {
+
+ /*
+ * Add a new edge to the active edge table when we
+ * get to the next edge.
+ */
+ if (pSLL && y == pSLL->scanline) {
+ loadAET(&AET, pSLL->edgelist);
+ pSLL = pSLL->next;
+ }
+ pPrevAET = &AET;
+ pAET = AET.next;
+
+ /*
+ * for each active edge
+ */
+ while (pAET) {
+ pts->setX(pAET->bres.minor_axis);
+ pts->setY(y);
+ ++pts;
+ ++iPts;
+
+ /*
+ * send out the buffer
+ */
+ if (iPts == NUMPTSTOBUFFER) {
+ tmpPtBlock = (POINTBLOCK *)malloc(sizeof(POINTBLOCK));
+ Q_CHECK_PTR(tmpPtBlock);
+ tmpPtBlock->pts = reinterpret_cast<QPoint *>(tmpPtBlock->data);
+ curPtBlock->next = tmpPtBlock;
+ curPtBlock = tmpPtBlock;
+ pts = curPtBlock->pts;
+ ++numFullPtBlocks;
+ iPts = 0;
+ }
+ EVALUATEEDGEEVENODD(pAET, pPrevAET, y)
+ }
+ InsertionSort(&AET);
+ }
+ } else {
+ /*
+ * for each scanline
+ */
+ for (y = ET.ymin; y < ET.ymax; ++y) {
+ /*
+ * Add a new edge to the active edge table when we
+ * get to the next edge.
+ */
+ if (pSLL && y == pSLL->scanline) {
+ loadAET(&AET, pSLL->edgelist);
+ computeWAET(&AET);
+ pSLL = pSLL->next;
+ }
+ pPrevAET = &AET;
+ pAET = AET.next;
+ pWETE = pAET;
+
+ /*
+ * for each active edge
+ */
+ while (pAET) {
+ /*
+ * add to the buffer only those edges that
+ * are in the Winding active edge table.
+ */
+ if (pWETE == pAET) {
+ pts->setX(pAET->bres.minor_axis);
+ pts->setY(y);
+ ++pts;
+ ++iPts;
+
+ /*
+ * send out the buffer
+ */
+ if (iPts == NUMPTSTOBUFFER) {
+ tmpPtBlock = static_cast<POINTBLOCK *>(malloc(sizeof(POINTBLOCK)));
+ tmpPtBlock->pts = reinterpret_cast<QPoint *>(tmpPtBlock->data);
+ curPtBlock->next = tmpPtBlock;
+ curPtBlock = tmpPtBlock;
+ pts = curPtBlock->pts;
+ ++numFullPtBlocks;
+ iPts = 0;
+ }
+ pWETE = pWETE->nextWETE;
+ }
+ EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET)
+ }
+
+ /*
+ * recompute the winding active edge table if
+ * we just resorted or have exited an edge.
+ */
+ if (InsertionSort(&AET) || fixWAET) {
+ computeWAET(&AET);
+ fixWAET = false;
+ }
+ }
+ }
+ } QT_CATCH(...) {
+ FreeStorage(SLLBlock.next);
+ PtsToRegion(numFullPtBlocks, iPts, &FirstPtBlock, region);
+ for (curPtBlock = FirstPtBlock.next; --numFullPtBlocks >= 0;) {
+ tmpPtBlock = curPtBlock->next;
+ free(curPtBlock);
+ curPtBlock = tmpPtBlock;
+ }
+ free(pETEs);
+ return 0; // this function returns 0 in case of an error
+ }
+
+ FreeStorage(SLLBlock.next);
+ PtsToRegion(numFullPtBlocks, iPts, &FirstPtBlock, region);
+ for (curPtBlock = FirstPtBlock.next; --numFullPtBlocks >= 0;) {
+ tmpPtBlock = curPtBlock->next;
+ free(curPtBlock);
+ curPtBlock = tmpPtBlock;
+ }
+ free(pETEs);
+ return region;
+}
+// END OF PolyReg.c extract
+
+QRegionPrivate *qt_bitmapToRegion(const QBitmap& bitmap)
+{
+ QImage image = bitmap.toImage();
+
+ QRegionPrivate *region = new QRegionPrivate;
+
+ QRect xr;
+
+#define AddSpan \
+ { \
+ xr.setCoords(prev1, y, x-1, y); \
+ UnionRectWithRegion(&xr, region, *region); \
+ }
+
+ const uchar zero = 0;
+ bool little = image.format() == QImage::Format_MonoLSB;
+
+ int x,
+ y;
+ for (y = 0; y < image.height(); ++y) {
+ uchar *line = image.scanLine(y);
+ int w = image.width();
+ uchar all = zero;
+ int prev1 = -1;
+ for (x = 0; x < w;) {
+ uchar byte = line[x / 8];
+ if (x > w - 8 || byte!=all) {
+ if (little) {
+ for (int b = 8; b > 0 && x < w; --b) {
+ if (!(byte & 0x01) == !all) {
+ // More of the same
+ } else {
+ // A change.
+ if (all!=zero) {
+ AddSpan
+ all = zero;
+ } else {
+ prev1 = x;
+ all = ~zero;
+ }
+ }
+ byte >>= 1;
+ ++x;
+ }
+ } else {
+ for (int b = 8; b > 0 && x < w; --b) {
+ if (!(byte & 0x80) == !all) {
+ // More of the same
+ } else {
+ // A change.
+ if (all != zero) {
+ AddSpan
+ all = zero;
+ } else {
+ prev1 = x;
+ all = ~zero;
+ }
+ }
+ byte <<= 1;
+ ++x;
+ }
+ }
+ } else {
+ x += 8;
+ }
+ }
+ if (all != zero) {
+ AddSpan
+ }
+ }
+#undef AddSpan
+
+ return region;
+}
+
+QRegion::QRegion()
+ : d(&shared_empty)
+{
+ d->ref.ref();
+}
+
+QRegion::QRegion(const QRect &r, RegionType t)
+{
+ if (r.isEmpty()) {
+ d = &shared_empty;
+ d->ref.ref();
+ } else {
+ d = new QRegionData;
+ d->ref = 1;
+#if defined(Q_WS_X11)
+ d->rgn = 0;
+ d->xrectangles = 0;
+#elif defined(Q_WS_WIN)
+ d->rgn = 0;
+#endif
+ if (t == Rectangle) {
+ d->qt_rgn = new QRegionPrivate(r);
+ } else if (t == Ellipse) {
+ QPainterPath path;
+ path.addEllipse(r.x(), r.y(), r.width(), r.height());
+ QPolygon a = path.toSubpathPolygons().at(0).toPolygon();
+ d->qt_rgn = PolygonRegion(a.constData(), a.size(), EvenOddRule);
+ }
+ }
+}
+
+QRegion::QRegion(const QPolygon &a, Qt::FillRule fillRule)
+{
+ if (a.count() > 2) {
+ QRegionPrivate *qt_rgn = PolygonRegion(a.constData(), a.size(),
+ fillRule == Qt::WindingFill ? WindingRule : EvenOddRule);
+ if (qt_rgn) {
+ d = new QRegionData;
+ d->ref = 1;
+#if defined(Q_WS_X11)
+ d->rgn = 0;
+ d->xrectangles = 0;
+#elif defined(Q_WS_WIN)
+ d->rgn = 0;
+#endif
+ d->qt_rgn = qt_rgn;
+ } else {
+ d = &shared_empty;
+ d->ref.ref();
+ }
+ } else {
+ d = &shared_empty;
+ d->ref.ref();
+ }
+}
+
+QRegion::QRegion(const QRegion &r)
+{
+ d = r.d;
+ d->ref.ref();
+}
+
+
+QRegion::QRegion(const QBitmap &bm)
+{
+ if (bm.isNull()) {
+ d = &shared_empty;
+ d->ref.ref();
+ } else {
+ d = new QRegionData;
+ d->ref = 1;
+#if defined(Q_WS_X11)
+ d->rgn = 0;
+ d->xrectangles = 0;
+#elif defined(Q_WS_WIN)
+ d->rgn = 0;
+#endif
+ d->qt_rgn = qt_bitmapToRegion(bm);
+ }
+}
+
+void QRegion::cleanUp(QRegion::QRegionData *x)
+{
+ delete x->qt_rgn;
+#if defined(Q_WS_X11)
+ if (x->rgn)
+ XDestroyRegion(x->rgn);
+ if (x->xrectangles)
+ free(x->xrectangles);
+#elif defined(Q_WS_WIN)
+ if (x->rgn)
+ qt_win_dispose_rgn(x->rgn);
+#endif
+ delete x;
+}
+
+QRegion::~QRegion()
+{
+ if (!d->ref.deref())
+ cleanUp(d);
+}
+
+
+QRegion &QRegion::operator=(const QRegion &r)
+{
+ r.d->ref.ref();
+ if (!d->ref.deref())
+ cleanUp(d);
+ d = r.d;
+ return *this;
+}
+
+
+/*!
+ \internal
+*/
+QRegion QRegion::copy() const
+{
+ QRegion r;
+ QScopedPointer<QRegionData> x(new QRegionData);
+ x->ref = 1;
+#if defined(Q_WS_X11)
+ x->rgn = 0;
+ x->xrectangles = 0;
+#elif defined(Q_WS_WIN)
+ x->rgn = 0;
+#endif
+ if (d->qt_rgn)
+ x->qt_rgn = new QRegionPrivate(*d->qt_rgn);
+ else
+ x->qt_rgn = new QRegionPrivate;
+ if (!r.d->ref.deref())
+ cleanUp(r.d);
+ r.d = x.take();
+ return r;
+}
+
+bool QRegion::isEmpty() const
+{
+ return d == &shared_empty || d->qt_rgn->numRects == 0;
+}
+
+
+bool QRegion::contains(const QPoint &p) const
+{
+ return PointInRegion(d->qt_rgn, p.x(), p.y());
+}
+
+bool QRegion::contains(const QRect &r) const
+{
+ return RectInRegion(d->qt_rgn, r.left(), r.top(), r.width(), r.height()) != RectangleOut;
+}
+
+
+
+void QRegion::translate(int dx, int dy)
+{
+ if ((dx == 0 && dy == 0) || isEmptyHelper(d->qt_rgn))
+ return;
+
+ detach();
+ OffsetRegion(*d->qt_rgn, dx, dy);
+}
+
+QRegion QRegion::unite(const QRegion &r) const
+{
+ if (isEmptyHelper(d->qt_rgn))
+ return r;
+ if (isEmptyHelper(r.d->qt_rgn))
+ return *this;
+ if (d == r.d)
+ return *this;
+
+ if (d->qt_rgn->contains(*r.d->qt_rgn)) {
+ return *this;
+ } else if (r.d->qt_rgn->contains(*d->qt_rgn)) {
+ return r;
+ } else if (d->qt_rgn->canAppend(r.d->qt_rgn)) {
+ QRegion result(*this);
+ result.detach();
+ result.d->qt_rgn->append(r.d->qt_rgn);
+ return result;
+ } else if (d->qt_rgn->canPrepend(r.d->qt_rgn)) {
+ QRegion result(*this);
+ result.detach();
+ result.d->qt_rgn->prepend(r.d->qt_rgn);
+ return result;
+ } else if (EqualRegion(d->qt_rgn, r.d->qt_rgn)) {
+ return *this;
+ } else {
+ QRegion result;
+ result.detach();
+ UnionRegion(d->qt_rgn, r.d->qt_rgn, *result.d->qt_rgn);
+ return result;
+ }
+}
+
+QRegion& QRegion::operator+=(const QRegion &r)
+{
+ if (isEmptyHelper(d->qt_rgn))
+ return *this = r;
+ if (isEmptyHelper(r.d->qt_rgn))
+ return *this;
+ if (d == r.d)
+ return *this;
+
+ if (d->qt_rgn->contains(*r.d->qt_rgn)) {
+ return *this;
+ } else if (r.d->qt_rgn->contains(*d->qt_rgn)) {
+ return *this = r;
+ } else if (d->qt_rgn->canAppend(r.d->qt_rgn)) {
+ detach();
+ d->qt_rgn->append(r.d->qt_rgn);
+ return *this;
+ } else if (d->qt_rgn->canPrepend(r.d->qt_rgn)) {
+ detach();
+ d->qt_rgn->prepend(r.d->qt_rgn);
+ return *this;
+ } else if (EqualRegion(d->qt_rgn, r.d->qt_rgn)) {
+ return *this;
+ } else {
+ detach();
+ UnionRegion(d->qt_rgn, r.d->qt_rgn, *d->qt_rgn);
+ return *this;
+ }
+}
+
+QRegion QRegion::unite(const QRect &r) const
+{
+ if (isEmptyHelper(d->qt_rgn))
+ return r;
+ if (r.isEmpty())
+ return *this;
+
+ if (d->qt_rgn->contains(r)) {
+ return *this;
+ } else if (d->qt_rgn->within(r)) {
+ return r;
+ } else if (d->qt_rgn->numRects == 1 && d->qt_rgn->extents == r) {
+ return *this;
+ } else if (d->qt_rgn->canAppend(&r)) {
+ QRegion result(*this);
+ result.detach();
+ result.d->qt_rgn->append(&r);
+ return result;
+ } else if (d->qt_rgn->canPrepend(&r)) {
+ QRegion result(*this);
+ result.detach();
+ result.d->qt_rgn->prepend(&r);
+ return result;
+ } else {
+ QRegion result;
+ result.detach();
+ QRegionPrivate rp(r);
+ UnionRegion(d->qt_rgn, &rp, *result.d->qt_rgn);
+ return result;
+ }
+}
+
+QRegion& QRegion::operator+=(const QRect &r)
+{
+ if (isEmptyHelper(d->qt_rgn))
+ return *this = r;
+ if (r.isEmpty())
+ return *this;
+
+ if (d->qt_rgn->contains(r)) {
+ return *this;
+ } else if (d->qt_rgn->within(r)) {
+ return *this = r;
+ } else if (d->qt_rgn->canAppend(&r)) {
+ detach();
+ d->qt_rgn->append(&r);
+ return *this;
+ } else if (d->qt_rgn->canPrepend(&r)) {
+ detach();
+ d->qt_rgn->prepend(&r);
+ return *this;
+ } else if (d->qt_rgn->numRects == 1 && d->qt_rgn->extents == r) {
+ return *this;
+ } else {
+ detach();
+ QRegionPrivate p(r);
+ UnionRegion(d->qt_rgn, &p, *d->qt_rgn);
+ return *this;
+ }
+}
+
+QRegion QRegion::intersect(const QRegion &r) const
+{
+ if (isEmptyHelper(d->qt_rgn) || isEmptyHelper(r.d->qt_rgn)
+ || !EXTENTCHECK(&d->qt_rgn->extents, &r.d->qt_rgn->extents))
+ return QRegion();
+
+ /* this is fully contained in r */
+ if (r.d->qt_rgn->contains(*d->qt_rgn))
+ return *this;
+
+ /* r is fully contained in this */
+ if (d->qt_rgn->contains(*r.d->qt_rgn))
+ return r;
+
+ if (r.d->qt_rgn->numRects == 1 && d->qt_rgn->numRects == 1) {
+ const QRect rect = qt_rect_intersect_normalized(r.d->qt_rgn->extents,
+ d->qt_rgn->extents);
+ return QRegion(rect);
+ } else if (r.d->qt_rgn->numRects == 1) {
+ QRegion result(*this);
+ result.detach();
+ result.d->qt_rgn->intersect(r.d->qt_rgn->extents);
+ return result;
+ } else if (d->qt_rgn->numRects == 1) {
+ QRegion result(r);
+ result.detach();
+ result.d->qt_rgn->intersect(d->qt_rgn->extents);
+ return result;
+ }
+
+ QRegion result;
+ result.detach();
+ miRegionOp(*result.d->qt_rgn, d->qt_rgn, r.d->qt_rgn, miIntersectO, 0, 0);
+
+ /*
+ * Can't alter dest's extents before we call miRegionOp because
+ * it might be one of the source regions and miRegionOp depends
+ * on the extents of those regions being the same. Besides, this
+ * way there's no checking against rectangles that will be nuked
+ * due to coalescing, so we have to examine fewer rectangles.
+ */
+ miSetExtents(*result.d->qt_rgn);
+ return result;
+}
+
+QRegion QRegion::intersect(const QRect &r) const
+{
+ if (isEmptyHelper(d->qt_rgn) || r.isEmpty()
+ || !EXTENTCHECK(&d->qt_rgn->extents, &r))
+ return QRegion();
+
+ /* this is fully contained in r */
+ if (d->qt_rgn->within(r))
+ return *this;
+
+ /* r is fully contained in this */
+ if (d->qt_rgn->contains(r))
+ return r;
+
+ if (d->qt_rgn->numRects == 1) {
+ const QRect rect = qt_rect_intersect_normalized(d->qt_rgn->extents,
+ r.normalized());
+ return QRegion(rect);
+ }
+
+ QRegion result(*this);
+ result.detach();
+ result.d->qt_rgn->intersect(r);
+ return result;
+}
+
+QRegion QRegion::subtract(const QRegion &r) const
+{
+ if (isEmptyHelper(d->qt_rgn) || isEmptyHelper(r.d->qt_rgn))
+ return *this;
+ if (r.d->qt_rgn->contains(*d->qt_rgn))
+ return QRegion();
+ if (!EXTENTCHECK(&d->qt_rgn->extents, &r.d->qt_rgn->extents))
+ return *this;
+ if (d == r.d || EqualRegion(d->qt_rgn, r.d->qt_rgn))
+ return QRegion();
+
+#ifdef QT_REGION_DEBUG
+ d->qt_rgn->selfTest();
+ r.d->qt_rgn->selfTest();
+#endif
+
+ QRegion result;
+ result.detach();
+ SubtractRegion(d->qt_rgn, r.d->qt_rgn, *result.d->qt_rgn);
+#ifdef QT_REGION_DEBUG
+ result.d->qt_rgn->selfTest();
+#endif
+ return result;
+}
+
+QRegion QRegion::eor(const QRegion &r) const
+{
+ if (isEmptyHelper(d->qt_rgn)) {
+ return r;
+ } else if (isEmptyHelper(r.d->qt_rgn)) {
+ return *this;
+ } else if (!EXTENTCHECK(&d->qt_rgn->extents, &r.d->qt_rgn->extents)) {
+ return (*this + r);
+ } else if (d == r.d || EqualRegion(d->qt_rgn, r.d->qt_rgn)) {
+ return QRegion();
+ } else {
+ QRegion result;
+ result.detach();
+ XorRegion(d->qt_rgn, r.d->qt_rgn, *result.d->qt_rgn);
+ return result;
+ }
+}
+
+QRect QRegion::boundingRect() const
+{
+ if (isEmpty())
+ return QRect();
+ return d->qt_rgn->extents;
+}
+
+/*! \internal
+ Returns true if \a rect is guaranteed to be fully contained in \a region.
+ A false return value does not guarantee the opposite.
+*/
+#if defined(Q_WS_QWS) || defined(Q_WS_QPA)
+Q_GUI_EXPORT
+#endif
+bool qt_region_strictContains(const QRegion &region, const QRect &rect)
+{
+ if (isEmptyHelper(region.d->qt_rgn) || !rect.isValid())
+ return false;
+
+#if 0 // TEST_INNERRECT
+ static bool guard = false;
+ if (guard)
+ return false;
+ guard = true;
+ QRegion inner = region.d->qt_rgn->innerRect;
+ Q_ASSERT((inner - region).isEmpty());
+ guard = false;
+
+ int maxArea = 0;
+ for (int i = 0; i < region.d->qt_rgn->numRects; ++i) {
+ const QRect r = region.d->qt_rgn->rects.at(i);
+ if (r.width() * r.height() > maxArea)
+ maxArea = r.width() * r.height();
+ }
+
+ if (maxArea > region.d->qt_rgn->innerArea) {
+ qDebug() << "not largest rectangle" << region << region.d->qt_rgn->innerRect;
+ }
+ Q_ASSERT(maxArea <= region.d->qt_rgn->innerArea);
+#endif
+
+ const QRect r1 = region.d->qt_rgn->innerRect;
+ return (rect.left() >= r1.left() && rect.right() <= r1.right()
+ && rect.top() >= r1.top() && rect.bottom() <= r1.bottom());
+}
+
+QVector<QRect> QRegion::rects() const
+{
+ if (d->qt_rgn) {
+ d->qt_rgn->vectorize();
+ // hw: modify the vector size directly to avoid reallocation
+ d->qt_rgn->rects.d->size = d->qt_rgn->numRects;
+ return d->qt_rgn->rects;
+ } else {
+ return QVector<QRect>();
+ }
+}
+
+void QRegion::setRects(const QRect *rects, int num)
+{
+ *this = QRegion();
+ if (!rects || num == 0 || (num == 1 && rects->isEmpty()))
+ return;
+
+ detach();
+
+ d->qt_rgn->numRects = num;
+ if (num == 1) {
+ d->qt_rgn->extents = *rects;
+ d->qt_rgn->innerRect = *rects;
+ } else {
+ d->qt_rgn->rects.resize(num);
+
+ int left = INT_MAX,
+ right = INT_MIN,
+ top = INT_MAX,
+ bottom = INT_MIN;
+ for (int i = 0; i < num; ++i) {
+ const QRect &rect = rects[i];
+ d->qt_rgn->rects[i] = rect;
+ left = qMin(rect.left(), left);
+ right = qMax(rect.right(), right);
+ top = qMin(rect.top(), top);
+ bottom = qMax(rect.bottom(), bottom);
+ d->qt_rgn->updateInnerRect(rect);
+ }
+ d->qt_rgn->extents = QRect(QPoint(left, top), QPoint(right, bottom));
+ }
+}
+
+int QRegion::numRects() const
+{
+ return (d->qt_rgn ? d->qt_rgn->numRects : 0);
+}
+
+int QRegion::rectCount() const
+{
+ return (d->qt_rgn ? d->qt_rgn->numRects : 0);
+}
+
+
+bool QRegion::operator==(const QRegion &r) const
+{
+ if (!d->qt_rgn)
+ return r.isEmpty();
+ if (!r.d->qt_rgn)
+ return isEmpty();
+
+ if (d == r.d)
+ return true;
+ else
+ return EqualRegion(d->qt_rgn, r.d->qt_rgn);
+}
+
+bool QRegion::intersects(const QRect &rect) const
+{
+ if (isEmptyHelper(d->qt_rgn) || rect.isNull())
+ return false;
+
+ const QRect r = rect.normalized();
+ if (!rect_intersects(d->qt_rgn->extents, r))
+ return false;
+ if (d->qt_rgn->numRects == 1)
+ return true;
+
+ const QVector<QRect> myRects = rects();
+ for (QVector<QRect>::const_iterator it = myRects.constBegin(); it < myRects.constEnd(); ++it)
+ if (rect_intersects(r, *it))
+ return true;
+ return false;
+}
+
+
+#endif
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qregion.h b/src/gui/painting/qregion.h
new file mode 100644
index 0000000000..1cd38d41b8
--- /dev/null
+++ b/src/gui/painting/qregion.h
@@ -0,0 +1,239 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QREGION_H
+#define QREGION_H
+
+#include <QtCore/qatomic.h>
+#include <QtCore/qrect.h>
+#include <QtGui/qwindowdefs.h>
+
+#ifndef QT_NO_DATASTREAM
+#include <QtCore/qdatastream.h>
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+template <class T> class QVector;
+class QVariant;
+
+#if defined(Q_WS_QWS) || defined(Q_WS_QPA) || defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_WIN) || defined(Q_OS_SYMBIAN)
+struct QRegionPrivate;
+#endif
+
+class QBitmap;
+
+class Q_GUI_EXPORT QRegion
+{
+public:
+ enum RegionType { Rectangle, Ellipse };
+
+ QRegion();
+ QRegion(int x, int y, int w, int h, RegionType t = Rectangle);
+ QRegion(const QRect &r, RegionType t = Rectangle);
+ QRegion(const QPolygon &pa, Qt::FillRule fillRule = Qt::OddEvenFill);
+#ifdef QT3_SUPPORT
+ QT3_SUPPORT_CONSTRUCTOR QRegion(const QPolygon &pa, bool winding);
+#endif
+ QRegion(const QRegion &region);
+ QRegion(const QBitmap &bitmap);
+ ~QRegion();
+ QRegion &operator=(const QRegion &);
+#ifdef Q_COMPILER_RVALUE_REFS
+ inline QRegion &operator=(QRegion &&other)
+ { qSwap(d, other.d); return *this; }
+#endif
+ inline void swap(QRegion &other) { qSwap(d, other.d); }
+#ifdef QT3_SUPPORT
+ inline QT3_SUPPORT bool isNull() const { return isEmpty(); }
+#endif
+ bool isEmpty() const;
+
+ bool contains(const QPoint &p) const;
+ bool contains(const QRect &r) const;
+
+ void translate(int dx, int dy);
+ inline void translate(const QPoint &p) { translate(p.x(), p.y()); }
+ QRegion translated(int dx, int dy) const;
+ inline QRegion translated(const QPoint &p) const { return translated(p.x(), p.y()); }
+
+ // ### Qt 5: make these four functions QT4_SUPPORT
+ QRegion unite(const QRegion &r) const;
+ QRegion unite(const QRect &r) const;
+ QRegion intersect(const QRegion &r) const;
+ QRegion intersect(const QRect &r) const;
+ QRegion subtract(const QRegion &r) const;
+ QRegion eor(const QRegion &r) const;
+
+ inline QRegion united(const QRegion &r) const { return unite(r); }
+ inline QRegion united(const QRect &r) const { return unite(r); }
+ inline QRegion intersected(const QRegion &r) const { return intersect(r); }
+ inline QRegion intersected(const QRect &r) const { return intersect(r); }
+ inline QRegion subtracted(const QRegion &r) const { return subtract(r); }
+ inline QRegion xored(const QRegion &r) const { return eor(r); }
+
+ bool intersects(const QRegion &r) const;
+ bool intersects(const QRect &r) const;
+
+ QRect boundingRect() const;
+ QVector<QRect> rects() const;
+ void setRects(const QRect *rect, int num);
+#ifdef QT_DEPRECATED
+ QT_DEPRECATED int numRects() const;
+#endif
+ int rectCount() const;
+
+ const QRegion operator|(const QRegion &r) const;
+ const QRegion operator+(const QRegion &r) const;
+ const QRegion operator+(const QRect &r) const;
+ const QRegion operator&(const QRegion &r) const;
+ const QRegion operator&(const QRect &r) const;
+ const QRegion operator-(const QRegion &r) const;
+ const QRegion operator^(const QRegion &r) const;
+ QRegion& operator|=(const QRegion &r);
+ QRegion& operator+=(const QRegion &r);
+ QRegion& operator+=(const QRect &r);
+ QRegion& operator&=(const QRegion &r);
+ QRegion& operator&=(const QRect &r);
+ QRegion& operator-=(const QRegion &r);
+ QRegion& operator^=(const QRegion &r);
+
+ bool operator==(const QRegion &r) const;
+ inline bool operator!=(const QRegion &r) const { return !(operator==(r)); }
+ operator QVariant() const;
+
+#ifdef qdoc
+ Handle handle() const;
+#endif
+#ifndef qdoc
+#if defined(Q_WS_WIN)
+ inline HRGN handle() const { ensureHandle(); return d->rgn; }
+#elif defined(Q_WS_X11)
+ inline Region handle() const { if(!d->rgn) updateX11Region(); return d->rgn; }
+#elif defined(Q_WS_MAC)
+#if defined Q_WS_MAC32
+ RgnHandle toQDRgn() const;
+ RgnHandle toQDRgnForUpdate_sys() const;
+ static QRegion fromQDRgn(RgnHandle shape);
+#endif
+#ifdef QT_MAC_USE_COCOA
+ inline HIMutableShapeRef handle(bool unused = false) const
+ { Q_UNUSED(unused); return toHIMutableShape(); }
+#else
+ inline RgnHandle handle() const { return handle(false); }
+ inline RgnHandle handle(bool) const { return toQDRgn(); }
+#endif
+ HIMutableShapeRef toHIMutableShape() const;
+ static QRegion fromHIShapeRef(HIShapeRef shape);
+#elif defined(Q_WS_QWS) || defined(Q_WS_QPA)
+ inline void *handle() const { return d->qt_rgn; }
+#endif
+#endif
+
+#ifndef QT_NO_DATASTREAM
+ friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QRegion &);
+ friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QRegion &);
+#endif
+private:
+ QRegion copy() const; // helper of detach.
+ void detach();
+#if defined(Q_WS_WIN)
+ void ensureHandle() const;
+ QRegion winCombine(const QRegion &r, int num) const;
+#elif defined(Q_WS_X11)
+ void updateX11Region() const;
+ void *clipRectangles(int &num) const;
+ friend void *qt_getClipRects(const QRegion &r, int &num);
+#elif defined(Q_WS_MAC)
+ static OSStatus shape2QRegionHelper(int inMessage, HIShapeRef inShape,
+ const CGRect *inRect, void *inRefcon);
+#endif
+ friend bool qt_region_strictContains(const QRegion &region,
+ const QRect &rect);
+ friend struct QRegionPrivate;
+
+#ifndef QT_NO_DATASTREAM
+ void exec(const QByteArray &ba, int ver = 0, QDataStream::ByteOrder byteOrder = QDataStream::BigEndian);
+#endif
+ struct QRegionData {
+ QBasicAtomicInt ref;
+#if defined(Q_WS_WIN)
+ HRGN rgn;
+#elif defined(Q_WS_X11)
+ Region rgn;
+ void *xrectangles;
+#elif defined(Q_WS_MAC) && !defined(QT_MAC_USE_COCOA)
+ mutable RgnHandle unused; // Here for binary compatibility reasons. ### Qt 5 remove.
+#endif
+#if defined(Q_WS_QWS) || defined(Q_WS_QPA) || defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_WIN) || defined(Q_OS_SYMBIAN)
+ QRegionPrivate *qt_rgn;
+#endif
+ };
+#if defined(Q_WS_WIN)
+ friend class QETWidget;
+#endif
+ struct QRegionData *d;
+ static struct QRegionData shared_empty;
+ static void cleanUp(QRegionData *x);
+};
+
+/*****************************************************************************
+ QRegion stream functions
+ *****************************************************************************/
+
+#ifndef QT_NO_DATASTREAM
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QRegion &);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QRegion &);
+#endif
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QRegion &);
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QREGION_H
diff --git a/src/gui/painting/qregion_mac.cpp b/src/gui/painting/qregion_mac.cpp
new file mode 100644
index 0000000000..50fd783df4
--- /dev/null
+++ b/src/gui/painting/qregion_mac.cpp
@@ -0,0 +1,286 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qt_mac_p.h>
+#include "qcoreapplication.h"
+#include <qlibrary.h>
+
+QT_BEGIN_NAMESPACE
+
+QRegion::QRegionData QRegion::shared_empty = { Q_BASIC_ATOMIC_INITIALIZER(1), 0 };
+
+#if defined(Q_WS_MAC32) && !defined(QT_MAC_USE_COCOA)
+#define RGN_CACHE_SIZE 200
+#ifdef RGN_CACHE_SIZE
+static bool rgncache_init = false;
+static int rgncache_used;
+static RgnHandle rgncache[RGN_CACHE_SIZE];
+static void qt_mac_cleanup_rgncache()
+{
+ rgncache_init = false;
+ for(int i = 0; i < RGN_CACHE_SIZE; ++i) {
+ if(rgncache[i]) {
+ --rgncache_used;
+ DisposeRgn(rgncache[i]);
+ rgncache[i] = 0;
+ }
+ }
+}
+#endif
+
+Q_GUI_EXPORT RgnHandle qt_mac_get_rgn()
+{
+#ifdef RGN_CACHE_SIZE
+ if(!rgncache_init) {
+ rgncache_used = 0;
+ rgncache_init = true;
+ for(int i = 0; i < RGN_CACHE_SIZE; ++i)
+ rgncache[i] = 0;
+ qAddPostRoutine(qt_mac_cleanup_rgncache);
+ } else if(rgncache_used) {
+ for(int i = 0; i < RGN_CACHE_SIZE; ++i) {
+ if(rgncache[i]) {
+ RgnHandle ret = rgncache[i];
+ SetEmptyRgn(ret);
+ rgncache[i] = 0;
+ --rgncache_used;
+ return ret;
+ }
+ }
+ }
+#endif
+ return NewRgn();
+}
+
+Q_GUI_EXPORT void qt_mac_dispose_rgn(RgnHandle r)
+{
+#ifdef RGN_CACHE_SIZE
+ if(rgncache_init && rgncache_used < RGN_CACHE_SIZE) {
+ for(int i = 0; i < RGN_CACHE_SIZE; ++i) {
+ if(!rgncache[i]) {
+ ++rgncache_used;
+ rgncache[i] = r;
+ return;
+ }
+ }
+ }
+#endif
+ DisposeRgn(r);
+}
+
+static OSStatus qt_mac_get_rgn_rect(UInt16 msg, RgnHandle, const Rect *rect, void *reg)
+{
+ if(msg == kQDRegionToRectsMsgParse) {
+ QRect rct(rect->left, rect->top, (rect->right - rect->left), (rect->bottom - rect->top));
+ if(!rct.isEmpty())
+ *((QRegion *)reg) += rct;
+ }
+ return noErr;
+}
+
+Q_GUI_EXPORT QRegion qt_mac_convert_mac_region(RgnHandle rgn)
+{
+ return QRegion::fromQDRgn(rgn);
+}
+
+QRegion QRegion::fromQDRgn(RgnHandle rgn)
+{
+ QRegion ret;
+ ret.detach();
+ OSStatus oss = QDRegionToRects(rgn, kQDParseRegionFromTopLeft, qt_mac_get_rgn_rect, (void *)&ret);
+ if(oss != noErr)
+ return QRegion();
+ return ret;
+}
+
+/*!
+ \internal
+ Create's a RegionHandle, it's the caller's responsibility to release.
+*/
+RgnHandle QRegion::toQDRgn() const
+{
+ RgnHandle rgnHandle = qt_mac_get_rgn();
+ if(d->qt_rgn && d->qt_rgn->numRects) {
+ RgnHandle tmp_rgn = qt_mac_get_rgn();
+ int n = d->qt_rgn->numRects;
+ const QRect *qt_r = (n == 1) ? &d->qt_rgn->extents : d->qt_rgn->rects.constData();
+ while (n--) {
+ SetRectRgn(tmp_rgn,
+ qMax(SHRT_MIN, qt_r->x()),
+ qMax(SHRT_MIN, qt_r->y()),
+ qMin(SHRT_MAX, qt_r->right() + 1),
+ qMin(SHRT_MAX, qt_r->bottom() + 1));
+ UnionRgn(rgnHandle, tmp_rgn, rgnHandle);
+ ++qt_r;
+ }
+ qt_mac_dispose_rgn(tmp_rgn);
+ }
+ return rgnHandle;
+}
+
+/*!
+ \internal
+ Create's a RegionHandle, it's the caller's responsibility to release.
+ Returns 0 if the QRegion overflows.
+*/
+RgnHandle QRegion::toQDRgnForUpdate_sys() const
+{
+ RgnHandle rgnHandle = qt_mac_get_rgn();
+ if(d->qt_rgn && d->qt_rgn->numRects) {
+ RgnHandle tmp_rgn = qt_mac_get_rgn();
+ int n = d->qt_rgn->numRects;
+ const QRect *qt_r = (n == 1) ? &d->qt_rgn->extents : d->qt_rgn->rects.constData();
+ while (n--) {
+
+ // detect overflow. Tested for use with HIViewSetNeedsDisplayInRegion
+ // in QWidgetPrivate::update_sys().
+ enum { HIViewSetNeedsDisplayInRegionOverflow = 10000 }; // empirically determined conservative value
+ if (qt_r->right() > HIViewSetNeedsDisplayInRegionOverflow || qt_r->bottom() > HIViewSetNeedsDisplayInRegionOverflow) {
+ qt_mac_dispose_rgn(tmp_rgn);
+ qt_mac_dispose_rgn(rgnHandle);
+ return 0;
+ }
+
+ SetRectRgn(tmp_rgn,
+ qMax(SHRT_MIN, qt_r->x()),
+ qMax(SHRT_MIN, qt_r->y()),
+ qMin(SHRT_MAX, qt_r->right() + 1),
+ qMin(SHRT_MAX, qt_r->bottom() + 1));
+ UnionRgn(rgnHandle, tmp_rgn, rgnHandle);
+ ++qt_r;
+ }
+ qt_mac_dispose_rgn(tmp_rgn);
+ }
+ return rgnHandle;
+}
+
+#endif
+
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+OSStatus QRegion::shape2QRegionHelper(int inMessage, HIShapeRef,
+ const CGRect *inRect, void *inRefcon)
+{
+ QRegion *region = static_cast<QRegion *>(inRefcon);
+ if (!region)
+ return paramErr;
+
+ switch (inMessage) {
+ case kHIShapeEnumerateRect:
+ *region += QRect(inRect->origin.x, inRect->origin.y,
+ inRect->size.width, inRect->size.height);
+ break;
+ case kHIShapeEnumerateInit:
+ // Assume the region is already setup correctly
+ case kHIShapeEnumerateTerminate:
+ default:
+ break;
+ }
+ return noErr;
+}
+#endif
+
+/*!
+ \internal
+ Create's a mutable shape, it's the caller's responsibility to release.
+ WARNING: this function clamps the coordinates to SHRT_MIN/MAX on 10.4 and below.
+*/
+HIMutableShapeRef QRegion::toHIMutableShape() const
+{
+ HIMutableShapeRef shape = HIShapeCreateMutable();
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
+ if (d->qt_rgn && d->qt_rgn->numRects) {
+ int n = d->qt_rgn->numRects;
+ const QRect *qt_r = (n == 1) ? &d->qt_rgn->extents : d->qt_rgn->rects.constData();
+ while (n--) {
+ CGRect cgRect = CGRectMake(qt_r->x(), qt_r->y(), qt_r->width(), qt_r->height());
+ HIShapeUnionWithRect(shape, &cgRect);
+ ++qt_r;
+ }
+ }
+ } else
+#endif
+ {
+#ifndef QT_MAC_USE_COCOA
+ QCFType<HIShapeRef> qdShape = HIShapeCreateWithQDRgn(QMacSmartQuickDrawRegion(toQDRgn()));
+ HIShapeUnion(qdShape, shape, shape);
+#endif
+ }
+ return shape;
+}
+
+#if !defined(Q_WS_MAC64) && !defined(QT_MAC_USE_COCOA)
+typedef OSStatus (*PtrHIShapeGetAsQDRgn)(HIShapeRef, RgnHandle);
+static PtrHIShapeGetAsQDRgn ptrHIShapeGetAsQDRgn = 0;
+#endif
+
+
+QRegion QRegion::fromHIShapeRef(HIShapeRef shape)
+{
+ QRegion returnRegion;
+ returnRegion.detach();
+ // Begin gratuitous #if-defery
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+# ifndef Q_WS_MAC64
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
+# endif
+ HIShapeEnumerate(shape, kHIShapeParseFromTopLeft, shape2QRegionHelper, &returnRegion);
+# ifndef Q_WS_MAC64
+ } else
+# endif
+#endif
+ {
+#if !defined(Q_WS_MAC64) && !defined(QT_MAC_USE_COCOA)
+ if (ptrHIShapeGetAsQDRgn == 0) {
+ QLibrary library(QLatin1String("/System/Library/Frameworks/Carbon.framework/Carbon"));
+ library.setLoadHints(QLibrary::ExportExternalSymbolsHint);
+ ptrHIShapeGetAsQDRgn = reinterpret_cast<PtrHIShapeGetAsQDRgn>(library.resolve("HIShapeGetAsQDRgn"));
+ }
+ RgnHandle rgn = qt_mac_get_rgn();
+ ptrHIShapeGetAsQDRgn(shape, rgn);
+ returnRegion = QRegion::fromQDRgn(rgn);
+ qt_mac_dispose_rgn(rgn);
+#endif
+ }
+ return returnRegion;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qregion_qws.cpp b/src/gui/painting/qregion_qws.cpp
new file mode 100644
index 0000000000..dca46d3ec0
--- /dev/null
+++ b/src/gui/painting/qregion_qws.cpp
@@ -0,0 +1,3183 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// XXX - add appropriate friendship relationships
+#define private public
+#include "qregion.h"
+#undef private
+#include "qpainterpath.h"
+#include "qpolygon.h"
+#include "qbuffer.h"
+#include "qimage.h"
+#include <qdebug.h>
+#include "qbitmap.h"
+#include <stdlib.h>
+#include <qatomic.h>
+#include <qsemaphore.h>
+
+QT_BEGIN_NAMESPACE
+
+class QFastMutex
+{
+ QAtomicInt contenders;
+ QSemaphore semaphore;
+public:
+ inline QFastMutex()
+ : contenders(0), semaphore(0)
+ { }
+ inline void lock()
+ {
+ if (contenders.fetchAndAddAcquire(1) != 0) {
+ semaphore.acquire();
+ contenders.deref();
+ }
+ }
+ inline bool tryLock()
+ {
+ return contenders.testAndSetAcquire(0, 1);
+ }
+ inline void unlock()
+ {
+ if (!contenders.testAndSetRelease(1, 0))
+ semaphore.release();
+ }
+};
+
+
+/*
+ * 1 if r1 contains r2
+ * 0 if r1 does not completely contain r2
+ */
+#define CONTAINSCHECK(r1, r2) \
+ ((r2).left() >= (r1).left() && (r2).right() <= (r1).right() && \
+ (r2).top() >= (r1).top() && (r2).bottom() <= (r1).bottom())
+
+/*
+ * clip region
+ */
+struct QRegionPrivate : public QRegion::QRegionData {
+ enum { Single, Vector } mode;
+ int numRects;
+ QVector<QRect> rects;
+ QRect single;
+ QRect extents;
+ QRect innerRect;
+ union {
+ int innerArea;
+ QRegionPrivate *next;
+ };
+
+ inline void vector()
+ {
+ if(mode != Vector && numRects) {
+ if(rects.size() < 1) rects.resize(1);
+ rects[0] = single;
+ }
+ mode = Vector;
+ }
+
+ inline QRegionPrivate() : mode(Single), numRects(0), innerArea(-1) {}
+ inline QRegionPrivate(const QRect &r) : mode(Single) {
+ numRects = 1;
+// rects[0] = r;
+ single = r;
+ extents = r;
+ innerRect = r;
+ innerArea = r.width() * r.height();
+ }
+
+ inline QRegionPrivate(const QRegionPrivate &r) {
+ mode = r.mode;
+ rects = r.rects;
+ single = r.single;
+ numRects = r.numRects;
+ extents = r.extents;
+ innerRect = r.innerRect;
+ innerArea = r.innerArea;
+ }
+
+ inline QRegionPrivate &operator=(const QRegionPrivate &r) {
+ mode = r.mode;
+ rects = r.rects;
+ single = r.single;
+ numRects = r.numRects;
+ extents = r.extents;
+ innerRect = r.innerRect;
+ innerArea = r.innerArea;
+ return *this;
+ }
+
+ /*
+ * Returns true if r is guaranteed to be fully contained in this region.
+ * A false return value does not guarantee the opposite.
+ */
+ inline bool contains(const QRegionPrivate &r) const {
+ const QRect &r1 = innerRect;
+ const QRect &r2 = r.extents;
+ return CONTAINSCHECK(r1, r2);
+ }
+
+ inline void updateInnerRect(const QRect &rect) {
+ const int area = rect.width() * rect.height();
+ if (area > innerArea) {
+ innerArea = area;
+ innerRect = rect;
+ }
+ }
+
+ void append(const QRegionPrivate *r);
+ void prepend(const QRegionPrivate *r);
+ inline bool canAppend(const QRegionPrivate *r) const;
+ inline bool canPrepend(const QRegionPrivate *r) const;
+};
+
+static QRegionPrivate *qt_nextRegionPtr = 0;
+static QFastMutex qt_nextRegionLock;
+
+static QRegionPrivate *qt_allocRegionMemory()
+{
+ QRegionPrivate *rv = 0;
+ qt_nextRegionLock.lock();
+
+ if(qt_nextRegionPtr) {
+ rv = qt_nextRegionPtr;
+ qt_nextRegionPtr = rv->next;
+ } else {
+ qt_nextRegionPtr =
+ (QRegionPrivate *)malloc(256 * sizeof(QRegionPrivate));
+ for(int ii = 0; ii < 256; ++ii) {
+ if(ii == 255) {
+ qt_nextRegionPtr[ii].next = 0;
+ } else {
+ qt_nextRegionPtr[ii].next = &qt_nextRegionPtr[ii + 1];
+ }
+ }
+
+ rv = qt_nextRegionPtr;
+ qt_nextRegionPtr = rv->next;
+ }
+
+ qt_nextRegionLock.unlock();
+ return rv;
+}
+
+static void qt_freeRegionMemory(QRegionPrivate *rp)
+{
+ qt_nextRegionLock.lock();
+ rp->next = qt_nextRegionPtr;
+ qt_nextRegionPtr = rp;
+ qt_nextRegionLock.unlock();
+}
+
+static QRegionPrivate *qt_allocRegion()
+{
+ QRegionPrivate *mem = qt_allocRegionMemory();
+ return new (mem) QRegionPrivate;
+}
+
+static QRegionPrivate *qt_allocRegion(const QRect &r)
+{
+ QRegionPrivate *mem = qt_allocRegionMemory();
+ return new (mem) QRegionPrivate(r);
+}
+
+static QRegionPrivate *qt_allocRegion(const QRegionPrivate &r)
+{
+ QRegionPrivate *mem = qt_allocRegionMemory();
+ return new (mem) QRegionPrivate(r);
+}
+
+void qt_freeRegion(QRegionPrivate *rp)
+{
+ rp->~QRegionPrivate();
+ qt_freeRegionMemory(rp);
+// delete rp;
+}
+
+static inline bool isEmptyHelper(const QRegionPrivate *preg)
+{
+ return !preg || preg->numRects == 0;
+}
+
+void QRegionPrivate::append(const QRegionPrivate *r)
+{
+ Q_ASSERT(!isEmptyHelper(r));
+
+ vector();
+ QRect *destRect = rects.data() + numRects;
+ const QRect *srcRect = (r->mode==Vector)?r->rects.constData():&r->single;
+ int numAppend = r->numRects;
+
+ // test for merge in x direction
+ {
+ const QRect *rFirst = srcRect;
+ QRect *myLast = rects.data() + (numRects - 1);
+ if (rFirst->top() == myLast->top()
+ && rFirst->height() == myLast->height()
+ && rFirst->left() == (myLast->right() + 1))
+ {
+ myLast->setWidth(myLast->width() + rFirst->width());
+ updateInnerRect(*myLast);
+ ++srcRect;
+ --numAppend;
+ }
+ }
+
+ // append rectangles
+ const int newNumRects = numRects + numAppend;
+ if (newNumRects > rects.size()) {
+ rects.resize(newNumRects);
+ destRect = rects.data() + numRects;
+ }
+ memcpy(destRect, srcRect, numAppend * sizeof(QRect));
+
+ // update inner rectangle
+ if (innerArea < r->innerArea) {
+ innerArea = r->innerArea;
+ innerRect = r->innerRect;
+ }
+
+ // update extents
+ destRect = &extents;
+ srcRect = &r->extents;
+ extents.setCoords(qMin(destRect->left(), srcRect->left()),
+ qMin(destRect->top(), srcRect->top()),
+ qMax(destRect->right(), srcRect->right()),
+ qMax(destRect->bottom(), srcRect->bottom()));
+
+ numRects = newNumRects;
+}
+
+void QRegionPrivate::prepend(const QRegionPrivate *r)
+{
+#if 1
+ Q_UNUSED(r);
+#else
+ // XXX ak: does not respect vectorization of region
+
+ Q_ASSERT(!isEmpty(r));
+
+ // move existing rectangles
+ memmove(rects.data() + r->numRects, rects.constData(),
+ numRects * sizeof(QRect));
+
+ // prepend new rectangles
+ memcpy(rects.data(), r->rects.constData(), r->numRects * sizeof(QRect));
+
+ // update inner rectangle
+ if (innerArea < r->innerArea) {
+ innerArea = r->innerArea;
+ innerRect = r->innerRect;
+ }
+
+ // update extents
+ destRect = &extents;
+ srcRect = &r->extents;
+ extents.setCoords(qMin(destRect->left(), srcRect->left()),
+ qMin(destRect->top(), srcRect->top()),
+ qMax(destRect->right(), srcRect->right()),
+ qMax(destRect->bottom(), srcRect->bottom()));
+
+ numRects = newNumRects;
+#endif
+}
+
+bool QRegionPrivate::canAppend(const QRegionPrivate *r) const
+{
+ Q_ASSERT(!isEmptyHelper(r));
+
+ const QRect *rFirst = (r->mode==Vector)?r->rects.constData():&r->single;
+ const QRect *myLast = (mode==Vector)?(rects.constData() + (numRects - 1)):&single;
+ // XXX: possible improvements:
+ // - nFirst->top() == myLast->bottom() + 1, must possibly merge bands
+ if (rFirst->top() > (myLast->bottom() + 1)
+ || (rFirst->top() == myLast->top()
+ && rFirst->height() == myLast->height()
+ && rFirst->left() > myLast->right()))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+bool QRegionPrivate::canPrepend(const QRegionPrivate *r) const
+{
+#if 1
+ Q_UNUSED(r);
+ return false;
+#else
+ return r->canAppend(this);
+#endif
+}
+
+#if defined(Q_WS_X11)
+QT_BEGIN_INCLUDE_NAMESPACE
+# include "qregion_x11.cpp"
+QT_END_INCLUDE_NAMESPACE
+#elif defined(Q_WS_MAC)
+QT_BEGIN_INCLUDE_NAMESPACE
+# include "qregion_mac.cpp"
+QT_END_INCLUDE_NAMESPACE
+#elif defined(Q_WS_QWS)
+static QRegionPrivate qrp;
+QRegion::QRegionData QRegion::shared_empty = {Q_BASIC_ATOMIC_INITIALIZER(1), &qrp};
+#endif
+
+typedef void (*OverlapFunc)(register QRegionPrivate &dest, register const QRect *r1, const QRect *r1End,
+ register const QRect *r2, const QRect *r2End, register int y1, register int y2);
+typedef void (*NonOverlapFunc)(register QRegionPrivate &dest, register const QRect *r, const QRect *rEnd,
+ register int y1, register int y2);
+
+static bool EqualRegion(const QRegionPrivate *r1, const QRegionPrivate *r2);
+static void UnionRegion(const QRegionPrivate *reg1, const QRegionPrivate *reg2, QRegionPrivate &dest);
+static void miRegionOp(register QRegionPrivate &dest, const QRegionPrivate *reg1, const QRegionPrivate *reg2,
+ OverlapFunc overlapFunc, NonOverlapFunc nonOverlap1Func,
+ NonOverlapFunc nonOverlap2Func);
+
+#define RectangleOut 0
+#define RectangleIn 1
+#define RectanglePart 2
+#define EvenOddRule 0
+#define WindingRule 1
+
+// START OF region.h extract
+/* $XConsortium: region.h,v 11.14 94/04/17 20:22:20 rws Exp $ */
+/************************************************************************
+
+Copyright (c) 1987 X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+
+
+Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Digital not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+************************************************************************/
+
+#ifndef _XREGION_H
+#define _XREGION_H
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <limits.h>
+QT_END_INCLUDE_NAMESPACE
+
+/* 1 if two BOXs overlap.
+ * 0 if two BOXs do not overlap.
+ * Remember, x2 and y2 are not in the region
+ */
+#define EXTENTCHECK(r1, r2) \
+ ((r1)->right() >= (r2)->left() && \
+ (r1)->left() <= (r2)->right() && \
+ (r1)->bottom() >= (r2)->top() && \
+ (r1)->top() <= (r2)->bottom())
+
+/*
+ * update region extents
+ */
+#define EXTENTS(r,idRect){\
+ if((r)->left() < (idRect)->extents.left())\
+ (idRect)->extents.setLeft((r)->left());\
+ if((r)->top() < (idRect)->extents.top())\
+ (idRect)->extents.setTop((r)->top());\
+ if((r)->right() > (idRect)->extents.right())\
+ (idRect)->extents.setRight((r)->right());\
+ if((r)->bottom() > (idRect)->extents.bottom())\
+ (idRect)->extents.setBottom((r)->bottom());\
+ }
+
+/*
+ * Check to see if there is enough memory in the present region.
+ */
+#define MEMCHECK(dest, rect, firstrect){\
+ if ((dest).numRects >= ((dest).rects.size()-1)){\
+ firstrect.resize(firstrect.size() * 2); \
+ (rect) = (firstrect).data() + (dest).numRects;\
+ }\
+ }
+
+
+/*
+ * number of points to buffer before sending them off
+ * to scanlines(): Must be an even number
+ */
+#define NUMPTSTOBUFFER 200
+
+/*
+ * used to allocate buffers for points and link
+ * the buffers together
+ */
+typedef struct _POINTBLOCK {
+ QPoint pts[NUMPTSTOBUFFER];
+ struct _POINTBLOCK *next;
+} POINTBLOCK;
+
+#endif
+// END OF region.h extract
+
+// START OF Region.c extract
+/* $XConsortium: Region.c /main/30 1996/10/22 14:21:24 kaleb $ */
+/************************************************************************
+
+Copyright (c) 1987, 1988 X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+
+
+Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Digital not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+************************************************************************/
+/*
+ * The functions in this file implement the Region abstraction, similar to one
+ * used in the X11 sample server. A Region is simply an area, as the name
+ * implies, and is implemented as a "y-x-banded" array of rectangles. To
+ * explain: Each Region is made up of a certain number of rectangles sorted
+ * by y coordinate first, and then by x coordinate.
+ *
+ * Furthermore, the rectangles are banded such that every rectangle with a
+ * given upper-left y coordinate (y1) will have the same lower-right y
+ * coordinate (y2) and vice versa. If a rectangle has scanlines in a band, it
+ * will span the entire vertical distance of the band. This means that some
+ * areas that could be merged into a taller rectangle will be represented as
+ * several shorter rectangles to account for shorter rectangles to its left
+ * or right but within its "vertical scope".
+ *
+ * An added constraint on the rectangles is that they must cover as much
+ * horizontal area as possible. E.g. no two rectangles in a band are allowed
+ * to touch.
+ *
+ * Whenever possible, bands will be merged together to cover a greater vertical
+ * distance (and thus reduce the number of rectangles). Two bands can be merged
+ * only if the bottom of one touches the top of the other and they have
+ * rectangles in the same places (of the same width, of course). This maintains
+ * the y-x-banding that's so nice to have...
+ */
+/* $XFree86: xc/lib/X11/Region.c,v 1.1.1.2.2.2 1998/10/04 15:22:50 hohndel Exp $ */
+
+static void UnionRectWithRegion(register const QRect *rect, const QRegionPrivate *source,
+ QRegionPrivate &dest)
+{
+ if (!rect->width() || !rect->height())
+ return;
+
+ QRegionPrivate region(*rect);
+
+ Q_ASSERT(EqualRegion(source, &dest));
+ Q_ASSERT(!isEmptyHelper(&region));
+
+ if (dest.numRects == 0)
+ dest = region;
+ else if (dest.canAppend(&region))
+ dest.append(&region);
+ else
+ UnionRegion(&region, source, dest);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * miSetExtents --
+ * Reset the extents and innerRect of a region to what they should be.
+ * Called by miSubtract and miIntersect b/c they can't figure it out
+ * along the way or do so easily, as miUnion can.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The region's 'extents' and 'innerRect' structure is overwritten.
+ *
+ *-----------------------------------------------------------------------
+ */
+static void miSetExtents(QRegionPrivate &dest)
+{
+ register const QRect *pBox,
+ *pBoxEnd;
+ register QRect *pExtents;
+
+ dest.innerRect.setCoords(0, 0, -1, -1);
+ dest.innerArea = -1;
+ if (dest.numRects == 0) {
+ dest.extents.setCoords(0, 0, 0, 0);
+ return;
+ }
+
+ pExtents = &dest.extents;
+ pBox = (dest.mode==QRegionPrivate::Vector)?(dest.rects.constData()):(&dest.single);
+ pBoxEnd = (dest.mode==QRegionPrivate::Vector)?(&pBox[dest.numRects - 1]):(&dest.single);
+
+ /*
+ * Since pBox is the first rectangle in the region, it must have the
+ * smallest y1 and since pBoxEnd is the last rectangle in the region,
+ * it must have the largest y2, because of banding. Initialize x1 and
+ * x2 from pBox and pBoxEnd, resp., as good things to initialize them
+ * to...
+ */
+ pExtents->setLeft(pBox->left());
+ pExtents->setTop(pBox->top());
+ pExtents->setRight(pBoxEnd->right());
+ pExtents->setBottom(pBoxEnd->bottom());
+
+ Q_ASSERT(pExtents->top() <= pExtents->bottom());
+ while (pBox <= pBoxEnd) {
+ if (pBox->left() < pExtents->left())
+ pExtents->setLeft(pBox->left());
+ if (pBox->right() > pExtents->right())
+ pExtents->setRight(pBox->right());
+ dest.updateInnerRect(*pBox);
+ ++pBox;
+ }
+ Q_ASSERT(pExtents->left() <= pExtents->right());
+}
+
+/* TranslateRegion(pRegion, x, y)
+ translates in place
+ added by raymond
+*/
+
+static void OffsetRegion(register QRegionPrivate &region, register int x, register int y)
+{
+ register int nbox;
+ register QRect *pbox;
+
+ if(region.mode == QRegionPrivate::Single) {
+ region.single.translate(x, y);
+ } else {
+ pbox = region.rects.data();
+ nbox = region.numRects;
+
+ while (nbox--) {
+ pbox->translate(x, y);
+ ++pbox;
+ }
+ }
+ region.extents.translate(x, y);
+ region.innerRect.translate(x, y);
+}
+
+/*======================================================================
+ * Region Intersection
+ *====================================================================*/
+/*-
+ *-----------------------------------------------------------------------
+ * miIntersectO --
+ * Handle an overlapping band for miIntersect.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Rectangles may be added to the region.
+ *
+ *-----------------------------------------------------------------------
+ */
+static void miIntersectO(register QRegionPrivate &dest, register const QRect *r1, const QRect *r1End,
+ register const QRect *r2, const QRect *r2End, int y1, int y2)
+{
+ register int x1;
+ register int x2;
+ register QRect *pNextRect;
+
+ pNextRect = dest.rects.data() + dest.numRects;
+
+ while (r1 != r1End && r2 != r2End) {
+ x1 = qMax(r1->left(), r2->left());
+ x2 = qMin(r1->right(), r2->right());
+
+ /*
+ * If there's any overlap between the two rectangles, add that
+ * overlap to the new region.
+ * There's no need to check for subsumption because the only way
+ * such a need could arise is if some region has two rectangles
+ * right next to each other. Since that should never happen...
+ */
+ if (x1 <= x2) {
+ Q_ASSERT(y1 <= y2);
+ MEMCHECK(dest, pNextRect, dest.rects)
+ pNextRect->setCoords(x1, y1, x2, y2);
+ ++dest.numRects;
+ ++pNextRect;
+ }
+
+ /*
+ * Need to advance the pointers. Shift the one that extends
+ * to the right the least, since the other still has a chance to
+ * overlap with that region's next rectangle, if you see what I mean.
+ */
+ if (r1->right() < r2->right()) {
+ ++r1;
+ } else if (r2->right() < r1->right()) {
+ ++r2;
+ } else {
+ ++r1;
+ ++r2;
+ }
+ }
+}
+
+/*======================================================================
+ * Generic Region Operator
+ *====================================================================*/
+
+/*-
+ *-----------------------------------------------------------------------
+ * miCoalesce --
+ * Attempt to merge the boxes in the current band with those in the
+ * previous one. Used only by miRegionOp.
+ *
+ * Results:
+ * The new index for the previous band.
+ *
+ * Side Effects:
+ * If coalescing takes place:
+ * - rectangles in the previous band will have their y2 fields
+ * altered.
+ * - dest.numRects will be decreased.
+ *
+ *-----------------------------------------------------------------------
+ */
+static int miCoalesce(register QRegionPrivate &dest, int prevStart, int curStart)
+{
+ register QRect *pPrevBox; /* Current box in previous band */
+ register QRect *pCurBox; /* Current box in current band */
+ register QRect *pRegEnd; /* End of region */
+ int curNumRects; /* Number of rectangles in current band */
+ int prevNumRects; /* Number of rectangles in previous band */
+ int bandY1; /* Y1 coordinate for current band */
+ QRect *rData = dest.rects.data();
+
+ pRegEnd = rData + dest.numRects;
+
+ pPrevBox = rData + prevStart;
+ prevNumRects = curStart - prevStart;
+
+ /*
+ * Figure out how many rectangles are in the current band. Have to do
+ * this because multiple bands could have been added in miRegionOp
+ * at the end when one region has been exhausted.
+ */
+ pCurBox = rData + curStart;
+ bandY1 = pCurBox->top();
+ for (curNumRects = 0; pCurBox != pRegEnd && pCurBox->top() == bandY1; ++curNumRects) {
+ ++pCurBox;
+ }
+
+ if (pCurBox != pRegEnd) {
+ /*
+ * If more than one band was added, we have to find the start
+ * of the last band added so the next coalescing job can start
+ * at the right place... (given when multiple bands are added,
+ * this may be pointless -- see above).
+ */
+ --pRegEnd;
+ while ((pRegEnd - 1)->top() == pRegEnd->top())
+ --pRegEnd;
+ curStart = pRegEnd - rData;
+ pRegEnd = rData + dest.numRects;
+ }
+
+ if (curNumRects == prevNumRects && curNumRects != 0) {
+ pCurBox -= curNumRects;
+ /*
+ * The bands may only be coalesced if the bottom of the previous
+ * matches the top scanline of the current.
+ */
+ if (pPrevBox->bottom() == pCurBox->top() - 1) {
+ /*
+ * Make sure the bands have boxes in the same places. This
+ * assumes that boxes have been added in such a way that they
+ * cover the most area possible. I.e. two boxes in a band must
+ * have some horizontal space between them.
+ */
+ do {
+ if (pPrevBox->left() != pCurBox->left() || pPrevBox->right() != pCurBox->right()) {
+ // The bands don't line up so they can't be coalesced.
+ return curStart;
+ }
+ ++pPrevBox;
+ ++pCurBox;
+ --prevNumRects;
+ } while (prevNumRects != 0);
+
+ dest.numRects -= curNumRects;
+ pCurBox -= curNumRects;
+ pPrevBox -= curNumRects;
+
+ /*
+ * The bands may be merged, so set the bottom y of each box
+ * in the previous band to that of the corresponding box in
+ * the current band.
+ */
+ do {
+ pPrevBox->setBottom(pCurBox->bottom());
+ dest.updateInnerRect(*pPrevBox);
+ ++pPrevBox;
+ ++pCurBox;
+ curNumRects -= 1;
+ } while (curNumRects != 0);
+
+ /*
+ * If only one band was added to the region, we have to backup
+ * curStart to the start of the previous band.
+ *
+ * If more than one band was added to the region, copy the
+ * other bands down. The assumption here is that the other bands
+ * came from the same region as the current one and no further
+ * coalescing can be done on them since it's all been done
+ * already... curStart is already in the right place.
+ */
+ if (pCurBox == pRegEnd) {
+ curStart = prevStart;
+ } else {
+ do {
+ *pPrevBox++ = *pCurBox++;
+ dest.updateInnerRect(*pPrevBox);
+ } while (pCurBox != pRegEnd);
+ }
+ }
+ }
+ return curStart;
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * miRegionOp --
+ * Apply an operation to two regions. Called by miUnion, miInverse,
+ * miSubtract, miIntersect...
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The new region is overwritten.
+ *
+ * Notes:
+ * The idea behind this function is to view the two regions as sets.
+ * Together they cover a rectangle of area that this function divides
+ * into horizontal bands where points are covered only by one region
+ * or by both. For the first case, the nonOverlapFunc is called with
+ * each the band and the band's upper and lower extents. For the
+ * second, the overlapFunc is called to process the entire band. It
+ * is responsible for clipping the rectangles in the band, though
+ * this function provides the boundaries.
+ * At the end of each band, the new region is coalesced, if possible,
+ * to reduce the number of rectangles in the region.
+ *
+ *-----------------------------------------------------------------------
+ */
+static void miRegionOp(register QRegionPrivate &dest, const QRegionPrivate *reg1, const QRegionPrivate *reg2,
+ OverlapFunc overlapFunc, NonOverlapFunc nonOverlap1Func,
+ NonOverlapFunc nonOverlap2Func)
+{
+ register const QRect *r1; // Pointer into first region
+ register const QRect *r2; // Pointer into 2d region
+ const QRect *r1End; // End of 1st region
+ const QRect *r2End; // End of 2d region
+ register int ybot; // Bottom of intersection
+ register int ytop; // Top of intersection
+ int prevBand; // Index of start of previous band in dest
+ int curBand; // Index of start of current band in dest
+ register const QRect *r1BandEnd; // End of current band in r1
+ register const QRect *r2BandEnd; // End of current band in r2
+ int top; // Top of non-overlapping band
+ int bot; // Bottom of non-overlapping band
+
+ /*
+ * Initialization:
+ * set r1, r2, r1End and r2End appropriately, preserve the important
+ * parts of the destination region until the end in case it's one of
+ * the two source regions, then mark the "new" region empty, allocating
+ * another array of rectangles for it to use.
+ */
+ r1 = (reg1->mode==QRegionPrivate::Vector)?reg1->rects.data():&reg1->single;
+ r2 = (reg2->mode==QRegionPrivate::Vector)?reg2->rects.data():&reg2->single;
+ r1End = r1 + reg1->numRects;
+ r2End = r2 + reg2->numRects;
+
+ dest.vector();
+ QVector<QRect> oldRects = dest.rects;
+
+ dest.numRects = 0;
+
+ /*
+ * Allocate a reasonable number of rectangles for the new region. The idea
+ * is to allocate enough so the individual functions don't need to
+ * reallocate and copy the array, which is time consuming, yet we don't
+ * have to worry about using too much memory. I hope to be able to
+ * nuke the realloc() at the end of this function eventually.
+ */
+ dest.rects.resize(qMax(reg1->numRects,reg2->numRects) * 2);
+
+ /*
+ * Initialize ybot and ytop.
+ * In the upcoming loop, ybot and ytop serve different functions depending
+ * on whether the band being handled is an overlapping or non-overlapping
+ * band.
+ * In the case of a non-overlapping band (only one of the regions
+ * has points in the band), ybot is the bottom of the most recent
+ * intersection and thus clips the top of the rectangles in that band.
+ * ytop is the top of the next intersection between the two regions and
+ * serves to clip the bottom of the rectangles in the current band.
+ * For an overlapping band (where the two regions intersect), ytop clips
+ * the top of the rectangles of both regions and ybot clips the bottoms.
+ */
+ if (reg1->extents.top() < reg2->extents.top())
+ ybot = reg1->extents.top() - 1;
+ else
+ ybot = reg2->extents.top() - 1;
+
+ /*
+ * prevBand serves to mark the start of the previous band so rectangles
+ * can be coalesced into larger rectangles. qv. miCoalesce, above.
+ * In the beginning, there is no previous band, so prevBand == curBand
+ * (curBand is set later on, of course, but the first band will always
+ * start at index 0). prevBand and curBand must be indices because of
+ * the possible expansion, and resultant moving, of the new region's
+ * array of rectangles.
+ */
+ prevBand = 0;
+
+ do {
+ curBand = dest.numRects;
+
+ /*
+ * This algorithm proceeds one source-band (as opposed to a
+ * destination band, which is determined by where the two regions
+ * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the
+ * rectangle after the last one in the current band for their
+ * respective regions.
+ */
+ r1BandEnd = r1;
+ while (r1BandEnd != r1End && r1BandEnd->top() == r1->top())
+ ++r1BandEnd;
+
+ r2BandEnd = r2;
+ while (r2BandEnd != r2End && r2BandEnd->top() == r2->top())
+ ++r2BandEnd;
+
+ /*
+ * First handle the band that doesn't intersect, if any.
+ *
+ * Note that attention is restricted to one band in the
+ * non-intersecting region at once, so if a region has n
+ * bands between the current position and the next place it overlaps
+ * the other, this entire loop will be passed through n times.
+ */
+ if (r1->top() < r2->top()) {
+ top = qMax(r1->top(), ybot + 1);
+ bot = qMin(r1->bottom(), r2->top() - 1);
+
+ if (nonOverlap1Func != 0 && bot >= top)
+ (*nonOverlap1Func)(dest, r1, r1BandEnd, top, bot);
+ ytop = r2->top();
+ } else if (r2->top() < r1->top()) {
+ top = qMax(r2->top(), ybot + 1);
+ bot = qMin(r2->bottom(), r1->top() - 1);
+
+ if (nonOverlap2Func != 0 && bot >= top)
+ (*nonOverlap2Func)(dest, r2, r2BandEnd, top, bot);
+ ytop = r1->top();
+ } else {
+ ytop = r1->top();
+ }
+
+ /*
+ * If any rectangles got added to the region, try and coalesce them
+ * with rectangles from the previous band. Note we could just do
+ * this test in miCoalesce, but some machines incur a not
+ * inconsiderable cost for function calls, so...
+ */
+ if (dest.numRects != curBand)
+ prevBand = miCoalesce(dest, prevBand, curBand);
+
+ /*
+ * Now see if we've hit an intersecting band. The two bands only
+ * intersect if ybot >= ytop
+ */
+ ybot = qMin(r1->bottom(), r2->bottom());
+ curBand = dest.numRects;
+ if (ybot >= ytop)
+ (*overlapFunc)(dest, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot);
+
+ if (dest.numRects != curBand)
+ prevBand = miCoalesce(dest, prevBand, curBand);
+
+ /*
+ * If we've finished with a band (y2 == ybot) we skip forward
+ * in the region to the next band.
+ */
+ if (r1->bottom() == ybot)
+ r1 = r1BandEnd;
+ if (r2->bottom() == ybot)
+ r2 = r2BandEnd;
+ } while (r1 != r1End && r2 != r2End);
+
+ /*
+ * Deal with whichever region still has rectangles left.
+ */
+ curBand = dest.numRects;
+ if (r1 != r1End) {
+ if (nonOverlap1Func != 0) {
+ do {
+ r1BandEnd = r1;
+ while (r1BandEnd < r1End && r1BandEnd->top() == r1->top())
+ ++r1BandEnd;
+ (*nonOverlap1Func)(dest, r1, r1BandEnd, qMax(r1->top(), ybot + 1), r1->bottom());
+ r1 = r1BandEnd;
+ } while (r1 != r1End);
+ }
+ } else if ((r2 != r2End) && (nonOverlap2Func != 0)) {
+ do {
+ r2BandEnd = r2;
+ while (r2BandEnd < r2End && r2BandEnd->top() == r2->top())
+ ++r2BandEnd;
+ (*nonOverlap2Func)(dest, r2, r2BandEnd, qMax(r2->top(), ybot + 1), r2->bottom());
+ r2 = r2BandEnd;
+ } while (r2 != r2End);
+ }
+
+ if (dest.numRects != curBand)
+ (void)miCoalesce(dest, prevBand, curBand);
+
+ /*
+ * A bit of cleanup. To keep regions from growing without bound,
+ * we shrink the array of rectangles to match the new number of
+ * rectangles in the region.
+ *
+ * Only do this stuff if the number of rectangles allocated is more than
+ * twice the number of rectangles in the region (a simple optimization).
+ */
+ if (qMax(4, dest.numRects) < (dest.rects.size() >> 1))
+ dest.rects.resize(dest.numRects);
+}
+
+/*======================================================================
+ * Region Union
+ *====================================================================*/
+
+/*-
+ *-----------------------------------------------------------------------
+ * miUnionNonO --
+ * Handle a non-overlapping band for the union operation. Just
+ * Adds the rectangles into the region. Doesn't have to check for
+ * subsumption or anything.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * dest.numRects is incremented and the final rectangles overwritten
+ * with the rectangles we're passed.
+ *
+ *-----------------------------------------------------------------------
+ */
+
+static void miUnionNonO(register QRegionPrivate &dest, register const QRect *r, const QRect *rEnd,
+ register int y1, register int y2)
+{
+ register QRect *pNextRect;
+
+ pNextRect = dest.rects.data() + dest.numRects;
+
+ Q_ASSERT(y1 <= y2);
+
+ while (r != rEnd) {
+ Q_ASSERT(r->left() <= r->right());
+ MEMCHECK(dest, pNextRect, dest.rects)
+ pNextRect->setCoords(r->left(), y1, r->right(), y2);
+ dest.numRects++;
+ ++pNextRect;
+ ++r;
+ }
+}
+
+
+/*-
+ *-----------------------------------------------------------------------
+ * miUnionO --
+ * Handle an overlapping band for the union operation. Picks the
+ * left-most rectangle each time and merges it into the region.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Rectangles are overwritten in dest.rects and dest.numRects will
+ * be changed.
+ *
+ *-----------------------------------------------------------------------
+ */
+
+static void miUnionO(register QRegionPrivate &dest, register const QRect *r1, const QRect *r1End,
+ register const QRect *r2, const QRect *r2End, register int y1, register int y2)
+{
+ register QRect *pNextRect;
+
+ pNextRect = dest.rects.data() + dest.numRects;
+
+#define MERGERECT(r) \
+ if ((dest.numRects != 0) && \
+ (pNextRect[-1].top() == y1) && \
+ (pNextRect[-1].bottom() == y2) && \
+ (pNextRect[-1].right() >= r->left()-1)) { \
+ if (pNextRect[-1].right() < r->right()) { \
+ pNextRect[-1].setRight(r->right()); \
+ dest.updateInnerRect(pNextRect[-1]); \
+ Q_ASSERT(pNextRect[-1].left() <= pNextRect[-1].right()); \
+ } \
+ } else { \
+ MEMCHECK(dest, pNextRect, dest.rects) \
+ pNextRect->setCoords(r->left(), y1, r->right(), y2); \
+ dest.updateInnerRect(*pNextRect); \
+ dest.numRects++; \
+ pNextRect++; \
+ } \
+ r++;
+
+ Q_ASSERT(y1 <= y2);
+ while (r1 != r1End && r2 != r2End) {
+ if (r1->left() < r2->left()) {
+ MERGERECT(r1)
+ } else {
+ MERGERECT(r2)
+ }
+ }
+
+ if (r1 != r1End) {
+ do {
+ MERGERECT(r1)
+ } while (r1 != r1End);
+ } else {
+ while (r2 != r2End) {
+ MERGERECT(r2)
+ }
+ }
+}
+
+static void UnionRegion(const QRegionPrivate *reg1, const QRegionPrivate *reg2, QRegionPrivate &dest)
+{
+ Q_ASSERT(!isEmptyHelper(reg1) && !isEmptyHelper(reg2));
+ Q_ASSERT(!reg1->contains(*reg2));
+ Q_ASSERT(!reg2->contains(*reg1));
+ Q_ASSERT(!EqualRegion(reg1, reg2));
+ Q_ASSERT(!reg1->canAppend(reg2));
+ Q_ASSERT(!reg2->canAppend(reg1));
+
+ if (reg1->innerArea > reg2->innerArea) {
+ dest.innerArea = reg1->innerArea;
+ dest.innerRect = reg1->innerRect;
+ } else {
+ dest.innerArea = reg2->innerArea;
+ dest.innerRect = reg2->innerRect;
+ }
+ miRegionOp(dest, reg1, reg2, miUnionO, miUnionNonO, miUnionNonO);
+
+ dest.extents.setCoords(qMin(reg1->extents.left(), reg2->extents.left()),
+ qMin(reg1->extents.top(), reg2->extents.top()),
+ qMax(reg1->extents.right(), reg2->extents.right()),
+ qMax(reg1->extents.bottom(), reg2->extents.bottom()));
+}
+
+/*======================================================================
+ * Region Subtraction
+ *====================================================================*/
+
+/*-
+ *-----------------------------------------------------------------------
+ * miSubtractNonO --
+ * Deal with non-overlapping band for subtraction. Any parts from
+ * region 2 we discard. Anything from region 1 we add to the region.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * dest may be affected.
+ *
+ *-----------------------------------------------------------------------
+ */
+
+static void miSubtractNonO1(register QRegionPrivate &dest, register const QRect *r,
+ const QRect *rEnd, register int y1, register int y2)
+{
+ register QRect *pNextRect;
+
+ pNextRect = dest.rects.data() + dest.numRects;
+
+ Q_ASSERT(y1<=y2);
+
+ while (r != rEnd) {
+ Q_ASSERT(r->left() <= r->right());
+ MEMCHECK(dest, pNextRect, dest.rects)
+ pNextRect->setCoords(r->left(), y1, r->right(), y2);
+ ++dest.numRects;
+ ++pNextRect;
+ ++r;
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * miSubtractO --
+ * Overlapping band subtraction. x1 is the left-most point not yet
+ * checked.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * dest may have rectangles added to it.
+ *
+ *-----------------------------------------------------------------------
+ */
+
+static void miSubtractO(register QRegionPrivate &dest, register const QRect *r1, const QRect *r1End,
+ register const QRect *r2, const QRect *r2End, register int y1, register int y2)
+{
+ register QRect *pNextRect;
+ register int x1;
+
+ x1 = r1->left();
+
+ Q_ASSERT(y1 <= y2);
+ pNextRect = dest.rects.data() + dest.numRects;
+
+ while (r1 != r1End && r2 != r2End) {
+ if (r2->right() < x1) {
+ /*
+ * Subtrahend missed the boat: go to next subtrahend.
+ */
+ ++r2;
+ } else if (r2->left() <= x1) {
+ /*
+ * Subtrahend precedes minuend: nuke left edge of minuend.
+ */
+ x1 = r2->right() + 1;
+ if (x1 > r1->right()) {
+ /*
+ * Minuend completely covered: advance to next minuend and
+ * reset left fence to edge of new minuend.
+ */
+ ++r1;
+ if (r1 != r1End)
+ x1 = r1->left();
+ } else {
+ // Subtrahend now used up since it doesn't extend beyond minuend
+ ++r2;
+ }
+ } else if (r2->left() <= r1->right()) {
+ /*
+ * Left part of subtrahend covers part of minuend: add uncovered
+ * part of minuend to region and skip to next subtrahend.
+ */
+ Q_ASSERT(x1 < r2->left());
+ MEMCHECK(dest, pNextRect, dest.rects)
+ pNextRect->setCoords(x1, y1, r2->left() - 1, y2);
+ ++dest.numRects;
+ ++pNextRect;
+
+ x1 = r2->right() + 1;
+ if (x1 > r1->right()) {
+ /*
+ * Minuend used up: advance to new...
+ */
+ ++r1;
+ if (r1 != r1End)
+ x1 = r1->left();
+ } else {
+ // Subtrahend used up
+ ++r2;
+ }
+ } else {
+ /*
+ * Minuend used up: add any remaining piece before advancing.
+ */
+ if (r1->right() >= x1) {
+ MEMCHECK(dest, pNextRect, dest.rects)
+ pNextRect->setCoords(x1, y1, r1->right(), y2);
+ ++dest.numRects;
+ ++pNextRect;
+ }
+ ++r1;
+ if (r1 != r1End)
+ x1 = r1->left();
+ }
+ }
+
+ /*
+ * Add remaining minuend rectangles to region.
+ */
+ while (r1 != r1End) {
+ Q_ASSERT(x1 <= r1->right());
+ MEMCHECK(dest, pNextRect, dest.rects)
+ pNextRect->setCoords(x1, y1, r1->right(), y2);
+ ++dest.numRects;
+ ++pNextRect;
+
+ ++r1;
+ if (r1 != r1End)
+ x1 = r1->left();
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * miSubtract --
+ * Subtract regS from regM and leave the result in regD.
+ * S stands for subtrahend, M for minuend and D for difference.
+ *
+ * Side Effects:
+ * regD is overwritten.
+ *
+ *-----------------------------------------------------------------------
+ */
+
+static void SubtractRegion(QRegionPrivate *regM, QRegionPrivate *regS,
+ register QRegionPrivate &dest)
+{
+ Q_ASSERT(!isEmptyHelper(regM));
+ Q_ASSERT(!isEmptyHelper(regS));
+ Q_ASSERT(EXTENTCHECK(&regM->extents, &regS->extents));
+ Q_ASSERT(!regS->contains(*regM));
+ Q_ASSERT(!EqualRegion(regM, regS));
+
+ miRegionOp(dest, regM, regS, miSubtractO, miSubtractNonO1, 0);
+
+ /*
+ * Can't alter dest's extents before we call miRegionOp because
+ * it might be one of the source regions and miRegionOp depends
+ * on the extents of those regions being the unaltered. Besides, this
+ * way there's no checking against rectangles that will be nuked
+ * due to coalescing, so we have to examine fewer rectangles.
+ */
+ miSetExtents(dest);
+}
+
+static void XorRegion(QRegionPrivate *sra, QRegionPrivate *srb, QRegionPrivate &dest)
+{
+ Q_ASSERT(!isEmptyHelper(sra) && !isEmptyHelper(srb));
+ Q_ASSERT(EXTENTCHECK(&sra->extents, &srb->extents));
+ Q_ASSERT(!EqualRegion(sra, srb));
+
+ QRegionPrivate tra, trb;
+
+ if (!srb->contains(*sra))
+ SubtractRegion(sra, srb, tra);
+ if (!sra->contains(*srb))
+ SubtractRegion(srb, sra, trb);
+
+ Q_ASSERT(isEmptyHelper(&trb) || !tra.contains(trb));
+ Q_ASSERT(isEmptyHelper(&tra) || !trb.contains(tra));
+
+ if (isEmptyHelper(&tra)) {
+ dest = trb;
+ } else if (isEmptyHelper(&trb)) {
+ dest = tra;
+ } else if (tra.canAppend(&trb)) {
+ dest = tra;
+ dest.append(&trb);
+ } else if (trb.canAppend(&tra)) {
+ dest = trb;
+ dest.append(&tra);
+ } else {
+ UnionRegion(&tra, &trb, dest);
+ }
+}
+
+/*
+ * Check to see if two regions are equal
+ */
+static bool EqualRegion(const QRegionPrivate *r1, const QRegionPrivate *r2)
+{
+ if (r1->numRects != r2->numRects) {
+ return false;
+ } else if (r1->numRects == 0) {
+ return true;
+ } else if (r1->extents != r2->extents) {
+ return false;
+ } else if (r1->mode == QRegionPrivate::Single && r2->mode == QRegionPrivate::Single) {
+ return r1->single == r2->single;
+ } else {
+ const QRect *rr1 = (r1->mode==QRegionPrivate::Vector)?r1->rects.constData():&r1->single;
+ const QRect *rr2 = (r2->mode==QRegionPrivate::Vector)?r2->rects.constData():&r2->single;
+ for (int i = 0; i < r1->numRects; ++i, ++rr1, ++rr2) {
+ if (*rr1 != *rr2)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool PointInRegion(QRegionPrivate *pRegion, int x, int y)
+{
+ int i;
+
+ if (pRegion->mode == QRegionPrivate::Single)
+ return pRegion->single.contains(x, y);
+ if (isEmptyHelper(pRegion))
+ return false;
+ if (!pRegion->extents.contains(x, y))
+ return false;
+ if (pRegion->innerRect.contains(x, y))
+ return true;
+ for (i = 0; i < pRegion->numRects; ++i) {
+ if (pRegion->rects[i].contains(x, y))
+ return true;
+ }
+ return false;
+}
+
+static bool RectInRegion(register QRegionPrivate *region, int rx, int ry, uint rwidth, uint rheight)
+{
+ register const QRect *pbox;
+ register const QRect *pboxEnd;
+ QRect rect(rx, ry, rwidth, rheight);
+ register QRect *prect = &rect;
+ int partIn, partOut;
+
+ if (!region || region->numRects == 0 || !EXTENTCHECK(&region->extents, prect))
+ return RectangleOut;
+
+ partOut = false;
+ partIn = false;
+
+ /* can stop when both partOut and partIn are true, or we reach prect->y2 */
+ for (pbox = (region->mode==QRegionPrivate::Vector)?region->rects.constData():&region->single, pboxEnd = pbox + region->numRects;
+ pbox < pboxEnd; ++pbox) {
+ if (pbox->bottom() < ry)
+ continue;
+
+ if (pbox->top() > ry) {
+ partOut = true;
+ if (partIn || pbox->top() > prect->bottom())
+ break;
+ ry = pbox->top();
+ }
+
+ if (pbox->right() < rx)
+ continue; /* not far enough over yet */
+
+ if (pbox->left() > rx) {
+ partOut = true; /* missed part of rectangle to left */
+ if (partIn)
+ break;
+ }
+
+ if (pbox->left() <= prect->right()) {
+ partIn = true; /* definitely overlap */
+ if (partOut)
+ break;
+ }
+
+ if (pbox->right() >= prect->right()) {
+ ry = pbox->bottom() + 1; /* finished with this band */
+ if (ry > prect->bottom())
+ break;
+ rx = prect->left(); /* reset x out to left again */
+ } else {
+ /*
+ * Because boxes in a band are maximal width, if the first box
+ * to overlap the rectangle doesn't completely cover it in that
+ * band, the rectangle must be partially out, since some of it
+ * will be uncovered in that band. partIn will have been set true
+ * by now...
+ */
+ break;
+ }
+ }
+ return partIn ? ((ry <= prect->bottom()) ? RectanglePart : RectangleIn) : RectangleOut;
+}
+// END OF Region.c extract
+// START OF poly.h extract
+/* $XConsortium: poly.h,v 1.4 94/04/17 20:22:19 rws Exp $ */
+/************************************************************************
+
+Copyright (c) 1987 X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+
+
+Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Digital not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+************************************************************************/
+
+/*
+ * This file contains a few macros to help track
+ * the edge of a filled object. The object is assumed
+ * to be filled in scanline order, and thus the
+ * algorithm used is an extension of Bresenham's line
+ * drawing algorithm which assumes that y is always the
+ * major axis.
+ * Since these pieces of code are the same for any filled shape,
+ * it is more convenient to gather the library in one
+ * place, but since these pieces of code are also in
+ * the inner loops of output primitives, procedure call
+ * overhead is out of the question.
+ * See the author for a derivation if needed.
+ */
+
+
+/*
+ * In scan converting polygons, we want to choose those pixels
+ * which are inside the polygon. Thus, we add .5 to the starting
+ * x coordinate for both left and right edges. Now we choose the
+ * first pixel which is inside the pgon for the left edge and the
+ * first pixel which is outside the pgon for the right edge.
+ * Draw the left pixel, but not the right.
+ *
+ * How to add .5 to the starting x coordinate:
+ * If the edge is moving to the right, then subtract dy from the
+ * error term from the general form of the algorithm.
+ * If the edge is moving to the left, then add dy to the error term.
+ *
+ * The reason for the difference between edges moving to the left
+ * and edges moving to the right is simple: If an edge is moving
+ * to the right, then we want the algorithm to flip immediately.
+ * If it is moving to the left, then we don't want it to flip until
+ * we traverse an entire pixel.
+ */
+#define BRESINITPGON(dy, x1, x2, xStart, d, m, m1, incr1, incr2) { \
+ int dx; /* local storage */ \
+\
+ /* \
+ * if the edge is horizontal, then it is ignored \
+ * and assumed not to be processed. Otherwise, do this stuff. \
+ */ \
+ if ((dy) != 0) { \
+ xStart = (x1); \
+ dx = (x2) - xStart; \
+ if (dx < 0) { \
+ m = dx / (dy); \
+ m1 = m - 1; \
+ incr1 = -2 * dx + 2 * (dy) * m1; \
+ incr2 = -2 * dx + 2 * (dy) * m; \
+ d = 2 * m * (dy) - 2 * dx - 2 * (dy); \
+ } else { \
+ m = dx / (dy); \
+ m1 = m + 1; \
+ incr1 = 2 * dx - 2 * (dy) * m1; \
+ incr2 = 2 * dx - 2 * (dy) * m; \
+ d = -2 * m * (dy) + 2 * dx; \
+ } \
+ } \
+}
+
+#define BRESINCRPGON(d, minval, m, m1, incr1, incr2) { \
+ if (m1 > 0) { \
+ if (d > 0) { \
+ minval += m1; \
+ d += incr1; \
+ } \
+ else { \
+ minval += m; \
+ d += incr2; \
+ } \
+ } else {\
+ if (d >= 0) { \
+ minval += m1; \
+ d += incr1; \
+ } \
+ else { \
+ minval += m; \
+ d += incr2; \
+ } \
+ } \
+}
+
+
+/*
+ * This structure contains all of the information needed
+ * to run the bresenham algorithm.
+ * The variables may be hardcoded into the declarations
+ * instead of using this structure to make use of
+ * register declarations.
+ */
+typedef struct {
+ int minor_axis; /* minor axis */
+ int d; /* decision variable */
+ int m, m1; /* slope and slope+1 */
+ int incr1, incr2; /* error increments */
+} BRESINFO;
+
+
+#define BRESINITPGONSTRUCT(dmaj, min1, min2, bres) \
+ BRESINITPGON(dmaj, min1, min2, bres.minor_axis, bres.d, \
+ bres.m, bres.m1, bres.incr1, bres.incr2)
+
+#define BRESINCRPGONSTRUCT(bres) \
+ BRESINCRPGON(bres.d, bres.minor_axis, bres.m, bres.m1, bres.incr1, bres.incr2)
+
+
+
+/*
+ * These are the data structures needed to scan
+ * convert regions. Two different scan conversion
+ * methods are available -- the even-odd method, and
+ * the winding number method.
+ * The even-odd rule states that a point is inside
+ * the polygon if a ray drawn from that point in any
+ * direction will pass through an odd number of
+ * path segments.
+ * By the winding number rule, a point is decided
+ * to be inside the polygon if a ray drawn from that
+ * point in any direction passes through a different
+ * number of clockwise and counter-clockwise path
+ * segments.
+ *
+ * These data structures are adapted somewhat from
+ * the algorithm in (Foley/Van Dam) for scan converting
+ * polygons.
+ * The basic algorithm is to start at the top (smallest y)
+ * of the polygon, stepping down to the bottom of
+ * the polygon by incrementing the y coordinate. We
+ * keep a list of edges which the current scanline crosses,
+ * sorted by x. This list is called the Active Edge Table (AET)
+ * As we change the y-coordinate, we update each entry in
+ * in the active edge table to reflect the edges new xcoord.
+ * This list must be sorted at each scanline in case
+ * two edges intersect.
+ * We also keep a data structure known as the Edge Table (ET),
+ * which keeps track of all the edges which the current
+ * scanline has not yet reached. The ET is basically a
+ * list of ScanLineList structures containing a list of
+ * edges which are entered at a given scanline. There is one
+ * ScanLineList per scanline at which an edge is entered.
+ * When we enter a new edge, we move it from the ET to the AET.
+ *
+ * From the AET, we can implement the even-odd rule as in
+ * (Foley/Van Dam).
+ * The winding number rule is a little trickier. We also
+ * keep the EdgeTableEntries in the AET linked by the
+ * nextWETE (winding EdgeTableEntry) link. This allows
+ * the edges to be linked just as before for updating
+ * purposes, but only uses the edges linked by the nextWETE
+ * link as edges representing spans of the polygon to
+ * drawn (as with the even-odd rule).
+ */
+
+/*
+ * for the winding number rule
+ */
+#define CLOCKWISE 1
+#define COUNTERCLOCKWISE -1
+
+typedef struct _EdgeTableEntry {
+ int ymax; /* ycoord at which we exit this edge. */
+ BRESINFO bres; /* Bresenham info to run the edge */
+ struct _EdgeTableEntry *next; /* next in the list */
+ struct _EdgeTableEntry *back; /* for insertion sort */
+ struct _EdgeTableEntry *nextWETE; /* for winding num rule */
+ int ClockWise; /* flag for winding number rule */
+} EdgeTableEntry;
+
+
+typedef struct _ScanLineList{
+ int scanline; /* the scanline represented */
+ EdgeTableEntry *edgelist; /* header node */
+ struct _ScanLineList *next; /* next in the list */
+} ScanLineList;
+
+
+typedef struct {
+ int ymax; /* ymax for the polygon */
+ int ymin; /* ymin for the polygon */
+ ScanLineList scanlines; /* header node */
+} EdgeTable;
+
+
+/*
+ * Here is a struct to help with storage allocation
+ * so we can allocate a big chunk at a time, and then take
+ * pieces from this heap when we need to.
+ */
+#define SLLSPERBLOCK 25
+
+typedef struct _ScanLineListBlock {
+ ScanLineList SLLs[SLLSPERBLOCK];
+ struct _ScanLineListBlock *next;
+} ScanLineListBlock;
+
+
+
+/*
+ *
+ * a few macros for the inner loops of the fill code where
+ * performance considerations don't allow a procedure call.
+ *
+ * Evaluate the given edge at the given scanline.
+ * If the edge has expired, then we leave it and fix up
+ * the active edge table; otherwise, we increment the
+ * x value to be ready for the next scanline.
+ * The winding number rule is in effect, so we must notify
+ * the caller when the edge has been removed so he
+ * can reorder the Winding Active Edge Table.
+ */
+#define EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET) { \
+ if (pAET->ymax == y) { /* leaving this edge */ \
+ pPrevAET->next = pAET->next; \
+ pAET = pPrevAET->next; \
+ fixWAET = 1; \
+ if (pAET) \
+ pAET->back = pPrevAET; \
+ } \
+ else { \
+ BRESINCRPGONSTRUCT(pAET->bres) \
+ pPrevAET = pAET; \
+ pAET = pAET->next; \
+ } \
+}
+
+
+/*
+ * Evaluate the given edge at the given scanline.
+ * If the edge has expired, then we leave it and fix up
+ * the active edge table; otherwise, we increment the
+ * x value to be ready for the next scanline.
+ * The even-odd rule is in effect.
+ */
+#define EVALUATEEDGEEVENODD(pAET, pPrevAET, y) { \
+ if (pAET->ymax == y) { /* leaving this edge */ \
+ pPrevAET->next = pAET->next; \
+ pAET = pPrevAET->next; \
+ if (pAET) \
+ pAET->back = pPrevAET; \
+ } \
+ else { \
+ BRESINCRPGONSTRUCT(pAET->bres) \
+ pPrevAET = pAET; \
+ pAET = pAET->next; \
+ } \
+}
+// END OF poly.h extract
+// START OF PolyReg.c extract
+/* $XConsortium: PolyReg.c,v 11.23 94/11/17 21:59:37 converse Exp $ */
+/************************************************************************
+
+Copyright (c) 1987 X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+
+
+Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Digital not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+************************************************************************/
+/* $XFree86: xc/lib/X11/PolyReg.c,v 1.1.1.2.8.2 1998/10/04 15:22:49 hohndel Exp $ */
+
+#define LARGE_COORDINATE 1000000
+#define SMALL_COORDINATE -LARGE_COORDINATE
+
+/*
+ * InsertEdgeInET
+ *
+ * Insert the given edge into the edge table.
+ * First we must find the correct bucket in the
+ * Edge table, then find the right slot in the
+ * bucket. Finally, we can insert it.
+ *
+ */
+static void InsertEdgeInET(EdgeTable *ET, EdgeTableEntry *ETE, int scanline,
+ ScanLineListBlock **SLLBlock, int *iSLLBlock)
+{
+ register EdgeTableEntry *start, *prev;
+ register ScanLineList *pSLL, *pPrevSLL;
+ ScanLineListBlock *tmpSLLBlock;
+
+ /*
+ * find the right bucket to put the edge into
+ */
+ pPrevSLL = &ET->scanlines;
+ pSLL = pPrevSLL->next;
+ while (pSLL && (pSLL->scanline < scanline)) {
+ pPrevSLL = pSLL;
+ pSLL = pSLL->next;
+ }
+
+ /*
+ * reassign pSLL (pointer to ScanLineList) if necessary
+ */
+ if ((!pSLL) || (pSLL->scanline > scanline)) {
+ if (*iSLLBlock > SLLSPERBLOCK-1)
+ {
+ tmpSLLBlock =
+ (ScanLineListBlock *)malloc(sizeof(ScanLineListBlock));
+ (*SLLBlock)->next = tmpSLLBlock;
+ tmpSLLBlock->next = (ScanLineListBlock *)NULL;
+ *SLLBlock = tmpSLLBlock;
+ *iSLLBlock = 0;
+ }
+ pSLL = &((*SLLBlock)->SLLs[(*iSLLBlock)++]);
+
+ pSLL->next = pPrevSLL->next;
+ pSLL->edgelist = (EdgeTableEntry *)NULL;
+ pPrevSLL->next = pSLL;
+ }
+ pSLL->scanline = scanline;
+
+ /*
+ * now insert the edge in the right bucket
+ */
+ prev = 0;
+ start = pSLL->edgelist;
+ while (start && (start->bres.minor_axis < ETE->bres.minor_axis)) {
+ prev = start;
+ start = start->next;
+ }
+ ETE->next = start;
+
+ if (prev)
+ prev->next = ETE;
+ else
+ pSLL->edgelist = ETE;
+}
+
+/*
+ * CreateEdgeTable
+ *
+ * This routine creates the edge table for
+ * scan converting polygons.
+ * The Edge Table (ET) looks like:
+ *
+ * EdgeTable
+ * --------
+ * | ymax | ScanLineLists
+ * |scanline|-->------------>-------------->...
+ * -------- |scanline| |scanline|
+ * |edgelist| |edgelist|
+ * --------- ---------
+ * | |
+ * | |
+ * V V
+ * list of ETEs list of ETEs
+ *
+ * where ETE is an EdgeTableEntry data structure,
+ * and there is one ScanLineList per scanline at
+ * which an edge is initially entered.
+ *
+ */
+
+static void CreateETandAET(register int count, register const QPoint *pts,
+ EdgeTable *ET, EdgeTableEntry *AET, register EdgeTableEntry *pETEs,
+ ScanLineListBlock *pSLLBlock)
+{
+ register const QPoint *top,
+ *bottom,
+ *PrevPt,
+ *CurrPt;
+ int iSLLBlock = 0;
+ int dy;
+
+ if (count < 2)
+ return;
+
+ /*
+ * initialize the Active Edge Table
+ */
+ AET->next = 0;
+ AET->back = 0;
+ AET->nextWETE = 0;
+ AET->bres.minor_axis = SMALL_COORDINATE;
+
+ /*
+ * initialize the Edge Table.
+ */
+ ET->scanlines.next = 0;
+ ET->ymax = SMALL_COORDINATE;
+ ET->ymin = LARGE_COORDINATE;
+ pSLLBlock->next = 0;
+
+ PrevPt = &pts[count - 1];
+
+ /*
+ * for each vertex in the array of points.
+ * In this loop we are dealing with two vertices at
+ * a time -- these make up one edge of the polygon.
+ */
+ while (count--) {
+ CurrPt = pts++;
+
+ /*
+ * find out which point is above and which is below.
+ */
+ if (PrevPt->y() > CurrPt->y()) {
+ bottom = PrevPt;
+ top = CurrPt;
+ pETEs->ClockWise = 0;
+ } else {
+ bottom = CurrPt;
+ top = PrevPt;
+ pETEs->ClockWise = 1;
+ }
+
+ /*
+ * don't add horizontal edges to the Edge table.
+ */
+ if (bottom->y() != top->y()) {
+ pETEs->ymax = bottom->y() - 1; /* -1 so we don't get last scanline */
+
+ /*
+ * initialize integer edge algorithm
+ */
+ dy = bottom->y() - top->y();
+ BRESINITPGONSTRUCT(dy, top->x(), bottom->x(), pETEs->bres)
+
+ InsertEdgeInET(ET, pETEs, top->y(), &pSLLBlock, &iSLLBlock);
+
+ if (PrevPt->y() > ET->ymax)
+ ET->ymax = PrevPt->y();
+ if (PrevPt->y() < ET->ymin)
+ ET->ymin = PrevPt->y();
+ ++pETEs;
+ }
+
+ PrevPt = CurrPt;
+ }
+}
+
+/*
+ * loadAET
+ *
+ * This routine moves EdgeTableEntries from the
+ * EdgeTable into the Active Edge Table,
+ * leaving them sorted by smaller x coordinate.
+ *
+ */
+
+static void loadAET(register EdgeTableEntry *AET, register EdgeTableEntry *ETEs)
+{
+ register EdgeTableEntry *pPrevAET;
+ register EdgeTableEntry *tmp;
+
+ pPrevAET = AET;
+ AET = AET->next;
+ while (ETEs) {
+ while (AET && AET->bres.minor_axis < ETEs->bres.minor_axis) {
+ pPrevAET = AET;
+ AET = AET->next;
+ }
+ tmp = ETEs->next;
+ ETEs->next = AET;
+ if (AET)
+ AET->back = ETEs;
+ ETEs->back = pPrevAET;
+ pPrevAET->next = ETEs;
+ pPrevAET = ETEs;
+
+ ETEs = tmp;
+ }
+}
+
+/*
+ * computeWAET
+ *
+ * This routine links the AET by the
+ * nextWETE (winding EdgeTableEntry) link for
+ * use by the winding number rule. The final
+ * Active Edge Table (AET) might look something
+ * like:
+ *
+ * AET
+ * ---------- --------- ---------
+ * |ymax | |ymax | |ymax |
+ * | ... | |... | |... |
+ * |next |->|next |->|next |->...
+ * |nextWETE| |nextWETE| |nextWETE|
+ * --------- --------- ^--------
+ * | | |
+ * V-------------------> V---> ...
+ *
+ */
+static void computeWAET(register EdgeTableEntry *AET)
+{
+ register EdgeTableEntry *pWETE;
+ register int inside = 1;
+ register int isInside = 0;
+
+ AET->nextWETE = 0;
+ pWETE = AET;
+ AET = AET->next;
+ while (AET) {
+ if (AET->ClockWise)
+ ++isInside;
+ else
+ --isInside;
+
+ if (!inside && !isInside || inside && isInside) {
+ pWETE->nextWETE = AET;
+ pWETE = AET;
+ inside = !inside;
+ }
+ AET = AET->next;
+ }
+ pWETE->nextWETE = 0;
+}
+
+/*
+ * InsertionSort
+ *
+ * Just a simple insertion sort using
+ * pointers and back pointers to sort the Active
+ * Edge Table.
+ *
+ */
+
+static int InsertionSort(register EdgeTableEntry *AET)
+{
+ register EdgeTableEntry *pETEchase;
+ register EdgeTableEntry *pETEinsert;
+ register EdgeTableEntry *pETEchaseBackTMP;
+ register int changed = 0;
+
+ AET = AET->next;
+ while (AET) {
+ pETEinsert = AET;
+ pETEchase = AET;
+ while (pETEchase->back->bres.minor_axis > AET->bres.minor_axis)
+ pETEchase = pETEchase->back;
+
+ AET = AET->next;
+ if (pETEchase != pETEinsert) {
+ pETEchaseBackTMP = pETEchase->back;
+ pETEinsert->back->next = AET;
+ if (AET)
+ AET->back = pETEinsert->back;
+ pETEinsert->next = pETEchase;
+ pETEchase->back->next = pETEinsert;
+ pETEchase->back = pETEinsert;
+ pETEinsert->back = pETEchaseBackTMP;
+ changed = 1;
+ }
+ }
+ return changed;
+}
+
+/*
+ * Clean up our act.
+ */
+static void FreeStorage(register ScanLineListBlock *pSLLBlock)
+{
+ register ScanLineListBlock *tmpSLLBlock;
+
+ while (pSLLBlock) {
+ tmpSLLBlock = pSLLBlock->next;
+ free(pSLLBlock);
+ pSLLBlock = tmpSLLBlock;
+ }
+}
+
+/*
+ * Create an array of rectangles from a list of points.
+ * If indeed these things (POINTS, RECTS) are the same,
+ * then this proc is still needed, because it allocates
+ * storage for the array, which was allocated on the
+ * stack by the calling procedure.
+ *
+ */
+static void PtsToRegion(register int numFullPtBlocks, register int iCurPtBlock,
+ POINTBLOCK *FirstPtBlock, QRegionPrivate *reg)
+{
+ register QRect *rects;
+ register QPoint *pts;
+ register POINTBLOCK *CurPtBlock;
+ register int i;
+ register QRect *extents;
+ register int numRects;
+
+ extents = &reg->extents;
+ numRects = ((numFullPtBlocks * NUMPTSTOBUFFER) + iCurPtBlock) >> 1;
+
+ reg->rects.resize(numRects);
+
+ CurPtBlock = FirstPtBlock;
+ rects = reg->rects.data() - 1;
+ numRects = 0;
+ extents->setLeft(INT_MAX);
+ extents->setRight(INT_MIN);
+ reg->innerArea = -1;
+
+ for (; numFullPtBlocks >= 0; --numFullPtBlocks) {
+ /* the loop uses 2 points per iteration */
+ i = NUMPTSTOBUFFER >> 1;
+ if (!numFullPtBlocks)
+ i = iCurPtBlock >> 1;
+ if(i) {
+ for (pts = CurPtBlock->pts; i--; pts += 2) {
+ if (pts->x() == pts[1].x())
+ continue;
+ if (numRects && pts->x() == rects->left() && pts->y() == rects->bottom() + 1
+ && pts[1].x() == rects->right()+1 && (numRects == 1 || rects[-1].top() != rects->top())
+ && (i && pts[2].y() > pts[1].y())) {
+ rects->setBottom(pts[1].y());
+ reg->updateInnerRect(*rects);
+ continue;
+ }
+ ++numRects;
+ ++rects;
+ rects->setCoords(pts->x(), pts->y(), pts[1].x() - 1, pts[1].y());
+ if (rects->left() < extents->left())
+ extents->setLeft(rects->left());
+ if (rects->right() > extents->right())
+ extents->setRight(rects->right());
+ reg->updateInnerRect(*rects);
+ }
+ }
+ CurPtBlock = CurPtBlock->next;
+ }
+
+ if (numRects) {
+ extents->setTop(reg->rects[0].top());
+ extents->setBottom(rects->bottom());
+ } else {
+ extents->setCoords(0, 0, 0, 0);
+ }
+ reg->numRects = numRects;
+}
+
+/*
+ * polytoregion
+ *
+ * Scan converts a polygon by returning a run-length
+ * encoding of the resultant bitmap -- the run-length
+ * encoding is in the form of an array of rectangles.
+ */
+static QRegionPrivate *PolygonRegion(const QPoint *Pts, int Count, int rule,
+ QRegionPrivate *region)
+ //Point *Pts; /* the pts */
+ //int Count; /* number of pts */
+ //int rule; /* winding rule */
+{
+ register EdgeTableEntry *pAET; /* Active Edge Table */
+ register int y; /* current scanline */
+ register int iPts = 0; /* number of pts in buffer */
+ register EdgeTableEntry *pWETE; /* Winding Edge Table Entry*/
+ register ScanLineList *pSLL; /* current scanLineList */
+ register QPoint *pts; /* output buffer */
+ EdgeTableEntry *pPrevAET; /* ptr to previous AET */
+ EdgeTable ET; /* header node for ET */
+ EdgeTableEntry AET; /* header node for AET */
+ EdgeTableEntry *pETEs; /* EdgeTableEntries pool */
+ ScanLineListBlock SLLBlock; /* header for scanlinelist */
+ int fixWAET = false;
+ POINTBLOCK FirstPtBlock, *curPtBlock; /* PtBlock buffers */
+ POINTBLOCK *tmpPtBlock;
+ int numFullPtBlocks = 0;
+
+ region->vector();
+
+ /* special case a rectangle */
+ if (((Count == 4) ||
+ ((Count == 5) && (Pts[4].x() == Pts[0].x()) && (Pts[4].y() == Pts[0].y())))
+ && (((Pts[0].y() == Pts[1].y()) && (Pts[1].x() == Pts[2].x()) && (Pts[2].y() == Pts[3].y())
+ && (Pts[3].x() == Pts[0].x())) || ((Pts[0].x() == Pts[1].x())
+ && (Pts[1].y() == Pts[2].y()) && (Pts[2].x() == Pts[3].x())
+ && (Pts[3].y() == Pts[0].y())))) {
+ int x = qMin(Pts[0].x(), Pts[2].x());
+ region->extents.setLeft(x);
+ int y = qMin(Pts[0].y(), Pts[2].y());
+ region->extents.setTop(y);
+ region->extents.setWidth(qMax(Pts[0].x(), Pts[2].x()) - x);
+ region->extents.setHeight(qMax(Pts[0].y(), Pts[2].y()) - y);
+ if ((region->extents.left() <= region->extents.right()) &&
+ (region->extents.top() <= region->extents.bottom())) {
+ region->numRects = 1;
+ region->rects.resize(1);
+ region->rects[0] = region->extents;
+ region->innerRect = region->extents;
+ region->innerArea = region->innerRect.width() * region->innerRect.height();
+ }
+ return region;
+ }
+
+ if (!(pETEs = static_cast<EdgeTableEntry *>(malloc(sizeof(EdgeTableEntry) * Count))))
+ return 0;
+
+ pts = FirstPtBlock.pts;
+ CreateETandAET(Count, Pts, &ET, &AET, pETEs, &SLLBlock);
+ pSLL = ET.scanlines.next;
+ curPtBlock = &FirstPtBlock;
+
+ if (rule == EvenOddRule) {
+ /*
+ * for each scanline
+ */
+ for (y = ET.ymin; y < ET.ymax; ++y) {
+ /*
+ * Add a new edge to the active edge table when we
+ * get to the next edge.
+ */
+ if (pSLL && y == pSLL->scanline) {
+ loadAET(&AET, pSLL->edgelist);
+ pSLL = pSLL->next;
+ }
+ pPrevAET = &AET;
+ pAET = AET.next;
+
+ /*
+ * for each active edge
+ */
+ while (pAET) {
+ pts->setX(pAET->bres.minor_axis);
+ pts->setY(y);
+ ++pts;
+ ++iPts;
+
+ /*
+ * send out the buffer
+ */
+ if (iPts == NUMPTSTOBUFFER) {
+ tmpPtBlock = (POINTBLOCK *)malloc(sizeof(POINTBLOCK));
+ curPtBlock->next = tmpPtBlock;
+ curPtBlock = tmpPtBlock;
+ pts = curPtBlock->pts;
+ ++numFullPtBlocks;
+ iPts = 0;
+ }
+ EVALUATEEDGEEVENODD(pAET, pPrevAET, y)
+ }
+ InsertionSort(&AET);
+ }
+ } else {
+ /*
+ * for each scanline
+ */
+ for (y = ET.ymin; y < ET.ymax; ++y) {
+ /*
+ * Add a new edge to the active edge table when we
+ * get to the next edge.
+ */
+ if (pSLL && y == pSLL->scanline) {
+ loadAET(&AET, pSLL->edgelist);
+ computeWAET(&AET);
+ pSLL = pSLL->next;
+ }
+ pPrevAET = &AET;
+ pAET = AET.next;
+ pWETE = pAET;
+
+ /*
+ * for each active edge
+ */
+ while (pAET) {
+ /*
+ * add to the buffer only those edges that
+ * are in the Winding active edge table.
+ */
+ if (pWETE == pAET) {
+ pts->setX(pAET->bres.minor_axis);
+ pts->setY(y);
+ ++pts;
+ ++iPts;
+
+ /*
+ * send out the buffer
+ */
+ if (iPts == NUMPTSTOBUFFER) {
+ tmpPtBlock = static_cast<POINTBLOCK *>(malloc(sizeof(POINTBLOCK)));
+ curPtBlock->next = tmpPtBlock;
+ curPtBlock = tmpPtBlock;
+ pts = curPtBlock->pts;
+ ++numFullPtBlocks;
+ iPts = 0;
+ }
+ pWETE = pWETE->nextWETE;
+ }
+ EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET)
+ }
+
+ /*
+ * recompute the winding active edge table if
+ * we just resorted or have exited an edge.
+ */
+ if (InsertionSort(&AET) || fixWAET) {
+ computeWAET(&AET);
+ fixWAET = false;
+ }
+ }
+ }
+ FreeStorage(SLLBlock.next);
+ PtsToRegion(numFullPtBlocks, iPts, &FirstPtBlock, region);
+ for (curPtBlock = FirstPtBlock.next; --numFullPtBlocks >= 0;) {
+ tmpPtBlock = curPtBlock->next;
+ free(curPtBlock);
+ curPtBlock = tmpPtBlock;
+ }
+ free(pETEs);
+ return region;
+}
+// END OF PolyReg.c extract
+
+QRegionPrivate *qt_bitmapToRegion(const QBitmap& bitmap, QRegionPrivate *region)
+{
+ region->vector();
+
+ QImage image = bitmap.toImage();
+
+ QRect xr;
+
+#define AddSpan \
+ { \
+ xr.setCoords(prev1, y, x-1, y); \
+ UnionRectWithRegion(&xr, region, *region); \
+ }
+
+ const uchar zero = 0;
+ bool little = image.format() == QImage::Format_MonoLSB;
+
+ int x,
+ y;
+ for (y = 0; y < image.height(); ++y) {
+ uchar *line = image.scanLine(y);
+ int w = image.width();
+ uchar all = zero;
+ int prev1 = -1;
+ for (x = 0; x < w;) {
+ uchar byte = line[x / 8];
+ if (x > w - 8 || byte!=all) {
+ if (little) {
+ for (int b = 8; b > 0 && x < w; --b) {
+ if (!(byte & 0x01) == !all) {
+ // More of the same
+ } else {
+ // A change.
+ if (all!=zero) {
+ AddSpan
+ all = zero;
+ } else {
+ prev1 = x;
+ all = ~zero;
+ }
+ }
+ byte >>= 1;
+ ++x;
+ }
+ } else {
+ for (int b = 8; b > 0 && x < w; --b) {
+ if (!(byte & 0x80) == !all) {
+ // More of the same
+ } else {
+ // A change.
+ if (all != zero) {
+ AddSpan
+ all = zero;
+ } else {
+ prev1 = x;
+ all = ~zero;
+ }
+ }
+ byte <<= 1;
+ ++x;
+ }
+ }
+ } else {
+ x += 8;
+ }
+ }
+ if (all != zero) {
+ AddSpan
+ }
+ }
+#undef AddSpan
+
+ return region;
+}
+
+/*
+ Constructs an empty region.
+
+ \sa isEmpty()
+*/
+
+QRegion::QRegion()
+ : d(&shared_empty)
+{
+ d->ref.ref();
+}
+
+/*
+ \overload
+
+ Create a region based on the rectange \a r with region type \a t.
+
+ If the rectangle is invalid a null region will be created.
+
+ \sa QRegion::RegionType
+*/
+
+QRegion::QRegion(const QRect &r, RegionType t)
+{
+ if (r.isEmpty()) {
+ d = &shared_empty;
+ d->ref.ref();
+ } else {
+// d = new QRegionData;
+ QRegionPrivate *rp = 0;
+ if (t == Rectangle) {
+// rp = new QRegionPrivate(r);
+ rp = qt_allocRegion(r);
+ } else if (t == Ellipse) {
+ QPainterPath path;
+ path.addEllipse(r.x(), r.y(), r.width(), r.height());
+ QPolygon a = path.toSubpathPolygons().at(0).toPolygon();
+ rp = qt_allocRegion();
+// rp = new QRegionPrivate;
+ PolygonRegion(a.constData(), a.size(), EvenOddRule, rp);
+ }
+ d = rp;
+ d->ref = 1;
+#if defined(Q_WS_X11)
+ d->rgn = 0;
+ d->xrectangles = 0;
+#elif defined(Q_WS_MAC)
+ d->rgn = 0;
+#endif
+ d->qt_rgn = rp;
+ }
+}
+
+/*
+ Constructs a polygon region from the point array \a a with the fill rule
+ specified by \a fillRule.
+
+ If \a fillRule is \l{Qt::WindingFill}, the polygon region is defined
+ using the winding algorithm; if it is \l{Qt::OddEvenFill}, the odd-even fill
+ algorithm is used.
+
+ \warning This constructor can be used to create complex regions that will
+ slow down painting when used.
+*/
+
+QRegion::QRegion(const QPolygon &a, Qt::FillRule fillRule)
+{
+ if (a.count() > 2) {
+ //d = new QRegionData;
+ // QRegionPrivate *rp = new QRegionPrivate;
+ QRegionPrivate *rp = qt_allocRegion();
+ PolygonRegion(a.constData(), a.size(),
+ fillRule == Qt::WindingFill ? WindingRule : EvenOddRule, rp);
+ d = rp;
+ d->ref = 1;
+#if defined(Q_WS_X11)
+ d->rgn = 0;
+ d->xrectangles = 0;
+#elif defined(Q_WS_MAC)
+ d->rgn = 0;
+#endif
+ d->qt_rgn = rp;
+ } else {
+ d = &shared_empty;
+ d->ref.ref();
+ }
+}
+
+
+/*
+ Constructs a new region which is equal to region \a r.
+*/
+
+QRegion::QRegion(const QRegion &r)
+{
+ d = r.d;
+ d->ref.ref();
+}
+
+
+/*
+ Constructs a region from the bitmap \a bm.
+
+ The resulting region consists of the pixels in bitmap \a bm that
+ are Qt::color1, as if each pixel was a 1 by 1 rectangle.
+
+ This constructor may create complex regions that will slow down
+ painting when used. Note that drawing masked pixmaps can be done
+ much faster using QPixmap::setMask().
+*/
+QRegion::QRegion(const QBitmap &bm)
+{
+ if (bm.isNull()) {
+ d = &shared_empty;
+ d->ref.ref();
+ } else {
+ // d = new QRegionData;
+// QRegionPrivate *rp = new QRegionPrivate;
+ QRegionPrivate *rp = qt_allocRegion();
+
+ qt_bitmapToRegion(bm, rp);
+ d = rp;
+ d->ref = 1;
+#if defined(Q_WS_X11)
+ d->rgn = 0;
+ d->xrectangles = 0;
+#elif defined(Q_WS_MAC)
+ d->rgn = 0;
+#endif
+ d->qt_rgn = rp;
+ }
+}
+
+void QRegion::cleanUp(QRegion::QRegionData *x)
+{
+ // delete x->qt_rgn;
+#if defined(Q_WS_X11)
+ if (x->rgn)
+ XDestroyRegion(x->rgn);
+ if (x->xrectangles)
+ free(x->xrectangles);
+#elif defined(Q_WS_MAC)
+ if (x->rgn)
+ qt_mac_dispose_rgn(x->rgn);
+#endif
+ if(x->qt_rgn) {
+// delete x->qt_rgn;
+ qt_freeRegion(x->qt_rgn);
+ } else {
+ delete x;
+ }
+}
+
+/*
+ Destroys the region.
+*/
+
+QRegion::~QRegion()
+{
+ if (!d->ref.deref())
+ cleanUp(d);
+}
+
+
+/*
+ Assigns \a r to this region and returns a reference to the region.
+*/
+
+QRegion &QRegion::operator=(const QRegion &r)
+{
+ r.d->ref.ref();
+ if (!d->ref.deref())
+ cleanUp(d);
+ d = r.d;
+ return *this;
+}
+
+
+/*
+ \internal
+*/
+
+QRegion QRegion::copy() const
+{
+ QRegion r;
+ QRegionData *x = 0; // new QRegionData;
+ QRegionPrivate *rp = 0;
+ if (d->qt_rgn)
+// rp = new QRegionPrivate(*d->qt_rgn);
+ rp = qt_allocRegion(*d->qt_rgn);
+ else
+ rp = qt_allocRegion();
+ x = rp;
+ x->qt_rgn = rp;
+ x->ref = 1;
+#if defined(Q_WS_X11)
+ x->rgn = 0;
+ x->xrectangles = 0;
+#elif defined(Q_WS_MAC)
+ x->rgn = 0;
+#endif
+
+ if (!r.d->ref.deref())
+ cleanUp(r.d);
+ r.d = x;
+ return r;
+}
+
+/*
+ Returns true if the region is empty; otherwise returns false. An
+ empty region is a region that contains no points.
+
+ Example:
+ \snippet doc/src/snippets/code/src.gui.painting.qregion_qws.cpp 0
+*/
+
+bool QRegion::isEmpty() const
+{
+ return d == &shared_empty || d->qt_rgn->numRects == 0;
+}
+
+
+/*
+ Returns true if the region contains the point \a p; otherwise
+ returns false.
+*/
+
+bool QRegion::contains(const QPoint &p) const
+{
+ return PointInRegion(d->qt_rgn, p.x(), p.y());
+}
+
+/*
+ \overload
+
+ Returns true if the region overlaps the rectangle \a r; otherwise
+ returns false.
+*/
+
+bool QRegion::contains(const QRect &r) const
+{
+ if(!d->qt_rgn)
+ return false;
+ if(d->qt_rgn->mode == QRegionPrivate::Single)
+ return d->qt_rgn->single.contains(r);
+
+ return RectInRegion(d->qt_rgn, r.left(), r.top(), r.width(), r.height()) != RectangleOut;
+}
+
+
+
+/*
+ Translates (moves) the region \a dx along the X axis and \a dy
+ along the Y axis.
+*/
+
+void QRegion::translate(int dx, int dy)
+{
+ if ((dx == 0 && dy == 0) || isEmptyHelper(d->qt_rgn))
+ return;
+
+ detach();
+ OffsetRegion(*d->qt_rgn, dx, dy);
+#if defined(Q_WS_X11)
+ if (d->xrectangles) {
+ free(d->xrectangles);
+ d->xrectangles = 0;
+ }
+#elif defined(Q_WS_MAC)
+ if(d->rgn) {
+ qt_mac_dispose_rgn(d->rgn);
+ d->rgn = 0;
+ }
+#endif
+}
+
+/*
+ \fn QRegion QRegion::unite(const QRegion &r) const
+ \obsolete
+
+ Use united(\a r) instead.
+*/
+
+/*
+ \fn QRegion QRegion::united(const QRegion &r) const
+ \since 4.2
+
+ Returns a region which is the union of this region and \a r.
+
+ \img runion.png Region Union
+
+ The figure shows the union of two elliptical regions.
+
+ \sa intersected(), subtracted(), xored()
+*/
+
+QRegion QRegion::unite(const QRegion &r) const
+{
+ if (isEmptyHelper(d->qt_rgn))
+ return r;
+ if (isEmptyHelper(r.d->qt_rgn))
+ return *this;
+
+ if (d->qt_rgn->contains(*r.d->qt_rgn)) {
+ return *this;
+ } else if (r.d->qt_rgn->contains(*d->qt_rgn)) {
+ return r;
+ } else if (d->qt_rgn->canAppend(r.d->qt_rgn)) {
+ QRegion result(*this);
+ result.detach();
+ result.d->qt_rgn->append(r.d->qt_rgn);
+ return result;
+ } else if (r.d->qt_rgn->canAppend(d->qt_rgn)) {
+ QRegion result(r);
+ result.detach();
+ result.d->qt_rgn->append(d->qt_rgn);
+ return result;
+ } else if (EqualRegion(d->qt_rgn, r.d->qt_rgn)) {
+ return *this;
+ } else {
+ QRegion result;
+ result.detach();
+ UnionRegion(d->qt_rgn, r.d->qt_rgn, *result.d->qt_rgn);
+ return result;
+ }
+}
+
+QRegion& QRegion::operator+=(const QRegion &r)
+{
+ if (isEmptyHelper(d->qt_rgn))
+ return *this = r;
+ if (isEmptyHelper(r.d->qt_rgn))
+ return *this;
+
+ if (d->qt_rgn->contains(*r.d->qt_rgn)) {
+ return *this;
+ } else if (r.d->qt_rgn->contains(*d->qt_rgn)) {
+ return *this = r;
+ } else if (d->qt_rgn->canAppend(r.d->qt_rgn)) {
+ detach();
+ d->qt_rgn->append(r.d->qt_rgn);
+ return *this;
+ } else if (d->qt_rgn->canPrepend(r.d->qt_rgn)) {
+ detach();
+ d->qt_rgn->prepend(r.d->qt_rgn);
+ return *this;
+ } else if (EqualRegion(d->qt_rgn, r.d->qt_rgn)) {
+ return *this;
+ }
+
+ return *this = unite(r);
+}
+
+/*
+ \fn QRegion QRegion::intersect(const QRegion &r) const
+ \obsolete
+
+ Use intersected(\a r) instead.
+*/
+
+/*
+ \fn QRegion QRegion::intersected(const QRegion &r) const
+ \since 4.2
+
+ Returns a region which is the intersection of this region and \a r.
+
+ \img rintersect.png Region Intersection
+
+ The figure shows the intersection of two elliptical regions.
+*/
+
+QRegion QRegion::intersect(const QRegion &r) const
+{
+ if (isEmptyHelper(d->qt_rgn) || isEmptyHelper(r.d->qt_rgn)
+ || !EXTENTCHECK(&d->qt_rgn->extents, &r.d->qt_rgn->extents))
+ return QRegion();
+
+ /* this is fully contained in r */
+ if (r.d->qt_rgn->contains(*d->qt_rgn))
+ return *this;
+
+ /* r is fully contained in this */
+ if (d->qt_rgn->contains(*r.d->qt_rgn))
+ return r;
+
+ if(r.d->qt_rgn->mode == QRegionPrivate::Single &&
+ d->qt_rgn->mode == QRegionPrivate::Single)
+ return QRegion(r.d->qt_rgn->single.intersected(d->qt_rgn->single));
+#ifdef QT_GREENPHONE_OPT
+ else if(r.d->qt_rgn->mode == QRegionPrivate::Single)
+ return intersect(r.d->qt_rgn->single);
+ else if(d->qt_rgn->mode == QRegionPrivate::Single)
+ return r.intersect(d->qt_rgn->single);
+#endif
+
+ QRegion result;
+ result.detach();
+ miRegionOp(*result.d->qt_rgn, d->qt_rgn, r.d->qt_rgn, miIntersectO, 0, 0);
+
+ /*
+ * Can't alter dest's extents before we call miRegionOp because
+ * it might be one of the source regions and miRegionOp depends
+ * on the extents of those regions being the same. Besides, this
+ * way there's no checking against rectangles that will be nuked
+ * due to coalescing, so we have to examine fewer rectangles.
+ */
+ miSetExtents(*result.d->qt_rgn);
+ return result;
+}
+
+#ifdef QT_GREENPHONE_OPT
+/*
+ \overload
+ */
+QRegion QRegion::intersect(const QRect &r) const
+{
+ // No intersection
+ if(r.isEmpty() || isEmpty() || !EXTENTCHECK(&r, &d->qt_rgn->extents))
+ return QRegion();
+
+ // This is fully contained in r
+ if(CONTAINSCHECK(r, d->qt_rgn->extents))
+ return *this;
+
+ // r is fully contained in this
+ if(CONTAINSCHECK(d->qt_rgn->innerRect, r))
+ return QRegion(r);
+
+ if(d->qt_rgn->mode == QRegionPrivate::Single) {
+ return QRegion(d->qt_rgn->single & r);
+ } else {
+ QRegion rv(*this);
+ rv.detach();
+
+ rv.d->qt_rgn->extents &= r;
+ rv.d->qt_rgn->innerRect &= r;
+ rv.d->qt_rgn->innerArea = rv.d->qt_rgn->innerRect.height() *
+ rv.d->qt_rgn->innerRect.width();
+
+ int numRects = 0;
+ for(int ii = 0; ii < rv.d->qt_rgn->numRects; ++ii) {
+ QRect result = rv.d->qt_rgn->rects[ii] & r;
+ if(!result.isEmpty())
+ rv.d->qt_rgn->rects[numRects++] = result;
+ }
+ rv.d->qt_rgn->numRects = numRects;
+ return rv;
+ }
+}
+
+/*
+ \overload
+ */
+const QRegion QRegion::operator&(const QRect &r) const
+{
+ return intersect(r);
+}
+
+/*
+ \overload
+ */
+QRegion& QRegion::operator&=(const QRect &r)
+{
+ if(isEmpty() || CONTAINSCHECK(r, d->qt_rgn->extents)) {
+ // Do nothing
+ } else if(r.isEmpty() || !EXTENTCHECK(&r, &d->qt_rgn->extents)) {
+ *this = QRegion();
+ } else if(CONTAINSCHECK(d->qt_rgn->innerRect, r)) {
+ *this = QRegion(r);
+ } else {
+ detach();
+ if(d->qt_rgn->mode == QRegionPrivate::Single) {
+ QRect result = d->qt_rgn->single & r;
+ d->qt_rgn->single = result;
+ d->qt_rgn->extents = result;
+ d->qt_rgn->innerRect = result;
+ d->qt_rgn->innerArea = result.height() * result.width();
+ } else {
+ d->qt_rgn->extents &= r;
+ d->qt_rgn->innerRect &= r;
+ d->qt_rgn->innerArea = d->qt_rgn->innerRect.height() *
+ d->qt_rgn->innerRect.width();
+
+ int numRects = 0;
+ for(int ii = 0; ii < d->qt_rgn->numRects; ++ii) {
+ QRect result = d->qt_rgn->rects[ii] & r;
+ if(!result.isEmpty())
+ d->qt_rgn->rects[numRects++] = result;
+ }
+ d->qt_rgn->numRects = numRects;
+ }
+ }
+ return *this;
+}
+#endif
+
+/*
+ \fn QRegion QRegion::subtract(const QRegion &r) const
+ \obsolete
+
+ Use subtracted(\a r) instead.
+*/
+
+/*
+ \fn QRegion QRegion::subtracted(const QRegion &r) const
+ \since 4.2
+
+ Returns a region which is \a r subtracted from this region.
+
+ \img rsubtract.png Region Subtraction
+
+ The figure shows the result when the ellipse on the right is
+ subtracted from the ellipse on the left (\c {left - right}).
+
+ \sa intersected(), united(), xored()
+*/
+
+QRegion QRegion::subtract(const QRegion &r) const
+{
+ if (isEmptyHelper(d->qt_rgn) || isEmptyHelper(r.d->qt_rgn))
+ return *this;
+ if (r.d->qt_rgn->contains(*d->qt_rgn))
+ return QRegion();
+ if (!EXTENTCHECK(&d->qt_rgn->extents, &r.d->qt_rgn->extents))
+ return *this;
+ if (EqualRegion(d->qt_rgn, r.d->qt_rgn))
+ return QRegion();
+
+ QRegion result;
+ result.detach();
+ SubtractRegion(d->qt_rgn, r.d->qt_rgn, *result.d->qt_rgn);
+ return result;
+}
+
+/*
+ \fn QRegion QRegion::eor(const QRegion &r) const
+ \obsolete
+
+ Use xored(\a r) instead.
+*/
+
+/*
+ \fn QRegion QRegion::xored(const QRegion &r) const
+ \since 4.2
+
+ Returns a region which is the exclusive or (XOR) of this region
+ and \a r.
+
+ \img rxor.png Region XORed
+
+ The figure shows the exclusive or of two elliptical regions.
+
+ \sa intersected(), united(), subtracted()
+*/
+
+QRegion QRegion::eor(const QRegion &r) const
+{
+ if (isEmptyHelper(d->qt_rgn)) {
+ return r;
+ } else if (isEmptyHelper(r.d->qt_rgn)) {
+ return *this;
+ } else if (!EXTENTCHECK(&d->qt_rgn->extents, &r.d->qt_rgn->extents)) {
+ return (*this + r);
+ } else if (EqualRegion(d->qt_rgn, r.d->qt_rgn)) {
+ return QRegion();
+ } else {
+ QRegion result;
+ result.detach();
+ XorRegion(d->qt_rgn, r.d->qt_rgn, *result.d->qt_rgn);
+ return result;
+ }
+}
+
+/*
+ Returns the bounding rectangle of this region. An empty region
+ gives a rectangle that is QRect::isNull().
+*/
+
+QRect QRegion::boundingRect() const
+{
+ if (isEmpty())
+ return QRect();
+ return d->qt_rgn->extents;
+}
+
+/* \internal
+ Returns true if \a rect is guaranteed to be fully contained in \a region.
+ A false return value does not guarantee the opposite.
+*/
+bool qt_region_strictContains(const QRegion &region, const QRect &rect)
+{
+ if (isEmptyHelper(region.d->qt_rgn) || !rect.isValid())
+ return false;
+
+#if 0 // TEST_INNERRECT
+ static bool guard = false;
+ if (guard)
+ return QRect();
+ guard = true;
+ QRegion inner = region.d->qt_rgn->innerRect;
+ Q_ASSERT((inner - region).isEmpty());
+ guard = false;
+
+ int maxArea = 0;
+ for (int i = 0; i < region.d->qt_rgn->numRects; ++i) {
+ const QRect r = region.d->qt_rgn->rects.at(i);
+ if (r.width() * r.height() > maxArea)
+ maxArea = r.width() * r.height();
+ }
+
+ if (maxArea > region.d->qt_rgn->innerArea) {
+ qDebug() << "not largest rectangle" << region << region.d->qt_rgn->innerRect;
+ }
+ Q_ASSERT(maxArea <= region.d->qt_rgn->innerArea);
+#endif
+
+ const QRect r1 = region.d->qt_rgn->innerRect;
+ return (rect.left() >= r1.left() && rect.right() <= r1.right()
+ && rect.top() >= r1.top() && rect.bottom() <= r1.bottom());
+}
+
+/*
+ Returns an array of non-overlapping rectangles that make up the
+ region.
+
+ The union of all the rectangles is equal to the original region.
+*/
+QVector<QRect> QRegion::rects() const
+{
+ if (d->qt_rgn) {
+ d->qt_rgn->vector();
+ d->qt_rgn->rects.resize(d->qt_rgn->numRects);
+ return d->qt_rgn->rects;
+ } else {
+ return QVector<QRect>();
+ }
+}
+
+/*
+ \fn void QRegion::setRects(const QRect *rects, int number)
+
+ Sets the region using the array of rectangles specified by \a rects and
+ \a number.
+ The rectangles \e must be optimally Y-X sorted and follow these restrictions:
+
+ \list
+ \o The rectangles must not intersect.
+ \o All rectangles with a given top coordinate must have the same height.
+ \o No two rectangles may abut horizontally (they should be combined
+ into a single wider rectangle in that case).
+ \o The rectangles must be sorted in ascending order, with Y as the major
+ sort key and X as the minor sort key.
+ \endlist
+ \omit
+ Only some platforms have these restrictions (Qt for Embedded Linux, X11 and Mac OS X).
+ \endomit
+*/
+void QRegion::setRects(const QRect *rects, int num)
+{
+ *this = QRegion();
+ if (!rects || num == 0 || (num == 1 && rects->isEmpty()))
+ return;
+
+ detach();
+
+ if(num == 1) {
+ d->qt_rgn->single = *rects;
+ d->qt_rgn->mode = QRegionPrivate::Single;
+ d->qt_rgn->numRects = num;
+ d->qt_rgn->extents = *rects;
+ d->qt_rgn->innerRect = *rects;
+ } else {
+ d->qt_rgn->mode = QRegionPrivate::Vector;
+ d->qt_rgn->rects.resize(num);
+ d->qt_rgn->numRects = num;
+ int left = INT_MAX,
+ right = INT_MIN,
+ top = INT_MAX,
+ bottom = INT_MIN;
+ for (int i = 0; i < num; ++i) {
+ const QRect &rect = rects[i];
+ d->qt_rgn->rects[i] = rect;
+ left = qMin(rect.left(), left);
+ right = qMax(rect.right(), right);
+ top = qMin(rect.top(), top);
+ bottom = qMax(rect.bottom(), bottom);
+ d->qt_rgn->updateInnerRect(rect);
+ }
+ d->qt_rgn->extents = QRect(QPoint(left, top), QPoint(right, bottom));
+ }
+}
+
+/*
+ Returns true if the region is equal to \a r; otherwise returns
+ false.
+*/
+
+bool QRegion::operator==(const QRegion &r) const
+{
+ if (!d->qt_rgn || !r.d->qt_rgn)
+ return r.d->qt_rgn == d->qt_rgn;
+
+ if (d == r.d)
+ return true;
+ else
+ return EqualRegion(d->qt_rgn, r.d->qt_rgn);
+}
+
+#ifdef QT_GREENPHONE_OPT
+bool QRegion::isRect() const
+{
+ return d->qt_rgn && d->qt_rgn->mode == QRegionPrivate::Single;
+}
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qregion_s60.cpp b/src/gui/painting/qregion_s60.cpp
new file mode 100644
index 0000000000..eafff1b965
--- /dev/null
+++ b/src/gui/painting/qregion_s60.cpp
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbitmap.h"
+#include "qbuffer.h"
+#include "qimage.h"
+#include "qpolygon.h"
+#include "qregion.h"
+
+QT_BEGIN_NAMESPACE
+
+QRegion::QRegionData QRegion::shared_empty = { Q_BASIC_ATOMIC_INITIALIZER(1), 0 };
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qregion_win.cpp b/src/gui/painting/qregion_win.cpp
new file mode 100644
index 0000000000..3466b62cbd
--- /dev/null
+++ b/src/gui/painting/qregion_win.cpp
@@ -0,0 +1,149 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qatomic.h"
+#include "qbitmap.h"
+#include "qbuffer.h"
+#include "qimage.h"
+#include "qpolygon.h"
+#include "qregion.h"
+#include "qt_windows.h"
+#include "qpainterpath.h"
+
+QT_BEGIN_NAMESPACE
+
+QRegion::QRegionData QRegion::shared_empty = { Q_BASIC_ATOMIC_INITIALIZER(1), 0, 0 };
+
+HRGN qt_tryCreateRegion(QRegion::RegionType type, int left, int top, int right, int bottom)
+{
+ const int tries = 10;
+ for (int i = 0; i < tries; ++i) {
+ HRGN region = 0;
+ switch (type) {
+ case QRegion::Rectangle:
+ region = CreateRectRgn(left, top, right, bottom);
+ break;
+ case QRegion::Ellipse:
+#ifndef Q_OS_WINCE
+ region = CreateEllipticRgn(left, top, right, bottom);
+#endif
+ break;
+ }
+ if (region) {
+ if (GetRegionData(region, 0, 0))
+ return region;
+ else
+ DeleteObject(region);
+ }
+ }
+ return 0;
+}
+
+QRegion qt_region_from_HRGN(HRGN rgn)
+{
+ int numBytes = GetRegionData(rgn, 0, 0);
+ if (numBytes == 0)
+ return QRegion();
+
+ char *buf = new char[numBytes];
+ if (buf == 0)
+ return QRegion();
+
+ RGNDATA *rd = reinterpret_cast<RGNDATA*>(buf);
+ if (GetRegionData(rgn, numBytes, rd) == 0) {
+ delete [] buf;
+ return QRegion();
+ }
+
+ QRegion region;
+ RECT *r = reinterpret_cast<RECT*>(rd->Buffer);
+ for (uint i = 0; i < rd->rdh.nCount; ++i) {
+ QRect rect;
+ rect.setCoords(r->left, r->top, r->right - 1, r->bottom - 1);
+ ++r;
+ region |= rect;
+ }
+
+ delete [] buf;
+
+ return region;
+}
+
+void qt_win_dispose_rgn(HRGN r)
+{
+ if (r)
+ DeleteObject(r);
+}
+
+static void qt_add_rect(HRGN &winRegion, QRect r)
+{
+ HRGN rgn = CreateRectRgn(r.left(), r.top(), r.x() + r.width(), r.y() + r.height());
+ if (rgn) {
+ HRGN dest = CreateRectRgn(0,0,0,0);
+ int result = CombineRgn(dest, winRegion, rgn, RGN_OR);
+ if (result) {
+ DeleteObject(winRegion);
+ winRegion = dest;
+ }
+ DeleteObject(rgn);
+ }
+}
+
+void QRegion::ensureHandle() const
+{
+ if (d->rgn)
+ DeleteObject(d->rgn);
+ d->rgn = CreateRectRgn(0,0,0,0);
+ if (d->qt_rgn) {
+ if (d->qt_rgn->numRects == 1) {
+ QRect r = d->qt_rgn->extents;
+ qt_add_rect(d->rgn, r);
+ return;
+ }
+ for (int i = 0;i < d->qt_rgn->numRects;i++) {
+ QRect r = d->qt_rgn->rects.at(i);
+ qt_add_rect(d->rgn, r);
+ }
+ }
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qregion_x11.cpp b/src/gui/painting/qregion_x11.cpp
new file mode 100644
index 0000000000..ef4e844bfa
--- /dev/null
+++ b/src/gui/painting/qregion_x11.cpp
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qt_x11_p.h>
+
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+QRegion::QRegionData QRegion::shared_empty = {Q_BASIC_ATOMIC_INITIALIZER(1), 0, 0, 0};
+
+void QRegion::updateX11Region() const
+{
+ d->rgn = XCreateRegion();
+ if (!d->qt_rgn)
+ return;
+
+ int n = d->qt_rgn->numRects;
+ const QRect *rect = (n == 1 ? &d->qt_rgn->extents : d->qt_rgn->rects.constData());
+ while (n--) {
+ XRectangle r;
+ r.x = qMax(SHRT_MIN, rect->x());
+ r.y = qMax(SHRT_MIN, rect->y());
+ r.width = qMin((int)USHRT_MAX, rect->width());
+ r.height = qMin((int)USHRT_MAX, rect->height());
+ XUnionRectWithRegion(&r, d->rgn, d->rgn);
+ ++rect;
+ }
+}
+
+void *QRegion::clipRectangles(int &num) const
+{
+ if (!d->xrectangles && !(d == &shared_empty || d->qt_rgn->numRects == 0)) {
+ XRectangle *r = static_cast<XRectangle*>(malloc(d->qt_rgn->numRects * sizeof(XRectangle)));
+ d->xrectangles = r;
+ int n = d->qt_rgn->numRects;
+ const QRect *rect = (n == 1 ? &d->qt_rgn->extents : d->qt_rgn->rects.constData());
+ while (n--) {
+ r->x = qMax(SHRT_MIN, rect->x());
+ r->y = qMax(SHRT_MIN, rect->y());
+ r->width = qMin((int)USHRT_MAX, rect->width());
+ r->height = qMin((int)USHRT_MAX, rect->height());
+ ++r;
+ ++rect;
+ }
+ }
+ if (d == &shared_empty || d->qt_rgn->numRects == 0)
+ num = 0;
+ else
+ num = d->qt_rgn->numRects;
+ return d->xrectangles;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qrgb.h b/src/gui/painting/qrgb.h
new file mode 100644
index 0000000000..df6a920e80
--- /dev/null
+++ b/src/gui/painting/qrgb.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRGB_H
+#define QRGB_H
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+typedef unsigned int QRgb; // RGB triplet
+
+const QRgb RGB_MASK = 0x00ffffff; // masks RGB values
+
+Q_GUI_EXPORT_INLINE int qRed(QRgb rgb) // get red part of RGB
+{ return ((rgb >> 16) & 0xff); }
+
+Q_GUI_EXPORT_INLINE int qGreen(QRgb rgb) // get green part of RGB
+{ return ((rgb >> 8) & 0xff); }
+
+Q_GUI_EXPORT_INLINE int qBlue(QRgb rgb) // get blue part of RGB
+{ return (rgb & 0xff); }
+
+Q_GUI_EXPORT_INLINE int qAlpha(QRgb rgb) // get alpha part of RGBA
+{ return rgb >> 24; }
+
+Q_GUI_EXPORT_INLINE QRgb qRgb(int r, int g, int b)// set RGB value
+{ return (0xffu << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff); }
+
+Q_GUI_EXPORT_INLINE QRgb qRgba(int r, int g, int b, int a)// set RGBA value
+{ return ((a & 0xff) << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff); }
+
+Q_GUI_EXPORT_INLINE int qGray(int r, int g, int b)// convert R,G,B to gray 0..255
+{ return (r*11+g*16+b*5)/32; }
+
+Q_GUI_EXPORT_INLINE int qGray(QRgb rgb) // convert RGB to gray 0..255
+{ return qGray(qRed(rgb), qGreen(rgb), qBlue(rgb)); }
+
+Q_GUI_EXPORT_INLINE bool qIsGray(QRgb rgb)
+{ return qRed(rgb) == qGreen(rgb) && qRed(rgb) == qBlue(rgb); }
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QRGB_H
diff --git a/src/gui/painting/qstroker.cpp b/src/gui/painting/qstroker.cpp
new file mode 100644
index 0000000000..fca46b45f7
--- /dev/null
+++ b/src/gui/painting/qstroker.cpp
@@ -0,0 +1,1263 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "private/qstroker_p.h"
+#include "private/qbezier_p.h"
+#include "private/qmath_p.h"
+#include "qline.h"
+#include "qtransform.h"
+#include <qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+// #define QPP_STROKE_DEBUG
+
+class QSubpathForwardIterator
+{
+public:
+ QSubpathForwardIterator(const QDataBuffer<QStrokerOps::Element> *path)
+ : m_path(path), m_pos(0) { }
+ inline int position() const { return m_pos; }
+ inline bool hasNext() const { return m_pos < m_path->size(); }
+ inline QStrokerOps::Element next() { Q_ASSERT(hasNext()); return m_path->at(m_pos++); }
+
+private:
+ const QDataBuffer<QStrokerOps::Element> *m_path;
+ int m_pos;
+};
+
+class QSubpathBackwardIterator
+{
+public:
+ QSubpathBackwardIterator(const QDataBuffer<QStrokerOps::Element> *path)
+ : m_path(path), m_pos(path->size() - 1) { }
+
+ inline int position() const { return m_pos; }
+
+ inline bool hasNext() const { return m_pos >= 0; }
+
+ inline QStrokerOps::Element next()
+ {
+ Q_ASSERT(hasNext());
+
+ QStrokerOps::Element ce = m_path->at(m_pos); // current element
+
+ if (m_pos == m_path->size() - 1) {
+ --m_pos;
+ ce.type = QPainterPath::MoveToElement;
+ return ce;
+ }
+
+ const QStrokerOps::Element &pe = m_path->at(m_pos + 1); // previous element
+
+ switch (pe.type) {
+ case QPainterPath::LineToElement:
+ ce.type = QPainterPath::LineToElement;
+ break;
+ case QPainterPath::CurveToDataElement:
+ // First control point?
+ if (ce.type == QPainterPath::CurveToElement) {
+ ce.type = QPainterPath::CurveToDataElement;
+ } else { // Second control point then
+ ce.type = QPainterPath::CurveToElement;
+ }
+ break;
+ case QPainterPath::CurveToElement:
+ ce.type = QPainterPath::CurveToDataElement;
+ break;
+ default:
+ qWarning("QSubpathReverseIterator::next: Case %d unhandled", ce.type);
+ break;
+ }
+ --m_pos;
+
+ return ce;
+ }
+
+private:
+ const QDataBuffer<QStrokerOps::Element> *m_path;
+ int m_pos;
+};
+
+class QSubpathFlatIterator
+{
+public:
+ QSubpathFlatIterator(const QDataBuffer<QStrokerOps::Element> *path, qreal threshold)
+ : m_path(path), m_pos(0), m_curve_index(-1), m_curve_threshold(threshold) { }
+
+ inline bool hasNext() const { return m_curve_index >= 0 || m_pos < m_path->size(); }
+
+ QStrokerOps::Element next()
+ {
+ Q_ASSERT(hasNext());
+
+ if (m_curve_index >= 0) {
+ QStrokerOps::Element e = { QPainterPath::LineToElement,
+ qt_real_to_fixed(m_curve.at(m_curve_index).x()),
+ qt_real_to_fixed(m_curve.at(m_curve_index).y())
+ };
+ ++m_curve_index;
+ if (m_curve_index >= m_curve.size())
+ m_curve_index = -1;
+ return e;
+ }
+
+ QStrokerOps::Element e = m_path->at(m_pos);
+ if (e.isCurveTo()) {
+ Q_ASSERT(m_pos > 0);
+ Q_ASSERT(m_pos < m_path->size());
+
+ m_curve = QBezier::fromPoints(QPointF(qt_fixed_to_real(m_path->at(m_pos-1).x),
+ qt_fixed_to_real(m_path->at(m_pos-1).y)),
+ QPointF(qt_fixed_to_real(e.x),
+ qt_fixed_to_real(e.y)),
+ QPointF(qt_fixed_to_real(m_path->at(m_pos+1).x),
+ qt_fixed_to_real(m_path->at(m_pos+1).y)),
+ QPointF(qt_fixed_to_real(m_path->at(m_pos+2).x),
+ qt_fixed_to_real(m_path->at(m_pos+2).y))).toPolygon(m_curve_threshold);
+ m_curve_index = 1;
+ e.type = QPainterPath::LineToElement;
+ e.x = m_curve.at(0).x();
+ e.y = m_curve.at(0).y();
+ m_pos += 2;
+ }
+ Q_ASSERT(e.isLineTo() || e.isMoveTo());
+ ++m_pos;
+ return e;
+ }
+
+private:
+ const QDataBuffer<QStrokerOps::Element> *m_path;
+ int m_pos;
+ QPolygonF m_curve;
+ int m_curve_index;
+ qreal m_curve_threshold;
+};
+
+template <class Iterator> bool qt_stroke_side(Iterator *it, QStroker *stroker,
+ bool capFirst, QLineF *startTangent);
+
+/*******************************************************************************
+ * QLineF::angle gives us the smalles angle between two lines. Here we
+ * want to identify the line's angle direction on the unit circle.
+ */
+static inline qreal adapted_angle_on_x(const QLineF &line)
+{
+ qreal angle = line.angle(QLineF(0, 0, 1, 0));
+ if (line.dy() > 0)
+ angle = 360 - angle;
+ return angle;
+}
+
+QStrokerOps::QStrokerOps()
+ : m_elements(0)
+ , m_curveThreshold(qt_real_to_fixed(0.25))
+ , m_dashThreshold(qt_real_to_fixed(0.25))
+ , m_customData(0)
+ , m_moveTo(0)
+ , m_lineTo(0)
+ , m_cubicTo(0)
+{
+}
+
+QStrokerOps::~QStrokerOps()
+{
+}
+
+/*!
+ Prepares the stroker. Call this function once before starting a
+ stroke by calling moveTo, lineTo or cubicTo.
+
+ The \a customData is passed back through that callback functions
+ and can be used by the user to for instance maintain state
+ information.
+*/
+void QStrokerOps::begin(void *customData)
+{
+ m_customData = customData;
+ m_elements.reset();
+}
+
+
+/*!
+ Finishes the stroke. Call this function once when an entire
+ primitive has been stroked.
+*/
+void QStrokerOps::end()
+{
+ if (m_elements.size() > 1)
+ processCurrentSubpath();
+ m_customData = 0;
+}
+
+/*!
+ Convenience function that decomposes \a path into begin(),
+ moveTo(), lineTo(), curevTo() and end() calls.
+
+ The \a customData parameter is used in the callback functions
+
+ The \a matrix is used to transform the points before input to the
+ stroker.
+
+ \sa begin()
+*/
+void QStrokerOps::strokePath(const QPainterPath &path, void *customData, const QTransform &matrix)
+{
+ if (path.isEmpty())
+ return;
+
+ setCurveThresholdFromTransform(QTransform());
+ begin(customData);
+ int count = path.elementCount();
+ if (matrix.isIdentity()) {
+ for (int i=0; i<count; ++i) {
+ const QPainterPath::Element &e = path.elementAt(i);
+ switch (e.type) {
+ case QPainterPath::MoveToElement:
+ moveTo(qt_real_to_fixed(e.x), qt_real_to_fixed(e.y));
+ break;
+ case QPainterPath::LineToElement:
+ lineTo(qt_real_to_fixed(e.x), qt_real_to_fixed(e.y));
+ break;
+ case QPainterPath::CurveToElement:
+ {
+ const QPainterPath::Element &cp2 = path.elementAt(++i);
+ const QPainterPath::Element &ep = path.elementAt(++i);
+ cubicTo(qt_real_to_fixed(e.x), qt_real_to_fixed(e.y),
+ qt_real_to_fixed(cp2.x), qt_real_to_fixed(cp2.y),
+ qt_real_to_fixed(ep.x), qt_real_to_fixed(ep.y));
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ } else {
+ for (int i=0; i<count; ++i) {
+ const QPainterPath::Element &e = path.elementAt(i);
+ QPointF pt = QPointF(e.x, e.y) * matrix;
+ switch (e.type) {
+ case QPainterPath::MoveToElement:
+ moveTo(qt_real_to_fixed(pt.x()), qt_real_to_fixed(pt.y()));
+ break;
+ case QPainterPath::LineToElement:
+ lineTo(qt_real_to_fixed(pt.x()), qt_real_to_fixed(pt.y()));
+ break;
+ case QPainterPath::CurveToElement:
+ {
+ QPointF cp2 = ((QPointF) path.elementAt(++i)) * matrix;
+ QPointF ep = ((QPointF) path.elementAt(++i)) * matrix;
+ cubicTo(qt_real_to_fixed(pt.x()), qt_real_to_fixed(pt.y()),
+ qt_real_to_fixed(cp2.x()), qt_real_to_fixed(cp2.y()),
+ qt_real_to_fixed(ep.x()), qt_real_to_fixed(ep.y()));
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ end();
+}
+
+/*!
+ Convenience function for stroking a polygon of the \a pointCount
+ first points in \a points. If \a implicit_close is set to true a
+ line is implictly drawn between the first and last point in the
+ polygon. Typically true for polygons and false for polylines.
+
+ The \a matrix is used to transform the points before they enter the
+ stroker.
+
+ \sa begin()
+*/
+
+void QStrokerOps::strokePolygon(const QPointF *points, int pointCount, bool implicit_close,
+ void *data, const QTransform &matrix)
+{
+ if (!pointCount)
+ return;
+
+ setCurveThresholdFromTransform(QTransform());
+ begin(data);
+ if (matrix.isIdentity()) {
+ moveTo(qt_real_to_fixed(points[0].x()), qt_real_to_fixed(points[0].y()));
+ for (int i=1; i<pointCount; ++i)
+ lineTo(qt_real_to_fixed(points[i].x()),
+ qt_real_to_fixed(points[i].y()));
+ if (implicit_close)
+ lineTo(qt_real_to_fixed(points[0].x()), qt_real_to_fixed(points[0].y()));
+ } else {
+ QPointF start = points[0] * matrix;
+ moveTo(qt_real_to_fixed(start.x()), qt_real_to_fixed(start.y()));
+ for (int i=1; i<pointCount; ++i) {
+ QPointF pt = points[i] * matrix;
+ lineTo(qt_real_to_fixed(pt.x()), qt_real_to_fixed(pt.y()));
+ }
+ if (implicit_close)
+ lineTo(qt_real_to_fixed(start.x()), qt_real_to_fixed(start.y()));
+ }
+ end();
+}
+
+/*!
+ Convenience function for stroking an ellipse with bounding rect \a
+ rect. The \a matrix is used to transform the coordinates before
+ they enter the stroker.
+*/
+void QStrokerOps::strokeEllipse(const QRectF &rect, void *data, const QTransform &matrix)
+{
+ int count = 0;
+ QPointF pts[12];
+ QPointF start = qt_curves_for_arc(rect, 0, -360, pts, &count);
+ Q_ASSERT(count == 12); // a perfect circle..
+
+ if (!matrix.isIdentity()) {
+ start = start * matrix;
+ for (int i=0; i<12; ++i) {
+ pts[i] = pts[i] * matrix;
+ }
+ }
+
+ setCurveThresholdFromTransform(QTransform());
+ begin(data);
+ moveTo(qt_real_to_fixed(start.x()), qt_real_to_fixed(start.y()));
+ for (int i=0; i<12; i+=3) {
+ cubicTo(qt_real_to_fixed(pts[i].x()), qt_real_to_fixed(pts[i].y()),
+ qt_real_to_fixed(pts[i+1].x()), qt_real_to_fixed(pts[i+1].y()),
+ qt_real_to_fixed(pts[i+2].x()), qt_real_to_fixed(pts[i+2].y()));
+ }
+ end();
+}
+
+
+QStroker::QStroker()
+ : m_capStyle(SquareJoin), m_joinStyle(FlatJoin),
+ m_back1X(0), m_back1Y(0),
+ m_back2X(0), m_back2Y(0)
+{
+ m_strokeWidth = qt_real_to_fixed(1);
+ m_miterLimit = qt_real_to_fixed(2);
+}
+
+QStroker::~QStroker()
+{
+}
+
+Qt::PenCapStyle QStroker::capForJoinMode(LineJoinMode mode)
+{
+ if (mode == FlatJoin) return Qt::FlatCap;
+ else if (mode == SquareJoin) return Qt::SquareCap;
+ else return Qt::RoundCap;
+}
+
+QStroker::LineJoinMode QStroker::joinModeForCap(Qt::PenCapStyle style)
+{
+ if (style == Qt::FlatCap) return FlatJoin;
+ else if (style == Qt::SquareCap) return SquareJoin;
+ else return RoundCap;
+}
+
+Qt::PenJoinStyle QStroker::joinForJoinMode(LineJoinMode mode)
+{
+ if (mode == FlatJoin) return Qt::BevelJoin;
+ else if (mode == MiterJoin) return Qt::MiterJoin;
+ else if (mode == SvgMiterJoin) return Qt::SvgMiterJoin;
+ else return Qt::RoundJoin;
+}
+
+QStroker::LineJoinMode QStroker::joinModeForJoin(Qt::PenJoinStyle joinStyle)
+{
+ if (joinStyle == Qt::BevelJoin) return FlatJoin;
+ else if (joinStyle == Qt::MiterJoin) return MiterJoin;
+ else if (joinStyle == Qt::SvgMiterJoin) return SvgMiterJoin;
+ else return RoundJoin;
+}
+
+
+/*!
+ This function is called to stroke the currently built up
+ subpath. The subpath is cleared when the function completes.
+*/
+void QStroker::processCurrentSubpath()
+{
+ Q_ASSERT(!m_elements.isEmpty());
+ Q_ASSERT(m_elements.first().type == QPainterPath::MoveToElement);
+ Q_ASSERT(m_elements.size() > 1);
+
+ QSubpathForwardIterator fwit(&m_elements);
+ QSubpathBackwardIterator bwit(&m_elements);
+
+ QLineF fwStartTangent, bwStartTangent;
+
+ bool fwclosed = qt_stroke_side(&fwit, this, false, &fwStartTangent);
+ bool bwclosed = qt_stroke_side(&bwit, this, !fwclosed, &bwStartTangent);
+
+ if (!bwclosed)
+ joinPoints(m_elements.at(0).x, m_elements.at(0).y, fwStartTangent, m_capStyle);
+}
+
+
+/*!
+ \internal
+*/
+void QStroker::joinPoints(qfixed focal_x, qfixed focal_y, const QLineF &nextLine, LineJoinMode join)
+{
+#ifdef QPP_STROKE_DEBUG
+ printf(" -----> joinPoints: around=(%.0f, %.0f), next_p1=(%.0f, %.f) next_p2=(%.0f, %.f)\n",
+ qt_fixed_to_real(focal_x),
+ qt_fixed_to_real(focal_y),
+ nextLine.x1(), nextLine.y1(), nextLine.x2(), nextLine.y2());
+#endif
+ // points connected already, don't join
+
+#if !defined (QFIXED_26_6) && !defined (Q_FIXED_32_32)
+ if (qFuzzyCompare(m_back1X, nextLine.x1()) && qFuzzyCompare(m_back1Y, nextLine.y1()))
+ return;
+#else
+ if (m_back1X == qt_real_to_fixed(nextLine.x1())
+ && m_back1Y == qt_real_to_fixed(nextLine.y1())) {
+ return;
+ }
+#endif
+
+ if (join == FlatJoin) {
+ QLineF prevLine(qt_fixed_to_real(m_back2X), qt_fixed_to_real(m_back2Y),
+ qt_fixed_to_real(m_back1X), qt_fixed_to_real(m_back1Y));
+ QPointF isect;
+ QLineF::IntersectType type = prevLine.intersect(nextLine, &isect);
+ QLineF shortCut(prevLine.p2(), nextLine.p1());
+ qreal angle = shortCut.angleTo(prevLine);
+ if (type == QLineF::BoundedIntersection || (angle > 90 && !qFuzzyCompare(angle, (qreal)90))) {
+ emitLineTo(focal_x, focal_y);
+ emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
+ return;
+ }
+ emitLineTo(qt_real_to_fixed(nextLine.x1()),
+ qt_real_to_fixed(nextLine.y1()));
+
+ } else {
+ QLineF prevLine(qt_fixed_to_real(m_back2X), qt_fixed_to_real(m_back2Y),
+ qt_fixed_to_real(m_back1X), qt_fixed_to_real(m_back1Y));
+
+ QPointF isect;
+ QLineF::IntersectType type = prevLine.intersect(nextLine, &isect);
+
+ if (join == MiterJoin) {
+ qreal appliedMiterLimit = qt_fixed_to_real(m_strokeWidth * m_miterLimit);
+
+ // If we are on the inside, do the short cut...
+ QLineF shortCut(prevLine.p2(), nextLine.p1());
+ qreal angle = shortCut.angleTo(prevLine);
+ if (type == QLineF::BoundedIntersection || (angle > 90 && !qFuzzyCompare(angle, (qreal)90))) {
+ emitLineTo(focal_x, focal_y);
+ emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
+ return;
+ }
+ QLineF miterLine(QPointF(qt_fixed_to_real(m_back1X),
+ qt_fixed_to_real(m_back1Y)), isect);
+ if (type == QLineF::NoIntersection || miterLine.length() > appliedMiterLimit) {
+ QLineF l1(prevLine);
+ l1.setLength(appliedMiterLimit);
+ l1.translate(prevLine.dx(), prevLine.dy());
+
+ QLineF l2(nextLine);
+ l2.setLength(appliedMiterLimit);
+ l2.translate(-l2.dx(), -l2.dy());
+
+ emitLineTo(qt_real_to_fixed(l1.x2()), qt_real_to_fixed(l1.y2()));
+ emitLineTo(qt_real_to_fixed(l2.x1()), qt_real_to_fixed(l2.y1()));
+ emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
+ } else {
+ emitLineTo(qt_real_to_fixed(isect.x()), qt_real_to_fixed(isect.y()));
+ emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
+ }
+
+ } else if (join == SquareJoin) {
+ qfixed offset = m_strokeWidth / 2;
+
+ QLineF l1(prevLine);
+ l1.translate(l1.dx(), l1.dy());
+ l1.setLength(qt_fixed_to_real(offset));
+ QLineF l2(nextLine.p2(), nextLine.p1());
+ l2.translate(l2.dx(), l2.dy());
+ l2.setLength(qt_fixed_to_real(offset));
+ emitLineTo(qt_real_to_fixed(l1.x2()), qt_real_to_fixed(l1.y2()));
+ emitLineTo(qt_real_to_fixed(l2.x2()), qt_real_to_fixed(l2.y2()));
+ emitLineTo(qt_real_to_fixed(l2.x1()), qt_real_to_fixed(l2.y1()));
+
+ } else if (join == RoundJoin) {
+ qfixed offset = m_strokeWidth / 2;
+
+ QLineF shortCut(prevLine.p2(), nextLine.p1());
+ qreal angle = shortCut.angleTo(prevLine);
+ if (type == QLineF::BoundedIntersection || (angle > 90 && !qFuzzyCompare(angle, (qreal)90))) {
+ emitLineTo(focal_x, focal_y);
+ emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
+ return;
+ }
+ qreal l1_on_x = adapted_angle_on_x(prevLine);
+ qreal l2_on_x = adapted_angle_on_x(nextLine);
+
+ qreal sweepLength = qAbs(l2_on_x - l1_on_x);
+
+ int point_count;
+ QPointF curves[15];
+
+ QPointF curve_start =
+ qt_curves_for_arc(QRectF(qt_fixed_to_real(focal_x - offset),
+ qt_fixed_to_real(focal_y - offset),
+ qt_fixed_to_real(offset * 2),
+ qt_fixed_to_real(offset * 2)),
+ l1_on_x + 90, -sweepLength,
+ curves, &point_count);
+
+// // line to the beginning of the arc segment, (should not be needed).
+// emitLineTo(qt_real_to_fixed(curve_start.x()), qt_real_to_fixed(curve_start.y()));
+
+ for (int i=0; i<point_count; i+=3) {
+ emitCubicTo(qt_real_to_fixed(curves[i].x()),
+ qt_real_to_fixed(curves[i].y()),
+ qt_real_to_fixed(curves[i+1].x()),
+ qt_real_to_fixed(curves[i+1].y()),
+ qt_real_to_fixed(curves[i+2].x()),
+ qt_real_to_fixed(curves[i+2].y()));
+ }
+
+ // line to the end of the arc segment, (should also not be needed).
+ emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
+
+ // Same as round join except we know its 180 degrees. Can also optimize this
+ // later based on the addEllipse logic
+ } else if (join == RoundCap) {
+ qfixed offset = m_strokeWidth / 2;
+
+ // first control line
+ QLineF l1 = prevLine;
+ l1.translate(l1.dx(), l1.dy());
+ l1.setLength(QT_PATH_KAPPA * offset);
+
+ // second control line, find through normal between prevLine and focal.
+ QLineF l2(qt_fixed_to_real(focal_x), qt_fixed_to_real(focal_y),
+ prevLine.x2(), prevLine.y2());
+ l2.translate(-l2.dy(), l2.dx());
+ l2.setLength(QT_PATH_KAPPA * offset);
+
+ emitCubicTo(qt_real_to_fixed(l1.x2()),
+ qt_real_to_fixed(l1.y2()),
+ qt_real_to_fixed(l2.x2()),
+ qt_real_to_fixed(l2.y2()),
+ qt_real_to_fixed(l2.x1()),
+ qt_real_to_fixed(l2.y1()));
+
+ // move so that it matches
+ l2 = QLineF(l2.x1(), l2.y1(), l2.x1()-l2.dx(), l2.y1()-l2.dy());
+
+ // last line is parallel to l1 so just shift it down.
+ l1.translate(nextLine.x1() - l1.x1(), nextLine.y1() - l1.y1());
+
+ emitCubicTo(qt_real_to_fixed(l2.x2()),
+ qt_real_to_fixed(l2.y2()),
+ qt_real_to_fixed(l1.x2()),
+ qt_real_to_fixed(l1.y2()),
+ qt_real_to_fixed(l1.x1()),
+ qt_real_to_fixed(l1.y1()));
+ } else if (join == SvgMiterJoin) {
+ QLineF shortCut(prevLine.p2(), nextLine.p1());
+ qreal angle = shortCut.angleTo(prevLine);
+ if (type == QLineF::BoundedIntersection || (angle > 90 && !qFuzzyCompare(angle, (qreal)90))) {
+ emitLineTo(focal_x, focal_y);
+ emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
+ return;
+ }
+ QLineF miterLine(QPointF(qt_fixed_to_real(focal_x),
+ qt_fixed_to_real(focal_y)), isect);
+ if (type == QLineF::NoIntersection || miterLine.length() > qt_fixed_to_real(m_strokeWidth * m_miterLimit) / 2) {
+ emitLineTo(qt_real_to_fixed(nextLine.x1()),
+ qt_real_to_fixed(nextLine.y1()));
+ } else {
+ emitLineTo(qt_real_to_fixed(isect.x()), qt_real_to_fixed(isect.y()));
+ emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
+ }
+ } else {
+ Q_ASSERT(!"QStroker::joinPoints(), bad join style...");
+ }
+ }
+}
+
+
+/*
+ Strokes a subpath side using the \a it as source. Results are put into
+ \a stroke. The function returns true if the subpath side was closed.
+ If \a capFirst is true, we will use capPoints instead of joinPoints to
+ connect the first segment, other segments will be joined using joinPoints.
+ This is to put capping in order...
+*/
+template <class Iterator> bool qt_stroke_side(Iterator *it,
+ QStroker *stroker,
+ bool capFirst,
+ QLineF *startTangent)
+{
+ // Used in CurveToElement section below.
+ const int MAX_OFFSET = 16;
+ QBezier offsetCurves[MAX_OFFSET];
+
+ Q_ASSERT(it->hasNext()); // The initaial move to
+ QStrokerOps::Element first_element = it->next();
+ Q_ASSERT(first_element.isMoveTo());
+
+ qfixed2d start = first_element;
+
+#ifdef QPP_STROKE_DEBUG
+ qDebug(" -> (side) [%.2f, %.2f], startPos=%d",
+ qt_fixed_to_real(start.x),
+ qt_fixed_to_real(start.y));
+#endif
+
+ qfixed2d prev = start;
+
+ bool first = true;
+
+ qfixed offset = stroker->strokeWidth() / 2;
+
+ while (it->hasNext()) {
+ QStrokerOps::Element e = it->next();
+
+ // LineToElement
+ if (e.isLineTo()) {
+#ifdef QPP_STROKE_DEBUG
+ qDebug("\n ---> (side) lineto [%.2f, %.2f]", e.x, e.y);
+#endif
+ QLineF line(qt_fixed_to_real(prev.x), qt_fixed_to_real(prev.y),
+ qt_fixed_to_real(e.x), qt_fixed_to_real(e.y));
+ if (line.p1() != line.p2()) {
+ QLineF normal = line.normalVector();
+ normal.setLength(offset);
+ line.translate(normal.dx(), normal.dy());
+
+ // If we are starting a new subpath, move to correct starting point.
+ if (first) {
+ if (capFirst)
+ stroker->joinPoints(prev.x, prev.y, line, stroker->capStyleMode());
+ else
+ stroker->emitMoveTo(qt_real_to_fixed(line.x1()), qt_real_to_fixed(line.y1()));
+ *startTangent = line;
+ first = false;
+ } else {
+ stroker->joinPoints(prev.x, prev.y, line, stroker->joinStyleMode());
+ }
+
+ // Add the stroke for this line.
+ stroker->emitLineTo(qt_real_to_fixed(line.x2()),
+ qt_real_to_fixed(line.y2()));
+ prev = e;
+ }
+
+ // CurveToElement
+ } else if (e.isCurveTo()) {
+ QStrokerOps::Element cp2 = it->next(); // control point 2
+ QStrokerOps::Element ep = it->next(); // end point
+
+#ifdef QPP_STROKE_DEBUG
+ qDebug("\n ---> (side) cubicTo [%.2f, %.2f]",
+ qt_fixed_to_real(ep.x),
+ qt_fixed_to_real(ep.y));
+#endif
+
+ QBezier bezier =
+ QBezier::fromPoints(QPointF(qt_fixed_to_real(prev.x), qt_fixed_to_real(prev.y)),
+ QPointF(qt_fixed_to_real(e.x), qt_fixed_to_real(e.y)),
+ QPointF(qt_fixed_to_real(cp2.x), qt_fixed_to_real(cp2.y)),
+ QPointF(qt_fixed_to_real(ep.x), qt_fixed_to_real(ep.y)));
+
+ int count = bezier.shifted(offsetCurves,
+ MAX_OFFSET,
+ offset,
+ stroker->curveThreshold());
+
+ if (count) {
+ // If we are starting a new subpath, move to correct starting point
+ QLineF tangent = bezier.startTangent();
+ tangent.translate(offsetCurves[0].pt1() - bezier.pt1());
+ if (first) {
+ QPointF pt = offsetCurves[0].pt1();
+ if (capFirst) {
+ stroker->joinPoints(prev.x, prev.y,
+ tangent,
+ stroker->capStyleMode());
+ } else {
+ stroker->emitMoveTo(qt_real_to_fixed(pt.x()),
+ qt_real_to_fixed(pt.y()));
+ }
+ *startTangent = tangent;
+ first = false;
+ } else {
+ stroker->joinPoints(prev.x, prev.y,
+ tangent,
+ stroker->joinStyleMode());
+ }
+
+ // Add these beziers
+ for (int i=0; i<count; ++i) {
+ QPointF cp1 = offsetCurves[i].pt2();
+ QPointF cp2 = offsetCurves[i].pt3();
+ QPointF ep = offsetCurves[i].pt4();
+ stroker->emitCubicTo(qt_real_to_fixed(cp1.x()), qt_real_to_fixed(cp1.y()),
+ qt_real_to_fixed(cp2.x()), qt_real_to_fixed(cp2.y()),
+ qt_real_to_fixed(ep.x()), qt_real_to_fixed(ep.y()));
+ }
+ }
+
+ prev = ep;
+ }
+ }
+
+ if (start == prev) {
+ // closed subpath, join first and last point
+#ifdef QPP_STROKE_DEBUG
+ qDebug("\n ---> (side) closed subpath");
+#endif
+ stroker->joinPoints(prev.x, prev.y, *startTangent, stroker->joinStyleMode());
+ return true;
+ } else {
+#ifdef QPP_STROKE_DEBUG
+ qDebug("\n ---> (side) open subpath");
+#endif
+ return false;
+ }
+}
+
+/*!
+ \internal
+
+ For a given angle in the range [0 .. 90], finds the corresponding parameter t
+ of the prototype cubic bezier arc segment
+ b = fromPoints(QPointF(1, 0), QPointF(1, KAPPA), QPointF(KAPPA, 1), QPointF(0, 1));
+
+ From the bezier equation:
+ b.pointAt(t).x() = (1-t)^3 + t*(1-t)^2 + t^2*(1-t)*KAPPA
+ b.pointAt(t).y() = t*(1-t)^2 * KAPPA + t^2*(1-t) + t^3
+
+ Third degree coefficients:
+ b.pointAt(t).x() = at^3 + bt^2 + ct + d
+ where a = 2-3*KAPPA, b = 3*(KAPPA-1), c = 0, d = 1
+
+ b.pointAt(t).y() = at^3 + bt^2 + ct + d
+ where a = 3*KAPPA-2, b = 6*KAPPA+3, c = 3*KAPPA, d = 0
+
+ Newton's method to find the zero of a function:
+ given a function f(x) and initial guess x_0
+ x_1 = f(x_0) / f'(x_0)
+ x_2 = f(x_1) / f'(x_1)
+ etc...
+*/
+
+qreal qt_t_for_arc_angle(qreal angle)
+{
+ if (qFuzzyIsNull(angle))
+ return 0;
+
+ if (qFuzzyCompare(angle, qreal(90)))
+ return 1;
+
+ qreal radians = Q_PI * angle / 180;
+ qreal cosAngle = qCos(radians);
+ qreal sinAngle = qSin(radians);
+
+ // initial guess
+ qreal tc = angle / 90;
+ // do some iterations of newton's method to approximate cosAngle
+ // finds the zero of the function b.pointAt(tc).x() - cosAngle
+ tc -= ((((2-3*QT_PATH_KAPPA) * tc + 3*(QT_PATH_KAPPA-1)) * tc) * tc + 1 - cosAngle) // value
+ / (((6-9*QT_PATH_KAPPA) * tc + 6*(QT_PATH_KAPPA-1)) * tc); // derivative
+ tc -= ((((2-3*QT_PATH_KAPPA) * tc + 3*(QT_PATH_KAPPA-1)) * tc) * tc + 1 - cosAngle) // value
+ / (((6-9*QT_PATH_KAPPA) * tc + 6*(QT_PATH_KAPPA-1)) * tc); // derivative
+
+ // initial guess
+ qreal ts = tc;
+ // do some iterations of newton's method to approximate sinAngle
+ // finds the zero of the function b.pointAt(tc).y() - sinAngle
+ ts -= ((((3*QT_PATH_KAPPA-2) * ts - 6*QT_PATH_KAPPA + 3) * ts + 3*QT_PATH_KAPPA) * ts - sinAngle)
+ / (((9*QT_PATH_KAPPA-6) * ts + 12*QT_PATH_KAPPA - 6) * ts + 3*QT_PATH_KAPPA);
+ ts -= ((((3*QT_PATH_KAPPA-2) * ts - 6*QT_PATH_KAPPA + 3) * ts + 3*QT_PATH_KAPPA) * ts - sinAngle)
+ / (((9*QT_PATH_KAPPA-6) * ts + 12*QT_PATH_KAPPA - 6) * ts + 3*QT_PATH_KAPPA);
+
+ // use the average of the t that best approximates cosAngle
+ // and the t that best approximates sinAngle
+ qreal t = 0.5 * (tc + ts);
+
+#if 0
+ printf("angle: %f, t: %f\n", angle, t);
+ qreal a, b, c, d;
+ bezierCoefficients(t, a, b, c, d);
+ printf("cosAngle: %.10f, value: %.10f\n", cosAngle, a + b + c * QT_PATH_KAPPA);
+ printf("sinAngle: %.10f, value: %.10f\n", sinAngle, b * QT_PATH_KAPPA + c + d);
+#endif
+
+ return t;
+}
+
+Q_GUI_EXPORT void qt_find_ellipse_coords(const QRectF &r, qreal angle, qreal length,
+ QPointF* startPoint, QPointF *endPoint);
+
+/*!
+ \internal
+
+ Creates a number of curves for a given arc definition. The arc is
+ defined an arc along the ellipses that fits into \a rect starting
+ at \a startAngle and an arc length of \a sweepLength.
+
+ The function has three out parameters. The return value is the
+ starting point of the arc. The \a curves array represents the list
+ of cubicTo elements up to a maximum of \a point_count. There are of course
+ 3 points pr curve.
+*/
+QPointF qt_curves_for_arc(const QRectF &rect, qreal startAngle, qreal sweepLength,
+ QPointF *curves, int *point_count)
+{
+ Q_ASSERT(point_count);
+ Q_ASSERT(curves);
+
+ *point_count = 0;
+ if (qt_is_nan(rect.x()) || qt_is_nan(rect.y()) || qt_is_nan(rect.width()) || qt_is_nan(rect.height())
+ || qt_is_nan(startAngle) || qt_is_nan(sweepLength)) {
+ qWarning("QPainterPath::arcTo: Adding arc where a parameter is NaN, results are undefined");
+ return QPointF();
+ }
+
+ if (rect.isNull()) {
+ return QPointF();
+ }
+
+ qreal x = rect.x();
+ qreal y = rect.y();
+
+ qreal w = rect.width();
+ qreal w2 = rect.width() / 2;
+ qreal w2k = w2 * QT_PATH_KAPPA;
+
+ qreal h = rect.height();
+ qreal h2 = rect.height() / 2;
+ qreal h2k = h2 * QT_PATH_KAPPA;
+
+ QPointF points[16] =
+ {
+ // start point
+ QPointF(x + w, y + h2),
+
+ // 0 -> 270 degrees
+ QPointF(x + w, y + h2 + h2k),
+ QPointF(x + w2 + w2k, y + h),
+ QPointF(x + w2, y + h),
+
+ // 270 -> 180 degrees
+ QPointF(x + w2 - w2k, y + h),
+ QPointF(x, y + h2 + h2k),
+ QPointF(x, y + h2),
+
+ // 180 -> 90 degrees
+ QPointF(x, y + h2 - h2k),
+ QPointF(x + w2 - w2k, y),
+ QPointF(x + w2, y),
+
+ // 90 -> 0 degrees
+ QPointF(x + w2 + w2k, y),
+ QPointF(x + w, y + h2 - h2k),
+ QPointF(x + w, y + h2)
+ };
+
+ if (sweepLength > 360) sweepLength = 360;
+ else if (sweepLength < -360) sweepLength = -360;
+
+ // Special case fast paths
+ if (startAngle == 0.0) {
+ if (sweepLength == 360.0) {
+ for (int i = 11; i >= 0; --i)
+ curves[(*point_count)++] = points[i];
+ return points[12];
+ } else if (sweepLength == -360.0) {
+ for (int i = 1; i <= 12; ++i)
+ curves[(*point_count)++] = points[i];
+ return points[0];
+ }
+ }
+
+ int startSegment = int(qFloor(startAngle / 90));
+ int endSegment = int(qFloor((startAngle + sweepLength) / 90));
+
+ qreal startT = (startAngle - startSegment * 90) / 90;
+ qreal endT = (startAngle + sweepLength - endSegment * 90) / 90;
+
+ int delta = sweepLength > 0 ? 1 : -1;
+ if (delta < 0) {
+ startT = 1 - startT;
+ endT = 1 - endT;
+ }
+
+ // avoid empty start segment
+ if (qFuzzyIsNull(startT - qreal(1))) {
+ startT = 0;
+ startSegment += delta;
+ }
+
+ // avoid empty end segment
+ if (qFuzzyIsNull(endT)) {
+ endT = 1;
+ endSegment -= delta;
+ }
+
+ startT = qt_t_for_arc_angle(startT * 90);
+ endT = qt_t_for_arc_angle(endT * 90);
+
+ const bool splitAtStart = !qFuzzyIsNull(startT);
+ const bool splitAtEnd = !qFuzzyIsNull(endT - qreal(1));
+
+ const int end = endSegment + delta;
+
+ // empty arc?
+ if (startSegment == end) {
+ const int quadrant = 3 - ((startSegment % 4) + 4) % 4;
+ const int j = 3 * quadrant;
+ return delta > 0 ? points[j + 3] : points[j];
+ }
+
+ QPointF startPoint, endPoint;
+ qt_find_ellipse_coords(rect, startAngle, sweepLength, &startPoint, &endPoint);
+
+ for (int i = startSegment; i != end; i += delta) {
+ const int quadrant = 3 - ((i % 4) + 4) % 4;
+ const int j = 3 * quadrant;
+
+ QBezier b;
+ if (delta > 0)
+ b = QBezier::fromPoints(points[j + 3], points[j + 2], points[j + 1], points[j]);
+ else
+ b = QBezier::fromPoints(points[j], points[j + 1], points[j + 2], points[j + 3]);
+
+ // empty arc?
+ if (startSegment == endSegment && qFuzzyCompare(startT, endT))
+ return startPoint;
+
+ if (i == startSegment) {
+ if (i == endSegment && splitAtEnd)
+ b = b.bezierOnInterval(startT, endT);
+ else if (splitAtStart)
+ b = b.bezierOnInterval(startT, 1);
+ } else if (i == endSegment && splitAtEnd) {
+ b = b.bezierOnInterval(0, endT);
+ }
+
+ // push control points
+ curves[(*point_count)++] = b.pt2();
+ curves[(*point_count)++] = b.pt3();
+ curves[(*point_count)++] = b.pt4();
+ }
+
+ Q_ASSERT(*point_count > 0);
+ curves[*(point_count)-1] = endPoint;
+
+ return startPoint;
+}
+
+
+static inline void qdashstroker_moveTo(qfixed x, qfixed y, void *data) {
+ ((QStroker *) data)->moveTo(x, y);
+}
+
+static inline void qdashstroker_lineTo(qfixed x, qfixed y, void *data) {
+ ((QStroker *) data)->lineTo(x, y);
+}
+
+static inline void qdashstroker_cubicTo(qfixed, qfixed, qfixed, qfixed, qfixed, qfixed, void *) {
+ Q_ASSERT(0);
+// ((QStroker *) data)->cubicTo(c1x, c1y, c2x, c2y, ex, ey);
+}
+
+
+/*******************************************************************************
+ * QDashStroker members
+ */
+QDashStroker::QDashStroker(QStroker *stroker)
+ : m_stroker(stroker), m_dashOffset(0), m_stroke_width(1), m_miter_limit(1)
+{
+ if (m_stroker) {
+ setMoveToHook(qdashstroker_moveTo);
+ setLineToHook(qdashstroker_lineTo);
+ setCubicToHook(qdashstroker_cubicTo);
+ }
+}
+
+QVector<qfixed> QDashStroker::patternForStyle(Qt::PenStyle style)
+{
+ const qfixed space = 2;
+ const qfixed dot = 1;
+ const qfixed dash = 4;
+
+ QVector<qfixed> pattern;
+
+ switch (style) {
+ case Qt::DashLine:
+ pattern << dash << space;
+ break;
+ case Qt::DotLine:
+ pattern << dot << space;
+ break;
+ case Qt::DashDotLine:
+ pattern << dash << space << dot << space;
+ break;
+ case Qt::DashDotDotLine:
+ pattern << dash << space << dot << space << dot << space;
+ break;
+ default:
+ break;
+ }
+
+ return pattern;
+}
+
+static inline bool lineRectIntersectsRect(qfixed2d p1, qfixed2d p2, const qfixed2d &tl, const qfixed2d &br)
+{
+ return ((p1.x > tl.x || p2.x > tl.x) && (p1.x < br.x || p2.x < br.x)
+ && (p1.y > tl.y || p2.y > tl.y) && (p1.y < br.y || p2.y < br.y));
+}
+
+// If the line intersects the rectangle, this function will return true.
+static bool lineIntersectsRect(qfixed2d p1, qfixed2d p2, const qfixed2d &tl, const qfixed2d &br)
+{
+ if (!lineRectIntersectsRect(p1, p2, tl, br))
+ return false;
+ if (p1.x == p2.x || p1.y == p2.y)
+ return true;
+
+ if (p1.y > p2.y)
+ qSwap(p1, p2); // make p1 above p2
+ qfixed2d u;
+ qfixed2d v;
+ qfixed2d w = {p2.x - p1.x, p2.y - p1.y};
+ if (p1.x < p2.x) {
+ // backslash
+ u.x = tl.x - p1.x; u.y = br.y - p1.y;
+ v.x = br.x - p1.x; v.y = tl.y - p1.y;
+ } else {
+ // slash
+ u.x = tl.x - p1.x; u.y = tl.y - p1.y;
+ v.x = br.x - p1.x; v.y = br.y - p1.y;
+ }
+#if defined(QFIXED_IS_26_6) || defined(QFIXED_IS_16_16)
+ qint64 val1 = qint64(u.x) * qint64(w.y) - qint64(u.y) * qint64(w.x);
+ qint64 val2 = qint64(v.x) * qint64(w.y) - qint64(v.y) * qint64(w.x);
+ return (val1 < 0 && val2 > 0) || (val1 > 0 && val2 < 0);
+#elif defined(QFIXED_IS_32_32)
+ // Cannot do proper test because it may overflow.
+ return true;
+#else
+ qreal val1 = u.x * w.y - u.y * w.x;
+ qreal val2 = v.x * w.y - v.y * w.x;
+ return (val1 < 0 && val2 > 0) || (val1 > 0 && val2 < 0);
+#endif
+}
+
+void QDashStroker::processCurrentSubpath()
+{
+ int dashCount = qMin(m_dashPattern.size(), 32);
+ qfixed dashes[32];
+
+ if (m_stroker) {
+ m_customData = m_stroker;
+ m_stroke_width = m_stroker->strokeWidth();
+ m_miter_limit = m_stroker->miterLimit();
+ }
+
+ qreal longestLength = 0;
+ qreal sumLength = 0;
+ for (int i=0; i<dashCount; ++i) {
+ dashes[i] = qMax(m_dashPattern.at(i), qreal(0)) * m_stroke_width;
+ sumLength += dashes[i];
+ if (dashes[i] > longestLength)
+ longestLength = dashes[i];
+ }
+
+ if (qFuzzyIsNull(sumLength))
+ return;
+
+ qreal invSumLength = qreal(1) / sumLength;
+
+ Q_ASSERT(dashCount > 0);
+
+ dashCount = dashCount & -2; // Round down to even number
+
+ int idash = 0; // Index to current dash
+ qreal pos = 0; // The position on the curve, 0 <= pos <= path.length
+ qreal elen = 0; // element length
+ qreal doffset = m_dashOffset * m_stroke_width;
+
+ // make sure doffset is in range [0..sumLength)
+ doffset -= qFloor(doffset * invSumLength) * sumLength;
+
+ while (doffset >= dashes[idash]) {
+ doffset -= dashes[idash];
+ if (++idash >= dashCount)
+ idash = 0;
+ }
+
+ qreal estart = 0; // The elements starting position
+ qreal estop = 0; // The element stop position
+
+ QLineF cline;
+
+ QPainterPath dashPath;
+
+ QSubpathFlatIterator it(&m_elements, m_dashThreshold);
+ qfixed2d prev = it.next();
+
+ bool clipping = !m_clip_rect.isEmpty();
+ qfixed2d move_to_pos = prev;
+ qfixed2d line_to_pos;
+
+ // Pad to avoid clipping the borders of thick pens.
+ qfixed padding = qt_real_to_fixed(qMax(m_stroke_width, m_miter_limit) * longestLength);
+ qfixed2d clip_tl = { qt_real_to_fixed(m_clip_rect.left()) - padding,
+ qt_real_to_fixed(m_clip_rect.top()) - padding };
+ qfixed2d clip_br = { qt_real_to_fixed(m_clip_rect.right()) + padding ,
+ qt_real_to_fixed(m_clip_rect.bottom()) + padding };
+
+ bool hasMoveTo = false;
+ while (it.hasNext()) {
+ QStrokerOps::Element e = it.next();
+
+ Q_ASSERT(e.isLineTo());
+ cline = QLineF(qt_fixed_to_real(prev.x),
+ qt_fixed_to_real(prev.y),
+ qt_fixed_to_real(e.x),
+ qt_fixed_to_real(e.y));
+ elen = cline.length();
+
+ estop = estart + elen;
+
+ bool done = pos >= estop;
+
+ if (clipping) {
+ // Check if the entire line can be clipped away.
+ if (!lineIntersectsRect(prev, e, clip_tl, clip_br)) {
+ // Cut away full dash sequences.
+ elen -= qFloor(elen * invSumLength) * sumLength;
+ // Update dash offset.
+ while (!done) {
+ qreal dpos = pos + dashes[idash] - doffset - estart;
+
+ Q_ASSERT(dpos >= 0);
+
+ if (dpos > elen) { // dash extends this line
+ doffset = dashes[idash] - (dpos - elen); // subtract the part already used
+ pos = estop; // move pos to next path element
+ done = true;
+ } else { // Dash is on this line
+ pos = dpos + estart;
+ done = pos >= estop;
+ if (++idash >= dashCount)
+ idash = 0;
+ doffset = 0; // full segment so no offset on next.
+ }
+ }
+ hasMoveTo = false;
+ move_to_pos = e;
+ }
+ }
+
+ // Dash away...
+ while (!done) {
+ QPointF p2;
+
+ bool has_offset = doffset > 0;
+ bool evenDash = (idash & 1) == 0;
+ qreal dpos = pos + dashes[idash] - doffset - estart;
+
+ Q_ASSERT(dpos >= 0);
+
+ if (dpos > elen) { // dash extends this line
+ doffset = dashes[idash] - (dpos - elen); // subtract the part already used
+ pos = estop; // move pos to next path element
+ done = true;
+ p2 = cline.p2();
+ } else { // Dash is on this line
+ p2 = cline.pointAt(dpos/elen);
+ pos = dpos + estart;
+ done = pos >= estop;
+ if (++idash >= dashCount)
+ idash = 0;
+ doffset = 0; // full segment so no offset on next.
+ }
+
+ if (evenDash) {
+ line_to_pos.x = qt_real_to_fixed(p2.x());
+ line_to_pos.y = qt_real_to_fixed(p2.y());
+
+ if (!clipping
+ || lineRectIntersectsRect(move_to_pos, line_to_pos, clip_tl, clip_br))
+ {
+ // If we have an offset, we're continuing a dash
+ // from a previous element and should only
+ // continue the current dash, without starting a
+ // new subpath.
+ if (!has_offset || !hasMoveTo) {
+ emitMoveTo(move_to_pos.x, move_to_pos.y);
+ hasMoveTo = true;
+ }
+
+ emitLineTo(line_to_pos.x, line_to_pos.y);
+ } else {
+ hasMoveTo = false;
+ }
+ move_to_pos = line_to_pos;
+ } else {
+ move_to_pos.x = qt_real_to_fixed(p2.x());
+ move_to_pos.y = qt_real_to_fixed(p2.y());
+ }
+ }
+
+ // Shuffle to the next cycle...
+ estart = estop;
+ prev = e;
+ }
+
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qstroker_p.h b/src/gui/painting/qstroker_p.h
new file mode 100644
index 0000000000..8344f6cf86
--- /dev/null
+++ b/src/gui/painting/qstroker_p.h
@@ -0,0 +1,394 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSTROKER_P_H
+#define QSTROKER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtGui/qpainterpath.h"
+#include "private/qdatabuffer_p.h"
+#include "private/qnumeric_p.h"
+
+QT_BEGIN_NAMESPACE
+
+// #define QFIXED_IS_26_6
+
+#if defined QFIXED_IS_26_6
+typedef int qfixed;
+#define qt_real_to_fixed(real) qfixed(real * 64)
+#define qt_int_to_fixed(real) qfixed(int(real) << 6)
+#define qt_fixed_to_real(fixed) qreal(fixed / qreal(64))
+#define qt_fixed_to_int(fixed) int(fixed >> 6)
+struct qfixed2d
+{
+ qfixed x;
+ qfixed y;
+
+ bool operator==(const qfixed2d &other) const { return x == other.x && y == other.y; }
+};
+#elif defined QFIXED_IS_32_32
+typedef qint64 qfixed;
+#define qt_real_to_fixed(real) qfixed(real * double(qint64(1) << 32))
+#define qt_fixed_to_real(fixed) qreal(fixed / double(qint64(1) << 32))
+struct qfixed2d
+{
+ qfixed x;
+ qfixed y;
+
+ bool operator==(const qfixed2d &other) const { return x == other.x && y == other.y; }
+};
+#elif defined QFIXED_IS_16_16
+typedef int qfixed;
+#define qt_real_to_fixed(real) qfixed(real * qreal(1 << 16))
+#define qt_fixed_to_real(fixed) qreal(fixed / qreal(1 << 16))
+struct qfixed2d
+{
+ qfixed x;
+ qfixed y;
+
+ bool operator==(const qfixed2d &other) const { return x == other.x && y == other.y; }
+};
+#else
+typedef qreal qfixed;
+#define qt_real_to_fixed(real) qfixed(real)
+#define qt_fixed_to_real(fixed) fixed
+struct qfixed2d
+{
+ qfixed x;
+ qfixed y;
+
+ bool operator==(const qfixed2d &other) const { return qFuzzyCompare(x, other.x)
+ && qFuzzyCompare(y, other.y); }
+};
+#endif
+
+#define QT_PATH_KAPPA 0.5522847498
+
+QPointF qt_curves_for_arc(const QRectF &rect, qreal startAngle, qreal sweepLength,
+ QPointF *controlPoints, int *point_count);
+
+qreal qt_t_for_arc_angle(qreal angle);
+
+typedef void (*qStrokerMoveToHook)(qfixed x, qfixed y, void *data);
+typedef void (*qStrokerLineToHook)(qfixed x, qfixed y, void *data);
+typedef void (*qStrokerCubicToHook)(qfixed c1x, qfixed c1y,
+ qfixed c2x, qfixed c2y,
+ qfixed ex, qfixed ey,
+ void *data);
+
+// qtransform.cpp
+Q_GUI_EXPORT bool qt_scaleForTransform(const QTransform &transform, qreal *scale);
+
+class Q_GUI_EXPORT QStrokerOps
+{
+public:
+ struct Element {
+ QPainterPath::ElementType type;
+ qfixed x;
+ qfixed y;
+
+ inline bool isMoveTo() const { return type == QPainterPath::MoveToElement; }
+ inline bool isLineTo() const { return type == QPainterPath::LineToElement; }
+ inline bool isCurveTo() const { return type == QPainterPath::CurveToElement; }
+
+ operator qfixed2d () { qfixed2d pt = { x, y }; return pt; }
+ };
+
+ QStrokerOps();
+ virtual ~QStrokerOps();
+
+ void setMoveToHook(qStrokerMoveToHook moveToHook) { m_moveTo = moveToHook; }
+ void setLineToHook(qStrokerLineToHook lineToHook) { m_lineTo = lineToHook; }
+ void setCubicToHook(qStrokerCubicToHook cubicToHook) { m_cubicTo = cubicToHook; }
+
+ virtual void begin(void *customData);
+ virtual void end();
+
+ inline void moveTo(qfixed x, qfixed y);
+ inline void lineTo(qfixed x, qfixed y);
+ inline void cubicTo(qfixed x1, qfixed y1, qfixed x2, qfixed y2, qfixed ex, qfixed ey);
+
+ void strokePath(const QPainterPath &path, void *data, const QTransform &matrix);
+ void strokePolygon(const QPointF *points, int pointCount, bool implicit_close,
+ void *data, const QTransform &matrix);
+ void strokeEllipse(const QRectF &ellipse, void *data, const QTransform &matrix);
+
+ QRectF clipRect() const { return m_clip_rect; }
+ void setClipRect(const QRectF &clip) { m_clip_rect = clip; }
+
+ void setCurveThresholdFromTransform(const QTransform &transform)
+ {
+ qreal scale;
+ qt_scaleForTransform(transform, &scale);
+ m_dashThreshold = scale == 0 ? qreal(0.5) : (qreal(0.5) / scale);
+ }
+
+ void setCurveThreshold(qfixed threshold) { m_curveThreshold = threshold; }
+ qfixed curveThreshold() const { return m_curveThreshold; }
+
+protected:
+ inline void emitMoveTo(qfixed x, qfixed y);
+ inline void emitLineTo(qfixed x, qfixed y);
+ inline void emitCubicTo(qfixed c1x, qfixed c1y, qfixed c2x, qfixed c2y, qfixed ex, qfixed ey);
+
+ virtual void processCurrentSubpath() = 0;
+ QDataBuffer<Element> m_elements;
+
+ QRectF m_clip_rect;
+ qfixed m_curveThreshold;
+ qfixed m_dashThreshold;
+
+ void *m_customData;
+ qStrokerMoveToHook m_moveTo;
+ qStrokerLineToHook m_lineTo;
+ qStrokerCubicToHook m_cubicTo;
+
+};
+
+class Q_GUI_EXPORT QStroker : public QStrokerOps
+{
+public:
+
+ enum LineJoinMode {
+ FlatJoin,
+ SquareJoin,
+ MiterJoin,
+ RoundJoin,
+ RoundCap,
+ SvgMiterJoin
+ };
+
+ QStroker();
+ ~QStroker();
+
+ void setStrokeWidth(qfixed width) { m_strokeWidth = width; }
+ qfixed strokeWidth() const { return m_strokeWidth; }
+
+ void setCapStyle(Qt::PenCapStyle capStyle) { m_capStyle = joinModeForCap(capStyle); }
+ Qt::PenCapStyle capStyle() const { return capForJoinMode(m_capStyle); }
+ LineJoinMode capStyleMode() const { return m_capStyle; }
+
+ void setJoinStyle(Qt::PenJoinStyle style) { m_joinStyle = joinModeForJoin(style); }
+ Qt::PenJoinStyle joinStyle() const { return joinForJoinMode(m_joinStyle); }
+ LineJoinMode joinStyleMode() const { return m_joinStyle; }
+
+ void setMiterLimit(qfixed length) { m_miterLimit = length; }
+ qfixed miterLimit() const { return m_miterLimit; }
+
+ void joinPoints(qfixed x, qfixed y, const QLineF &nextLine, LineJoinMode join);
+ inline void emitMoveTo(qfixed x, qfixed y);
+ inline void emitLineTo(qfixed x, qfixed y);
+ inline void emitCubicTo(qfixed c1x, qfixed c1y, qfixed c2x, qfixed c2y, qfixed ex, qfixed ey);
+
+protected:
+ static Qt::PenCapStyle capForJoinMode(LineJoinMode mode);
+ static LineJoinMode joinModeForCap(Qt::PenCapStyle);
+
+ static Qt::PenJoinStyle joinForJoinMode(LineJoinMode mode);
+ static LineJoinMode joinModeForJoin(Qt::PenJoinStyle joinStyle);
+
+ virtual void processCurrentSubpath();
+
+ qfixed m_strokeWidth;
+ qfixed m_miterLimit;
+
+ LineJoinMode m_capStyle;
+ LineJoinMode m_joinStyle;
+
+ qfixed m_back1X;
+ qfixed m_back1Y;
+
+ qfixed m_back2X;
+ qfixed m_back2Y;
+};
+
+class Q_GUI_EXPORT QDashStroker : public QStrokerOps
+{
+public:
+ QDashStroker(QStroker *stroker);
+
+ QStroker *stroker() const { return m_stroker; }
+
+ static QVector<qfixed> patternForStyle(Qt::PenStyle style);
+
+ void setDashPattern(const QVector<qfixed> &dashPattern) { m_dashPattern = dashPattern; }
+ QVector<qfixed> dashPattern() const { return m_dashPattern; }
+
+ void setDashOffset(qreal offset) { m_dashOffset = offset; }
+ qreal dashOffset() const { return m_dashOffset; }
+
+ virtual void begin(void *data);
+ virtual void end();
+
+ inline void setStrokeWidth(qreal width) { m_stroke_width = width; }
+ inline void setMiterLimit(qreal limit) { m_miter_limit = limit; }
+
+protected:
+ virtual void processCurrentSubpath();
+
+ QStroker *m_stroker;
+ QVector<qfixed> m_dashPattern;
+ qreal m_dashOffset;
+
+ qreal m_stroke_width;
+ qreal m_miter_limit;
+};
+
+
+/*******************************************************************************
+ * QStrokerOps inline membmers
+ */
+
+inline void QStrokerOps::emitMoveTo(qfixed x, qfixed y)
+{
+ Q_ASSERT(m_moveTo);
+ m_moveTo(x, y, m_customData);
+}
+
+inline void QStrokerOps::emitLineTo(qfixed x, qfixed y)
+{
+ Q_ASSERT(m_lineTo);
+ m_lineTo(x, y, m_customData);
+}
+
+inline void QStrokerOps::emitCubicTo(qfixed c1x, qfixed c1y, qfixed c2x, qfixed c2y, qfixed ex, qfixed ey)
+{
+ Q_ASSERT(m_cubicTo);
+ m_cubicTo(c1x, c1y, c2x, c2y, ex, ey, m_customData);
+}
+
+inline void QStrokerOps::moveTo(qfixed x, qfixed y)
+{
+ if (m_elements.size()>1)
+ processCurrentSubpath();
+ m_elements.reset();
+ Element e = { QPainterPath::MoveToElement, x, y };
+ m_elements.add(e);
+}
+
+inline void QStrokerOps::lineTo(qfixed x, qfixed y)
+{
+ Element e = { QPainterPath::LineToElement, x, y };
+ m_elements.add(e);
+}
+
+inline void QStrokerOps::cubicTo(qfixed x1, qfixed y1, qfixed x2, qfixed y2, qfixed ex, qfixed ey)
+{
+ Element c1 = { QPainterPath::CurveToElement, x1, y1 };
+ Element c2 = { QPainterPath::CurveToDataElement, x2, y2 };
+ Element e = { QPainterPath::CurveToDataElement, ex, ey };
+ m_elements.add(c1);
+ m_elements.add(c2);
+ m_elements.add(e);
+}
+
+/*******************************************************************************
+ * QStroker inline members
+ */
+inline void QStroker::emitMoveTo(qfixed x, qfixed y)
+{
+ m_back2X = m_back1X;
+ m_back2Y = m_back1Y;
+ m_back1X = x;
+ m_back1Y = y;
+ QStrokerOps::emitMoveTo(x, y);
+}
+
+inline void QStroker::emitLineTo(qfixed x, qfixed y)
+{
+ m_back2X = m_back1X;
+ m_back2Y = m_back1Y;
+ m_back1X = x;
+ m_back1Y = y;
+ QStrokerOps::emitLineTo(x, y);
+}
+
+inline void QStroker::emitCubicTo(qfixed c1x, qfixed c1y,
+ qfixed c2x, qfixed c2y,
+ qfixed ex, qfixed ey)
+{
+ if (c2x == ex && c2y == ey) {
+ if (c1x == ex && c1y == ey) {
+ m_back2X = m_back1X;
+ m_back2Y = m_back1Y;
+ } else {
+ m_back2X = c1x;
+ m_back2Y = c1y;
+ }
+ } else {
+ m_back2X = c2x;
+ m_back2Y = c2y;
+ }
+ m_back1X = ex;
+ m_back1Y = ey;
+ QStrokerOps::emitCubicTo(c1x, c1y, c2x, c2y, ex, ey);
+}
+
+/*******************************************************************************
+ * QDashStroker inline members
+ */
+inline void QDashStroker::begin(void *data)
+{
+ if (m_stroker)
+ m_stroker->begin(data);
+ QStrokerOps::begin(data);
+}
+
+inline void QDashStroker::end()
+{
+ QStrokerOps::end();
+ if (m_stroker)
+ m_stroker->end();
+}
+
+QT_END_NAMESPACE
+
+#endif // QSTROKER_P_H
diff --git a/src/gui/painting/qstylepainter.cpp b/src/gui/painting/qstylepainter.cpp
new file mode 100644
index 0000000000..c8f9d05315
--- /dev/null
+++ b/src/gui/painting/qstylepainter.cpp
@@ -0,0 +1,176 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qstylepainter.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QStylePainter
+
+ \brief The QStylePainter class is a convenience class for drawing QStyle
+ elements inside a widget.
+
+ \ingroup appearance
+ \ingroup painting
+
+ QStylePainter extends QPainter with a set of high-level \c
+ draw...() functions implemented on top of QStyle's API. The
+ advantage of using QStylePainter is that the parameter lists get
+ considerably shorter. Whereas a QStyle object must be able to
+ draw on any widget using any painter (because the application
+ normally has one QStyle object shared by all widget), a
+ QStylePainter is initialized with a widget, eliminating the need
+ to specify the QWidget, the QPainter, and the QStyle for every
+ function call.
+
+ Example using QStyle directly:
+
+ \snippet doc/src/snippets/styles/styles.cpp 1
+
+ Example using QStylePainter:
+
+ \snippet doc/src/snippets/styles/styles.cpp 0
+ \snippet doc/src/snippets/styles/styles.cpp 4
+ \snippet doc/src/snippets/styles/styles.cpp 6
+
+ \sa QStyle, QStyleOption
+*/
+
+/*!
+ \fn QStylePainter::QStylePainter()
+
+ Constructs a QStylePainter.
+*/
+
+/*!
+ \fn QStylePainter::QStylePainter(QWidget *widget)
+
+ Construct a QStylePainter using widget \a widget for its paint device.
+*/
+
+/*!
+ \fn QStylePainter::QStylePainter(QPaintDevice *pd, QWidget *widget)
+
+ Construct a QStylePainter using \a pd for its paint device, and
+ attributes from \a widget.
+*/
+
+
+/*!
+ \fn bool QStylePainter::begin(QWidget *widget)
+
+ Begin painting operations on the specified \a widget.
+ Returns true if the painter is ready to use; otherwise returns false.
+
+ This is automatically called by the constructor that takes a QWidget.
+*/
+
+/*!
+ \fn bool QStylePainter::begin(QPaintDevice *pd, QWidget *widget)
+ \overload
+
+ Begin painting operations on paint device \a pd as if it was \a
+ widget.
+
+ This is automatically called by the constructor that
+ takes a QPaintDevice and a QWidget.
+*/
+
+
+/*!
+ \fn void QStylePainter::drawPrimitive(QStyle::PrimitiveElement pe, const QStyleOption &option)
+
+ Use the widget's style to draw a primitive element \a pe specified by QStyleOption \a option.
+
+ \sa QStyle::drawPrimitive()
+*/
+
+/*!
+ \fn void QStylePainter::drawControl(QStyle::ControlElement ce, const QStyleOption &option)
+
+ Use the widget's style to draw a control element \a ce specified by QStyleOption \a option.
+
+ \sa QStyle::drawControl()
+*/
+
+/*!
+ \fn void QStylePainter::drawComplexControl(QStyle::ComplexControl cc,
+ const QStyleOptionComplex &option)
+
+ Use the widget's style to draw a complex control \a cc specified by the
+ QStyleOptionComplex \a option.
+
+ \sa QStyle::drawComplexControl()
+*/
+
+/*!
+ \fn void QStylePainter::drawItemText(const QRect &rect, int flags, const QPalette &pal,
+ bool enabled, const QString &text,
+ QPalette::ColorRole textRole = QPalette::NoRole)
+
+ Draws the \a text in rectangle \a rect and palette \a pal.
+ The text is aligned and wrapped according to \a
+ flags.
+
+ The pen color is specified with \a textRole. The \a enabled bool
+ indicates whether or not the item is enabled; when reimplementing
+ this bool should influence how the item is drawn.
+
+ \sa QStyle::drawItemText(), Qt::Alignment
+*/
+
+/*!
+ \fn void QStylePainter::drawItemPixmap(const QRect &rect, int flags, const QPixmap &pixmap)
+
+ Draws the \a pixmap in rectangle \a rect.
+ The pixmap is aligned according to \a flags.
+
+ \sa QStyle::drawItemPixmap(), Qt::Alignment
+*/
+
+/*!
+ \fn QStyle *QStylePainter::style() const
+
+ Return the current style used by the QStylePainter.
+*/
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qstylepainter.h b/src/gui/painting/qstylepainter.h
new file mode 100644
index 0000000000..0fc3f2b4b9
--- /dev/null
+++ b/src/gui/painting/qstylepainter.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSTYLEPAINTER_H
+#define QSTYLEPAINTER_H
+
+#include <QtGui/qpainter.h>
+#include <QtGui/qstyle.h>
+#include <QtGui/qwidget.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QStylePainter : public QPainter
+{
+public:
+ inline QStylePainter() : QPainter(), widget(0), wstyle(0) {}
+ inline explicit QStylePainter(QWidget *w) { begin(w, w); }
+ inline QStylePainter(QPaintDevice *pd, QWidget *w) { begin(pd, w); }
+ inline bool begin(QWidget *w) { return begin(w, w); }
+ inline bool begin(QPaintDevice *pd, QWidget *w) {
+ Q_ASSERT_X(w, "QStylePainter::QStylePainter", "Widget must be non-zero");
+ widget = w;
+ wstyle = w->style();
+ return QPainter::begin(pd);
+ };
+ inline void drawPrimitive(QStyle::PrimitiveElement pe, const QStyleOption &opt);
+ inline void drawControl(QStyle::ControlElement ce, const QStyleOption &opt);
+ inline void drawComplexControl(QStyle::ComplexControl cc, const QStyleOptionComplex &opt);
+ inline void drawItemText(const QRect &r, int flags, const QPalette &pal, bool enabled,
+ const QString &text, QPalette::ColorRole textRole = QPalette::NoRole);
+ inline void drawItemPixmap(const QRect &r, int flags, const QPixmap &pixmap);
+ inline QStyle *style() const { return wstyle; }
+
+private:
+ QWidget *widget;
+ QStyle *wstyle;
+ Q_DISABLE_COPY(QStylePainter)
+};
+
+void QStylePainter::drawPrimitive(QStyle::PrimitiveElement pe, const QStyleOption &opt)
+{
+ wstyle->drawPrimitive(pe, &opt, this, widget);
+}
+
+void QStylePainter::drawControl(QStyle::ControlElement ce, const QStyleOption &opt)
+{
+ wstyle->drawControl(ce, &opt, this, widget);
+}
+
+void QStylePainter::drawComplexControl(QStyle::ComplexControl cc, const QStyleOptionComplex &opt)
+{
+ wstyle->drawComplexControl(cc, &opt, this, widget);
+}
+
+void QStylePainter::drawItemText(const QRect &r, int flags, const QPalette &pal, bool enabled,
+ const QString &text, QPalette::ColorRole textRole)
+{
+ wstyle->drawItemText(this, r, flags, pal, enabled, text, textRole);
+}
+
+void QStylePainter::drawItemPixmap(const QRect &r, int flags, const QPixmap &pixmap)
+{
+ wstyle->drawItemPixmap(this, r, flags, pixmap);
+}
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSTYLEPAINTER_H
diff --git a/src/gui/painting/qtessellator.cpp b/src/gui/painting/qtessellator.cpp
new file mode 100644
index 0000000000..328f4dd96a
--- /dev/null
+++ b/src/gui/painting/qtessellator.cpp
@@ -0,0 +1,1498 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtessellator_p.h"
+
+#include <QRect>
+#include <QList>
+#include <QDebug>
+
+#include <qmath.h>
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+//#define DEBUG
+#ifdef DEBUG
+#define QDEBUG qDebug
+#else
+#define QDEBUG if (1){} else qDebug
+#endif
+
+static const bool emit_clever = true;
+static const bool mark_clever = false;
+
+enum VertexFlags {
+ LineBeforeStarts = 0x1,
+ LineBeforeEnds = 0x2,
+ LineBeforeHorizontal = 0x4,
+ LineAfterStarts = 0x8,
+ LineAfterEnds = 0x10,
+ LineAfterHorizontal = 0x20
+};
+
+
+
+class QTessellatorPrivate {
+public:
+ struct Vertices;
+
+ QTessellatorPrivate() {}
+
+ QRectF collectAndSortVertices(const QPointF *points, int *maxActiveEdges);
+ void cancelCoincidingEdges();
+
+ void emitEdges(QTessellator *tessellator);
+ void processIntersections();
+ void removeEdges();
+ void addEdges();
+ void addIntersections();
+
+ struct Vertex : public QTessellator::Vertex
+ {
+ int flags;
+ };
+
+ struct Intersection
+ {
+ Q27Dot5 y;
+ int edge;
+ bool operator <(const Intersection &other) const {
+ if (y != other.y)
+ return y < other.y;
+ return edge < other.edge;
+ }
+ };
+ struct IntersectionLink
+ {
+ int next;
+ int prev;
+ };
+ typedef QMap<Intersection, IntersectionLink> Intersections;
+
+ struct Edge {
+ Edge(const Vertices &v, int _edge);
+ int edge;
+ const Vertex *v0;
+ const Vertex *v1;
+ Q27Dot5 y_left;
+ Q27Dot5 y_right;
+ signed int winding : 8;
+ bool mark;
+ bool free;
+ bool intersect_left;
+ bool intersect_right;
+ bool isLeftOf(const Edge &other, Q27Dot5 y) const;
+ Q27Dot5 positionAt(Q27Dot5 y) const;
+ bool intersect(const Edge &other, Q27Dot5 *y, bool *det_positive) const;
+
+ };
+
+ class EdgeSorter
+ {
+ public:
+ EdgeSorter(int _y) : y(_y) {}
+ bool operator() (const Edge *e1, const Edge *e2);
+ int y;
+ };
+
+ class Scanline {
+ public:
+ Scanline();
+ ~Scanline();
+
+ void init(int maxActiveEdges);
+ void done();
+
+ int findEdgePosition(Q27Dot5 x, Q27Dot5 y) const;
+ int findEdgePosition(const Edge &e) const;
+ int findEdge(int edge) const;
+ void clearMarks();
+
+ void swap(int p1, int p2) {
+ Edge *tmp = edges[p1];
+ edges[p1] = edges[p2];
+ edges[p2] = tmp;
+ }
+ void insert(int pos, const Edge &e);
+ void removeAt(int pos);
+ void markEdges(int pos1, int pos2);
+
+ void prepareLine();
+ void lineDone();
+
+ Edge **old;
+ int old_size;
+
+ Edge **edges;
+ int size;
+
+ private:
+ Edge *edge_table;
+ int first_unused;
+ int max_edges;
+ enum { default_alloc = 32 };
+ };
+
+ struct Vertices {
+ enum { default_alloc = 128 };
+ Vertices();
+ ~Vertices();
+ void init(int maxVertices);
+ void done();
+ Vertex *storage;
+ Vertex **sorted;
+
+ Vertex *operator[] (int i) { return storage + i; }
+ const Vertex *operator[] (int i) const { return storage + i; }
+ int position(const Vertex *v) const {
+ return v - storage;
+ }
+ Vertex *next(Vertex *v) {
+ ++v;
+ if (v == storage + nPoints)
+ v = storage;
+ return v;
+ }
+ const Vertex *next(const Vertex *v) const {
+ ++v;
+ if (v == storage + nPoints)
+ v = storage;
+ return v;
+ }
+ int nextPos(const Vertex *v) const {
+ ++v;
+ if (v == storage + nPoints)
+ return 0;
+ return v - storage;
+ }
+ Vertex *prev(Vertex *v) {
+ if (v == storage)
+ v = storage + nPoints;
+ --v;
+ return v;
+ }
+ const Vertex *prev(const Vertex *v) const {
+ if (v == storage)
+ v = storage + nPoints;
+ --v;
+ return v;
+ }
+ int prevPos(const Vertex *v) const {
+ if (v == storage)
+ v = storage + nPoints;
+ --v;
+ return v - storage;
+ }
+ int nPoints;
+ int allocated;
+ };
+ Vertices vertices;
+ Intersections intersections;
+ Scanline scanline;
+ bool winding;
+ Q27Dot5 y;
+ int currentVertex;
+
+private:
+ void addIntersection(const Edge *e1, const Edge *e2);
+ bool edgeInChain(Intersection i, int edge);
+};
+
+
+QTessellatorPrivate::Edge::Edge(const QTessellatorPrivate::Vertices &vertices, int edge)
+{
+ this->edge = edge;
+ intersect_left = intersect_right = true;
+ mark = false;
+ free = false;
+
+ v0 = vertices[edge];
+ v1 = vertices.next(v0);
+
+ Q_ASSERT(v0->y != v1->y);
+
+ if (v0->y > v1->y) {
+ qSwap(v0, v1);
+ winding = -1;
+ } else {
+ winding = 1;
+ }
+ y_left = y_right = v0->y;
+}
+
+// This is basically the algorithm from graphics gems. The algorithm
+// is cubic in the coordinates at one place. Since we use 64bit
+// integers, this implies, that the allowed range for our coordinates
+// is limited to 21 bits. With 5 bits behind the decimal, this
+// implies that differences in coordaintes can range from 2*SHORT_MIN
+// to 2*SHORT_MAX, giving us efficiently a coordinate system from
+// SHORT_MIN to SHORT_MAX.
+//
+
+// WARNING: It's absolutely critical that the intersect() and isLeftOf() methods use
+// exactly the same algorithm to calulate yi. It's also important to be sure the algorithms
+// are transitive (ie. the conditions below are true for all input data):
+//
+// a.intersect(b) == b.intersect(a)
+// a.isLeftOf(b) != b.isLeftOf(a)
+//
+// This is tricky to get right, so be very careful when changing anything in here!
+
+static inline bool sameSign(qint64 a, qint64 b) {
+ return (((qint64) ((quint64) a ^ (quint64) b)) >= 0 );
+}
+
+bool QTessellatorPrivate::Edge::intersect(const Edge &other, Q27Dot5 *y, bool *det_positive) const
+{
+ qint64 a1 = v1->y - v0->y;
+ qint64 b1 = v0->x - v1->x;
+
+ qint64 a2 = other.v1->y - other.v0->y;
+ qint64 b2 = other.v0->x - other.v1->x;
+
+ qint64 det = a1 * b2 - a2 * b1;
+ if (det == 0)
+ return false;
+
+ qint64 c1 = qint64(v1->x) * v0->y - qint64(v0->x) * v1->y;
+
+ qint64 r3 = a1 * other.v0->x + b1 * other.v0->y + c1;
+ qint64 r4 = a1 * other.v1->x + b1 * other.v1->y + c1;
+
+ // Check signs of r3 and r4. If both point 3 and point 4 lie on
+ // same side of line 1, the line segments do not intersect.
+ QDEBUG() << " " << r3 << r4;
+ if (r3 != 0 && r4 != 0 && sameSign( r3, r4 ))
+ return false;
+
+ qint64 c2 = qint64(other.v1->x) * other.v0->y - qint64(other.v0->x) * other.v1->y;
+
+ qint64 r1 = a2 * v0->x + b2 * v0->y + c2;
+ qint64 r2 = a2 * v1->x + b2 * v1->y + c2;
+
+ // Check signs of r1 and r2. If both point 1 and point 2 lie
+ // on same side of second line segment, the line segments do not intersect.
+ QDEBUG() << " " << r1 << r2;
+ if (r1 != 0 && r2 != 0 && sameSign( r1, r2 ))
+ return false;
+
+ // The det/2 is to get rounding instead of truncating. It
+ // is added or subtracted to the numerator, depending upon the
+ // sign of the numerator.
+ qint64 offset = det < 0 ? -det : det;
+ offset >>= 1;
+
+ qint64 num = a2 * c1 - a1 * c2;
+ *y = ( num < 0 ? num - offset : num + offset ) / det;
+
+ *det_positive = (det > 0);
+
+ return true;
+}
+
+#undef SAME_SIGNS
+
+bool QTessellatorPrivate::Edge::isLeftOf(const Edge &other, Q27Dot5 y) const
+{
+// QDEBUG() << "isLeftOf" << edge << other.edge << y;
+ qint64 a1 = v1->y - v0->y;
+ qint64 b1 = v0->x - v1->x;
+ qint64 a2 = other.v1->y - other.v0->y;
+ qint64 b2 = other.v0->x - other.v1->x;
+
+ qint64 c2 = qint64(other.v1->x) * other.v0->y - qint64(other.v0->x) * other.v1->y;
+
+ qint64 det = a1 * b2 - a2 * b1;
+ if (det == 0) {
+ // lines are parallel. Only need to check side of one point
+ // fixed ordering for coincident edges
+ qint64 r1 = a2 * v0->x + b2 * v0->y + c2;
+// QDEBUG() << "det = 0" << r1;
+ if (r1 == 0)
+ return edge < other.edge;
+ return (r1 < 0);
+ }
+
+ // not parallel, need to find the y coordinate of the intersection point
+ qint64 c1 = qint64(v1->x) * v0->y - qint64(v0->x) * v1->y;
+
+ qint64 offset = det < 0 ? -det : det;
+ offset >>= 1;
+
+ qint64 num = a2 * c1 - a1 * c2;
+ qint64 yi = ( num < 0 ? num - offset : num + offset ) / det;
+// QDEBUG() << " num=" << num << "offset=" << offset << "det=" << det;
+
+ return ((yi > y) ^ (det < 0));
+}
+
+static inline bool compareVertex(const QTessellatorPrivate::Vertex *p1,
+ const QTessellatorPrivate::Vertex *p2)
+{
+ if (p1->y == p2->y) {
+ if (p1->x == p2->x)
+ return p1 < p2;
+ return p1->x < p2->x;
+ }
+ return p1->y < p2->y;
+}
+
+Q27Dot5 QTessellatorPrivate::Edge::positionAt(Q27Dot5 y) const
+{
+ if (y == v0->y)
+ return v0->x;
+ else if (y == v1->y)
+ return v1->x;
+
+ qint64 d = v1->x - v0->x;
+ return (v0->x + d*(y - v0->y)/(v1->y-v0->y));
+}
+
+bool QTessellatorPrivate::EdgeSorter::operator() (const Edge *e1, const Edge *e2)
+{
+ return e1->isLeftOf(*e2, y);
+}
+
+
+QTessellatorPrivate::Scanline::Scanline()
+{
+ edges = 0;
+ edge_table = 0;
+ old = 0;
+}
+
+void QTessellatorPrivate::Scanline::init(int maxActiveEdges)
+{
+ maxActiveEdges *= 2;
+ if (!edges || maxActiveEdges > default_alloc) {
+ max_edges = maxActiveEdges;
+ int s = qMax(maxActiveEdges + 1, default_alloc + 1);
+ edges = q_check_ptr((Edge **)realloc(edges, s*sizeof(Edge *)));
+ edge_table = q_check_ptr((Edge *)realloc(edge_table, s*sizeof(Edge)));
+ old = q_check_ptr((Edge **)realloc(old, s*sizeof(Edge *)));
+ }
+ size = 0;
+ old_size = 0;
+ first_unused = 0;
+ for (int i = 0; i < maxActiveEdges; ++i)
+ edge_table[i].edge = i+1;
+ edge_table[maxActiveEdges].edge = -1;
+}
+
+void QTessellatorPrivate::Scanline::done()
+{
+ if (max_edges > default_alloc) {
+ free(edges);
+ free(old);
+ free(edge_table);
+ edges = 0;
+ old = 0;
+ edge_table = 0;
+ }
+}
+
+QTessellatorPrivate::Scanline::~Scanline()
+{
+ free(edges);
+ free(old);
+ free(edge_table);
+}
+
+int QTessellatorPrivate::Scanline::findEdgePosition(Q27Dot5 x, Q27Dot5 y) const
+{
+ int min = 0;
+ int max = size - 1;
+ while (min < max) {
+ int pos = min + ((max - min + 1) >> 1);
+ Q27Dot5 ax = edges[pos]->positionAt(y);
+ if (ax > x) {
+ max = pos - 1;
+ } else {
+ min = pos;
+ }
+ }
+ return min;
+}
+
+int QTessellatorPrivate::Scanline::findEdgePosition(const Edge &e) const
+{
+// qDebug() << ">> findEdgePosition";
+ int min = 0;
+ int max = size;
+ while (min < max) {
+ int pos = min + ((max - min) >> 1);
+// qDebug() << " " << min << max << pos << edges[pos]->isLeftOf(e, e.y0);
+ if (edges[pos]->isLeftOf(e, e.v0->y)) {
+ min = pos + 1;
+ } else {
+ max = pos;
+ }
+ }
+// qDebug() << "<< findEdgePosition got" << min;
+ return min;
+}
+
+int QTessellatorPrivate::Scanline::findEdge(int edge) const
+{
+ for (int i = 0; i < size; ++i) {
+ int item_edge = edges[i]->edge;
+ if (item_edge == edge)
+ return i;
+ }
+ //Q_ASSERT(false);
+ return -1;
+}
+
+void QTessellatorPrivate::Scanline::clearMarks()
+{
+ for (int i = 0; i < size; ++i) {
+ edges[i]->mark = false;
+ edges[i]->intersect_left = false;
+ edges[i]->intersect_right = false;
+ }
+}
+
+void QTessellatorPrivate::Scanline::prepareLine()
+{
+ Edge **end = edges + size;
+ Edge **e = edges;
+ Edge **o = old;
+ while (e < end) {
+ *o = *e;
+ ++o;
+ ++e;
+ }
+ old_size = size;
+}
+
+void QTessellatorPrivate::Scanline::lineDone()
+{
+ Edge **end = old + old_size;
+ Edge **e = old;
+ while (e < end) {
+ if ((*e)->free) {
+ (*e)->edge = first_unused;
+ first_unused = (*e - edge_table);
+ }
+ ++e;
+ }
+}
+
+void QTessellatorPrivate::Scanline::insert(int pos, const Edge &e)
+{
+ Edge *edge = edge_table + first_unused;
+ first_unused = edge->edge;
+ Q_ASSERT(first_unused != -1);
+ *edge = e;
+ memmove(edges + pos + 1, edges + pos, (size - pos)*sizeof(Edge *));
+ edges[pos] = edge;
+ ++size;
+}
+
+void QTessellatorPrivate::Scanline::removeAt(int pos)
+{
+ Edge *e = edges[pos];
+ e->free = true;
+ --size;
+ memmove(edges + pos, edges + pos + 1, (size - pos)*sizeof(Edge *));
+}
+
+void QTessellatorPrivate::Scanline::markEdges(int pos1, int pos2)
+{
+ if (pos2 < pos1)
+ return;
+
+ for (int i = pos1; i <= pos2; ++i)
+ edges[i]->mark = true;
+}
+
+
+QTessellatorPrivate::Vertices::Vertices()
+{
+ storage = 0;
+ sorted = 0;
+ allocated = 0;
+ nPoints = 0;
+}
+
+QTessellatorPrivate::Vertices::~Vertices()
+{
+ if (storage) {
+ free(storage);
+ free(sorted);
+ }
+}
+
+void QTessellatorPrivate::Vertices::init(int maxVertices)
+{
+ if (!storage || maxVertices > allocated) {
+ int size = qMax((int)default_alloc, maxVertices);
+ storage = q_check_ptr((Vertex *)realloc(storage, size*sizeof(Vertex)));
+ sorted = q_check_ptr((Vertex **)realloc(sorted, size*sizeof(Vertex *)));
+ allocated = maxVertices;
+ }
+}
+
+void QTessellatorPrivate::Vertices::done()
+{
+ if (allocated > default_alloc) {
+ free(storage);
+ free(sorted);
+ storage = 0;
+ sorted = 0;
+ allocated = 0;
+ }
+}
+
+
+
+static inline void fillTrapezoid(Q27Dot5 y1, Q27Dot5 y2, int left, int right,
+ const QTessellatorPrivate::Vertices &vertices,
+ QTessellator::Trapezoid *trap)
+{
+ trap->top = y1;
+ trap->bottom = y2;
+ const QTessellatorPrivate::Vertex *v = vertices[left];
+ trap->topLeft = v;
+ trap->bottomLeft = vertices.next(v);
+ if (trap->topLeft->y > trap->bottomLeft->y)
+ qSwap(trap->topLeft,trap->bottomLeft);
+ v = vertices[right];
+ trap->topRight = v;
+ trap->bottomRight = vertices.next(v);
+ if (trap->topRight->y > trap->bottomRight->y)
+ qSwap(trap->topRight, trap->bottomRight);
+}
+
+QRectF QTessellatorPrivate::collectAndSortVertices(const QPointF *points, int *maxActiveEdges)
+{
+ *maxActiveEdges = 0;
+ Vertex *v = vertices.storage;
+ Vertex **vv = vertices.sorted;
+
+ qreal xmin(points[0].x());
+ qreal xmax(points[0].x());
+ qreal ymin(points[0].y());
+ qreal ymax(points[0].y());
+
+ // collect vertex data
+ Q27Dot5 y_prev = FloatToQ27Dot5(points[vertices.nPoints-1].y());
+ Q27Dot5 x_next = FloatToQ27Dot5(points[0].x());
+ Q27Dot5 y_next = FloatToQ27Dot5(points[0].y());
+ int j = 0;
+ int i = 0;
+ while (i < vertices.nPoints) {
+ Q27Dot5 y_curr = y_next;
+
+ *vv = v;
+
+ v->x = x_next;
+ v->y = y_next;
+ v->flags = 0;
+
+ next_point:
+
+ xmin = qMin(xmin, points[i+1].x());
+ xmax = qMax(xmax, points[i+1].x());
+ ymin = qMin(ymin, points[i+1].y());
+ ymax = qMax(ymax, points[i+1].y());
+
+ y_next = FloatToQ27Dot5(points[i+1].y());
+ x_next = FloatToQ27Dot5(points[i+1].x());
+
+ // skip vertices on top of each other
+ if (v->x == x_next && v->y == y_next) {
+ ++i;
+ if (i < vertices.nPoints)
+ goto next_point;
+ Vertex *v0 = vertices.storage;
+ v0->flags &= ~(LineBeforeStarts|LineBeforeEnds|LineBeforeHorizontal);
+ if (y_prev < y_curr)
+ v0->flags |= LineBeforeEnds;
+ else if (y_prev > y_curr)
+ v0->flags |= LineBeforeStarts;
+ else
+ v0->flags |= LineBeforeHorizontal;
+ if ((v0->flags & (LineBeforeStarts|LineAfterStarts))
+ && !(v0->flags & (LineAfterEnds|LineBeforeEnds)))
+ *maxActiveEdges += 2;
+ break;
+ }
+
+ if (y_prev < y_curr)
+ v->flags |= LineBeforeEnds;
+ else if (y_prev > y_curr)
+ v->flags |= LineBeforeStarts;
+ else
+ v->flags |= LineBeforeHorizontal;
+
+
+ if (y_curr < y_next)
+ v->flags |= LineAfterStarts;
+ else if (y_curr > y_next)
+ v->flags |= LineAfterEnds;
+ else
+ v->flags |= LineAfterHorizontal;
+ // ### could probably get better limit by looping over sorted list and counting down on ending edges
+ if ((v->flags & (LineBeforeStarts|LineAfterStarts))
+ && !(v->flags & (LineAfterEnds|LineBeforeEnds)))
+ *maxActiveEdges += 2;
+ y_prev = y_curr;
+ ++v;
+ ++vv;
+ ++j;
+ ++i;
+ }
+ vertices.nPoints = j;
+
+ QDEBUG() << "maxActiveEdges=" << *maxActiveEdges;
+ vv = vertices.sorted;
+ qSort(vv, vv + vertices.nPoints, compareVertex);
+
+ return QRectF(xmin, ymin, xmax-xmin, ymax-ymin);
+}
+
+struct QCoincidingEdge {
+ QTessellatorPrivate::Vertex *start;
+ QTessellatorPrivate::Vertex *end;
+ bool used;
+ bool before;
+
+ inline bool operator<(const QCoincidingEdge &e2) const
+ {
+ return end->y == e2.end->y ? end->x < e2.end->x : end->y < e2.end->y;
+ }
+};
+
+static void cancelEdges(QCoincidingEdge &e1, QCoincidingEdge &e2)
+{
+ if (e1.before) {
+ e1.start->flags &= ~(LineBeforeStarts|LineBeforeHorizontal);
+ e1.end->flags &= ~(LineAfterEnds|LineAfterHorizontal);
+ } else {
+ e1.start->flags &= ~(LineAfterStarts|LineAfterHorizontal);
+ e1.end->flags &= ~(LineBeforeEnds|LineBeforeHorizontal);
+ }
+ if (e2.before) {
+ e2.start->flags &= ~(LineBeforeStarts|LineBeforeHorizontal);
+ e2.end->flags &= ~(LineAfterEnds|LineAfterHorizontal);
+ } else {
+ e2.start->flags &= ~(LineAfterStarts|LineAfterHorizontal);
+ e2.end->flags &= ~(LineBeforeEnds|LineBeforeHorizontal);
+ }
+ e1.used = e2.used = true;
+}
+
+void QTessellatorPrivate::cancelCoincidingEdges()
+{
+ Vertex **vv = vertices.sorted;
+
+ QCoincidingEdge *tl = 0;
+ int tlSize = 0;
+
+ for (int i = 0; i < vertices.nPoints - 1; ++i) {
+ Vertex *v = vv[i];
+ int testListSize = 0;
+ while (i < vertices.nPoints - 1) {
+ Vertex *n = vv[i];
+ if (v->x != n->x || v->y != n->y)
+ break;
+
+ if (testListSize > tlSize - 2) {
+ tlSize = qMax(tlSize*2, 16);
+ tl = q_check_ptr((QCoincidingEdge *)realloc(tl, tlSize*sizeof(QCoincidingEdge)));
+ }
+ if (n->flags & (LineBeforeStarts|LineBeforeHorizontal)) {
+ tl[testListSize].start = n;
+ tl[testListSize].end = vertices.prev(n);
+ tl[testListSize].used = false;
+ tl[testListSize].before = true;
+ ++testListSize;
+ }
+ if (n->flags & (LineAfterStarts|LineAfterHorizontal)) {
+ tl[testListSize].start = n;
+ tl[testListSize].end = vertices.next(n);
+ tl[testListSize].used = false;
+ tl[testListSize].before = false;
+ ++testListSize;
+ }
+ ++i;
+ }
+ if (!testListSize)
+ continue;
+
+ qSort(tl, tl + testListSize);
+
+ for (int j = 0; j < testListSize; ++j) {
+ if (tl[j].used)
+ continue;
+
+ for (int k = j + 1; k < testListSize; ++k) {
+ if (tl[j].end->x != tl[k].end->x
+ || tl[j].end->y != tl[k].end->y
+ || tl[k].used)
+ break;
+
+ if (!winding || tl[j].before != tl[k].before) {
+ cancelEdges(tl[j], tl[k]);
+ break;
+ }
+ ++k;
+ }
+ ++j;
+ }
+ }
+ free(tl);
+}
+
+
+void QTessellatorPrivate::emitEdges(QTessellator *tessellator)
+{
+ //QDEBUG() << "TRAPS:";
+ if (!scanline.old_size)
+ return;
+
+ // emit edges
+ if (winding) {
+ // winding fill rule
+ int w = 0;
+
+ scanline.old[0]->y_left = y;
+
+ for (int i = 0; i < scanline.old_size - 1; ++i) {
+ Edge *left = scanline.old[i];
+ Edge *right = scanline.old[i+1];
+ w += left->winding;
+// qDebug() << "i=" << i << "edge->winding=" << left->winding << "winding=" << winding;
+ if (w == 0) {
+ left->y_right = y;
+ right->y_left = y;
+ } else if (!emit_clever || left->mark || right->mark) {
+ Q27Dot5 top = qMax(left->y_right, right->y_left);
+ if (top != y) {
+ QTessellator::Trapezoid trap;
+ fillTrapezoid(top, y, left->edge, right->edge, vertices, &trap);
+ tessellator->addTrap(trap);
+// QDEBUG() << " top=" << Q27Dot5ToDouble(top) << "left=" << left->edge << "right=" << right->edge;
+ }
+ right->y_left = y;
+ left->y_right = y;
+ }
+ left->mark = false;
+ }
+ if (scanline.old[scanline.old_size - 1]->mark) {
+ scanline.old[scanline.old_size - 1]->y_right = y;
+ scanline.old[scanline.old_size - 1]->mark = false;
+ }
+ } else {
+ // odd-even fill rule
+ for (int i = 0; i < scanline.old_size; i += 2) {
+ Edge *left = scanline.old[i];
+ Edge *right = scanline.old[i+1];
+ if (!emit_clever || left->mark || right->mark) {
+ Q27Dot5 top = qMax(left->y_right, right->y_left);
+ if (top != y) {
+ QTessellator::Trapezoid trap;
+ fillTrapezoid(top, y, left->edge, right->edge, vertices, &trap);
+ tessellator->addTrap(trap);
+ }
+// QDEBUG() << " top=" << Q27Dot5ToDouble(top) << "left=" << left->edge << "right=" << right->edge;
+ left->y_left = y;
+ left->y_right = y;
+ right->y_left = y;
+ right->y_right = y;
+ left->mark = right->mark = false;
+ }
+ }
+ }
+}
+
+
+void QTessellatorPrivate::processIntersections()
+{
+ QDEBUG() << "PROCESS INTERSECTIONS";
+ // process intersections
+ while (!intersections.isEmpty()) {
+ Intersections::iterator it = intersections.begin();
+ if (it.key().y != y)
+ break;
+
+ // swap edges
+ QDEBUG() << " swapping intersecting edges ";
+ int min = scanline.size;
+ int max = 0;
+ Q27Dot5 xmin = INT_MAX;
+ Q27Dot5 xmax = INT_MIN;
+ int num = 0;
+ while (1) {
+ const Intersection &i = it.key();
+ int next = it->next;
+
+ int edgePos = scanline.findEdge(i.edge);
+ if (edgePos >= 0) {
+ ++num;
+ min = qMin(edgePos, min);
+ max = qMax(edgePos, max);
+ Edge *edge = scanline.edges[edgePos];
+ xmin = qMin(xmin, edge->positionAt(y));
+ xmax = qMax(xmax, edge->positionAt(y));
+ }
+ Intersection key;
+ key.y = y;
+ key.edge = next;
+ it = intersections.find(key);
+ intersections.remove(i);
+ if (it == intersections.end())
+ break;
+ }
+ if (num < 2)
+ continue;
+
+ Q_ASSERT(min != max);
+ QDEBUG() << "sorting between" << min << "and" << max << "xpos=" << xmin << xmax;
+ while (min > 0 && scanline.edges[min - 1]->positionAt(y) >= xmin) {
+ QDEBUG() << " adding edge on left";
+ --min;
+ }
+ while (max < scanline.size - 1 && scanline.edges[max + 1]->positionAt(y) <= xmax) {
+ QDEBUG() << " adding edge on right";
+ ++max;
+ }
+
+ qSort(scanline.edges + min, scanline.edges + max + 1, EdgeSorter(y));
+#ifdef DEBUG
+ for (int i = min; i <= max; ++i)
+ QDEBUG() << " " << scanline.edges[i]->edge << "at pos" << i;
+#endif
+ for (int i = min; i <= max; ++i) {
+ Edge *edge = scanline.edges[i];
+ edge->intersect_left = true;
+ edge->intersect_right = true;
+ edge->mark = true;
+ }
+ }
+}
+
+void QTessellatorPrivate::removeEdges()
+{
+ int cv = currentVertex;
+ while (cv < vertices.nPoints) {
+ const Vertex *v = vertices.sorted[cv];
+ if (v->y > y)
+ break;
+ if (v->flags & LineBeforeEnds) {
+ QDEBUG() << " removing edge" << vertices.prevPos(v);
+ int pos = scanline.findEdge(vertices.prevPos(v));
+ if (pos == -1)
+ continue;
+ scanline.edges[pos]->mark = true;
+ if (pos > 0)
+ scanline.edges[pos - 1]->intersect_right = true;
+ if (pos < scanline.size - 1)
+ scanline.edges[pos + 1]->intersect_left = true;
+ scanline.removeAt(pos);
+ }
+ if (v->flags & LineAfterEnds) {
+ QDEBUG() << " removing edge" << vertices.position(v);
+ int pos = scanline.findEdge(vertices.position(v));
+ if (pos == -1)
+ continue;
+ scanline.edges[pos]->mark = true;
+ if (pos > 0)
+ scanline.edges[pos - 1]->intersect_right = true;
+ if (pos < scanline.size - 1)
+ scanline.edges[pos + 1]->intersect_left = true;
+ scanline.removeAt(pos);
+ }
+ ++cv;
+ }
+}
+
+void QTessellatorPrivate::addEdges()
+{
+ while (currentVertex < vertices.nPoints) {
+ const Vertex *v = vertices.sorted[currentVertex];
+ if (v->y > y)
+ break;
+ if (v->flags & LineBeforeStarts) {
+ // add new edge
+ int start = vertices.prevPos(v);
+ Edge e(vertices, start);
+ int pos = scanline.findEdgePosition(e);
+ QDEBUG() << " adding edge" << start << "at position" << pos;
+ scanline.insert(pos, e);
+ if (!mark_clever || !(v->flags & LineAfterEnds)) {
+ if (pos > 0)
+ scanline.edges[pos - 1]->mark = true;
+ if (pos < scanline.size - 1)
+ scanline.edges[pos + 1]->mark = true;
+ }
+ }
+ if (v->flags & LineAfterStarts) {
+ Edge e(vertices, vertices.position(v));
+ int pos = scanline.findEdgePosition(e);
+ QDEBUG() << " adding edge" << vertices.position(v) << "at position" << pos;
+ scanline.insert(pos, e);
+ if (!mark_clever || !(v->flags & LineBeforeEnds)) {
+ if (pos > 0)
+ scanline.edges[pos - 1]->mark = true;
+ if (pos < scanline.size - 1)
+ scanline.edges[pos + 1]->mark = true;
+ }
+ }
+ if (v->flags & LineAfterHorizontal) {
+ int pos1 = scanline.findEdgePosition(v->x, v->y);
+ const Vertex *next = vertices.next(v);
+ Q_ASSERT(v->y == next->y);
+ int pos2 = scanline.findEdgePosition(next->x, next->y);
+ if (pos2 < pos1)
+ qSwap(pos1, pos2);
+ if (pos1 > 0)
+ --pos1;
+ if (pos2 == scanline.size)
+ --pos2;
+ //QDEBUG() << "marking horizontal edge from " << pos1 << "to" << pos2;
+ scanline.markEdges(pos1, pos2);
+ }
+ ++currentVertex;
+ }
+}
+
+#ifdef DEBUG
+static void checkLinkChain(const QTessellatorPrivate::Intersections &intersections,
+ QTessellatorPrivate::Intersection i)
+{
+// qDebug() << " Link chain: ";
+ int end = i.edge;
+ while (1) {
+ QTessellatorPrivate::IntersectionLink l = intersections.value(i);
+// qDebug() << " " << i.edge << "next=" << l.next << "prev=" << l.prev;
+ if (l.next == end)
+ break;
+ Q_ASSERT(l.next != -1);
+ Q_ASSERT(l.prev != -1);
+
+ QTessellatorPrivate::Intersection i2 = i;
+ i2.edge = l.next;
+ QTessellatorPrivate::IntersectionLink l2 = intersections.value(i2);
+
+ Q_ASSERT(l2.next != -1);
+ Q_ASSERT(l2.prev != -1);
+ Q_ASSERT(l.next == i2.edge);
+ Q_ASSERT(l2.prev == i.edge);
+ i = i2;
+ }
+}
+#endif
+
+bool QTessellatorPrivate::edgeInChain(Intersection i, int edge)
+{
+ int end = i.edge;
+ while (1) {
+ if (i.edge == edge)
+ return true;
+ IntersectionLink l = intersections.value(i);
+ if (l.next == end)
+ break;
+ Q_ASSERT(l.next != -1);
+ Q_ASSERT(l.prev != -1);
+
+ Intersection i2 = i;
+ i2.edge = l.next;
+
+#ifndef QT_NO_DEBUG
+ IntersectionLink l2 = intersections.value(i2);
+ Q_ASSERT(l2.next != -1);
+ Q_ASSERT(l2.prev != -1);
+ Q_ASSERT(l.next == i2.edge);
+ Q_ASSERT(l2.prev == i.edge);
+#endif
+ i = i2;
+ }
+ return false;
+}
+
+
+void QTessellatorPrivate::addIntersection(const Edge *e1, const Edge *e2)
+{
+ const IntersectionLink emptyLink = {-1, -1};
+
+ int next = vertices.nextPos(vertices[e1->edge]);
+ if (e2->edge == next)
+ return;
+ int prev = vertices.prevPos(vertices[e1->edge]);
+ if (e2->edge == prev)
+ return;
+
+ Q27Dot5 yi;
+ bool det_positive;
+ bool isect = e1->intersect(*e2, &yi, &det_positive);
+ QDEBUG("checking edges %d and %d", e1->edge, e2->edge);
+ if (!isect) {
+ QDEBUG() << " no intersection";
+ return;
+ }
+
+ // don't emit an intersection if it's at the start of a line segment or above us
+ if (yi <= y) {
+ if (!det_positive)
+ return;
+ QDEBUG() << " ----->>>>>> WRONG ORDER!";
+ yi = y;
+ }
+ QDEBUG() << " between edges " << e1->edge << "and" << e2->edge << "at point ("
+ << Q27Dot5ToDouble(yi) << ')';
+
+ Intersection i1;
+ i1.y = yi;
+ i1.edge = e1->edge;
+ IntersectionLink link1 = intersections.value(i1, emptyLink);
+ Intersection i2;
+ i2.y = yi;
+ i2.edge = e2->edge;
+ IntersectionLink link2 = intersections.value(i2, emptyLink);
+
+ // new pair of edges
+ if (link1.next == -1 && link2.next == -1) {
+ link1.next = link1.prev = i2.edge;
+ link2.next = link2.prev = i1.edge;
+ } else if (link1.next == i2.edge || link1.prev == i2.edge
+ || link2.next == i1.edge || link2.prev == i1.edge) {
+#ifdef DEBUG
+ checkLinkChain(intersections, i1);
+ checkLinkChain(intersections, i2);
+ Q_ASSERT(edgeInChain(i1, i2.edge));
+#endif
+ return;
+ } else if (link1.next == -1 || link2.next == -1) {
+ if (link2.next == -1) {
+ qSwap(i1, i2);
+ qSwap(link1, link2);
+ }
+ Q_ASSERT(link1.next == -1);
+#ifdef DEBUG
+ checkLinkChain(intersections, i2);
+#endif
+ // only i2 in list
+ link1.next = i2.edge;
+ link1.prev = link2.prev;
+ link2.prev = i1.edge;
+ Intersection other;
+ other.y = yi;
+ other.edge = link1.prev;
+ IntersectionLink link = intersections.value(other, emptyLink);
+ Q_ASSERT(link.next == i2.edge);
+ Q_ASSERT(link.prev != -1);
+ link.next = i1.edge;
+ intersections.insert(other, link);
+ } else {
+ bool connected = edgeInChain(i1, i2.edge);
+ if (connected)
+ return;
+#ifdef DEBUG
+ checkLinkChain(intersections, i1);
+ checkLinkChain(intersections, i2);
+#endif
+ // both already in some list. Have to make sure they are connected
+ // this can be done by cutting open the ring(s) after the two eges and
+ // connecting them again
+ Intersection other1;
+ other1.y = yi;
+ other1.edge = link1.next;
+ IntersectionLink linko1 = intersections.value(other1, emptyLink);
+ Intersection other2;
+ other2.y = yi;
+ other2.edge = link2.next;
+ IntersectionLink linko2 = intersections.value(other2, emptyLink);
+
+ linko1.prev = i2.edge;
+ link2.next = other1.edge;
+
+ linko2.prev = i1.edge;
+ link1.next = other2.edge;
+ intersections.insert(other1, linko1);
+ intersections.insert(other2, linko2);
+ }
+ intersections.insert(i1, link1);
+ intersections.insert(i2, link2);
+#ifdef DEBUG
+ checkLinkChain(intersections, i1);
+ checkLinkChain(intersections, i2);
+ Q_ASSERT(edgeInChain(i1, i2.edge));
+#endif
+ return;
+
+}
+
+
+void QTessellatorPrivate::addIntersections()
+{
+ if (scanline.size) {
+ QDEBUG() << "INTERSECTIONS";
+ // check marked edges for intersections
+#ifdef DEBUG
+ for (int i = 0; i < scanline.size; ++i) {
+ Edge *e = scanline.edges[i];
+ QDEBUG() << " " << i << e->edge << "isect=(" << e->intersect_left << e->intersect_right
+ << ')';
+ }
+#endif
+
+ for (int i = 0; i < scanline.size - 1; ++i) {
+ Edge *e1 = scanline.edges[i];
+ Edge *e2 = scanline.edges[i + 1];
+ // check for intersection
+ if (e1->intersect_right || e2->intersect_left)
+ addIntersection(e1, e2);
+ }
+ }
+#if 0
+ if (intersections.constBegin().key().y == y) {
+ QDEBUG() << "----------------> intersection on same line";
+ scanline.clearMarks();
+ scanline.processIntersections(y, &intersections);
+ goto redo;
+ }
+#endif
+}
+
+
+QTessellator::QTessellator()
+{
+ d = new QTessellatorPrivate;
+}
+
+QTessellator::~QTessellator()
+{
+ delete d;
+}
+
+void QTessellator::setWinding(bool w)
+{
+ d->winding = w;
+}
+
+
+QRectF QTessellator::tessellate(const QPointF *points, int nPoints)
+{
+ Q_ASSERT(points[0] == points[nPoints-1]);
+ --nPoints;
+
+#ifdef DEBUG
+ QDEBUG()<< "POINTS:";
+ for (int i = 0; i < nPoints; ++i) {
+ QDEBUG() << points[i];
+ }
+#endif
+
+ // collect edges and calculate bounds
+ d->vertices.nPoints = nPoints;
+ d->vertices.init(nPoints);
+
+ int maxActiveEdges = 0;
+ QRectF br = d->collectAndSortVertices(points, &maxActiveEdges);
+ d->cancelCoincidingEdges();
+
+#ifdef DEBUG
+ QDEBUG() << "nPoints = " << nPoints << "using " << d->vertices.nPoints;
+ QDEBUG()<< "VERTICES:";
+ for (int i = 0; i < d->vertices.nPoints; ++i) {
+ QDEBUG() << " " << i << ": "
+ << "point=" << d->vertices.position(d->vertices.sorted[i])
+ << "flags=" << d->vertices.sorted[i]->flags
+ << "pos=(" << Q27Dot5ToDouble(d->vertices.sorted[i]->x) << '/'
+ << Q27Dot5ToDouble(d->vertices.sorted[i]->y) << ')';
+ }
+#endif
+
+ d->scanline.init(maxActiveEdges);
+ d->y = INT_MIN/256;
+ d->currentVertex = 0;
+
+ while (d->currentVertex < d->vertices.nPoints) {
+ d->scanline.clearMarks();
+
+ d->y = d->vertices.sorted[d->currentVertex]->y;
+ if (!d->intersections.isEmpty())
+ d->y = qMin(d->y, d->intersections.constBegin().key().y);
+
+ QDEBUG()<< "===== SCANLINE: y =" << Q27Dot5ToDouble(d->y) << " =====";
+
+ d->scanline.prepareLine();
+ d->processIntersections();
+ d->removeEdges();
+ d->addEdges();
+ d->addIntersections();
+ d->emitEdges(this);
+ d->scanline.lineDone();
+
+#ifdef DEBUG
+ QDEBUG()<< "===== edges:";
+ for (int i = 0; i < d->scanline.size; ++i) {
+ QDEBUG() << " " << d->scanline.edges[i]->edge
+ << "p0= (" << Q27Dot5ToDouble(d->scanline.edges[i]->v0->x)
+ << '/' << Q27Dot5ToDouble(d->scanline.edges[i]->v0->y)
+ << ") p1= (" << Q27Dot5ToDouble(d->scanline.edges[i]->v1->x)
+ << '/' << Q27Dot5ToDouble(d->scanline.edges[i]->v1->y) << ')'
+ << "x=" << Q27Dot5ToDouble(d->scanline.edges[i]->positionAt(d->y))
+ << "isLeftOfNext="
+ << ((i < d->scanline.size - 1)
+ ? d->scanline.edges[i]->isLeftOf(*d->scanline.edges[i+1], d->y)
+ : true);
+ }
+#endif
+}
+
+ d->scanline.done();
+ d->intersections.clear();
+ return br;
+}
+
+// tessellates the given convex polygon
+void QTessellator::tessellateConvex(const QPointF *points, int nPoints)
+{
+ Q_ASSERT(points[0] == points[nPoints-1]);
+ --nPoints;
+
+ d->vertices.nPoints = nPoints;
+ d->vertices.init(nPoints);
+
+ for (int i = 0; i < nPoints; ++i) {
+ d->vertices[i]->x = FloatToQ27Dot5(points[i].x());
+ d->vertices[i]->y = FloatToQ27Dot5(points[i].y());
+ }
+
+ int left = 0, right = 0;
+
+ int top = 0;
+ for (int i = 1; i < nPoints; ++i) {
+ if (d->vertices[i]->y < d->vertices[top]->y)
+ top = i;
+ }
+
+ left = (top + nPoints - 1) % nPoints;
+ right = (top + 1) % nPoints;
+
+ while (d->vertices[left]->x == d->vertices[top]->x && d->vertices[left]->y == d->vertices[top]->y && left != right)
+ left = (left + nPoints - 1) % nPoints;
+
+ while (d->vertices[right]->x == d->vertices[top]->x && d->vertices[right]->y == d->vertices[top]->y && left != right)
+ right = (right + 1) % nPoints;
+
+ if (left == right)
+ return;
+
+ int dir = 1;
+
+ Vertex dLeft = { d->vertices[top]->x - d->vertices[left]->x,
+ d->vertices[top]->y - d->vertices[left]->y };
+
+ Vertex dRight = { d->vertices[right]->x - d->vertices[top]->x,
+ d->vertices[right]->y - d->vertices[top]->y };
+
+ Q27Dot5 cross = dLeft.x * dRight.y - dLeft.y * dRight.x;
+
+ // flip direction if polygon is clockwise
+ if (cross < 0 || (cross == 0 && dLeft.x > 0)) {
+ qSwap(left, right);
+ dir = -1;
+ }
+
+ Vertex *lastLeft = d->vertices[top];
+ Vertex *lastRight = d->vertices[top];
+
+ QTessellator::Trapezoid trap;
+
+ while (lastLeft->y == d->vertices[left]->y && left != right) {
+ lastLeft = d->vertices[left];
+ left = (left + nPoints - dir) % nPoints;
+ }
+
+ while (lastRight->y == d->vertices[right]->y && left != right) {
+ lastRight = d->vertices[right];
+ right = (right + nPoints + dir) % nPoints;
+ }
+
+ while (true) {
+ trap.top = qMax(lastRight->y, lastLeft->y);
+ trap.bottom = qMin(d->vertices[left]->y, d->vertices[right]->y);
+ trap.topLeft = lastLeft;
+ trap.topRight = lastRight;
+ trap.bottomLeft = d->vertices[left];
+ trap.bottomRight = d->vertices[right];
+
+ if (trap.bottom > trap.top)
+ addTrap(trap);
+
+ if (left == right)
+ break;
+
+ if (d->vertices[right]->y < d->vertices[left]->y) {
+ do {
+ lastRight = d->vertices[right];
+ right = (right + nPoints + dir) % nPoints;
+ }
+ while (lastRight->y == d->vertices[right]->y && left != right);
+ } else {
+ do {
+ lastLeft = d->vertices[left];
+ left = (left + nPoints - dir) % nPoints;
+ }
+ while (lastLeft->y == d->vertices[left]->y && left != right);
+ }
+ }
+}
+
+// tessellates the stroke of the line from a_ to b_ with the given width and a flat cap
+void QTessellator::tessellateRect(const QPointF &a_, const QPointF &b_, qreal width)
+{
+ Vertex a = { FloatToQ27Dot5(a_.x()), FloatToQ27Dot5(a_.y()) };
+ Vertex b = { FloatToQ27Dot5(b_.x()), FloatToQ27Dot5(b_.y()) };
+
+ QPointF pa = a_, pb = b_;
+
+ if (a.y > b.y) {
+ qSwap(a, b);
+ qSwap(pa, pb);
+ }
+
+ Vertex delta = { b.x - a.x, b.y - a.y };
+
+ if (delta.x == 0 && delta.y == 0)
+ return;
+
+ qreal hw = 0.5 * width;
+
+ if (delta.x == 0) {
+ Q27Dot5 halfWidth = FloatToQ27Dot5(hw);
+
+ if (halfWidth == 0)
+ return;
+
+ Vertex topLeft = { a.x - halfWidth, a.y };
+ Vertex topRight = { a.x + halfWidth, a.y };
+ Vertex bottomLeft = { a.x - halfWidth, b.y };
+ Vertex bottomRight = { a.x + halfWidth, b.y };
+
+ QTessellator::Trapezoid trap = { topLeft.y, bottomLeft.y, &topLeft, &bottomLeft, &topRight, &bottomRight };
+ addTrap(trap);
+ } else if (delta.y == 0) {
+ Q27Dot5 halfWidth = FloatToQ27Dot5(hw);
+
+ if (halfWidth == 0)
+ return;
+
+ if (a.x > b.x)
+ qSwap(a.x, b.x);
+
+ Vertex topLeft = { a.x, a.y - halfWidth };
+ Vertex topRight = { b.x, a.y - halfWidth };
+ Vertex bottomLeft = { a.x, a.y + halfWidth };
+ Vertex bottomRight = { b.x, a.y + halfWidth };
+
+ QTessellator::Trapezoid trap = { topLeft.y, bottomLeft.y, &topLeft, &bottomLeft, &topRight, &bottomRight };
+ addTrap(trap);
+ } else {
+ QPointF perp(pb.y() - pa.y(), pa.x() - pb.x());
+ qreal length = qSqrt(perp.x() * perp.x() + perp.y() * perp.y());
+
+ if (qFuzzyIsNull(length))
+ return;
+
+ // need the half of the width
+ perp *= hw / length;
+
+ QPointF pta = pa + perp;
+ QPointF ptb = pa - perp;
+ QPointF ptc = pb - perp;
+ QPointF ptd = pb + perp;
+
+ Vertex ta = { FloatToQ27Dot5(pta.x()), FloatToQ27Dot5(pta.y()) };
+ Vertex tb = { FloatToQ27Dot5(ptb.x()), FloatToQ27Dot5(ptb.y()) };
+ Vertex tc = { FloatToQ27Dot5(ptc.x()), FloatToQ27Dot5(ptc.y()) };
+ Vertex td = { FloatToQ27Dot5(ptd.x()), FloatToQ27Dot5(ptd.y()) };
+
+ if (ta.y < tb.y) {
+ if (tb.y < td.y) {
+ QTessellator::Trapezoid top = { ta.y, tb.y, &ta, &tb, &ta, &td };
+ QTessellator::Trapezoid bottom = { td.y, tc.y, &tb, &tc, &td, &tc };
+ addTrap(top);
+ addTrap(bottom);
+
+ QTessellator::Trapezoid middle = { tb.y, td.y, &tb, &tc, &ta, &td };
+ addTrap(middle);
+ } else {
+ QTessellator::Trapezoid top = { ta.y, td.y, &ta, &tb, &ta, &td };
+ QTessellator::Trapezoid bottom = { tb.y, tc.y, &tb, &tc, &td, &tc };
+ addTrap(top);
+ addTrap(bottom);
+
+ if (tb.y != td.y) {
+ QTessellator::Trapezoid middle = { td.y, tb.y, &ta, &tb, &td, &tc };
+ addTrap(middle);
+ }
+ }
+ } else {
+ if (ta.y < tc.y) {
+ QTessellator::Trapezoid top = { tb.y, ta.y, &tb, &tc, &tb, &ta };
+ QTessellator::Trapezoid bottom = { tc.y, td.y, &tc, &td, &ta, &td };
+ addTrap(top);
+ addTrap(bottom);
+
+ QTessellator::Trapezoid middle = { ta.y, tc.y, &tb, &tc, &ta, &td };
+ addTrap(middle);
+ } else {
+ QTessellator::Trapezoid top = { tb.y, tc.y, &tb, &tc, &tb, &ta };
+ QTessellator::Trapezoid bottom = { ta.y, td.y, &tc, &td, &ta, &td };
+ addTrap(top);
+ addTrap(bottom);
+
+ if (ta.y != tc.y) {
+ QTessellator::Trapezoid middle = { tc.y, ta.y, &tc, &td, &tb, &ta };
+ addTrap(middle);
+ }
+ }
+ }
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qtessellator_p.h b/src/gui/painting/qtessellator_p.h
new file mode 100644
index 0000000000..ad3ef9776f
--- /dev/null
+++ b/src/gui/painting/qtessellator_p.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTESSELATOR_P_H
+#define QTESSELATOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qpoint.h>
+#include <qrect.h>
+
+QT_BEGIN_NAMESPACE
+
+class QTessellatorPrivate;
+
+typedef int Q27Dot5;
+#define Q27Dot5ToDouble(i) ((i)/32.)
+#define FloatToQ27Dot5(i) (int)((i) * 32)
+#define IntToQ27Dot5(i) ((i) << 5)
+#define Q27Dot5ToXFixed(i) ((i) << 11)
+#define Q27Dot5Factor 32
+
+class Q_GUI_EXPORT QTessellator {
+public:
+ QTessellator();
+ virtual ~QTessellator();
+
+ QRectF tessellate(const QPointF *points, int nPoints);
+ void tessellateConvex(const QPointF *points, int nPoints);
+ void tessellateRect(const QPointF &a, const QPointF &b, qreal width);
+
+ void setWinding(bool w);
+
+ struct Vertex {
+ Q27Dot5 x;
+ Q27Dot5 y;
+ };
+ struct Trapezoid {
+ Q27Dot5 top;
+ Q27Dot5 bottom;
+ const Vertex *topLeft;
+ const Vertex *bottomLeft;
+ const Vertex *topRight;
+ const Vertex *bottomRight;
+ };
+ virtual void addTrap(const Trapezoid &trap) = 0;
+
+private:
+ friend class QTessellatorPrivate;
+ QTessellatorPrivate *d;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/painting/qtextureglyphcache.cpp b/src/gui/painting/qtextureglyphcache.cpp
new file mode 100644
index 0000000000..53f025f819
--- /dev/null
+++ b/src/gui/painting/qtextureglyphcache.cpp
@@ -0,0 +1,468 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qmath.h>
+
+#include "qtextureglyphcache_p.h"
+
+#include "private/qnumeric_p.h"
+#include "private/qnativeimage_p.h"
+#include "private/qfontengine_ft_p.h"
+
+QT_BEGIN_NAMESPACE
+
+// #define CACHE_DEBUG
+
+// returns the highest number closest to v, which is a power of 2
+// NB! assumes 32 bit ints
+static inline int qt_next_power_of_two(int v)
+{
+ v--;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ ++v;
+ return v;
+}
+
+int QTextureGlyphCache::calculateSubPixelPositionCount(glyph_t glyph) const
+{
+ // Test 12 different subpixel positions since it factors into 3*4 so it gives
+ // the coverage we need.
+
+ QList<QImage> images;
+ for (int i=0; i<12; ++i) {
+ QImage img = textureMapForGlyph(glyph, QFixed::fromReal(i / 12.0));
+
+ if (images.isEmpty()) {
+ QPainterPath path;
+ QFixedPoint point;
+ m_current_fontengine->addGlyphsToPath(&glyph, &point, 1, &path, QTextItem::RenderFlags());
+
+ // Glyph is space, return 0 to indicate that we need to keep trying
+ if (path.isEmpty())
+ break;
+
+ images.append(img);
+ } else {
+ bool found = false;
+ for (int j=0; j<images.size(); ++j) {
+ if (images.at(j) == img) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ images.append(img);
+ }
+ }
+
+ return images.size();
+}
+
+QFixed QTextureGlyphCache::subPixelPositionForX(QFixed x) const
+{
+ if (m_subPixelPositionCount <= 1)
+ return QFixed();
+
+ QFixed subPixelPosition;
+ if (x != 0) {
+ subPixelPosition = x - x.floor();
+ QFixed fraction = (subPixelPosition / QFixed::fromReal(1.0 / m_subPixelPositionCount)).floor();
+
+ // Compensate for precision loss in fixed point to make sure we are always drawing at a subpixel position over
+ // the lower boundary for the selected rasterization by adding 1/64.
+ subPixelPosition = fraction / QFixed(m_subPixelPositionCount) + QFixed::fromReal(0.015625);
+ }
+ return subPixelPosition;
+}
+
+bool QTextureGlyphCache::populate(QFontEngine *fontEngine, int numGlyphs, const glyph_t *glyphs,
+ const QFixedPoint *positions)
+{
+#ifdef CACHE_DEBUG
+ printf("Populating with %d glyphs\n", numGlyphs);
+ qDebug() << " -> current transformation: " << m_transform;
+#endif
+
+ m_current_fontengine = fontEngine;
+ const int margin = glyphMargin();
+ const int paddingDoubled = glyphPadding() * 2;
+
+ bool supportsSubPixelPositions = fontEngine->supportsSubPixelPositions();
+ if (m_subPixelPositionCount == 0) {
+ if (!supportsSubPixelPositions) {
+ m_subPixelPositionCount = 1;
+ } else {
+#if !defined(Q_WS_X11)
+ int i = 0;
+ while (m_subPixelPositionCount == 0 && i < numGlyphs)
+ m_subPixelPositionCount = calculateSubPixelPositionCount(glyphs[i++]);
+#else
+ m_subPixelPositionCount = 4;
+#endif
+ }
+ }
+
+ QHash<GlyphAndSubPixelPosition, Coord> listItemCoordinates;
+ int rowHeight = 0;
+
+ QFontEngine::GlyphFormat format;
+ switch (m_type) {
+ case Raster_A8: format = QFontEngine::Format_A8; break;
+ case Raster_RGBMask: format = QFontEngine::Format_A32; break;
+ default: format = QFontEngine::Format_Mono; break;
+ }
+
+ // check each glyph for its metrics and get the required rowHeight.
+ for (int i=0; i < numGlyphs; ++i) {
+ const glyph_t glyph = glyphs[i];
+
+ QFixed subPixelPosition;
+ if (supportsSubPixelPositions) {
+ QFixed x = positions != 0 ? positions[i].x : QFixed();
+ subPixelPosition = subPixelPositionForX(x);
+ }
+
+ if (coords.contains(GlyphAndSubPixelPosition(glyph, subPixelPosition)))
+ continue;
+ if (listItemCoordinates.contains(GlyphAndSubPixelPosition(glyph, subPixelPosition)))
+ continue;
+ glyph_metrics_t metrics = fontEngine->alphaMapBoundingBox(glyph, subPixelPosition, m_transform, format);
+
+#ifdef CACHE_DEBUG
+ printf("(%4x): w=%.2f, h=%.2f, xoff=%.2f, yoff=%.2f, x=%.2f, y=%.2f\n",
+ glyph,
+ metrics.width.toReal(),
+ metrics.height.toReal(),
+ metrics.xoff.toReal(),
+ metrics.yoff.toReal(),
+ metrics.x.toReal(),
+ metrics.y.toReal());
+#endif
+ GlyphAndSubPixelPosition key(glyph, subPixelPosition);
+ int glyph_width = metrics.width.ceil().toInt();
+ int glyph_height = metrics.height.ceil().toInt();
+ if (glyph_height == 0 || glyph_width == 0) {
+ // Avoid multiple calls to boundingBox() for non-printable characters
+ Coord c = { 0, 0, 0, 0, 0, 0 };
+ coords.insert(key, c);
+ continue;
+ }
+ glyph_width += margin * 2 + 4;
+ glyph_height += margin * 2 + 4;
+ // align to 8-bit boundary
+ if (m_type == QFontEngineGlyphCache::Raster_Mono)
+ glyph_width = (glyph_width+7)&~7;
+
+ Coord c = { 0, 0, // will be filled in later
+ glyph_width,
+ glyph_height, // texture coords
+ metrics.x.round().truncate(),
+ -metrics.y.truncate() }; // baseline for horizontal scripts
+
+ listItemCoordinates.insert(key, c);
+ rowHeight = qMax(rowHeight, glyph_height);
+ }
+ if (listItemCoordinates.isEmpty())
+ return true;
+
+ rowHeight += margin * 2 + paddingDoubled;
+
+ if (m_w == 0) {
+ if (fontEngine->maxCharWidth() <= QT_DEFAULT_TEXTURE_GLYPH_CACHE_WIDTH)
+ m_w = QT_DEFAULT_TEXTURE_GLYPH_CACHE_WIDTH;
+ else
+ m_w = qt_next_power_of_two(fontEngine->maxCharWidth());
+ }
+
+ // now actually use the coords and paint the wanted glyps into cache.
+ QHash<GlyphAndSubPixelPosition, Coord>::iterator iter = listItemCoordinates.begin();
+ int requiredWidth = m_w;
+ while (iter != listItemCoordinates.end()) {
+ Coord c = iter.value();
+
+ m_currentRowHeight = qMax(m_currentRowHeight, c.h + margin * 2);
+
+ if (m_cx + c.w > requiredWidth) {
+ int new_width = requiredWidth*2;
+ while (new_width < m_cx + c.w)
+ new_width *= 2;
+ if (new_width <= maxTextureWidth()) {
+ requiredWidth = new_width;
+ } else {
+ // no room on the current line, start new glyph strip
+ m_cx = 0;
+ m_cy += m_currentRowHeight + paddingDoubled;
+ m_currentRowHeight = c.h + margin * 2; // New row
+ }
+ }
+
+ if (maxTextureHeight() > 0 && m_cy + c.h > maxTextureHeight()) {
+ // We can't make a cache of the required size, so we bail out
+ return false;
+ }
+
+ c.x = m_cx;
+ c.y = m_cy;
+
+ coords.insert(iter.key(), c);
+ m_pendingGlyphs.insert(iter.key(), c);
+
+ m_cx += c.w + paddingDoubled;
+ ++iter;
+ }
+ return true;
+
+}
+
+void QTextureGlyphCache::fillInPendingGlyphs()
+{
+ if (m_pendingGlyphs.isEmpty())
+ return;
+
+ int requiredHeight = m_h;
+ int requiredWidth = m_w; // Use a minimum size to avoid a lot of initial reallocations
+ {
+ QHash<GlyphAndSubPixelPosition, Coord>::iterator iter = m_pendingGlyphs.begin();
+ while (iter != m_pendingGlyphs.end()) {
+ Coord c = iter.value();
+ requiredHeight = qMax(requiredHeight, c.y + c.h);
+ requiredWidth = qMax(requiredWidth, c.x + c.w);
+ ++iter;
+ }
+ }
+
+ if (isNull() || requiredHeight > m_h || requiredWidth > m_w) {
+ if (isNull())
+ createCache(qt_next_power_of_two(requiredWidth), qt_next_power_of_two(requiredHeight));
+ else
+ resizeCache(qt_next_power_of_two(requiredWidth), qt_next_power_of_two(requiredHeight));
+ }
+
+ {
+ QHash<GlyphAndSubPixelPosition, Coord>::iterator iter = m_pendingGlyphs.begin();
+ while (iter != m_pendingGlyphs.end()) {
+ GlyphAndSubPixelPosition key = iter.key();
+ fillTexture(iter.value(), key.glyph, key.subPixelPosition);
+
+ ++iter;
+ }
+ }
+
+ m_pendingGlyphs.clear();
+}
+
+QImage QTextureGlyphCache::textureMapForGlyph(glyph_t g, QFixed subPixelPosition) const
+{
+#if defined(Q_WS_X11)
+ if (m_transform.type() > QTransform::TxTranslate) {
+ QFontEngineFT::GlyphFormat format = QFontEngineFT::Format_None;
+ QImage::Format imageFormat = QImage::Format_Invalid;
+ switch (m_type) {
+ case Raster_RGBMask:
+ format = QFontEngineFT::Format_A32;
+ imageFormat = QImage::Format_RGB32;
+ break;
+ case Raster_A8:
+ format = QFontEngineFT::Format_A8;
+ imageFormat = QImage::Format_Indexed8;
+ break;
+ case Raster_Mono:
+ format = QFontEngineFT::Format_Mono;
+ imageFormat = QImage::Format_Mono;
+ break;
+ };
+
+ QFontEngineFT *ft = static_cast<QFontEngineFT*> (m_current_fontengine);
+ QFontEngineFT::QGlyphSet *gset = ft->loadTransformedGlyphSet(m_transform);
+ QFixedPoint positions[1];
+ positions[0].x = subPixelPosition;
+
+ if (gset && ft->loadGlyphs(gset, &g, 1, positions, format)) {
+ QFontEngineFT::Glyph *glyph = gset->getGlyph(g, subPixelPosition);
+ const int bytesPerLine = (format == QFontEngineFT::Format_Mono ? ((glyph->width + 31) & ~31) >> 3
+ : (glyph->width + 3) & ~3);
+ return QImage(glyph->data, glyph->width, glyph->height, bytesPerLine, imageFormat);
+ }
+ } else
+#endif
+ if (m_type == QFontEngineGlyphCache::Raster_RGBMask)
+ return m_current_fontengine->alphaRGBMapForGlyph(g, subPixelPosition, glyphMargin(), m_transform);
+ else
+ return m_current_fontengine->alphaMapForGlyph(g, subPixelPosition, m_transform);
+
+ return QImage();
+}
+
+/************************************************************************
+ * QImageTextureGlyphCache
+ */
+
+void QImageTextureGlyphCache::resizeTextureData(int width, int height)
+{
+ m_image = m_image.copy(0, 0, width, height);
+}
+
+void QImageTextureGlyphCache::createTextureData(int width, int height)
+{
+ switch (m_type) {
+ case QFontEngineGlyphCache::Raster_Mono:
+ m_image = QImage(width, height, QImage::Format_Mono);
+ break;
+ case QFontEngineGlyphCache::Raster_A8: {
+ m_image = QImage(width, height, QImage::Format_Indexed8);
+ m_image.fill(0);
+ QVector<QRgb> colors(256);
+ QRgb *it = colors.data();
+ for (int i=0; i<256; ++i, ++it)
+ *it = 0xff000000 | i | (i<<8) | (i<<16);
+ m_image.setColorTable(colors);
+ break; }
+ case QFontEngineGlyphCache::Raster_RGBMask:
+ m_image = QImage(width, height, QImage::Format_RGB32);
+ break;
+ }
+}
+
+int QImageTextureGlyphCache::glyphMargin() const
+{
+#if (defined(Q_WS_MAC) && defined(QT_MAC_USE_COCOA)) || defined(Q_WS_X11)
+ return 0;
+#else
+ return m_type == QFontEngineGlyphCache::Raster_RGBMask ? 2 : 0;
+#endif
+}
+
+void QImageTextureGlyphCache::fillTexture(const Coord &c, glyph_t g, QFixed subPixelPosition)
+{
+ QImage mask = textureMapForGlyph(g, subPixelPosition);
+
+#ifdef CACHE_DEBUG
+ printf("fillTexture of %dx%d at %d,%d in the cache of %dx%d\n", c.w, c.h, c.x, c.y, m_image.width(), m_image.height());
+ if (mask.width() > c.w || mask.height() > c.h) {
+ printf(" ERROR; mask is bigger than reserved space! %dx%d instead of %dx%d\n", mask.width(), mask.height(), c.w,c.h);
+ return;
+ }
+#endif
+
+ if (m_type == QFontEngineGlyphCache::Raster_RGBMask) {
+ QImage ref(m_image.bits() + (c.x * 4 + c.y * m_image.bytesPerLine()),
+ qMax(mask.width(), c.w), qMax(mask.height(), c.h), m_image.bytesPerLine(),
+ m_image.format());
+ QPainter p(&ref);
+ p.setCompositionMode(QPainter::CompositionMode_Source);
+ p.fillRect(0, 0, c.w, c.h, QColor(0,0,0,0)); // TODO optimize this
+ p.drawImage(0, 0, mask);
+ p.end();
+ } else if (m_type == QFontEngineGlyphCache::Raster_Mono) {
+ if (mask.depth() > 1) {
+ // TODO optimize this
+ mask = mask.alphaChannel();
+ mask.invertPixels();
+ mask = mask.convertToFormat(QImage::Format_Mono);
+ }
+
+ int mw = qMin(mask.width(), c.w);
+ int mh = qMin(mask.height(), c.h);
+ uchar *d = m_image.bits();
+ int dbpl = m_image.bytesPerLine();
+
+ for (int y = 0; y < c.h; ++y) {
+ uchar *dest = d + (c.y + y) *dbpl + c.x/8;
+
+ if (y < mh) {
+ uchar *src = mask.scanLine(y);
+ for (int x = 0; x < c.w/8; ++x) {
+ if (x < (mw+7)/8)
+ dest[x] = src[x];
+ else
+ dest[x] = 0;
+ }
+ } else {
+ for (int x = 0; x < c.w/8; ++x)
+ dest[x] = 0;
+ }
+ }
+ } else { // A8
+ int mw = qMin(mask.width(), c.w);
+ int mh = qMin(mask.height(), c.h);
+ uchar *d = m_image.bits();
+ int dbpl = m_image.bytesPerLine();
+
+ if (mask.depth() == 1) {
+ for (int y = 0; y < c.h; ++y) {
+ uchar *dest = d + (c.y + y) *dbpl + c.x;
+ if (y < mh) {
+ uchar *src = (uchar *) mask.scanLine(y);
+ for (int x = 0; x < c.w; ++x) {
+ if (x < mw)
+ dest[x] = (src[x >> 3] & (1 << (7 - (x & 7)))) > 0 ? 255 : 0;
+ }
+ }
+ }
+ } else if (mask.depth() == 8) {
+ for (int y = 0; y < c.h; ++y) {
+ uchar *dest = d + (c.y + y) *dbpl + c.x;
+ if (y < mh) {
+ uchar *src = (uchar *) mask.scanLine(y);
+ for (int x = 0; x < c.w; ++x) {
+ if (x < mw)
+ dest[x] = src[x];
+ }
+ }
+ }
+ }
+ }
+
+#ifdef CACHE_DEBUG
+// QPainter p(&m_image);
+// p.drawLine(
+ QPoint base(c.x + glyphMargin(), c.y + glyphMargin() + c.baseLineY-1);
+ if (m_image.rect().contains(base))
+ m_image.setPixel(base, 255);
+ m_image.save(QString::fromLatin1("cache-%1.png").arg(qint64(this)));
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qtextureglyphcache_p.h b/src/gui/painting/qtextureglyphcache_p.h
new file mode 100644
index 0000000000..922e2901bd
--- /dev/null
+++ b/src/gui/painting/qtextureglyphcache_p.h
@@ -0,0 +1,189 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTEXTUREGLYPHCACHE_P_H
+#define QTEXTUREGLYPHCACHE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qhash.h>
+#include <qimage.h>
+#include <qobject.h>
+#include <qtransform.h>
+
+#include <private/qfontengineglyphcache_p.h>
+
+#if defined(Q_OS_VXWORKS) && defined(m_type)
+# undef m_type
+#endif
+
+#ifndef QT_DEFAULT_TEXTURE_GLYPH_CACHE_WIDTH
+#define QT_DEFAULT_TEXTURE_GLYPH_CACHE_WIDTH 256
+#endif
+
+struct glyph_metrics_t;
+typedef unsigned int glyph_t;
+
+
+QT_BEGIN_NAMESPACE
+
+class QTextItemInt;
+
+class Q_GUI_EXPORT QTextureGlyphCache : public QFontEngineGlyphCache
+{
+public:
+ QTextureGlyphCache(QFontEngineGlyphCache::Type type, const QTransform &matrix)
+ : QFontEngineGlyphCache(matrix, type), m_current_fontengine(0),
+ m_w(0), m_h(0), m_cx(0), m_cy(0), m_currentRowHeight(0), m_subPixelPositionCount(0)
+ { }
+
+ virtual ~QTextureGlyphCache() { }
+
+ struct GlyphAndSubPixelPosition
+ {
+ GlyphAndSubPixelPosition(glyph_t g, QFixed spp) : glyph(g), subPixelPosition(spp) {}
+
+ bool operator==(const GlyphAndSubPixelPosition &other) const
+ {
+ return glyph == other.glyph && subPixelPosition == other.subPixelPosition;
+ }
+
+ glyph_t glyph;
+ QFixed subPixelPosition;
+ };
+
+ struct Coord {
+ int x;
+ int y;
+ int w;
+ int h;
+
+ int baseLineX;
+ int baseLineY;
+
+ bool isNull() const
+ {
+ return w == 0 || h == 0;
+ }
+ };
+
+ bool populate(QFontEngine *fontEngine, int numGlyphs, const glyph_t *glyphs,
+ const QFixedPoint *positions);
+ void fillInPendingGlyphs();
+
+ virtual void createTextureData(int width, int height) = 0;
+ virtual void resizeTextureData(int width, int height) = 0;
+ virtual int glyphMargin() const { return 0; }
+ virtual int glyphPadding() const { return 0; }
+
+ virtual void fillTexture(const Coord &coord, glyph_t glyph, QFixed subPixelPosition) = 0;
+
+ inline void createCache(int width, int height) {
+ m_w = width;
+ m_h = height;
+ createTextureData(width, height);
+ }
+
+ inline void resizeCache(int width, int height)
+ {
+ resizeTextureData(width, height);
+ m_w = width;
+ m_h = height;
+ }
+
+ inline bool isNull() const { return m_h == 0; }
+
+ QHash<GlyphAndSubPixelPosition, Coord> coords;
+ virtual int maxTextureWidth() const { return QT_DEFAULT_TEXTURE_GLYPH_CACHE_WIDTH; }
+ virtual int maxTextureHeight() const { return -1; }
+
+ QImage textureMapForGlyph(glyph_t g, QFixed subPixelPosition) const;
+
+ QFixed subPixelPositionForX(QFixed x) const;
+
+protected:
+ int calculateSubPixelPositionCount(glyph_t) const;
+
+ QFontEngine *m_current_fontengine;
+ QHash<GlyphAndSubPixelPosition, Coord> m_pendingGlyphs;
+
+ int m_w; // image width
+ int m_h; // image height
+ int m_cx; // current x
+ int m_cy; // current y
+ int m_currentRowHeight; // Height of last row
+ int m_subPixelPositionCount; // Number of positions within a single pixel for this cache
+};
+
+inline uint qHash(const QTextureGlyphCache::GlyphAndSubPixelPosition &g)
+{
+ return (g.glyph << 8) | (g.subPixelPosition * 10).round().toInt();
+}
+
+
+class Q_GUI_EXPORT QImageTextureGlyphCache : public QTextureGlyphCache
+{
+public:
+ QImageTextureGlyphCache(QFontEngineGlyphCache::Type type, const QTransform &matrix)
+ : QTextureGlyphCache(type, matrix) { }
+ virtual int glyphMargin() const;
+ virtual void createTextureData(int width, int height);
+ virtual void resizeTextureData(int width, int height);
+ virtual void fillTexture(const Coord &c, glyph_t glyph, QFixed subPixelPosition);
+
+ inline const QImage &image() const { return m_image; }
+
+private:
+ QImage m_image;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/painting/qtransform.cpp b/src/gui/painting/qtransform.cpp
new file mode 100644
index 0000000000..7d11e2fd07
--- /dev/null
+++ b/src/gui/painting/qtransform.cpp
@@ -0,0 +1,2301 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qtransform.h"
+
+#include "qdatastream.h"
+#include "qdebug.h"
+#include "qmatrix.h"
+#include "qregion.h"
+#include "qpainterpath.h"
+#include "qvariant.h"
+#include <qmath.h>
+#include <qnumeric.h>
+
+#include <private/qbezier_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#define Q_NEAR_CLIP (sizeof(qreal) == sizeof(double) ? 0.000001 : 0.0001)
+
+#ifdef MAP
+# undef MAP
+#endif
+#define MAP(x, y, nx, ny) \
+ do { \
+ qreal FX_ = x; \
+ qreal FY_ = y; \
+ switch(t) { \
+ case TxNone: \
+ nx = FX_; \
+ ny = FY_; \
+ break; \
+ case TxTranslate: \
+ nx = FX_ + affine._dx; \
+ ny = FY_ + affine._dy; \
+ break; \
+ case TxScale: \
+ nx = affine._m11 * FX_ + affine._dx; \
+ ny = affine._m22 * FY_ + affine._dy; \
+ break; \
+ case TxRotate: \
+ case TxShear: \
+ case TxProject: \
+ nx = affine._m11 * FX_ + affine._m21 * FY_ + affine._dx; \
+ ny = affine._m12 * FX_ + affine._m22 * FY_ + affine._dy; \
+ if (t == TxProject) { \
+ qreal w = (m_13 * FX_ + m_23 * FY_ + m_33); \
+ if (w < qreal(Q_NEAR_CLIP)) w = qreal(Q_NEAR_CLIP); \
+ w = 1./w; \
+ nx *= w; \
+ ny *= w; \
+ } \
+ } \
+ } while (0)
+
+/*!
+ \class QTransform
+ \brief The QTransform class specifies 2D transformations of a coordinate system.
+ \since 4.3
+ \ingroup painting
+
+ A transformation specifies how to translate, scale, shear, rotate
+ or project the coordinate system, and is typically used when
+ rendering graphics.
+
+ QTransform differs from QMatrix in that it is a true 3x3 matrix,
+ allowing perspective transformations. QTransform's toAffine()
+ method allows casting QTransform to QMatrix. If a perspective
+ transformation has been specified on the matrix, then the
+ conversion will cause loss of data.
+
+ QTransform is the recommended transformation class in Qt.
+
+ A QTransform object can be built using the setMatrix(), scale(),
+ rotate(), translate() and shear() functions. Alternatively, it
+ can be built by applying \l {QTransform#Basic Matrix
+ Operations}{basic matrix operations}. The matrix can also be
+ defined when constructed, and it can be reset to the identity
+ matrix (the default) using the reset() function.
+
+ The QTransform class supports mapping of graphic primitives: A given
+ point, line, polygon, region, or painter path can be mapped to the
+ coordinate system defined by \e this matrix using the map()
+ function. In case of a rectangle, its coordinates can be
+ transformed using the mapRect() function. A rectangle can also be
+ transformed into a \e polygon (mapped to the coordinate system
+ defined by \e this matrix), using the mapToPolygon() function.
+
+ QTransform provides the isIdentity() function which returns true if
+ the matrix is the identity matrix, and the isInvertible() function
+ which returns true if the matrix is non-singular (i.e. AB = BA =
+ I). The inverted() function returns an inverted copy of \e this
+ matrix if it is invertible (otherwise it returns the identity
+ matrix), and adjoint() returns the matrix's classical adjoint.
+ In addition, QTransform provides the determinant() function which
+ returns the matrix's determinant.
+
+ Finally, the QTransform class supports matrix multiplication, addition
+ and subtraction, and objects of the class can be streamed as well
+ as compared.
+
+ \tableofcontents
+
+ \section1 Rendering Graphics
+
+ When rendering graphics, the matrix defines the transformations
+ but the actual transformation is performed by the drawing routines
+ in QPainter.
+
+ By default, QPainter operates on the associated device's own
+ coordinate system. The standard coordinate system of a
+ QPaintDevice has its origin located at the top-left position. The
+ \e x values increase to the right; \e y values increase
+ downward. For a complete description, see the \l {Coordinate
+ System} {coordinate system} documentation.
+
+ QPainter has functions to translate, scale, shear and rotate the
+ coordinate system without using a QTransform. For example:
+
+ \table 100%
+ \row
+ \o \inlineimage qtransform-simpletransformation.png
+ \o
+ \snippet doc/src/snippets/transform/main.cpp 0
+ \endtable
+
+ Although these functions are very convenient, it can be more
+ efficient to build a QTransform and call QPainter::setTransform() if you
+ want to perform more than a single transform operation. For
+ example:
+
+ \table 100%
+ \row
+ \o \inlineimage qtransform-combinedtransformation.png
+ \o
+ \snippet doc/src/snippets/transform/main.cpp 1
+ \endtable
+
+ \section1 Basic Matrix Operations
+
+ \image qtransform-representation.png
+
+ A QTransform object contains a 3 x 3 matrix. The \c m31 (\c dx) and
+ \c m32 (\c dy) elements specify horizontal and vertical translation.
+ The \c m11 and \c m22 elements specify horizontal and vertical scaling.
+ The \c m21 and \c m12 elements specify horizontal and vertical \e shearing.
+ And finally, the \c m13 and \c m23 elements specify horizontal and vertical
+ projection, with \c m33 as an additional projection factor.
+
+ QTransform transforms a point in the plane to another point using the
+ following formulas:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qtransform.cpp 0
+
+ The point \e (x, y) is the original point, and \e (x', y') is the
+ transformed point. \e (x', y') can be transformed back to \e (x,
+ y) by performing the same operation on the inverted() matrix.
+
+ The various matrix elements can be set when constructing the
+ matrix, or by using the setMatrix() function later on. They can also
+ be manipulated using the translate(), rotate(), scale() and
+ shear() convenience functions. The currently set values can be
+ retrieved using the m11(), m12(), m13(), m21(), m22(), m23(),
+ m31(), m32(), m33(), dx() and dy() functions.
+
+ Translation is the simplest transformation. Setting \c dx and \c
+ dy will move the coordinate system \c dx units along the X axis
+ and \c dy units along the Y axis. Scaling can be done by setting
+ \c m11 and \c m22. For example, setting \c m11 to 2 and \c m22 to
+ 1.5 will double the height and increase the width by 50%. The
+ identity matrix has \c m11, \c m22, and \c m33 set to 1 (all others are set
+ to 0) mapping a point to itself. Shearing is controlled by \c m12
+ and \c m21. Setting these elements to values different from zero
+ will twist the coordinate system. Rotation is achieved by
+ setting both the shearing factors and the scaling factors. Perspective
+ transformation is achieved by setting both the projection factors and
+ the scaling factors.
+
+ Here's the combined transformations example using basic matrix
+ operations:
+
+ \table 100%
+ \row
+ \o \inlineimage qtransform-combinedtransformation2.png
+ \o
+ \snippet doc/src/snippets/transform/main.cpp 2
+ \endtable
+
+ \sa QPainter, {Coordinate System}, {demos/affine}{Affine
+ Transformations Demo}, {Transformations Example}
+*/
+
+/*!
+ \enum QTransform::TransformationType
+
+ \value TxNone
+ \value TxTranslate
+ \value TxScale
+ \value TxRotate
+ \value TxShear
+ \value TxProject
+*/
+
+/*!
+ \fn QTransform::QTransform(Qt::Initialization)
+ \internal
+*/
+
+/*!
+ Constructs an identity matrix.
+
+ All elements are set to zero except \c m11 and \c m22 (specifying
+ the scale) and \c m13 which are set to 1.
+
+ \sa reset()
+*/
+QTransform::QTransform()
+ : affine(true)
+ , m_13(0), m_23(0), m_33(1)
+ , m_type(TxNone)
+ , m_dirty(TxNone)
+{
+}
+
+/*!
+ \fn QTransform::QTransform(qreal m11, qreal m12, qreal m13, qreal m21, qreal m22, qreal m23, qreal m31, qreal m32, qreal m33)
+
+ Constructs a matrix with the elements, \a m11, \a m12, \a m13,
+ \a m21, \a m22, \a m23, \a m31, \a m32, \a m33.
+
+ \sa setMatrix()
+*/
+QTransform::QTransform(qreal h11, qreal h12, qreal h13,
+ qreal h21, qreal h22, qreal h23,
+ qreal h31, qreal h32, qreal h33)
+ : affine(h11, h12, h21, h22, h31, h32, true)
+ , m_13(h13), m_23(h23), m_33(h33)
+ , m_type(TxNone)
+ , m_dirty(TxProject)
+{
+}
+
+/*!
+ \fn QTransform::QTransform(qreal m11, qreal m12, qreal m21, qreal m22, qreal dx, qreal dy)
+
+ Constructs a matrix with the elements, \a m11, \a m12, \a m21, \a m22, \a dx and \a dy.
+
+ \sa setMatrix()
+*/
+QTransform::QTransform(qreal h11, qreal h12, qreal h21,
+ qreal h22, qreal dx, qreal dy)
+ : affine(h11, h12, h21, h22, dx, dy, true)
+ , m_13(0), m_23(0), m_33(1)
+ , m_type(TxNone)
+ , m_dirty(TxShear)
+{
+}
+
+/*!
+ \fn QTransform::QTransform(const QMatrix &matrix)
+
+ Constructs a matrix that is a copy of the given \a matrix.
+ Note that the \c m13, \c m23, and \c m33 elements are set to 0, 0,
+ and 1 respectively.
+ */
+QTransform::QTransform(const QMatrix &mtx)
+ : affine(mtx._m11, mtx._m12, mtx._m21, mtx._m22, mtx._dx, mtx._dy, true),
+ m_13(0), m_23(0), m_33(1)
+ , m_type(TxNone)
+ , m_dirty(TxShear)
+{
+}
+
+/*!
+ Returns the adjoint of this matrix.
+*/
+QTransform QTransform::adjoint() const
+{
+ qreal h11, h12, h13,
+ h21, h22, h23,
+ h31, h32, h33;
+ h11 = affine._m22*m_33 - m_23*affine._dy;
+ h21 = m_23*affine._dx - affine._m21*m_33;
+ h31 = affine._m21*affine._dy - affine._m22*affine._dx;
+ h12 = m_13*affine._dy - affine._m12*m_33;
+ h22 = affine._m11*m_33 - m_13*affine._dx;
+ h32 = affine._m12*affine._dx - affine._m11*affine._dy;
+ h13 = affine._m12*m_23 - m_13*affine._m22;
+ h23 = m_13*affine._m21 - affine._m11*m_23;
+ h33 = affine._m11*affine._m22 - affine._m12*affine._m21;
+
+ return QTransform(h11, h12, h13,
+ h21, h22, h23,
+ h31, h32, h33, true);
+}
+
+/*!
+ Returns the transpose of this matrix.
+*/
+QTransform QTransform::transposed() const
+{
+ QTransform t(affine._m11, affine._m21, affine._dx,
+ affine._m12, affine._m22, affine._dy,
+ m_13, m_23, m_33, true);
+ t.m_type = m_type;
+ t.m_dirty = m_dirty;
+ return t;
+}
+
+/*!
+ Returns an inverted copy of this matrix.
+
+ If the matrix is singular (not invertible), the returned matrix is
+ the identity matrix. If \a invertible is valid (i.e. not 0), its
+ value is set to true if the matrix is invertible, otherwise it is
+ set to false.
+
+ \sa isInvertible()
+*/
+QTransform QTransform::inverted(bool *invertible) const
+{
+ QTransform invert(true);
+ bool inv = true;
+
+ switch(inline_type()) {
+ case TxNone:
+ break;
+ case TxTranslate:
+ invert.affine._dx = -affine._dx;
+ invert.affine._dy = -affine._dy;
+ break;
+ case TxScale:
+ inv = !qFuzzyIsNull(affine._m11);
+ inv &= !qFuzzyIsNull(affine._m22);
+ if (inv) {
+ invert.affine._m11 = 1. / affine._m11;
+ invert.affine._m22 = 1. / affine._m22;
+ invert.affine._dx = -affine._dx * invert.affine._m11;
+ invert.affine._dy = -affine._dy * invert.affine._m22;
+ }
+ break;
+ case TxRotate:
+ case TxShear:
+ invert.affine = affine.inverted(&inv);
+ break;
+ default:
+ // general case
+ qreal det = determinant();
+ inv = !qFuzzyIsNull(det);
+ if (inv)
+ invert = adjoint() / det;
+ break;
+ }
+
+ if (invertible)
+ *invertible = inv;
+
+ if (inv) {
+ // inverting doesn't change the type
+ invert.m_type = m_type;
+ invert.m_dirty = m_dirty;
+ }
+
+ return invert;
+}
+
+/*!
+ Moves the coordinate system \a dx along the x axis and \a dy along
+ the y axis, and returns a reference to the matrix.
+
+ \sa setMatrix()
+*/
+QTransform &QTransform::translate(qreal dx, qreal dy)
+{
+ if (dx == 0 && dy == 0)
+ return *this;
+#ifndef QT_NO_DEBUG
+ if (qIsNaN(dx) | qIsNaN(dy)) {
+ qWarning() << "QTransform::translate with NaN called";
+ return *this;
+ }
+#endif
+
+ switch(inline_type()) {
+ case TxNone:
+ affine._dx = dx;
+ affine._dy = dy;
+ break;
+ case TxTranslate:
+ affine._dx += dx;
+ affine._dy += dy;
+ break;
+ case TxScale:
+ affine._dx += dx*affine._m11;
+ affine._dy += dy*affine._m22;
+ break;
+ case TxProject:
+ m_33 += dx*m_13 + dy*m_23;
+ // Fall through
+ case TxShear:
+ case TxRotate:
+ affine._dx += dx*affine._m11 + dy*affine._m21;
+ affine._dy += dy*affine._m22 + dx*affine._m12;
+ break;
+ }
+ if (m_dirty < TxTranslate)
+ m_dirty = TxTranslate;
+ return *this;
+}
+
+/*!
+ Creates a matrix which corresponds to a translation of \a dx along
+ the x axis and \a dy along the y axis. This is the same as
+ QTransform().translate(dx, dy) but slightly faster.
+
+ \since 4.5
+*/
+QTransform QTransform::fromTranslate(qreal dx, qreal dy)
+{
+#ifndef QT_NO_DEBUG
+ if (qIsNaN(dx) | qIsNaN(dy)) {
+ qWarning() << "QTransform::fromTranslate with NaN called";
+ return QTransform();
+}
+#endif
+ QTransform transform(1, 0, 0, 0, 1, 0, dx, dy, 1, true);
+ if (dx == 0 && dy == 0)
+ transform.m_type = TxNone;
+ else
+ transform.m_type = TxTranslate;
+ transform.m_dirty = TxNone;
+ return transform;
+}
+
+/*!
+ Scales the coordinate system by \a sx horizontally and \a sy
+ vertically, and returns a reference to the matrix.
+
+ \sa setMatrix()
+*/
+QTransform & QTransform::scale(qreal sx, qreal sy)
+{
+ if (sx == 1 && sy == 1)
+ return *this;
+#ifndef QT_NO_DEBUG
+ if (qIsNaN(sx) | qIsNaN(sy)) {
+ qWarning() << "QTransform::scale with NaN called";
+ return *this;
+ }
+#endif
+
+ switch(inline_type()) {
+ case TxNone:
+ case TxTranslate:
+ affine._m11 = sx;
+ affine._m22 = sy;
+ break;
+ case TxProject:
+ m_13 *= sx;
+ m_23 *= sy;
+ // fall through
+ case TxRotate:
+ case TxShear:
+ affine._m12 *= sx;
+ affine._m21 *= sy;
+ // fall through
+ case TxScale:
+ affine._m11 *= sx;
+ affine._m22 *= sy;
+ break;
+ }
+ if (m_dirty < TxScale)
+ m_dirty = TxScale;
+ return *this;
+}
+
+/*!
+ Creates a matrix which corresponds to a scaling of
+ \a sx horizontally and \a sy vertically.
+ This is the same as QTransform().scale(sx, sy) but slightly faster.
+
+ \since 4.5
+*/
+QTransform QTransform::fromScale(qreal sx, qreal sy)
+{
+#ifndef QT_NO_DEBUG
+ if (qIsNaN(sx) | qIsNaN(sy)) {
+ qWarning() << "QTransform::fromScale with NaN called";
+ return QTransform();
+}
+#endif
+ QTransform transform(sx, 0, 0, 0, sy, 0, 0, 0, 1, true);
+ if (sx == 1. && sy == 1.)
+ transform.m_type = TxNone;
+ else
+ transform.m_type = TxScale;
+ transform.m_dirty = TxNone;
+ return transform;
+}
+
+/*!
+ Shears the coordinate system by \a sh horizontally and \a sv
+ vertically, and returns a reference to the matrix.
+
+ \sa setMatrix()
+*/
+QTransform & QTransform::shear(qreal sh, qreal sv)
+{
+ if (sh == 0 && sv == 0)
+ return *this;
+#ifndef QT_NO_DEBUG
+ if (qIsNaN(sh) | qIsNaN(sv)) {
+ qWarning() << "QTransform::shear with NaN called";
+ return *this;
+ }
+#endif
+
+ switch(inline_type()) {
+ case TxNone:
+ case TxTranslate:
+ affine._m12 = sv;
+ affine._m21 = sh;
+ break;
+ case TxScale:
+ affine._m12 = sv*affine._m22;
+ affine._m21 = sh*affine._m11;
+ break;
+ case TxProject: {
+ qreal tm13 = sv*m_23;
+ qreal tm23 = sh*m_13;
+ m_13 += tm13;
+ m_23 += tm23;
+ }
+ // fall through
+ case TxRotate:
+ case TxShear: {
+ qreal tm11 = sv*affine._m21;
+ qreal tm22 = sh*affine._m12;
+ qreal tm12 = sv*affine._m22;
+ qreal tm21 = sh*affine._m11;
+ affine._m11 += tm11; affine._m12 += tm12;
+ affine._m21 += tm21; affine._m22 += tm22;
+ break;
+ }
+ }
+ if (m_dirty < TxShear)
+ m_dirty = TxShear;
+ return *this;
+}
+
+const qreal deg2rad = qreal(0.017453292519943295769); // pi/180
+const qreal inv_dist_to_plane = 1. / 1024.;
+
+/*!
+ \fn QTransform &QTransform::rotate(qreal angle, Qt::Axis axis)
+
+ Rotates the coordinate system counterclockwise by the given \a angle
+ about the specified \a axis and returns a reference to the matrix.
+
+ Note that if you apply a QTransform to a point defined in widget
+ coordinates, the direction of the rotation will be clockwise
+ because the y-axis points downwards.
+
+ The angle is specified in degrees.
+
+ \sa setMatrix()
+*/
+QTransform & QTransform::rotate(qreal a, Qt::Axis axis)
+{
+ if (a == 0)
+ return *this;
+#ifndef QT_NO_DEBUG
+ if (qIsNaN(a)) {
+ qWarning() << "QTransform::rotate with NaN called";
+ return *this;
+ }
+#endif
+
+ qreal sina = 0;
+ qreal cosa = 0;
+ if (a == 90. || a == -270.)
+ sina = 1.;
+ else if (a == 270. || a == -90.)
+ sina = -1.;
+ else if (a == 180.)
+ cosa = -1.;
+ else{
+ qreal b = deg2rad*a; // convert to radians
+ sina = qSin(b); // fast and convenient
+ cosa = qCos(b);
+ }
+
+ if (axis == Qt::ZAxis) {
+ switch(inline_type()) {
+ case TxNone:
+ case TxTranslate:
+ affine._m11 = cosa;
+ affine._m12 = sina;
+ affine._m21 = -sina;
+ affine._m22 = cosa;
+ break;
+ case TxScale: {
+ qreal tm11 = cosa*affine._m11;
+ qreal tm12 = sina*affine._m22;
+ qreal tm21 = -sina*affine._m11;
+ qreal tm22 = cosa*affine._m22;
+ affine._m11 = tm11; affine._m12 = tm12;
+ affine._m21 = tm21; affine._m22 = tm22;
+ break;
+ }
+ case TxProject: {
+ qreal tm13 = cosa*m_13 + sina*m_23;
+ qreal tm23 = -sina*m_13 + cosa*m_23;
+ m_13 = tm13;
+ m_23 = tm23;
+ // fall through
+ }
+ case TxRotate:
+ case TxShear: {
+ qreal tm11 = cosa*affine._m11 + sina*affine._m21;
+ qreal tm12 = cosa*affine._m12 + sina*affine._m22;
+ qreal tm21 = -sina*affine._m11 + cosa*affine._m21;
+ qreal tm22 = -sina*affine._m12 + cosa*affine._m22;
+ affine._m11 = tm11; affine._m12 = tm12;
+ affine._m21 = tm21; affine._m22 = tm22;
+ break;
+ }
+ }
+ if (m_dirty < TxRotate)
+ m_dirty = TxRotate;
+ } else {
+ QTransform result;
+ if (axis == Qt::YAxis) {
+ result.affine._m11 = cosa;
+ result.m_13 = -sina * inv_dist_to_plane;
+ } else {
+ result.affine._m22 = cosa;
+ result.m_23 = -sina * inv_dist_to_plane;
+ }
+ result.m_type = TxProject;
+ *this = result * *this;
+ }
+
+ return *this;
+}
+
+/*!
+ \fn QTransform & QTransform::rotateRadians(qreal angle, Qt::Axis axis)
+
+ Rotates the coordinate system counterclockwise by the given \a angle
+ about the specified \a axis and returns a reference to the matrix.
+
+ Note that if you apply a QTransform to a point defined in widget
+ coordinates, the direction of the rotation will be clockwise
+ because the y-axis points downwards.
+
+ The angle is specified in radians.
+
+ \sa setMatrix()
+*/
+QTransform & QTransform::rotateRadians(qreal a, Qt::Axis axis)
+{
+#ifndef QT_NO_DEBUG
+ if (qIsNaN(a)) {
+ qWarning() << "QTransform::rotateRadians with NaN called";
+ return *this;
+ }
+#endif
+ qreal sina = qSin(a);
+ qreal cosa = qCos(a);
+
+ if (axis == Qt::ZAxis) {
+ switch(inline_type()) {
+ case TxNone:
+ case TxTranslate:
+ affine._m11 = cosa;
+ affine._m12 = sina;
+ affine._m21 = -sina;
+ affine._m22 = cosa;
+ break;
+ case TxScale: {
+ qreal tm11 = cosa*affine._m11;
+ qreal tm12 = sina*affine._m22;
+ qreal tm21 = -sina*affine._m11;
+ qreal tm22 = cosa*affine._m22;
+ affine._m11 = tm11; affine._m12 = tm12;
+ affine._m21 = tm21; affine._m22 = tm22;
+ break;
+ }
+ case TxProject: {
+ qreal tm13 = cosa*m_13 + sina*m_23;
+ qreal tm23 = -sina*m_13 + cosa*m_23;
+ m_13 = tm13;
+ m_23 = tm23;
+ // fall through
+ }
+ case TxRotate:
+ case TxShear: {
+ qreal tm11 = cosa*affine._m11 + sina*affine._m21;
+ qreal tm12 = cosa*affine._m12 + sina*affine._m22;
+ qreal tm21 = -sina*affine._m11 + cosa*affine._m21;
+ qreal tm22 = -sina*affine._m12 + cosa*affine._m22;
+ affine._m11 = tm11; affine._m12 = tm12;
+ affine._m21 = tm21; affine._m22 = tm22;
+ break;
+ }
+ }
+ if (m_dirty < TxRotate)
+ m_dirty = TxRotate;
+ } else {
+ QTransform result;
+ if (axis == Qt::YAxis) {
+ result.affine._m11 = cosa;
+ result.m_13 = -sina * inv_dist_to_plane;
+ } else {
+ result.affine._m22 = cosa;
+ result.m_23 = -sina * inv_dist_to_plane;
+ }
+ result.m_type = TxProject;
+ *this = result * *this;
+ }
+ return *this;
+}
+
+/*!
+ \fn bool QTransform::operator==(const QTransform &matrix) const
+ Returns true if this matrix is equal to the given \a matrix,
+ otherwise returns false.
+*/
+bool QTransform::operator==(const QTransform &o) const
+{
+ return affine._m11 == o.affine._m11 &&
+ affine._m12 == o.affine._m12 &&
+ affine._m21 == o.affine._m21 &&
+ affine._m22 == o.affine._m22 &&
+ affine._dx == o.affine._dx &&
+ affine._dy == o.affine._dy &&
+ m_13 == o.m_13 &&
+ m_23 == o.m_23 &&
+ m_33 == o.m_33;
+}
+
+/*!
+ \fn bool QTransform::operator!=(const QTransform &matrix) const
+ Returns true if this matrix is not equal to the given \a matrix,
+ otherwise returns false.
+*/
+bool QTransform::operator!=(const QTransform &o) const
+{
+ return !operator==(o);
+}
+
+/*!
+ \fn QTransform & QTransform::operator*=(const QTransform &matrix)
+ \overload
+
+ Returns the result of multiplying this matrix by the given \a
+ matrix.
+*/
+QTransform & QTransform::operator*=(const QTransform &o)
+{
+ const TransformationType otherType = o.inline_type();
+ if (otherType == TxNone)
+ return *this;
+
+ const TransformationType thisType = inline_type();
+ if (thisType == TxNone)
+ return operator=(o);
+
+ TransformationType t = qMax(thisType, otherType);
+ switch(t) {
+ case TxNone:
+ break;
+ case TxTranslate:
+ affine._dx += o.affine._dx;
+ affine._dy += o.affine._dy;
+ break;
+ case TxScale:
+ {
+ qreal m11 = affine._m11*o.affine._m11;
+ qreal m22 = affine._m22*o.affine._m22;
+
+ qreal m31 = affine._dx*o.affine._m11 + o.affine._dx;
+ qreal m32 = affine._dy*o.affine._m22 + o.affine._dy;
+
+ affine._m11 = m11;
+ affine._m22 = m22;
+ affine._dx = m31; affine._dy = m32;
+ break;
+ }
+ case TxRotate:
+ case TxShear:
+ {
+ qreal m11 = affine._m11*o.affine._m11 + affine._m12*o.affine._m21;
+ qreal m12 = affine._m11*o.affine._m12 + affine._m12*o.affine._m22;
+
+ qreal m21 = affine._m21*o.affine._m11 + affine._m22*o.affine._m21;
+ qreal m22 = affine._m21*o.affine._m12 + affine._m22*o.affine._m22;
+
+ qreal m31 = affine._dx*o.affine._m11 + affine._dy*o.affine._m21 + o.affine._dx;
+ qreal m32 = affine._dx*o.affine._m12 + affine._dy*o.affine._m22 + o.affine._dy;
+
+ affine._m11 = m11; affine._m12 = m12;
+ affine._m21 = m21; affine._m22 = m22;
+ affine._dx = m31; affine._dy = m32;
+ break;
+ }
+ case TxProject:
+ {
+ qreal m11 = affine._m11*o.affine._m11 + affine._m12*o.affine._m21 + m_13*o.affine._dx;
+ qreal m12 = affine._m11*o.affine._m12 + affine._m12*o.affine._m22 + m_13*o.affine._dy;
+ qreal m13 = affine._m11*o.m_13 + affine._m12*o.m_23 + m_13*o.m_33;
+
+ qreal m21 = affine._m21*o.affine._m11 + affine._m22*o.affine._m21 + m_23*o.affine._dx;
+ qreal m22 = affine._m21*o.affine._m12 + affine._m22*o.affine._m22 + m_23*o.affine._dy;
+ qreal m23 = affine._m21*o.m_13 + affine._m22*o.m_23 + m_23*o.m_33;
+
+ qreal m31 = affine._dx*o.affine._m11 + affine._dy*o.affine._m21 + m_33*o.affine._dx;
+ qreal m32 = affine._dx*o.affine._m12 + affine._dy*o.affine._m22 + m_33*o.affine._dy;
+ qreal m33 = affine._dx*o.m_13 + affine._dy*o.m_23 + m_33*o.m_33;
+
+ affine._m11 = m11; affine._m12 = m12; m_13 = m13;
+ affine._m21 = m21; affine._m22 = m22; m_23 = m23;
+ affine._dx = m31; affine._dy = m32; m_33 = m33;
+ }
+ }
+
+ m_dirty = t;
+ m_type = t;
+
+ return *this;
+}
+
+/*!
+ \fn QTransform QTransform::operator*(const QTransform &matrix) const
+ Returns the result of multiplying this matrix by the given \a
+ matrix.
+
+ Note that matrix multiplication is not commutative, i.e. a*b !=
+ b*a.
+*/
+QTransform QTransform::operator*(const QTransform &m) const
+{
+ const TransformationType otherType = m.inline_type();
+ if (otherType == TxNone)
+ return *this;
+
+ const TransformationType thisType = inline_type();
+ if (thisType == TxNone)
+ return m;
+
+ QTransform t(true);
+ TransformationType type = qMax(thisType, otherType);
+ switch(type) {
+ case TxNone:
+ break;
+ case TxTranslate:
+ t.affine._dx = affine._dx + m.affine._dx;
+ t.affine._dy += affine._dy + m.affine._dy;
+ break;
+ case TxScale:
+ {
+ qreal m11 = affine._m11*m.affine._m11;
+ qreal m22 = affine._m22*m.affine._m22;
+
+ qreal m31 = affine._dx*m.affine._m11 + m.affine._dx;
+ qreal m32 = affine._dy*m.affine._m22 + m.affine._dy;
+
+ t.affine._m11 = m11;
+ t.affine._m22 = m22;
+ t.affine._dx = m31; t.affine._dy = m32;
+ break;
+ }
+ case TxRotate:
+ case TxShear:
+ {
+ qreal m11 = affine._m11*m.affine._m11 + affine._m12*m.affine._m21;
+ qreal m12 = affine._m11*m.affine._m12 + affine._m12*m.affine._m22;
+
+ qreal m21 = affine._m21*m.affine._m11 + affine._m22*m.affine._m21;
+ qreal m22 = affine._m21*m.affine._m12 + affine._m22*m.affine._m22;
+
+ qreal m31 = affine._dx*m.affine._m11 + affine._dy*m.affine._m21 + m.affine._dx;
+ qreal m32 = affine._dx*m.affine._m12 + affine._dy*m.affine._m22 + m.affine._dy;
+
+ t.affine._m11 = m11; t.affine._m12 = m12;
+ t.affine._m21 = m21; t.affine._m22 = m22;
+ t.affine._dx = m31; t.affine._dy = m32;
+ break;
+ }
+ case TxProject:
+ {
+ qreal m11 = affine._m11*m.affine._m11 + affine._m12*m.affine._m21 + m_13*m.affine._dx;
+ qreal m12 = affine._m11*m.affine._m12 + affine._m12*m.affine._m22 + m_13*m.affine._dy;
+ qreal m13 = affine._m11*m.m_13 + affine._m12*m.m_23 + m_13*m.m_33;
+
+ qreal m21 = affine._m21*m.affine._m11 + affine._m22*m.affine._m21 + m_23*m.affine._dx;
+ qreal m22 = affine._m21*m.affine._m12 + affine._m22*m.affine._m22 + m_23*m.affine._dy;
+ qreal m23 = affine._m21*m.m_13 + affine._m22*m.m_23 + m_23*m.m_33;
+
+ qreal m31 = affine._dx*m.affine._m11 + affine._dy*m.affine._m21 + m_33*m.affine._dx;
+ qreal m32 = affine._dx*m.affine._m12 + affine._dy*m.affine._m22 + m_33*m.affine._dy;
+ qreal m33 = affine._dx*m.m_13 + affine._dy*m.m_23 + m_33*m.m_33;
+
+ t.affine._m11 = m11; t.affine._m12 = m12; t.m_13 = m13;
+ t.affine._m21 = m21; t.affine._m22 = m22; t.m_23 = m23;
+ t.affine._dx = m31; t.affine._dy = m32; t.m_33 = m33;
+ }
+ }
+
+ t.m_dirty = type;
+ t.m_type = type;
+
+ return t;
+}
+
+/*!
+ \fn QTransform & QTransform::operator*=(qreal scalar)
+ \overload
+
+ Returns the result of performing an element-wise multiplication of this
+ matrix with the given \a scalar.
+*/
+
+/*!
+ \fn QTransform & QTransform::operator/=(qreal scalar)
+ \overload
+
+ Returns the result of performing an element-wise division of this
+ matrix by the given \a scalar.
+*/
+
+/*!
+ \fn QTransform & QTransform::operator+=(qreal scalar)
+ \overload
+
+ Returns the matrix obtained by adding the given \a scalar to each
+ element of this matrix.
+*/
+
+/*!
+ \fn QTransform & QTransform::operator-=(qreal scalar)
+ \overload
+
+ Returns the matrix obtained by subtracting the given \a scalar from each
+ element of this matrix.
+*/
+
+/*!
+ Assigns the given \a matrix's values to this matrix.
+*/
+QTransform & QTransform::operator=(const QTransform &matrix)
+{
+ affine._m11 = matrix.affine._m11;
+ affine._m12 = matrix.affine._m12;
+ affine._m21 = matrix.affine._m21;
+ affine._m22 = matrix.affine._m22;
+ affine._dx = matrix.affine._dx;
+ affine._dy = matrix.affine._dy;
+ m_13 = matrix.m_13;
+ m_23 = matrix.m_23;
+ m_33 = matrix.m_33;
+ m_type = matrix.m_type;
+ m_dirty = matrix.m_dirty;
+
+ return *this;
+}
+
+/*!
+ Resets the matrix to an identity matrix, i.e. all elements are set
+ to zero, except \c m11 and \c m22 (specifying the scale) and \c m33
+ which are set to 1.
+
+ \sa QTransform(), isIdentity(), {QTransform#Basic Matrix
+ Operations}{Basic Matrix Operations}
+*/
+void QTransform::reset()
+{
+ affine._m11 = affine._m22 = m_33 = 1.0;
+ affine._m12 = m_13 = affine._m21 = m_23 = affine._dx = affine._dy = 0;
+ m_type = TxNone;
+ m_dirty = TxNone;
+}
+
+#ifndef QT_NO_DATASTREAM
+/*!
+ \fn QDataStream &operator<<(QDataStream &stream, const QTransform &matrix)
+ \since 4.3
+ \relates QTransform
+
+ Writes the given \a matrix to the given \a stream and returns a
+ reference to the stream.
+
+ \sa {Serializing Qt Data Types}
+*/
+QDataStream & operator<<(QDataStream &s, const QTransform &m)
+{
+ s << double(m.m11())
+ << double(m.m12())
+ << double(m.m13())
+ << double(m.m21())
+ << double(m.m22())
+ << double(m.m23())
+ << double(m.m31())
+ << double(m.m32())
+ << double(m.m33());
+ return s;
+}
+
+/*!
+ \fn QDataStream &operator>>(QDataStream &stream, QTransform &matrix)
+ \since 4.3
+ \relates QTransform
+
+ Reads the given \a matrix from the given \a stream and returns a
+ reference to the stream.
+
+ \sa {Serializing Qt Data Types}
+*/
+QDataStream & operator>>(QDataStream &s, QTransform &t)
+{
+ double m11, m12, m13,
+ m21, m22, m23,
+ m31, m32, m33;
+
+ s >> m11;
+ s >> m12;
+ s >> m13;
+ s >> m21;
+ s >> m22;
+ s >> m23;
+ s >> m31;
+ s >> m32;
+ s >> m33;
+ t.setMatrix(m11, m12, m13,
+ m21, m22, m23,
+ m31, m32, m33);
+ return s;
+}
+
+#endif // QT_NO_DATASTREAM
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QTransform &m)
+{
+ static const char *typeStr[] =
+ {
+ "TxNone",
+ "TxTranslate",
+ "TxScale",
+ 0,
+ "TxRotate",
+ 0, 0, 0,
+ "TxShear",
+ 0, 0, 0, 0, 0, 0, 0,
+ "TxProject"
+ };
+
+ dbg.nospace() << "QTransform(type=" << typeStr[m.type()] << ','
+ << " 11=" << m.m11()
+ << " 12=" << m.m12()
+ << " 13=" << m.m13()
+ << " 21=" << m.m21()
+ << " 22=" << m.m22()
+ << " 23=" << m.m23()
+ << " 31=" << m.m31()
+ << " 32=" << m.m32()
+ << " 33=" << m.m33()
+ << ')';
+
+ return dbg.space();
+}
+#endif
+
+/*!
+ \fn QPoint operator*(const QPoint &point, const QTransform &matrix)
+ \relates QTransform
+
+ This is the same as \a{matrix}.map(\a{point}).
+
+ \sa QTransform::map()
+*/
+QPoint QTransform::map(const QPoint &p) const
+{
+ qreal fx = p.x();
+ qreal fy = p.y();
+
+ qreal x = 0, y = 0;
+
+ TransformationType t = inline_type();
+ switch(t) {
+ case TxNone:
+ x = fx;
+ y = fy;
+ break;
+ case TxTranslate:
+ x = fx + affine._dx;
+ y = fy + affine._dy;
+ break;
+ case TxScale:
+ x = affine._m11 * fx + affine._dx;
+ y = affine._m22 * fy + affine._dy;
+ break;
+ case TxRotate:
+ case TxShear:
+ case TxProject:
+ x = affine._m11 * fx + affine._m21 * fy + affine._dx;
+ y = affine._m12 * fx + affine._m22 * fy + affine._dy;
+ if (t == TxProject) {
+ qreal w = 1./(m_13 * fx + m_23 * fy + m_33);
+ x *= w;
+ y *= w;
+ }
+ }
+ return QPoint(qRound(x), qRound(y));
+}
+
+
+/*!
+ \fn QPointF operator*(const QPointF &point, const QTransform &matrix)
+ \relates QTransform
+
+ Same as \a{matrix}.map(\a{point}).
+
+ \sa QTransform::map()
+*/
+
+/*!
+ \overload
+
+ Creates and returns a QPointF object that is a copy of the given point,
+ \a p, mapped into the coordinate system defined by this matrix.
+*/
+QPointF QTransform::map(const QPointF &p) const
+{
+ qreal fx = p.x();
+ qreal fy = p.y();
+
+ qreal x = 0, y = 0;
+
+ TransformationType t = inline_type();
+ switch(t) {
+ case TxNone:
+ x = fx;
+ y = fy;
+ break;
+ case TxTranslate:
+ x = fx + affine._dx;
+ y = fy + affine._dy;
+ break;
+ case TxScale:
+ x = affine._m11 * fx + affine._dx;
+ y = affine._m22 * fy + affine._dy;
+ break;
+ case TxRotate:
+ case TxShear:
+ case TxProject:
+ x = affine._m11 * fx + affine._m21 * fy + affine._dx;
+ y = affine._m12 * fx + affine._m22 * fy + affine._dy;
+ if (t == TxProject) {
+ qreal w = 1./(m_13 * fx + m_23 * fy + m_33);
+ x *= w;
+ y *= w;
+ }
+ }
+ return QPointF(x, y);
+}
+
+/*!
+ \fn QPoint QTransform::map(const QPoint &point) const
+ \overload
+
+ Creates and returns a QPoint object that is a copy of the given \a
+ point, mapped into the coordinate system defined by this
+ matrix. Note that the transformed coordinates are rounded to the
+ nearest integer.
+*/
+
+/*!
+ \fn QLineF operator*(const QLineF &line, const QTransform &matrix)
+ \relates QTransform
+
+ This is the same as \a{matrix}.map(\a{line}).
+
+ \sa QTransform::map()
+*/
+
+/*!
+ \fn QLine operator*(const QLine &line, const QTransform &matrix)
+ \relates QTransform
+
+ This is the same as \a{matrix}.map(\a{line}).
+
+ \sa QTransform::map()
+*/
+
+/*!
+ \overload
+
+ Creates and returns a QLineF object that is a copy of the given line,
+ \a l, mapped into the coordinate system defined by this matrix.
+*/
+QLine QTransform::map(const QLine &l) const
+{
+ qreal fx1 = l.x1();
+ qreal fy1 = l.y1();
+ qreal fx2 = l.x2();
+ qreal fy2 = l.y2();
+
+ qreal x1 = 0, y1 = 0, x2 = 0, y2 = 0;
+
+ TransformationType t = inline_type();
+ switch(t) {
+ case TxNone:
+ x1 = fx1;
+ y1 = fy1;
+ x2 = fx2;
+ y2 = fy2;
+ break;
+ case TxTranslate:
+ x1 = fx1 + affine._dx;
+ y1 = fy1 + affine._dy;
+ x2 = fx2 + affine._dx;
+ y2 = fy2 + affine._dy;
+ break;
+ case TxScale:
+ x1 = affine._m11 * fx1 + affine._dx;
+ y1 = affine._m22 * fy1 + affine._dy;
+ x2 = affine._m11 * fx2 + affine._dx;
+ y2 = affine._m22 * fy2 + affine._dy;
+ break;
+ case TxRotate:
+ case TxShear:
+ case TxProject:
+ x1 = affine._m11 * fx1 + affine._m21 * fy1 + affine._dx;
+ y1 = affine._m12 * fx1 + affine._m22 * fy1 + affine._dy;
+ x2 = affine._m11 * fx2 + affine._m21 * fy2 + affine._dx;
+ y2 = affine._m12 * fx2 + affine._m22 * fy2 + affine._dy;
+ if (t == TxProject) {
+ qreal w = 1./(m_13 * fx1 + m_23 * fy1 + m_33);
+ x1 *= w;
+ y1 *= w;
+ w = 1./(m_13 * fx2 + m_23 * fy2 + m_33);
+ x2 *= w;
+ y2 *= w;
+ }
+ }
+ return QLine(qRound(x1), qRound(y1), qRound(x2), qRound(y2));
+}
+
+/*!
+ \overload
+
+ \fn QLineF QTransform::map(const QLineF &line) const
+
+ Creates and returns a QLine object that is a copy of the given \a
+ line, mapped into the coordinate system defined by this matrix.
+ Note that the transformed coordinates are rounded to the nearest
+ integer.
+*/
+
+QLineF QTransform::map(const QLineF &l) const
+{
+ qreal fx1 = l.x1();
+ qreal fy1 = l.y1();
+ qreal fx2 = l.x2();
+ qreal fy2 = l.y2();
+
+ qreal x1 = 0, y1 = 0, x2 = 0, y2 = 0;
+
+ TransformationType t = inline_type();
+ switch(t) {
+ case TxNone:
+ x1 = fx1;
+ y1 = fy1;
+ x2 = fx2;
+ y2 = fy2;
+ break;
+ case TxTranslate:
+ x1 = fx1 + affine._dx;
+ y1 = fy1 + affine._dy;
+ x2 = fx2 + affine._dx;
+ y2 = fy2 + affine._dy;
+ break;
+ case TxScale:
+ x1 = affine._m11 * fx1 + affine._dx;
+ y1 = affine._m22 * fy1 + affine._dy;
+ x2 = affine._m11 * fx2 + affine._dx;
+ y2 = affine._m22 * fy2 + affine._dy;
+ break;
+ case TxRotate:
+ case TxShear:
+ case TxProject:
+ x1 = affine._m11 * fx1 + affine._m21 * fy1 + affine._dx;
+ y1 = affine._m12 * fx1 + affine._m22 * fy1 + affine._dy;
+ x2 = affine._m11 * fx2 + affine._m21 * fy2 + affine._dx;
+ y2 = affine._m12 * fx2 + affine._m22 * fy2 + affine._dy;
+ if (t == TxProject) {
+ qreal w = 1./(m_13 * fx1 + m_23 * fy1 + m_33);
+ x1 *= w;
+ y1 *= w;
+ w = 1./(m_13 * fx2 + m_23 * fy2 + m_33);
+ x2 *= w;
+ y2 *= w;
+ }
+ }
+ return QLineF(x1, y1, x2, y2);
+}
+
+static QPolygonF mapProjective(const QTransform &transform, const QPolygonF &poly)
+{
+ if (poly.size() == 0)
+ return poly;
+
+ if (poly.size() == 1)
+ return QPolygonF() << transform.map(poly.at(0));
+
+ QPainterPath path;
+ path.addPolygon(poly);
+
+ path = transform.map(path);
+
+ QPolygonF result;
+ for (int i = 0; i < path.elementCount(); ++i)
+ result << path.elementAt(i);
+ return result;
+}
+
+
+/*!
+ \fn QPolygonF operator *(const QPolygonF &polygon, const QTransform &matrix)
+ \since 4.3
+ \relates QTransform
+
+ This is the same as \a{matrix}.map(\a{polygon}).
+
+ \sa QTransform::map()
+*/
+
+/*!
+ \fn QPolygon operator*(const QPolygon &polygon, const QTransform &matrix)
+ \relates QTransform
+
+ This is the same as \a{matrix}.map(\a{polygon}).
+
+ \sa QTransform::map()
+*/
+
+/*!
+ \fn QPolygonF QTransform::map(const QPolygonF &polygon) const
+ \overload
+
+ Creates and returns a QPolygonF object that is a copy of the given
+ \a polygon, mapped into the coordinate system defined by this
+ matrix.
+*/
+QPolygonF QTransform::map(const QPolygonF &a) const
+{
+ TransformationType t = inline_type();
+ if (t <= TxTranslate)
+ return a.translated(affine._dx, affine._dy);
+
+ if (t >= QTransform::TxProject)
+ return mapProjective(*this, a);
+
+ int size = a.size();
+ int i;
+ QPolygonF p(size);
+ const QPointF *da = a.constData();
+ QPointF *dp = p.data();
+
+ for(i = 0; i < size; ++i) {
+ MAP(da[i].xp, da[i].yp, dp[i].xp, dp[i].yp);
+ }
+ return p;
+}
+
+/*!
+ \fn QPolygon QTransform::map(const QPolygon &polygon) const
+ \overload
+
+ Creates and returns a QPolygon object that is a copy of the given
+ \a polygon, mapped into the coordinate system defined by this
+ matrix. Note that the transformed coordinates are rounded to the
+ nearest integer.
+*/
+QPolygon QTransform::map(const QPolygon &a) const
+{
+ TransformationType t = inline_type();
+ if (t <= TxTranslate)
+ return a.translated(qRound(affine._dx), qRound(affine._dy));
+
+ if (t >= QTransform::TxProject)
+ return mapProjective(*this, QPolygonF(a)).toPolygon();
+
+ int size = a.size();
+ int i;
+ QPolygon p(size);
+ const QPoint *da = a.constData();
+ QPoint *dp = p.data();
+
+ for(i = 0; i < size; ++i) {
+ qreal nx = 0, ny = 0;
+ MAP(da[i].xp, da[i].yp, nx, ny);
+ dp[i].xp = qRound(nx);
+ dp[i].yp = qRound(ny);
+ }
+ return p;
+}
+
+/*!
+ \fn QRegion operator*(const QRegion &region, const QTransform &matrix)
+ \relates QTransform
+
+ This is the same as \a{matrix}.map(\a{region}).
+
+ \sa QTransform::map()
+*/
+
+extern QPainterPath qt_regionToPath(const QRegion &region);
+
+/*!
+ \fn QRegion QTransform::map(const QRegion &region) const
+ \overload
+
+ Creates and returns a QRegion object that is a copy of the given
+ \a region, mapped into the coordinate system defined by this matrix.
+
+ Calling this method can be rather expensive if rotations or
+ shearing are used.
+*/
+QRegion QTransform::map(const QRegion &r) const
+{
+ TransformationType t = inline_type();
+ if (t == TxNone)
+ return r;
+
+ if (t == TxTranslate) {
+ QRegion copy(r);
+ copy.translate(qRound(affine._dx), qRound(affine._dy));
+ return copy;
+ }
+
+ if (t == TxScale && r.rectCount() == 1)
+ return QRegion(mapRect(r.boundingRect()));
+
+ QPainterPath p = map(qt_regionToPath(r));
+ return p.toFillPolygon(QTransform()).toPolygon();
+}
+
+struct QHomogeneousCoordinate
+{
+ qreal x;
+ qreal y;
+ qreal w;
+
+ QHomogeneousCoordinate() {}
+ QHomogeneousCoordinate(qreal x_, qreal y_, qreal w_) : x(x_), y(y_), w(w_) {}
+
+ const QPointF toPoint() const {
+ qreal iw = 1. / w;
+ return QPointF(x * iw, y * iw);
+ }
+};
+
+static inline QHomogeneousCoordinate mapHomogeneous(const QTransform &transform, const QPointF &p)
+{
+ QHomogeneousCoordinate c;
+ c.x = transform.m11() * p.x() + transform.m21() * p.y() + transform.m31();
+ c.y = transform.m12() * p.x() + transform.m22() * p.y() + transform.m32();
+ c.w = transform.m13() * p.x() + transform.m23() * p.y() + transform.m33();
+ return c;
+}
+
+static inline bool lineTo_clipped(QPainterPath &path, const QTransform &transform, const QPointF &a, const QPointF &b,
+ bool needsMoveTo, bool needsLineTo = true)
+{
+ QHomogeneousCoordinate ha = mapHomogeneous(transform, a);
+ QHomogeneousCoordinate hb = mapHomogeneous(transform, b);
+
+ if (ha.w < Q_NEAR_CLIP && hb.w < Q_NEAR_CLIP)
+ return false;
+
+ if (hb.w < Q_NEAR_CLIP) {
+ const qreal t = (Q_NEAR_CLIP - hb.w) / (ha.w - hb.w);
+
+ hb.x += (ha.x - hb.x) * t;
+ hb.y += (ha.y - hb.y) * t;
+ hb.w = qreal(Q_NEAR_CLIP);
+ } else if (ha.w < Q_NEAR_CLIP) {
+ const qreal t = (Q_NEAR_CLIP - ha.w) / (hb.w - ha.w);
+
+ ha.x += (hb.x - ha.x) * t;
+ ha.y += (hb.y - ha.y) * t;
+ ha.w = qreal(Q_NEAR_CLIP);
+
+ const QPointF p = ha.toPoint();
+ if (needsMoveTo) {
+ path.moveTo(p);
+ needsMoveTo = false;
+ } else {
+ path.lineTo(p);
+ }
+ }
+
+ if (needsMoveTo)
+ path.moveTo(ha.toPoint());
+
+ if (needsLineTo)
+ path.lineTo(hb.toPoint());
+
+ return true;
+}
+Q_GUI_EXPORT bool qt_scaleForTransform(const QTransform &transform, qreal *scale);
+
+static inline bool cubicTo_clipped(QPainterPath &path, const QTransform &transform, const QPointF &a, const QPointF &b, const QPointF &c, const QPointF &d, bool needsMoveTo)
+{
+ // Convert projective xformed curves to line
+ // segments so they can be transformed more accurately
+
+ qreal scale;
+ qt_scaleForTransform(transform, &scale);
+
+ qreal curveThreshold = scale == 0 ? qreal(0.25) : (qreal(0.25) / scale);
+
+ QPolygonF segment = QBezier::fromPoints(a, b, c, d).toPolygon(curveThreshold);
+
+ for (int i = 0; i < segment.size() - 1; ++i)
+ if (lineTo_clipped(path, transform, segment.at(i), segment.at(i+1), needsMoveTo))
+ needsMoveTo = false;
+
+ return !needsMoveTo;
+}
+
+static QPainterPath mapProjective(const QTransform &transform, const QPainterPath &path)
+{
+ QPainterPath result;
+
+ QPointF last;
+ QPointF lastMoveTo;
+ bool needsMoveTo = true;
+ for (int i = 0; i < path.elementCount(); ++i) {
+ switch (path.elementAt(i).type) {
+ case QPainterPath::MoveToElement:
+ if (i > 0 && lastMoveTo != last)
+ lineTo_clipped(result, transform, last, lastMoveTo, needsMoveTo);
+
+ lastMoveTo = path.elementAt(i);
+ last = path.elementAt(i);
+ needsMoveTo = true;
+ break;
+ case QPainterPath::LineToElement:
+ if (lineTo_clipped(result, transform, last, path.elementAt(i), needsMoveTo))
+ needsMoveTo = false;
+ last = path.elementAt(i);
+ break;
+ case QPainterPath::CurveToElement:
+ if (cubicTo_clipped(result, transform, last, path.elementAt(i), path.elementAt(i+1), path.elementAt(i+2), needsMoveTo))
+ needsMoveTo = false;
+ i += 2;
+ last = path.elementAt(i);
+ break;
+ default:
+ Q_ASSERT(false);
+ }
+ }
+
+ if (path.elementCount() > 0 && lastMoveTo != last)
+ lineTo_clipped(result, transform, last, lastMoveTo, needsMoveTo, false);
+
+ result.setFillRule(path.fillRule());
+ return result;
+}
+
+/*!
+ \fn QPainterPath operator *(const QPainterPath &path, const QTransform &matrix)
+ \since 4.3
+ \relates QTransform
+
+ This is the same as \a{matrix}.map(\a{path}).
+
+ \sa QTransform::map()
+*/
+
+/*!
+ \overload
+
+ Creates and returns a QPainterPath object that is a copy of the
+ given \a path, mapped into the coordinate system defined by this
+ matrix.
+*/
+QPainterPath QTransform::map(const QPainterPath &path) const
+{
+ TransformationType t = inline_type();
+ if (t == TxNone || path.elementCount() == 0)
+ return path;
+
+ if (t >= TxProject)
+ return mapProjective(*this, path);
+
+ QPainterPath copy = path;
+
+ if (t == TxTranslate) {
+ copy.translate(affine._dx, affine._dy);
+ } else {
+ copy.detach();
+ // Full xform
+ for (int i=0; i<path.elementCount(); ++i) {
+ QPainterPath::Element &e = copy.d_ptr->elements[i];
+ MAP(e.x, e.y, e.x, e.y);
+ }
+ }
+
+ return copy;
+}
+
+/*!
+ \fn QPolygon QTransform::mapToPolygon(const QRect &rectangle) const
+
+ Creates and returns a QPolygon representation of the given \a
+ rectangle, mapped into the coordinate system defined by this
+ matrix.
+
+ The rectangle's coordinates are transformed using the following
+ formulas:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qtransform.cpp 1
+
+ Polygons and rectangles behave slightly differently when
+ transformed (due to integer rounding), so
+ \c{matrix.map(QPolygon(rectangle))} is not always the same as
+ \c{matrix.mapToPolygon(rectangle)}.
+
+ \sa mapRect(), {QTransform#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+QPolygon QTransform::mapToPolygon(const QRect &rect) const
+{
+ TransformationType t = inline_type();
+
+ QPolygon a(4);
+ qreal x[4] = { 0, 0, 0, 0 }, y[4] = { 0, 0, 0, 0 };
+ if (t <= TxScale) {
+ x[0] = affine._m11*rect.x() + affine._dx;
+ y[0] = affine._m22*rect.y() + affine._dy;
+ qreal w = affine._m11*rect.width();
+ qreal h = affine._m22*rect.height();
+ if (w < 0) {
+ w = -w;
+ x[0] -= w;
+ }
+ if (h < 0) {
+ h = -h;
+ y[0] -= h;
+ }
+ x[1] = x[0]+w;
+ x[2] = x[1];
+ x[3] = x[0];
+ y[1] = y[0];
+ y[2] = y[0]+h;
+ y[3] = y[2];
+ } else {
+ qreal right = rect.x() + rect.width();
+ qreal bottom = rect.y() + rect.height();
+ MAP(rect.x(), rect.y(), x[0], y[0]);
+ MAP(right, rect.y(), x[1], y[1]);
+ MAP(right, bottom, x[2], y[2]);
+ MAP(rect.x(), bottom, x[3], y[3]);
+ }
+
+ // all coordinates are correctly, tranform to a pointarray
+ // (rounding to the next integer)
+ a.setPoints(4, qRound(x[0]), qRound(y[0]),
+ qRound(x[1]), qRound(y[1]),
+ qRound(x[2]), qRound(y[2]),
+ qRound(x[3]), qRound(y[3]));
+ return a;
+}
+
+/*!
+ Creates a transformation matrix, \a trans, that maps a unit square
+ to a four-sided polygon, \a quad. Returns true if the transformation
+ is constructed or false if such a transformation does not exist.
+
+ \sa quadToSquare(), quadToQuad()
+*/
+bool QTransform::squareToQuad(const QPolygonF &quad, QTransform &trans)
+{
+ if (quad.count() != 4)
+ return false;
+
+ qreal dx0 = quad[0].x();
+ qreal dx1 = quad[1].x();
+ qreal dx2 = quad[2].x();
+ qreal dx3 = quad[3].x();
+
+ qreal dy0 = quad[0].y();
+ qreal dy1 = quad[1].y();
+ qreal dy2 = quad[2].y();
+ qreal dy3 = quad[3].y();
+
+ double ax = dx0 - dx1 + dx2 - dx3;
+ double ay = dy0 - dy1 + dy2 - dy3;
+
+ if (!ax && !ay) { //afine transform
+ trans.setMatrix(dx1 - dx0, dy1 - dy0, 0,
+ dx2 - dx1, dy2 - dy1, 0,
+ dx0, dy0, 1);
+ } else {
+ double ax1 = dx1 - dx2;
+ double ax2 = dx3 - dx2;
+ double ay1 = dy1 - dy2;
+ double ay2 = dy3 - dy2;
+
+ /*determinants */
+ double gtop = ax * ay2 - ax2 * ay;
+ double htop = ax1 * ay - ax * ay1;
+ double bottom = ax1 * ay2 - ax2 * ay1;
+
+ double a, b, c, d, e, f, g, h; /*i is always 1*/
+
+ if (!bottom)
+ return false;
+
+ g = gtop/bottom;
+ h = htop/bottom;
+
+ a = dx1 - dx0 + g * dx1;
+ b = dx3 - dx0 + h * dx3;
+ c = dx0;
+ d = dy1 - dy0 + g * dy1;
+ e = dy3 - dy0 + h * dy3;
+ f = dy0;
+
+ trans.setMatrix(a, d, g,
+ b, e, h,
+ c, f, 1.0);
+ }
+
+ return true;
+}
+
+/*!
+ \fn bool QTransform::quadToSquare(const QPolygonF &quad, QTransform &trans)
+
+ Creates a transformation matrix, \a trans, that maps a four-sided polygon,
+ \a quad, to a unit square. Returns true if the transformation is constructed
+ or false if such a transformation does not exist.
+
+ \sa squareToQuad(), quadToQuad()
+*/
+bool QTransform::quadToSquare(const QPolygonF &quad, QTransform &trans)
+{
+ if (!squareToQuad(quad, trans))
+ return false;
+
+ bool invertible = false;
+ trans = trans.inverted(&invertible);
+
+ return invertible;
+}
+
+/*!
+ Creates a transformation matrix, \a trans, that maps a four-sided
+ polygon, \a one, to another four-sided polygon, \a two.
+ Returns true if the transformation is possible; otherwise returns
+ false.
+
+ This is a convenience method combining quadToSquare() and
+ squareToQuad() methods. It allows the input quad to be
+ transformed into any other quad.
+
+ \sa squareToQuad(), quadToSquare()
+*/
+bool QTransform::quadToQuad(const QPolygonF &one,
+ const QPolygonF &two,
+ QTransform &trans)
+{
+ QTransform stq;
+ if (!quadToSquare(one, trans))
+ return false;
+ if (!squareToQuad(two, stq))
+ return false;
+ trans *= stq;
+ //qDebug()<<"Final = "<<trans;
+ return true;
+}
+
+/*!
+ Sets the matrix elements to the specified values, \a m11,
+ \a m12, \a m13 \a m21, \a m22, \a m23 \a m31, \a m32 and
+ \a m33. Note that this function replaces the previous values.
+ QTransform provides the translate(), rotate(), scale() and shear()
+ convenience functions to manipulate the various matrix elements
+ based on the currently defined coordinate system.
+
+ \sa QTransform()
+*/
+
+void QTransform::setMatrix(qreal m11, qreal m12, qreal m13,
+ qreal m21, qreal m22, qreal m23,
+ qreal m31, qreal m32, qreal m33)
+{
+ affine._m11 = m11; affine._m12 = m12; m_13 = m13;
+ affine._m21 = m21; affine._m22 = m22; m_23 = m23;
+ affine._dx = m31; affine._dy = m32; m_33 = m33;
+ m_type = TxNone;
+ m_dirty = TxProject;
+}
+
+static inline bool needsPerspectiveClipping(const QRectF &rect, const QTransform &transform)
+{
+ const qreal wx = qMin(transform.m13() * rect.left(), transform.m13() * rect.right());
+ const qreal wy = qMin(transform.m23() * rect.top(), transform.m23() * rect.bottom());
+
+ return wx + wy + transform.m33() < Q_NEAR_CLIP;
+}
+
+QRect QTransform::mapRect(const QRect &rect) const
+{
+ TransformationType t = inline_type();
+ if (t <= TxTranslate)
+ return rect.translated(qRound(affine._dx), qRound(affine._dy));
+
+ if (t <= TxScale) {
+ int x = qRound(affine._m11*rect.x() + affine._dx);
+ int y = qRound(affine._m22*rect.y() + affine._dy);
+ int w = qRound(affine._m11*rect.width());
+ int h = qRound(affine._m22*rect.height());
+ if (w < 0) {
+ w = -w;
+ x -= w;
+ }
+ if (h < 0) {
+ h = -h;
+ y -= h;
+ }
+ return QRect(x, y, w, h);
+ } else if (t < TxProject || !needsPerspectiveClipping(rect, *this)) {
+ // see mapToPolygon for explanations of the algorithm.
+ qreal x = 0, y = 0;
+ MAP(rect.left(), rect.top(), x, y);
+ qreal xmin = x;
+ qreal ymin = y;
+ qreal xmax = x;
+ qreal ymax = y;
+ MAP(rect.right() + 1, rect.top(), x, y);
+ xmin = qMin(xmin, x);
+ ymin = qMin(ymin, y);
+ xmax = qMax(xmax, x);
+ ymax = qMax(ymax, y);
+ MAP(rect.right() + 1, rect.bottom() + 1, x, y);
+ xmin = qMin(xmin, x);
+ ymin = qMin(ymin, y);
+ xmax = qMax(xmax, x);
+ ymax = qMax(ymax, y);
+ MAP(rect.left(), rect.bottom() + 1, x, y);
+ xmin = qMin(xmin, x);
+ ymin = qMin(ymin, y);
+ xmax = qMax(xmax, x);
+ ymax = qMax(ymax, y);
+ return QRect(qRound(xmin), qRound(ymin), qRound(xmax)-qRound(xmin), qRound(ymax)-qRound(ymin));
+ } else {
+ QPainterPath path;
+ path.addRect(rect);
+ return map(path).boundingRect().toRect();
+ }
+}
+
+/*!
+ \fn QRectF QTransform::mapRect(const QRectF &rectangle) const
+
+ Creates and returns a QRectF object that is a copy of the given \a
+ rectangle, mapped into the coordinate system defined by this
+ matrix.
+
+ The rectangle's coordinates are transformed using the following
+ formulas:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qtransform.cpp 2
+
+ If rotation or shearing has been specified, this function returns
+ the \e bounding rectangle. To retrieve the exact region the given
+ \a rectangle maps to, use the mapToPolygon() function instead.
+
+ \sa mapToPolygon(), {QTransform#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+QRectF QTransform::mapRect(const QRectF &rect) const
+{
+ TransformationType t = inline_type();
+ if (t <= TxTranslate)
+ return rect.translated(affine._dx, affine._dy);
+
+ if (t <= TxScale) {
+ qreal x = affine._m11*rect.x() + affine._dx;
+ qreal y = affine._m22*rect.y() + affine._dy;
+ qreal w = affine._m11*rect.width();
+ qreal h = affine._m22*rect.height();
+ if (w < 0) {
+ w = -w;
+ x -= w;
+ }
+ if (h < 0) {
+ h = -h;
+ y -= h;
+ }
+ return QRectF(x, y, w, h);
+ } else if (t < TxProject || !needsPerspectiveClipping(rect, *this)) {
+ qreal x = 0, y = 0;
+ MAP(rect.x(), rect.y(), x, y);
+ qreal xmin = x;
+ qreal ymin = y;
+ qreal xmax = x;
+ qreal ymax = y;
+ MAP(rect.x() + rect.width(), rect.y(), x, y);
+ xmin = qMin(xmin, x);
+ ymin = qMin(ymin, y);
+ xmax = qMax(xmax, x);
+ ymax = qMax(ymax, y);
+ MAP(rect.x() + rect.width(), rect.y() + rect.height(), x, y);
+ xmin = qMin(xmin, x);
+ ymin = qMin(ymin, y);
+ xmax = qMax(xmax, x);
+ ymax = qMax(ymax, y);
+ MAP(rect.x(), rect.y() + rect.height(), x, y);
+ xmin = qMin(xmin, x);
+ ymin = qMin(ymin, y);
+ xmax = qMax(xmax, x);
+ ymax = qMax(ymax, y);
+ return QRectF(xmin, ymin, xmax-xmin, ymax - ymin);
+ } else {
+ QPainterPath path;
+ path.addRect(rect);
+ return map(path).boundingRect();
+ }
+}
+
+/*!
+ \fn QRect QTransform::mapRect(const QRect &rectangle) const
+ \overload
+
+ Creates and returns a QRect object that is a copy of the given \a
+ rectangle, mapped into the coordinate system defined by this
+ matrix. Note that the transformed coordinates are rounded to the
+ nearest integer.
+*/
+
+/*!
+ Maps the given coordinates \a x and \a y into the coordinate
+ system defined by this matrix. The resulting values are put in *\a
+ tx and *\a ty, respectively.
+
+ The coordinates are transformed using the following formulas:
+
+ \snippet doc/src/snippets/code/src_gui_painting_qtransform.cpp 3
+
+ The point (x, y) is the original point, and (x', y') is the
+ transformed point.
+
+ \sa {QTransform#Basic Matrix Operations}{Basic Matrix Operations}
+*/
+void QTransform::map(qreal x, qreal y, qreal *tx, qreal *ty) const
+{
+ TransformationType t = inline_type();
+ MAP(x, y, *tx, *ty);
+}
+
+/*!
+ \overload
+
+ Maps the given coordinates \a x and \a y into the coordinate
+ system defined by this matrix. The resulting values are put in *\a
+ tx and *\a ty, respectively. Note that the transformed coordinates
+ are rounded to the nearest integer.
+*/
+void QTransform::map(int x, int y, int *tx, int *ty) const
+{
+ TransformationType t = inline_type();
+ qreal fx = 0, fy = 0;
+ MAP(x, y, fx, fy);
+ *tx = qRound(fx);
+ *ty = qRound(fy);
+}
+
+/*!
+ Returns the QTransform as an affine matrix.
+
+ \warning If a perspective transformation has been specified,
+ then the conversion will cause loss of data.
+*/
+const QMatrix &QTransform::toAffine() const
+{
+ return affine;
+}
+
+/*!
+ Returns the transformation type of this matrix.
+
+ The transformation type is the highest enumeration value
+ capturing all of the matrix's transformations. For example,
+ if the matrix both scales and shears, the type would be \c TxShear,
+ because \c TxShear has a higher enumeration value than \c TxScale.
+
+ Knowing the transformation type of a matrix is useful for optimization:
+ you can often handle specific types more optimally than handling
+ the generic case.
+ */
+QTransform::TransformationType QTransform::type() const
+{
+ if(m_dirty == TxNone || m_dirty < m_type)
+ return static_cast<TransformationType>(m_type);
+
+ switch (static_cast<TransformationType>(m_dirty)) {
+ case TxProject:
+ if (!qFuzzyIsNull(m_13) || !qFuzzyIsNull(m_23) || !qFuzzyIsNull(m_33 - 1)) {
+ m_type = TxProject;
+ break;
+ }
+ case TxShear:
+ case TxRotate:
+ if (!qFuzzyIsNull(affine._m12) || !qFuzzyIsNull(affine._m21)) {
+ const qreal dot = affine._m11 * affine._m12 + affine._m21 * affine._m22;
+ if (qFuzzyIsNull(dot))
+ m_type = TxRotate;
+ else
+ m_type = TxShear;
+ break;
+ }
+ case TxScale:
+ if (!qFuzzyIsNull(affine._m11 - 1) || !qFuzzyIsNull(affine._m22 - 1)) {
+ m_type = TxScale;
+ break;
+ }
+ case TxTranslate:
+ if (!qFuzzyIsNull(affine._dx) || !qFuzzyIsNull(affine._dy)) {
+ m_type = TxTranslate;
+ break;
+ }
+ case TxNone:
+ m_type = TxNone;
+ break;
+ }
+
+ m_dirty = TxNone;
+ return static_cast<TransformationType>(m_type);
+}
+
+/*!
+
+ Returns the transform as a QVariant.
+*/
+QTransform::operator QVariant() const
+{
+ return QVariant(QVariant::Transform, this);
+}
+
+
+/*!
+ \fn bool QTransform::isInvertible() const
+
+ Returns true if the matrix is invertible, otherwise returns false.
+
+ \sa inverted()
+*/
+
+/*!
+ \fn qreal QTransform::det() const
+ \obsolete
+
+ Returns the matrix's determinant. Use determinant() instead.
+*/
+
+
+/*!
+ \fn qreal QTransform::m11() const
+
+ Returns the horizontal scaling factor.
+
+ \sa scale(), {QTransform#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+
+/*!
+ \fn qreal QTransform::m12() const
+
+ Returns the vertical shearing factor.
+
+ \sa shear(), {QTransform#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+
+/*!
+ \fn qreal QTransform::m21() const
+
+ Returns the horizontal shearing factor.
+
+ \sa shear(), {QTransform#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+
+/*!
+ \fn qreal QTransform::m22() const
+
+ Returns the vertical scaling factor.
+
+ \sa scale(), {QTransform#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+
+/*!
+ \fn qreal QTransform::dx() const
+
+ Returns the horizontal translation factor.
+
+ \sa m31(), translate(), {QTransform#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+
+/*!
+ \fn qreal QTransform::dy() const
+
+ Returns the vertical translation factor.
+
+ \sa translate(), {QTransform#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+
+
+/*!
+ \fn qreal QTransform::m13() const
+
+ Returns the horizontal projection factor.
+
+ \sa translate(), {QTransform#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+
+
+/*!
+ \fn qreal QTransform::m23() const
+
+ Returns the vertical projection factor.
+
+ \sa translate(), {QTransform#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+
+/*!
+ \fn qreal QTransform::m31() const
+
+ Returns the horizontal translation factor.
+
+ \sa dx(), translate(), {QTransform#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+
+/*!
+ \fn qreal QTransform::m32() const
+
+ Returns the vertical translation factor.
+
+ \sa dy(), translate(), {QTransform#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+
+/*!
+ \fn qreal QTransform::m33() const
+
+ Returns the division factor.
+
+ \sa translate(), {QTransform#Basic Matrix Operations}{Basic Matrix
+ Operations}
+*/
+
+/*!
+ \fn qreal QTransform::determinant() const
+
+ Returns the matrix's determinant.
+*/
+
+/*!
+ \fn bool QTransform::isIdentity() const
+
+ Returns true if the matrix is the identity matrix, otherwise
+ returns false.
+
+ \sa reset()
+*/
+
+/*!
+ \fn bool QTransform::isAffine() const
+
+ Returns true if the matrix represent an affine transformation,
+ otherwise returns false.
+*/
+
+/*!
+ \fn bool QTransform::isScaling() const
+
+ Returns true if the matrix represents a scaling
+ transformation, otherwise returns false.
+
+ \sa reset()
+*/
+
+/*!
+ \fn bool QTransform::isRotating() const
+
+ Returns true if the matrix represents some kind of a
+ rotating transformation, otherwise returns false.
+
+ \sa reset()
+*/
+
+/*!
+ \fn bool QTransform::isTranslating() const
+
+ Returns true if the matrix represents a translating
+ transformation, otherwise returns false.
+
+ \sa reset()
+*/
+
+/*!
+ \fn bool qFuzzyCompare(const QTransform& t1, const QTransform& t2)
+
+ \relates QTransform
+ \since 4.6
+
+ Returns true if \a t1 and \a t2 are equal, allowing for a small
+ fuzziness factor for floating-point comparisons; false otherwise.
+*/
+
+
+// returns true if the transform is uniformly scaling
+// (same scale in x and y direction)
+// scale is set to the max of x and y scaling factors
+Q_GUI_EXPORT
+bool qt_scaleForTransform(const QTransform &transform, qreal *scale)
+{
+ const QTransform::TransformationType type = transform.type();
+ if (type <= QTransform::TxTranslate) {
+ if (scale)
+ *scale = 1;
+ return true;
+ } else if (type == QTransform::TxScale) {
+ const qreal xScale = qAbs(transform.m11());
+ const qreal yScale = qAbs(transform.m22());
+ if (scale)
+ *scale = qMax(xScale, yScale);
+ return qFuzzyCompare(xScale, yScale);
+ }
+
+ const qreal xScale = transform.m11() * transform.m11()
+ + transform.m21() * transform.m21();
+ const qreal yScale = transform.m12() * transform.m12()
+ + transform.m22() * transform.m22();
+ if (scale)
+ *scale = qSqrt(qMax(xScale, yScale));
+ return type == QTransform::TxRotate && qFuzzyCompare(xScale, yScale);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qtransform.h b/src/gui/painting/qtransform.h
new file mode 100644
index 0000000000..a165bd5ce3
--- /dev/null
+++ b/src/gui/painting/qtransform.h
@@ -0,0 +1,395 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QTRANSFORM_H
+#define QTRANSFORM_H
+
+#include <QtGui/qmatrix.h>
+#include <QtGui/qpainterpath.h>
+#include <QtGui/qpolygon.h>
+#include <QtGui/qregion.h>
+#include <QtGui/qwindowdefs.h>
+#include <QtCore/qline.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qrect.h>
+
+#if defined(Q_OS_VXWORKS) && defined(m_type)
+# undef m_type
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QVariant;
+
+class Q_GUI_EXPORT QTransform
+{
+public:
+ enum TransformationType {
+ TxNone = 0x00,
+ TxTranslate = 0x01,
+ TxScale = 0x02,
+ TxRotate = 0x04,
+ TxShear = 0x08,
+ TxProject = 0x10
+ };
+
+ inline explicit QTransform(Qt::Initialization) : affine(Qt::Uninitialized) {}
+ QTransform();
+ QTransform(qreal h11, qreal h12, qreal h13,
+ qreal h21, qreal h22, qreal h23,
+ qreal h31, qreal h32, qreal h33 = 1.0);
+ QTransform(qreal h11, qreal h12, qreal h21,
+ qreal h22, qreal dx, qreal dy);
+ explicit QTransform(const QMatrix &mtx);
+
+ bool isAffine() const;
+ bool isIdentity() const;
+ bool isInvertible() const;
+ bool isScaling() const;
+ bool isRotating() const;
+ bool isTranslating() const;
+
+ TransformationType type() const;
+
+ inline qreal determinant() const;
+ qreal det() const;
+
+ qreal m11() const;
+ qreal m12() const;
+ qreal m13() const;
+ qreal m21() const;
+ qreal m22() const;
+ qreal m23() const;
+ qreal m31() const;
+ qreal m32() const;
+ qreal m33() const;
+ qreal dx() const;
+ qreal dy() const;
+
+ void setMatrix(qreal m11, qreal m12, qreal m13,
+ qreal m21, qreal m22, qreal m23,
+ qreal m31, qreal m32, qreal m33);
+
+ QTransform inverted(bool *invertible = 0) const;
+ QTransform adjoint() const;
+ QTransform transposed() const;
+
+ QTransform &translate(qreal dx, qreal dy);
+ QTransform &scale(qreal sx, qreal sy);
+ QTransform &shear(qreal sh, qreal sv);
+ QTransform &rotate(qreal a, Qt::Axis axis = Qt::ZAxis);
+ QTransform &rotateRadians(qreal a, Qt::Axis axis = Qt::ZAxis);
+
+ static bool squareToQuad(const QPolygonF &square, QTransform &result);
+ static bool quadToSquare(const QPolygonF &quad, QTransform &result);
+ static bool quadToQuad(const QPolygonF &one,
+ const QPolygonF &two,
+ QTransform &result);
+
+ bool operator==(const QTransform &) const;
+ bool operator!=(const QTransform &) const;
+
+ QTransform &operator*=(const QTransform &);
+ QTransform operator*(const QTransform &o) const;
+
+ QTransform &operator=(const QTransform &);
+
+ operator QVariant() const;
+
+ void reset();
+ QPoint map(const QPoint &p) const;
+ QPointF map(const QPointF &p) const;
+ QLine map(const QLine &l) const;
+ QLineF map(const QLineF &l) const;
+ QPolygonF map(const QPolygonF &a) const;
+ QPolygon map(const QPolygon &a) const;
+ QRegion map(const QRegion &r) const;
+ QPainterPath map(const QPainterPath &p) const;
+ QPolygon mapToPolygon(const QRect &r) const;
+ QRect mapRect(const QRect &) const;
+ QRectF mapRect(const QRectF &) const;
+ void map(int x, int y, int *tx, int *ty) const;
+ void map(qreal x, qreal y, qreal *tx, qreal *ty) const;
+
+ const QMatrix &toAffine() const;
+
+ QTransform &operator*=(qreal div);
+ QTransform &operator/=(qreal div);
+ QTransform &operator+=(qreal div);
+ QTransform &operator-=(qreal div);
+
+ static QTransform fromTranslate(qreal dx, qreal dy);
+ static QTransform fromScale(qreal dx, qreal dy);
+
+private:
+ inline QTransform(qreal h11, qreal h12, qreal h13,
+ qreal h21, qreal h22, qreal h23,
+ qreal h31, qreal h32, qreal h33, bool)
+ : affine(h11, h12, h21, h22, h31, h32, true)
+ , m_13(h13), m_23(h23), m_33(h33)
+ , m_type(TxNone)
+ , m_dirty(TxProject) {}
+ inline QTransform(bool)
+ : affine(true)
+ , m_13(0), m_23(0), m_33(1)
+ , m_type(TxNone)
+ , m_dirty(TxNone) {}
+ inline TransformationType inline_type() const;
+ QMatrix affine;
+ qreal m_13;
+ qreal m_23;
+ qreal m_33;
+
+ mutable uint m_type : 5;
+ mutable uint m_dirty : 5;
+
+ class Private;
+ Private *d;
+};
+Q_DECLARE_TYPEINFO(QTransform, Q_MOVABLE_TYPE);
+
+/******* inlines *****/
+inline QTransform::TransformationType QTransform::inline_type() const
+{
+ if (m_dirty == TxNone)
+ return static_cast<TransformationType>(m_type);
+ return type();
+}
+
+inline bool QTransform::isAffine() const
+{
+ return inline_type() < TxProject;
+}
+inline bool QTransform::isIdentity() const
+{
+ return inline_type() == TxNone;
+}
+
+inline bool QTransform::isInvertible() const
+{
+ return !qFuzzyIsNull(determinant());
+}
+
+inline bool QTransform::isScaling() const
+{
+ return type() >= TxScale;
+}
+inline bool QTransform::isRotating() const
+{
+ return inline_type() >= TxRotate;
+}
+
+inline bool QTransform::isTranslating() const
+{
+ return inline_type() >= TxTranslate;
+}
+
+inline qreal QTransform::determinant() const
+{
+ return affine._m11*(m_33*affine._m22-affine._dy*m_23) -
+ affine._m21*(m_33*affine._m12-affine._dy*m_13)+affine._dx*(m_23*affine._m12-affine._m22*m_13);
+}
+inline qreal QTransform::det() const
+{
+ return determinant();
+}
+inline qreal QTransform::m11() const
+{
+ return affine._m11;
+}
+inline qreal QTransform::m12() const
+{
+ return affine._m12;
+}
+inline qreal QTransform::m13() const
+{
+ return m_13;
+}
+inline qreal QTransform::m21() const
+{
+ return affine._m21;
+}
+inline qreal QTransform::m22() const
+{
+ return affine._m22;
+}
+inline qreal QTransform::m23() const
+{
+ return m_23;
+}
+inline qreal QTransform::m31() const
+{
+ return affine._dx;
+}
+inline qreal QTransform::m32() const
+{
+ return affine._dy;
+}
+inline qreal QTransform::m33() const
+{
+ return m_33;
+}
+inline qreal QTransform::dx() const
+{
+ return affine._dx;
+}
+inline qreal QTransform::dy() const
+{
+ return affine._dy;
+}
+
+inline QTransform &QTransform::operator*=(qreal num)
+{
+ if (num == 1.)
+ return *this;
+ affine._m11 *= num;
+ affine._m12 *= num;
+ m_13 *= num;
+ affine._m21 *= num;
+ affine._m22 *= num;
+ m_23 *= num;
+ affine._dx *= num;
+ affine._dy *= num;
+ m_33 *= num;
+ if (m_dirty < TxScale)
+ m_dirty = TxScale;
+ return *this;
+}
+inline QTransform &QTransform::operator/=(qreal div)
+{
+ if (div == 0)
+ return *this;
+ div = 1/div;
+ return operator*=(div);
+}
+inline QTransform &QTransform::operator+=(qreal num)
+{
+ if (num == 0)
+ return *this;
+ affine._m11 += num;
+ affine._m12 += num;
+ m_13 += num;
+ affine._m21 += num;
+ affine._m22 += num;
+ m_23 += num;
+ affine._dx += num;
+ affine._dy += num;
+ m_33 += num;
+ m_dirty = TxProject;
+ return *this;
+}
+inline QTransform &QTransform::operator-=(qreal num)
+{
+ if (num == 0)
+ return *this;
+ affine._m11 -= num;
+ affine._m12 -= num;
+ m_13 -= num;
+ affine._m21 -= num;
+ affine._m22 -= num;
+ m_23 -= num;
+ affine._dx -= num;
+ affine._dy -= num;
+ m_33 -= num;
+ m_dirty = TxProject;
+ return *this;
+}
+
+inline bool qFuzzyCompare(const QTransform& t1, const QTransform& t2)
+{
+ return qFuzzyCompare(t1.m11(), t2.m11())
+ && qFuzzyCompare(t1.m12(), t2.m12())
+ && qFuzzyCompare(t1.m13(), t2.m13())
+ && qFuzzyCompare(t1.m21(), t2.m21())
+ && qFuzzyCompare(t1.m22(), t2.m22())
+ && qFuzzyCompare(t1.m23(), t2.m23())
+ && qFuzzyCompare(t1.m31(), t2.m31())
+ && qFuzzyCompare(t1.m32(), t2.m32())
+ && qFuzzyCompare(t1.m33(), t2.m33());
+}
+
+
+/****** stream functions *******************/
+#ifndef QT_NO_DATASTREAM
+Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QTransform &);
+Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QTransform &);
+#endif
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QTransform &);
+#endif
+/****** end stream functions *******************/
+
+// mathematical semantics
+Q_GUI_EXPORT_INLINE QPoint operator*(const QPoint &p, const QTransform &m)
+{ return m.map(p); }
+Q_GUI_EXPORT_INLINE QPointF operator*(const QPointF &p, const QTransform &m)
+{ return m.map(p); }
+Q_GUI_EXPORT_INLINE QLineF operator*(const QLineF &l, const QTransform &m)
+{ return m.map(l); }
+Q_GUI_EXPORT_INLINE QLine operator*(const QLine &l, const QTransform &m)
+{ return m.map(l); }
+Q_GUI_EXPORT_INLINE QPolygon operator *(const QPolygon &a, const QTransform &m)
+{ return m.map(a); }
+Q_GUI_EXPORT_INLINE QPolygonF operator *(const QPolygonF &a, const QTransform &m)
+{ return m.map(a); }
+Q_GUI_EXPORT_INLINE QRegion operator *(const QRegion &r, const QTransform &m)
+{ return m.map(r); }
+Q_GUI_EXPORT_INLINE QPainterPath operator *(const QPainterPath &p, const QTransform &m)
+{ return m.map(p); }
+
+Q_GUI_EXPORT_INLINE QTransform operator *(const QTransform &a, qreal n)
+{ QTransform t(a); t *= n; return t; }
+Q_GUI_EXPORT_INLINE QTransform operator /(const QTransform &a, qreal n)
+{ QTransform t(a); t /= n; return t; }
+Q_GUI_EXPORT_INLINE QTransform operator +(const QTransform &a, qreal n)
+{ QTransform t(a); t += n; return t; }
+Q_GUI_EXPORT_INLINE QTransform operator -(const QTransform &a, qreal n)
+{ QTransform t(a); t -= n; return t; }
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/gui/painting/qunifiedtoolbarsurface_mac.cpp b/src/gui/painting/qunifiedtoolbarsurface_mac.cpp
new file mode 100644
index 0000000000..3876c3d1a2
--- /dev/null
+++ b/src/gui/painting/qunifiedtoolbarsurface_mac.cpp
@@ -0,0 +1,264 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qunifiedtoolbarsurface_mac_p.h"
+#include <private/qt_cocoa_helpers_mac_p.h>
+#include <private/qbackingstore_p.h>
+#include <private/qmainwindowlayout_p.h>
+
+#include <QDebug>
+
+#ifdef QT_MAC_USE_COCOA
+
+QT_BEGIN_NAMESPACE
+
+QUnifiedToolbarSurface::QUnifiedToolbarSurface(QWidget *widget)
+ : QRasterWindowSurface(widget, false), d_ptr(new QUnifiedToolbarSurfacePrivate)
+{
+ d_ptr->image = 0;
+ d_ptr->inSetGeometry = false;
+
+ setGeometry(QRect(QPoint(0, 0), QSize(widget->width(), 100))); // FIXME: Fix height.
+}
+
+QUnifiedToolbarSurface::~QUnifiedToolbarSurface()
+{
+ if (d_ptr->image)
+ delete d_ptr->image;
+}
+
+QPaintDevice *QUnifiedToolbarSurface::paintDevice()
+{
+ return &d_ptr->image->image;
+}
+
+void QUnifiedToolbarSurface::recursiveRedirect(QObject *object, QWidget *parent_toolbar, const QPoint &offset)
+{
+ if (object != 0) {
+ if (object->isWidgetType()) {
+ QWidget *widget = qobject_cast<QWidget *>(object);
+
+ // We redirect the painting only if the widget is in the same window
+ // and is not a window in itself.
+ if (!(widget->windowType() & Qt::Window)) {
+ widget->d_func()->unifiedSurface = this;
+ widget->d_func()->isInUnifiedToolbar = true;
+ widget->d_func()->toolbar_offset = offset;
+ widget->d_func()->toolbar_ancestor = parent_toolbar;
+
+ for (int i = 0; i < object->children().size(); ++i) {
+ recursiveRedirect(object->children().at(i), parent_toolbar, offset);
+ }
+ }
+ }
+ }
+}
+
+void QUnifiedToolbarSurface::insertToolbar(QWidget *toolbar, const QPoint &offset)
+{
+ setGeometry(QRect(QPoint(0, 0), QSize(offset.x() + toolbar->width(), 100))); // FIXME
+ recursiveRedirect(toolbar, toolbar, offset);
+}
+
+// We basically undo what we set in recursiveRedirect().
+void QUnifiedToolbarSurface::recursiveRemoval(QObject *object)
+{
+ if (object != 0) {
+ if (object->isWidgetType()) {
+ QWidget *widget = qobject_cast<QWidget *>(object);
+
+ // If it's a pop-up or something similar, we don't redirect it.
+ if (widget->windowType() & Qt::Window)
+ return;
+
+ widget->d_func()->unifiedSurface = 0;
+ widget->d_func()->isInUnifiedToolbar = false;
+ widget->d_func()->toolbar_offset = QPoint();
+ widget->d_func()->toolbar_ancestor = 0;
+ }
+
+ for (int i = 0; i < object->children().size(); ++i) {
+ recursiveRemoval(object->children().at(i));
+ }
+ }
+}
+
+void QUnifiedToolbarSurface::removeToolbar(QToolBar *toolbar)
+{
+ recursiveRemoval(toolbar);
+}
+
+void QUnifiedToolbarSurface::setGeometry(const QRect &rect)
+{
+ QWindowSurface::setGeometry(rect);
+ Q_D(QUnifiedToolbarSurface);
+ d->inSetGeometry = true;
+ if (d->image == 0 || d->image->width() < rect.width() || d->image->height() < rect.height())
+ prepareBuffer(QImage::Format_ARGB32_Premultiplied, window());
+ d->inSetGeometry = false;
+
+ // FIXME: set unified toolbar height.
+}
+
+void QUnifiedToolbarSurface::beginPaint(const QRegion &rgn)
+{
+ QPainter p(&d_ptr->image->image);
+ p.setCompositionMode(QPainter::CompositionMode_Source);
+ const QVector<QRect> rects = rgn.rects();
+ const QColor blank = Qt::transparent;
+ for (QVector<QRect>::const_iterator it = rects.begin(); it != rects.end(); ++it) {
+ p.fillRect(*it, blank);
+ }
+}
+
+void QUnifiedToolbarSurface::updateToolbarOffset(QWidget *widget)
+{
+ QMainWindowLayout *mlayout = qobject_cast<QMainWindowLayout*> (widget->window()->layout());
+ mlayout->updateUnifiedToolbarOffset();
+}
+
+void QUnifiedToolbarSurface::flush(QWidget *widget, const QRegion &region, const QPoint &offset)
+{
+ Q_UNUSED(region);
+ Q_UNUSED(offset);
+
+ this->flush(widget);
+}
+
+void QUnifiedToolbarSurface::flush(QWidget *widget)
+{
+ Q_D(QUnifiedToolbarSurface);
+
+ if (!d->image)
+ return;
+
+ if (widget->d_func()->flushRequested) {
+ // We call display: directly to avoid flickering in the toolbar.
+ qt_mac_display(widget);
+ }
+}
+
+void QUnifiedToolbarSurface::prepareBuffer(QImage::Format format, QWidget *widget)
+{
+ Q_D(QUnifiedToolbarSurface);
+
+ int width = geometry().width();
+ int height = 100; // FIXME
+ if (d->image) {
+ width = qMax(d->image->width(), width);
+ height = qMax(d->image->height(), height);
+ }
+
+ if (width == 0 || height == 0) {
+ delete d->image;
+ d->image = 0;
+ return;
+ }
+
+ QNativeImage *oldImage = d->image;
+
+ d->image = new QNativeImage(width, height, format, false, widget);
+
+ if (oldImage && d->inSetGeometry && hasStaticContents()) {
+ // Make sure we use the const version of bits() (no detach).
+ const uchar *src = const_cast<const QImage &>(oldImage->image).bits();
+ uchar *dst = d->image->image.bits();
+
+ const int srcBytesPerLine = oldImage->image.bytesPerLine();
+ const int dstBytesPerLine = d->image->image.bytesPerLine();
+ const int bytesPerPixel = oldImage->image.depth() >> 3;
+
+ QRegion staticRegion(staticContents());
+ // Make sure we're inside the boundaries of the old image.
+ staticRegion &= QRect(0, 0, oldImage->image.width(), oldImage->image.height());
+ const QVector<QRect> &rects = staticRegion.rects();
+ const QRect *srcRect = rects.constData();
+
+ // Copy the static content of the old image into the new one.
+ int numRectsLeft = rects.size();
+ do {
+ const int bytesOffset = srcRect->x() * bytesPerPixel;
+ const int dy = srcRect->y();
+
+ // Adjust src and dst to point to the right offset.
+ const uchar *s = src + dy * srcBytesPerLine + bytesOffset;
+ uchar *d = dst + dy * dstBytesPerLine + bytesOffset;
+ const int numBytes = srcRect->width() * bytesPerPixel;
+
+ int numScanLinesLeft = srcRect->height();
+ do {
+ ::memcpy(d, s, numBytes);
+ d += dstBytesPerLine;
+ s += srcBytesPerLine;
+ } while (--numScanLinesLeft);
+
+ ++srcRect;
+ } while (--numRectsLeft);
+ }
+
+ delete oldImage;
+}
+
+CGContextRef QUnifiedToolbarSurface::imageContext()
+{
+ Q_D(QUnifiedToolbarSurface);
+ return d->image->cg;
+}
+
+void QUnifiedToolbarSurface::renderToolbar(QWidget *widget, bool forceFlush)
+{
+ QWidget *toolbar = widget->d_func()->toolbar_ancestor;
+
+ updateToolbarOffset(toolbar);
+ QRect beginPaintRect(toolbar->d_func()->toolbar_offset.x(), toolbar->d_func()->toolbar_offset.y(), toolbar->geometry().width(), toolbar->geometry().height());
+ QRegion beginPaintRegion(beginPaintRect);
+
+ beginPaint(beginPaintRegion);
+ toolbar->render(paintDevice(), toolbar->d_func()->toolbar_offset, QRegion(toolbar->geometry()), QWidget::DrawChildren);
+ toolbar->d_func()->flushRequested = true;
+
+ if (forceFlush)
+ flush(toolbar);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_MAC_USE_COCOA
diff --git a/src/gui/painting/qunifiedtoolbarsurface_mac_p.h b/src/gui/painting/qunifiedtoolbarsurface_mac_p.h
new file mode 100644
index 0000000000..0a7ebf1759
--- /dev/null
+++ b/src/gui/painting/qunifiedtoolbarsurface_mac_p.h
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QUNIFIEDTOOLBARSURFACE_MAC_P_H
+#define QUNIFIEDTOOLBARSURFACE_MAC_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qwindowsurface_raster_p.h>
+#include <QWidget>
+#include <QToolBar>
+#include <private/qwidget_p.h>
+#include <private/qnativeimage_p.h>
+
+#ifdef QT_MAC_USE_COCOA
+
+QT_BEGIN_NAMESPACE
+
+class QNativeImage;
+
+
+class QUnifiedToolbarSurfacePrivate
+{
+public:
+ QNativeImage *image;
+ uint inSetGeometry : 1;
+};
+
+class Q_GUI_EXPORT QUnifiedToolbarSurface : public QRasterWindowSurface
+{
+public:
+ QUnifiedToolbarSurface(QWidget *widget);
+ ~QUnifiedToolbarSurface();
+
+ void flush(QWidget *widget);
+ void flush(QWidget *widget, const QRegion &region, const QPoint &offset);
+ void setGeometry(const QRect &rect);
+ void beginPaint(const QRegion &rgn);
+ void insertToolbar(QWidget *toolbar, const QPoint &offset);
+ void removeToolbar(QToolBar *toolbar);
+ void updateToolbarOffset(QWidget *widget);
+ void renderToolbar(QWidget *widget, bool forceFlush = false);
+ void recursiveRedirect(QObject *widget, QWidget *parent_toolbar, const QPoint &offset);
+
+ QPaintDevice *paintDevice();
+ CGContextRef imageContext();
+
+private:
+ void prepareBuffer(QImage::Format format, QWidget *widget);
+ void recursiveRemoval(QObject *object);
+
+ Q_DECLARE_PRIVATE(QUnifiedToolbarSurface)
+ QScopedPointer<QUnifiedToolbarSurfacePrivate> d_ptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_MAC_USE_COCOA
+
+#endif // QUNIFIEDTOOLBARSURFACE_MAC_P_H
diff --git a/src/gui/painting/qvectorpath_p.h b/src/gui/painting/qvectorpath_p.h
new file mode 100644
index 0000000000..76212be5d4
--- /dev/null
+++ b/src/gui/painting/qvectorpath_p.h
@@ -0,0 +1,184 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QVECTORPATH_P_H
+#define QVECTORPATH_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/qpaintengine.h>
+
+#include <private/qpaintengine_p.h>
+#include <private/qstroker_p.h>
+#include <private/qpainter_p.h>
+
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QPaintEngineEx;
+
+typedef void (*qvectorpath_cache_cleanup)(QPaintEngineEx *engine, void *data);
+
+struct QRealRect {
+ qreal x1, y1, x2, y2;
+};
+
+class Q_GUI_EXPORT QVectorPath
+{
+public:
+ enum Hint {
+ // Shape hints, in 0x000000ff, access using shape()
+ AreaShapeMask = 0x0001, // shape covers an area
+ NonConvexShapeMask = 0x0002, // shape is not convex
+ CurvedShapeMask = 0x0004, // shape contains curves...
+ LinesShapeMask = 0x0008,
+ RectangleShapeMask = 0x0010,
+ ShapeMask = 0x001f,
+
+ // Shape hints merged into basic shapes..
+ LinesHint = LinesShapeMask,
+ RectangleHint = AreaShapeMask | RectangleShapeMask,
+ EllipseHint = AreaShapeMask | CurvedShapeMask,
+ ConvexPolygonHint = AreaShapeMask,
+ PolygonHint = AreaShapeMask | NonConvexShapeMask,
+ RoundedRectHint = AreaShapeMask | CurvedShapeMask,
+ ArbitraryShapeHint = AreaShapeMask | NonConvexShapeMask | CurvedShapeMask,
+
+ // Other hints
+ IsCachedHint = 0x0100, // Set if the cache hint is set
+ ShouldUseCacheHint = 0x0200, // Set if the path should be cached when possible..
+ ControlPointRect = 0x0400, // Set if the control point rect has been calculated...
+
+ // Shape rendering specifiers...
+ OddEvenFill = 0x1000,
+ WindingFill = 0x2000,
+ ImplicitClose = 0x4000
+ };
+
+ // ### Falcon: introduca a struct XY for points so lars is not so confused...
+ QVectorPath(const qreal *points,
+ int count,
+ const QPainterPath::ElementType *elements = 0,
+ uint hints = ArbitraryShapeHint)
+ : m_elements(elements),
+ m_points(points),
+ m_count(count),
+ m_hints(hints)
+ {
+ }
+
+ ~QVectorPath();
+
+ QRectF controlPointRect() const;
+
+ inline Hint shape() const { return (Hint) (m_hints & ShapeMask); }
+ inline bool isConvex() const { return (m_hints & NonConvexShapeMask) == 0; }
+ inline bool isCurved() const { return m_hints & CurvedShapeMask; }
+
+ inline bool isCacheable() const { return m_hints & ShouldUseCacheHint; }
+ inline bool hasImplicitClose() const { return m_hints & ImplicitClose; }
+ inline bool hasWindingFill() const { return m_hints & WindingFill; }
+
+ inline void makeCacheable() const { m_hints |= ShouldUseCacheHint; m_cache = 0; }
+ inline uint hints() const { return m_hints; }
+
+ inline const QPainterPath::ElementType *elements() const { return m_elements; }
+ inline const qreal *points() const { return m_points; }
+ inline bool isEmpty() const { return m_points == 0; }
+
+ inline int elementCount() const { return m_count; }
+ inline const QPainterPath convertToPainterPath() const;
+
+ static inline uint polygonFlags(QPaintEngine::PolygonDrawMode mode);
+
+ struct CacheEntry {
+ QPaintEngineEx *engine;
+ void *data;
+ qvectorpath_cache_cleanup cleanup;
+ CacheEntry *next;
+ };
+
+ CacheEntry *addCacheData(QPaintEngineEx *engine, void *data, qvectorpath_cache_cleanup cleanup) const;
+ inline CacheEntry *lookupCacheData(QPaintEngineEx *engine) const {
+ Q_ASSERT(m_hints & ShouldUseCacheHint);
+ CacheEntry *e = m_cache;
+ while (e) {
+ if (e->engine == engine)
+ return e;
+ e = e->next;
+ }
+ return 0;
+ }
+
+
+private:
+ Q_DISABLE_COPY(QVectorPath)
+
+ const QPainterPath::ElementType *m_elements;
+ const qreal *m_points;
+ const int m_count;
+
+ mutable uint m_hints;
+ mutable QRealRect m_cp_rect;
+
+ mutable CacheEntry *m_cache;
+};
+
+Q_GUI_EXPORT const QVectorPath &qtVectorPathForPath(const QPainterPath &path);
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/gui/painting/qwindowsurface.cpp b/src/gui/painting/qwindowsurface.cpp
new file mode 100644
index 0000000000..0fb9bf78a9
--- /dev/null
+++ b/src/gui/painting/qwindowsurface.cpp
@@ -0,0 +1,383 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qwindowsurface_p.h>
+#include <qwidget.h>
+#include <private/qwidget_p.h>
+#include <private/qbackingstore_p.h>
+#include <private/qapplication_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWindowSurfacePrivate
+{
+public:
+ QWindowSurfacePrivate(QWidget *w)
+ : window(w)
+ {
+ }
+
+ QWidget *window;
+#if !defined(Q_WS_QPA)
+ QRect geometry;
+#else
+ QSize size;
+#endif //Q_WS_QPA
+ QRegion staticContents;
+ QList<QImage*> bufferImages;
+};
+
+/*!
+ \class QWindowSurface
+ \since 4.3
+ \internal
+ \preliminary
+ \ingroup qws qpa
+
+ \brief The QWindowSurface class provides the drawing area for top-level
+ windows.
+*/
+
+
+/*!
+ \fn void QWindowSurface::beginPaint(const QRegion &region)
+
+ This function is called before painting onto the surface begins,
+ with the \a region in which the painting will occur.
+
+ \sa endPaint(), paintDevice()
+*/
+
+/*!
+ \fn void QWindowSurface::endPaint(const QRegion &region)
+
+ This function is called after painting onto the surface has ended,
+ with the \a region in which the painting was performed.
+
+ \sa beginPaint(), paintDevice()
+*/
+
+/*!
+ \fn void QWindowSurface::flush(QWidget *widget, const QRegion &region,
+ const QPoint &offset)
+
+ Flushes the given \a region from the specified \a widget onto the
+ screen.
+
+ Note that the \a offset parameter is currently unused.
+*/
+
+/*!
+ \fn QPaintDevice* QWindowSurface::paintDevice()
+
+ Implement this function to return the appropriate paint device.
+*/
+
+/*!
+ Constructs an empty surface for the given top-level \a window.
+*/
+QWindowSurface::QWindowSurface(QWidget *window, bool setDefaultSurface)
+ : d_ptr(new QWindowSurfacePrivate(window))
+{
+ if (!QApplicationPrivate::runtime_graphics_system) {
+ if(setDefaultSurface && window)
+ window->setWindowSurface(this);
+ }
+}
+
+/*!
+ Destroys this surface.
+*/
+QWindowSurface::~QWindowSurface()
+{
+ if (d_ptr->window)
+ d_ptr->window->d_func()->extra->topextra->windowSurface = 0;
+ delete d_ptr;
+}
+
+/*!
+ Returns a pointer to the top-level window associated with this
+ surface.
+*/
+QWidget* QWindowSurface::window() const
+{
+ return d_ptr->window;
+}
+
+void QWindowSurface::beginPaint(const QRegion &)
+{
+}
+
+void QWindowSurface::endPaint(const QRegion &)
+{
+// QApplication::syncX();
+ qDeleteAll(d_ptr->bufferImages);
+ d_ptr->bufferImages.clear();
+}
+
+#if !defined(Q_WS_QPA)
+/*!
+ Sets the currently allocated area to be the given \a rect.
+
+ This function is called whenever area covered by the top-level
+ window changes.
+
+ \sa geometry()
+*/
+void QWindowSurface::setGeometry(const QRect &rect)
+{
+ d_ptr->geometry = rect;
+}
+
+/*!
+ Returns the currently allocated area on the screen.
+*/
+QRect QWindowSurface::geometry() const
+{
+ return d_ptr->geometry;
+}
+#else
+
+/*!
+ Sets the size of the windowsurface to be \a size.
+
+ \sa size()
+*/
+void QWindowSurface::resize(const QSize &size)
+{
+ d_ptr->size = size;
+}
+
+/*!
+ Returns the current size of the windowsurface.
+*/
+QSize QWindowSurface::size() const
+{
+ return d_ptr->size;
+}
+#endif //Q_WS_QPA
+
+/*!
+ Scrolls the given \a area \a dx pixels to the right and \a dy
+ downward; both \a dx and \a dy may be negative.
+
+ Returns true if the area was scrolled successfully; false otherwise.
+*/
+bool QWindowSurface::scroll(const QRegion &area, int dx, int dy)
+{
+ Q_UNUSED(area);
+ Q_UNUSED(dx);
+ Q_UNUSED(dy);
+
+ return false;
+}
+
+/*!
+ Returns a QImage pointer which represents the actual buffer the \a widget
+ is drawn into or 0 if this is unavailable.
+
+ You must call beginPaint() before you call this function and the returned
+ pointer is only valid until endPaint() is called.
+*/
+QImage* QWindowSurface::buffer(const QWidget *widget)
+{
+ if (widget->window() != window())
+ return 0;
+
+ QPaintDevice *pdev = paintDevice();
+ if (!pdev || pdev->devType() != QInternal::Image)
+ return 0;
+
+ const QPoint off = offset(widget);
+ QImage *img = static_cast<QImage*>(pdev);
+
+ QRect rect(off, widget->size());
+ rect &= QRect(QPoint(), img->size());
+
+ if (rect.isEmpty())
+ return 0;
+
+ img = new QImage(img->scanLine(rect.y()) + rect.x() * img->depth() / 8,
+ rect.width(), rect.height(),
+ img->bytesPerLine(), img->format());
+ d_ptr->bufferImages.append(img);
+
+ return img;
+}
+
+/*!
+ Returns a QPixmap generated from the part of the backing store
+ corresponding to \a widget. Returns a null QPixmap if an error
+ occurs. The contents of the pixmap are only defined for the regions
+ of \a widget that have received paint events since the last resize
+ of the backing store.
+
+ If \a rectangle is a null rectangle (the default), the entire widget
+ is grabbed. Otherwise, the grabbed area is limited to \a rectangle.
+
+ The default implementation uses QWindowSurface::buffer().
+
+ \sa QPixmap::grabWidget()
+*/
+QPixmap QWindowSurface::grabWidget(const QWidget *widget, const QRect &rectangle) const
+{
+ QPixmap result;
+
+ if (widget->window() != window())
+ return result;
+
+ const QImage *img = const_cast<QWindowSurface *>(this)->buffer(widget->window());
+
+ if (!img || img->isNull())
+ return result;
+
+ QRect rect = rectangle.isEmpty() ? widget->rect() : (widget->rect() & rectangle);
+
+ rect.translate(offset(widget) - offset(widget->window()));
+ rect &= QRect(QPoint(), img->size());
+
+ if (rect.isEmpty())
+ return result;
+
+ QImage subimg(img->scanLine(rect.y()) + rect.x() * img->depth() / 8,
+ rect.width(), rect.height(),
+ img->bytesPerLine(), img->format());
+ subimg.detach(); //### expensive -- maybe we should have a real SubImage that shares reference count
+
+ result = QPixmap::fromImage(subimg);
+ return result;
+}
+
+/*!
+ Returns the offset of \a widget in the coordinates of this
+ window surface.
+ */
+QPoint QWindowSurface::offset(const QWidget *widget) const
+{
+ QWidget *window = d_ptr->window;
+ QPoint offset = widget->mapTo(window, QPoint());
+#ifdef Q_WS_QWS
+ offset += window->geometry().topLeft() - window->frameGeometry().topLeft();
+#endif
+ return offset;
+}
+
+/*!
+ \fn QRect QWindowSurface::rect(const QWidget *widget) const
+
+ Returns the rectangle for \a widget in the coordinates of this
+ window surface.
+*/
+
+void QWindowSurface::setStaticContents(const QRegion &region)
+{
+ d_ptr->staticContents = region;
+}
+
+QRegion QWindowSurface::staticContents() const
+{
+ return d_ptr->staticContents;
+}
+
+bool QWindowSurface::hasStaticContents() const
+{
+ return hasFeature(QWindowSurface::StaticContents) && !d_ptr->staticContents.isEmpty();
+}
+
+QWindowSurface::WindowSurfaceFeatures QWindowSurface::features() const
+{
+ return PartialUpdates | PreservedContents;
+}
+
+#ifdef Q_WS_QPA
+#define Q_EXPORT_SCROLLRECT Q_GUI_EXPORT
+#else
+#define Q_EXPORT_SCROLLRECT
+#endif
+
+void Q_EXPORT_SCROLLRECT qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &offset)
+{
+ // make sure we don't detach
+ uchar *mem = const_cast<uchar*>(const_cast<const QImage &>(img).bits());
+
+ int lineskip = img.bytesPerLine();
+ int depth = img.depth() >> 3;
+
+ const QRect imageRect(0, 0, img.width(), img.height());
+ const QRect r = rect & imageRect & imageRect.translated(-offset);
+ const QPoint p = rect.topLeft() + offset;
+
+ if (r.isEmpty())
+ return;
+
+ const uchar *src;
+ uchar *dest;
+
+ if (r.top() < p.y()) {
+ src = mem + r.bottom() * lineskip + r.left() * depth;
+ dest = mem + (p.y() + r.height() - 1) * lineskip + p.x() * depth;
+ lineskip = -lineskip;
+ } else {
+ src = mem + r.top() * lineskip + r.left() * depth;
+ dest = mem + p.y() * lineskip + p.x() * depth;
+ }
+
+ const int w = r.width();
+ int h = r.height();
+ const int bytes = w * depth;
+
+ // overlapping segments?
+ if (offset.y() == 0 && qAbs(offset.x()) < w) {
+ do {
+ ::memmove(dest, src, bytes);
+ dest += lineskip;
+ src += lineskip;
+ } while (--h);
+ } else {
+ do {
+ ::memcpy(dest, src, bytes);
+ dest += lineskip;
+ src += lineskip;
+ } while (--h);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qwindowsurface_mac.cpp b/src/gui/painting/qwindowsurface_mac.cpp
new file mode 100644
index 0000000000..620a50f3ea
--- /dev/null
+++ b/src/gui/painting/qwindowsurface_mac.cpp
@@ -0,0 +1,139 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwindowsurface_mac_p.h"
+
+#include <private/qt_mac_p.h>
+#include <private/qt_cocoa_helpers_mac_p.h>
+#include <QtGui/qwidget.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QMacWindowSurfacePrivate
+{
+ QWidget *widget;
+ QPixmap device;
+};
+
+QMacWindowSurface::QMacWindowSurface(QWidget *widget)
+ : QWindowSurface(widget), d_ptr(new QMacWindowSurfacePrivate)
+{
+ d_ptr->widget = widget;
+}
+
+QMacWindowSurface::~QMacWindowSurface()
+{
+ delete d_ptr;
+}
+
+QPaintDevice *QMacWindowSurface::paintDevice()
+{
+ return &d_ptr->device;
+}
+
+void QMacWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoint &offset)
+{
+ Q_UNUSED(offset);
+
+ // Get a context for the widget.
+#ifndef QT_MAC_USE_COCOA
+ CGContextRef context;
+ CGrafPtr port = GetWindowPort(qt_mac_window_for(widget));
+ QDBeginCGContext(port, &context);
+#else
+ extern CGContextRef qt_mac_graphicsContextFor(QWidget *);
+ CGContextRef context = qt_mac_graphicsContextFor(widget);
+#endif
+ CGContextRetain(context);
+ CGContextSaveGState(context);
+
+ // Flip context.
+ CGContextTranslateCTM(context, 0, widget->height());
+ CGContextScaleCTM(context, 1, -1);
+
+ // Clip to region.
+ const QVector<QRect> &rects = rgn.rects();
+ for (int i = 0; i < rects.size(); ++i) {
+ const QRect &rect = rects.at(i);
+ CGContextAddRect(context, CGRectMake(rect.x(), rect.y(), rect.width(), rect.height()));
+ }
+ CGContextClip(context);
+
+ // Draw the image onto the window.
+ const CGRect dest = CGRectMake(0, 0, widget->width(), widget->height());
+ const CGImageRef image = d_ptr->device.toMacCGImageRef();
+ qt_mac_drawCGImage(context, &dest, image);
+ CFRelease(image);
+
+ // Restore context.
+ CGContextRestoreGState(context);
+#ifndef QT_MAC_USE_COCOA
+ QDEndCGContext(port, &context);
+#else
+ CGContextFlush(context);
+#endif
+ CGContextRelease(context);
+}
+
+void QMacWindowSurface::setGeometry(const QRect &rect)
+{
+ QWindowSurface::setGeometry(rect);
+ const QSize size = rect.size();
+ if (d_ptr->device.size() != size)
+ d_ptr->device = QPixmap(size);
+}
+
+bool QMacWindowSurface::scroll(const QRegion &area, int dx, int dy)
+{
+ if (d_ptr->device.size().isNull())
+ return false;
+
+ QCFType<CGImageRef> image = d_ptr->device.toMacCGImageRef();
+ const QRect rect(area.boundingRect());
+ const CGRect dest = CGRectMake(rect.x(), rect.y(), rect.width(), rect.height());
+ QCFType<CGImageRef> subimage = CGImageCreateWithImageInRect(image, dest);
+ QCFType<CGContextRef> context = qt_mac_cg_context(&d_ptr->device);
+ CGContextTranslateCTM(context, dx, dy);
+ qt_mac_drawCGImage(context, &dest, subimage);
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qwindowsurface_mac_p.h b/src/gui/painting/qwindowsurface_mac_p.h
new file mode 100644
index 0000000000..fd68b1fe91
--- /dev/null
+++ b/src/gui/painting/qwindowsurface_mac_p.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSURFACE_MAC_P_H
+#define QWINDOWSURFACE_MAC_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qglobal.h>
+#include "private/qwindowsurface_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QPaintDevice;
+class QPoint;
+class QRegion;
+class QWidget;
+struct QMacWindowSurfacePrivate;
+
+class QMacWindowSurface : public QWindowSurface
+{
+public:
+ QMacWindowSurface(QWidget *widget);
+ ~QMacWindowSurface();
+
+ QPaintDevice *paintDevice();
+ void flush(QWidget *widget, const QRegion &region, const QPoint &offset);
+ void setGeometry(const QRect &rect);
+ bool scroll(const QRegion &area, int dx, int dy);
+
+private:
+ QMacWindowSurfacePrivate *d_ptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSURFACE_MAC_P_H
diff --git a/src/gui/painting/qwindowsurface_p.h b/src/gui/painting/qwindowsurface_p.h
new file mode 100644
index 0000000000..a3fea67f24
--- /dev/null
+++ b/src/gui/painting/qwindowsurface_p.h
@@ -0,0 +1,138 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSURFACE_P_H
+#define QWINDOWSURFACE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/qwidget.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPaintDevice;
+class QRegion;
+class QRect;
+class QPoint;
+class QImage;
+class QWindowSurfacePrivate;
+class QPlatformWindow;
+
+class Q_GUI_EXPORT QWindowSurface
+{
+public:
+ enum WindowSurfaceFeature {
+ PartialUpdates = 0x00000001, // Supports doing partial updates.
+ PreservedContents = 0x00000002, // Supports doing flush without first doing a repaint.
+ StaticContents = 0x00000004, // Supports having static content regions when being resized.
+ AllFeatures = 0xffffffff // For convenience
+ };
+ Q_DECLARE_FLAGS(WindowSurfaceFeatures, WindowSurfaceFeature)
+
+ QWindowSurface(QWidget *window, bool setDefaultSurface = true);
+ virtual ~QWindowSurface();
+
+ QWidget *window() const;
+
+ virtual QPaintDevice *paintDevice() = 0;
+
+ // 'widget' can be a child widget, in which case 'region' is in child widget coordinates and
+ // offset is the (child) widget's offset in relation to the window surface. On QWS, 'offset'
+ // can be larger than just the offset from the top-level widget as there may also be window
+ // decorations which are painted into the window surface.
+ virtual void flush(QWidget *widget, const QRegion &region, const QPoint &offset) = 0;
+#if !defined(Q_WS_QPA)
+ virtual void setGeometry(const QRect &rect);
+ QRect geometry() const;
+#else
+ virtual void resize(const QSize &size);
+ QSize size() const;
+ inline QRect geometry() const { return QRect(QPoint(), size()); } //### cleanup before Qt 5
+#endif
+
+ virtual bool scroll(const QRegion &area, int dx, int dy);
+
+ virtual void beginPaint(const QRegion &);
+ virtual void endPaint(const QRegion &);
+
+ virtual QImage* buffer(const QWidget *widget);
+ virtual QPixmap grabWidget(const QWidget *widget, const QRect& rectangle = QRect()) const;
+
+ virtual QPoint offset(const QWidget *widget) const;
+ inline QRect rect(const QWidget *widget) const;
+
+ bool hasFeature(WindowSurfaceFeature feature) const;
+ virtual WindowSurfaceFeatures features() const;
+
+ void setStaticContents(const QRegion &region);
+ QRegion staticContents() const;
+
+protected:
+ bool hasStaticContents() const;
+
+private:
+ QWindowSurfacePrivate *d_ptr;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QWindowSurface::WindowSurfaceFeatures)
+
+inline QRect QWindowSurface::rect(const QWidget *widget) const
+{
+ return widget->rect().translated(offset(widget));
+}
+
+inline bool QWindowSurface::hasFeature(WindowSurfaceFeature feature) const
+{
+ return (features() & feature) != 0;
+}
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSURFACE_P_H
diff --git a/src/gui/painting/qwindowsurface_qws.cpp b/src/gui/painting/qwindowsurface_qws.cpp
new file mode 100644
index 0000000000..cb293cb135
--- /dev/null
+++ b/src/gui/painting/qwindowsurface_qws.cpp
@@ -0,0 +1,1433 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwindowsurface_qws_p.h"
+#include <qwidget.h>
+#include <qscreen_qws.h>
+#include <qwsmanager_qws.h>
+#include <qapplication.h>
+#include <qwsdisplay_qws.h>
+#include <qrgb.h>
+#include <qpaintengine.h>
+#include <qdesktopwidget.h>
+#include <private/qapplication_p.h>
+#include <private/qwsdisplay_qws_p.h>
+#include <private/qwidget_p.h>
+#include <private/qwsmanager_p.h>
+#include <private/qwslock_p.h>
+#include <private/qbackingstore_p.h>
+#include <stdio.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+
+typedef QMap<int, QWSWindowSurface*> SurfaceMap;
+Q_GLOBAL_STATIC(SurfaceMap, winIdToSurfaceMap);
+
+QWSWindowSurface* qt_findWindowSurface(int winId)
+{
+ return winIdToSurfaceMap()->value(winId);
+}
+
+static void qt_insertWindowSurface(int winId, QWSWindowSurface *surface)
+{
+ if (!surface)
+ winIdToSurfaceMap()->remove(winId);
+ else
+ winIdToSurfaceMap()->insert(winId, surface);
+}
+
+#endif // Q_BACKINGSTORE_SUBSURFACES
+
+inline bool isWidgetOpaque(const QWidget *w)
+{
+ return w->d_func()->isOpaque && !w->testAttribute(Qt::WA_TranslucentBackground);
+}
+
+static inline QScreen *getScreen(const QWidget *w)
+{
+ const QList<QScreen*> subScreens = qt_screen->subScreens();
+ if (subScreens.isEmpty())
+ return qt_screen;
+
+ const int screen = QApplication::desktop()->screenNumber(w);
+
+ return qt_screen->subScreens().at(screen < 0 ? 0 : screen);
+}
+
+static int bytesPerPixel(QImage::Format format)
+{
+ switch (format) {
+ case QImage::Format_Invalid:
+ return 0;
+#ifndef QT_NO_DEBUG
+ case QImage::Format_Mono:
+ case QImage::Format_MonoLSB:
+ qFatal("QWSWindowSurface: Invalid backingstore format: %i",
+ int(format));
+#endif
+ case QImage::Format_Indexed8:
+ return 1;
+ case QImage::Format_RGB32:
+ case QImage::Format_ARGB32:
+ case QImage::Format_ARGB32_Premultiplied:
+ return 4;
+ case QImage::Format_RGB16:
+ case QImage::Format_RGB555:
+ case QImage::Format_RGB444:
+ case QImage::Format_ARGB4444_Premultiplied:
+ return 2;
+ case QImage::Format_ARGB8565_Premultiplied:
+ case QImage::Format_ARGB8555_Premultiplied:
+ case QImage::Format_ARGB6666_Premultiplied:
+ case QImage::Format_RGB666:
+ case QImage::Format_RGB888:
+ return 3;
+ default:
+#ifndef QT_NO_DEBUG
+ qFatal("QWSWindowSurface: Invalid backingstore format: %i",
+ int(format));
+#endif
+ return 0;
+ }
+}
+
+static inline int nextMulOf4(int n)
+{
+ return ((n + 3) & 0xfffffffc);
+}
+
+static inline void setImageMetrics(QImage &img, QWidget *window) {
+ QScreen *myScreen = getScreen(window);
+ if (myScreen) {
+ int dpmx = myScreen->width()*1000 / myScreen->physicalWidth();
+ int dpmy = myScreen->height()*1000 / myScreen->physicalHeight();
+ img.setDotsPerMeterX(dpmx);
+ img.setDotsPerMeterY(dpmy);
+ }
+}
+
+void QWSWindowSurface::invalidateBuffer()
+{
+
+ QWidget *win = window();
+ if (win) {
+ win->d_func()->invalidateBuffer(win->rect());
+#ifndef QT_NO_QWS_MANAGER
+ QTLWExtra *topextra = win->d_func()->extra->topextra;
+ QWSManager *manager = topextra->qwsManager;
+ if (manager)
+ manager->d_func()->dirtyRegion(QDecoration::All,
+ QDecoration::Normal);
+#endif
+ }
+}
+
+QWSWindowSurfacePrivate::QWSWindowSurfacePrivate()
+ : flags(0),
+#ifdef QT_QWS_CLIENTBLIT
+ directId(-1),
+#endif
+ winId(0)
+{
+}
+
+void QWSWindowSurfacePrivate::setWinId(int id)
+{
+ winId = id;
+}
+
+int QWSWindowSurface::winId() const
+{
+ // XXX: the widget winId may change during the lifetime of the widget!!!
+
+ const QWidget *win = window();
+ if (win && win->isWindow())
+ return win->internalWinId();
+
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+ if (!d_ptr->winId) {
+ QWSWindowSurface *that = const_cast<QWSWindowSurface*>(this);
+ QWSDisplay *display = QWSDisplay::instance();
+ const int id = display->takeId();
+ qt_insertWindowSurface(id, that);
+ that->d_ptr->winId = id;
+
+ if (win)
+ display->nameRegion(id, win->objectName(), win->windowTitle());
+ else
+ display->nameRegion(id, QString(), QString());
+
+ display->setAltitude(id, 1, true); // XXX
+ }
+#endif
+
+ return d_ptr->winId;
+}
+
+void QWSWindowSurface::setWinId(int id)
+{
+ d_ptr->winId = id;
+}
+
+/*!
+ \class QWSWindowSurface
+ \since 4.2
+ \ingroup qws
+ \preliminary
+ \internal
+
+ \brief The QWSWindowSurface class provides the drawing area for top-level
+ windows in Qt for Embedded Linux.
+
+ Note that this class is only available in Qt for Embedded Linux.
+
+ In \l{Qt for Embedded Linux}, the default behavior is for each client to
+ render its widgets into memory while the server is responsible for
+ putting the contents of the memory onto the
+ screen. QWSWindowSurface is used by the window system to implement
+ the associated memory allocation.
+
+ When a screen update is required, the server runs through all the
+ top-level windows that intersect with the region that is about to
+ be updated, and ensures that the associated clients have updated
+ their memory buffer. Then the server uses the screen driver to
+ copy the content of the memory to the screen. To locate the
+ relevant parts of memory, the driver is provided with the list of
+ top-level windows that intersect with the given region. Associated
+ with each of the top-level windows there is a window surface
+ representing the drawing area of the window.
+
+ When deriving from the QWSWindowSurface class, e.g., when adding
+ an \l {Adding an Accelerated Graphics Driver to Qt for Embedded Linux}
+ {accelerated graphics driver}, there are several pure virtual
+ functions that must be implemented. In addition, QWSWindowSurface
+ provides several virtual functions that can be reimplemented to
+ customize the drawing process.
+
+ \tableofcontents
+
+ \section1 Pure Virtual Functions
+
+ There are in fact two window surface instances for each top-level
+ window; one used by the application when drawing a window, and
+ another used by the server application to perform window
+ compositioning. Implement the attach() to create the server-side
+ representation of the surface. The data() function must be
+ implemented to provide the required data.
+
+ Implement the key() function to uniquely identify the surface
+ class, and the isValid() function to determine is a surface
+ corresponds to a given widget.
+
+ The geometry() function must be implemented to let the window
+ system determine the area required by the window surface
+ (QWSWindowSurface also provides a corresponding virtual
+ setGeometry() function that is called whenever the area necessary
+ for the top-level window to be drawn, changes). The image()
+ function is called by the window system during window
+ compositioning, and must be implemented to return an image of the
+ top-level window.
+
+ Finally, the paintDevice() function must be implemented to return
+ the appropriate paint device, and the scroll() function must be
+ implemented to scroll the given region of the surface the given
+ number of pixels.
+
+ \section1 Virtual Functions
+
+ When painting onto the surface, the window system will always call
+ the beginPaint() function before any painting operations are
+ performed. Likewise the endPaint() function is automatically
+ called when the painting is done. Reimplement the painterOffset()
+ function to alter the offset that is applied when drawing.
+
+ The window system uses the flush() function to put a given region
+ of the widget onto the screen, and the release() function to
+ deallocate the screen region corresponding to this window surface.
+
+ \section1 Other Members
+
+ QWSWindowSurface provides the window() function returning a
+ pointer to the top-level window the surface is representing. The
+ currently visible region of the associated widget can be retrieved
+ and set using the clipRegion() and setClipRegion() functions,
+ respectively.
+
+ When the window system performs the window compositioning, it uses
+ the SurfaceFlag enum describing the surface content. The currently
+ set surface flags can be retrieved and altered using the
+ surfaceFlags() and setSurfaceFlags() functions. In addition,
+ QWSWindowSurface provides the isBuffered(), isOpaque() and
+ isRegionReserved() convenience functions.
+
+ \sa {Qt for Embedded Linux Architecture#Drawing on Screen}{Qt for
+ Embedded Linux Architecture}
+*/
+
+/*!
+ \enum QWSWindowSurface::SurfaceFlag
+
+ This enum is used to describe the window surface's contents. It
+ is used by the screen driver to handle region allocation and
+ composition.
+
+ \value RegionReserved The surface contains a reserved area. Once
+ allocated, a reserved area can not not be changed by the window
+ system, i.e., no other widgets can be drawn on top of this.
+
+ \value Buffered
+ The surface is in a memory area which is not part of a framebuffer.
+ (A top-level window with QWidget::windowOpacity() other than 1.0 must use
+ a buffered surface in order to making blending with the background work.)
+
+ \value Opaque
+ The surface contains only opaque pixels.
+
+ \sa surfaceFlags(), setSurfaceFlags()
+*/
+
+/*!
+ \fn bool QWSWindowSurface::isValid() const
+ \since 4.3
+
+ Implement this function to return true if the surface is a valid
+ surface for the given top-level \a window; otherwise return
+ false.
+
+ \sa window(), key()
+*/
+
+/*!
+ \fn QString QWSWindowSurface::key() const
+
+ Implement this function to return a string that uniquely
+ identifies the class of this surface.
+
+ \sa window(), isValid()
+*/
+
+/*!
+ \fn QByteArray QWSWindowSurface::permanentState() const
+ \since 4.3
+
+ Implement this function to return the data required for creating a
+ server-side representation of the surface.
+
+ \sa attach()
+*/
+
+/*!
+ \fn void QWSWindowSurface::setPermanentState(const QByteArray &data)
+ \since 4.3
+
+ Implement this function to attach a server-side surface instance
+ to the corresponding client side instance using the given \a
+ data. Return true if successful; otherwise return false.
+
+ \sa data()
+*/
+
+/*!
+ \fn const QImage QWSWindowSurface::image() const
+
+ Implement this function to return an image of the top-level window.
+
+ \sa geometry()
+*/
+
+/*!
+ \fn bool QWSWindowSurface::isRegionReserved() const
+
+ Returns true if the QWSWindowSurface::RegionReserved is set; otherwise
+ returns false.
+
+ \sa surfaceFlags()
+*/
+
+/*!
+ \fn bool QWSWindowSurface::isBuffered() const
+
+ Returns true if the QWSWindowSurface::Buffered is set; otherwise returns false.
+
+ \sa surfaceFlags()
+*/
+
+/*!
+ \fn bool QWSWindowSurface::isOpaque() const
+
+ Returns true if the QWSWindowSurface::Opaque is set; otherwise
+ returns false.
+
+ \sa surfaceFlags()
+*/
+
+
+/*!
+ Constructs an empty surface.
+*/
+QWSWindowSurface::QWSWindowSurface()
+ : QWindowSurface(0), d_ptr(new QWSWindowSurfacePrivate)
+{
+}
+
+/*!
+ Constructs an empty surface for the given top-level \a widget.
+*/
+QWSWindowSurface::QWSWindowSurface(QWidget *widget)
+ : QWindowSurface(widget), d_ptr(new QWSWindowSurfacePrivate)
+{
+}
+
+QWSWindowSurface::~QWSWindowSurface()
+{
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+ if (d_ptr->winId)
+ winIdToSurfaceMap()->remove(d_ptr->winId);
+#endif
+
+ delete d_ptr;
+}
+
+/*!
+ Returns the offset to be used when painting.
+
+ \sa paintDevice()
+*/
+QPoint QWSWindowSurface::painterOffset() const
+{
+ const QWidget *w = window();
+ if (!w)
+ return QPoint();
+ return w->geometry().topLeft() - w->frameGeometry().topLeft();
+}
+
+void QWSWindowSurface::beginPaint(const QRegion &)
+{
+ lock();
+}
+
+void QWSWindowSurface::endPaint(const QRegion &)
+{
+ unlock();
+}
+
+// XXX: documentation!!!
+QByteArray QWSWindowSurface::transientState() const
+{
+ return QByteArray();
+}
+
+QByteArray QWSWindowSurface::permanentState() const
+{
+ return QByteArray();
+}
+
+void QWSWindowSurface::setTransientState(const QByteArray &state)
+{
+ Q_UNUSED(state);
+}
+
+void QWSWindowSurface::setPermanentState(const QByteArray &state)
+{
+ Q_UNUSED(state);
+}
+
+bool QWSWindowSurface::lock(int timeout)
+{
+ Q_UNUSED(timeout);
+ return true;
+}
+
+void QWSWindowSurface::unlock()
+{
+}
+
+#ifdef QT_QWS_CLIENTBLIT
+/*! \internal */
+const QRegion QWSWindowSurface::directRegion() const
+{
+ return d_ptr->direct;
+}
+
+/*! \internal */
+int QWSWindowSurface::directRegionId() const
+{
+ return d_ptr->directId;
+}
+
+/*! \internal */
+void QWSWindowSurface::setDirectRegion(const QRegion &r, int id)
+{
+ d_ptr->direct = r;
+ d_ptr->directId = id;
+}
+#endif
+
+/*!
+ Returns the region currently visible on the screen.
+
+ \sa setClipRegion()
+*/
+const QRegion QWSWindowSurface::clipRegion() const
+{
+ return d_ptr->clip;
+}
+
+/*!
+ Sets the region currently visible on the screen to be the given \a
+ clip region.
+
+ \sa clipRegion()
+*/
+void QWSWindowSurface::setClipRegion(const QRegion &clip)
+{
+ if (clip == d_ptr->clip)
+ return;
+
+ QRegion expose = (clip - d_ptr->clip);
+ d_ptr->clip = clip;
+
+ if (expose.isEmpty() || clip.isEmpty())
+ return; // No repaint or flush required.
+
+ QWidget *win = window();
+ if (!win)
+ return;
+
+ if (isBuffered()) {
+ // No repaint required. Flush exposed area via the backing store.
+ win->d_func()->syncBackingStore(expose);
+ return;
+ }
+
+#ifndef QT_NO_QWS_MANAGER
+ // Invalidate exposed decoration area.
+ if (win && win->isWindow()) {
+ QTLWExtra *topextra = win->d_func()->extra->topextra;
+ if (QWSManager *manager = topextra->qwsManager) {
+ QRegion decorationExpose(manager->region());
+ decorationExpose.translate(-win->geometry().topLeft());
+ decorationExpose &= expose;
+ if (!decorationExpose.isEmpty()) {
+ expose -= decorationExpose;
+ manager->d_func()->dirtyRegion(QDecoration::All, QDecoration::Normal, decorationExpose);
+ }
+ }
+ }
+#endif
+
+ // Invalidate exposed widget area.
+ win->d_func()->invalidateBuffer(expose);
+}
+
+/*!
+ Returns the surface flags describing the contents of this surface.
+
+ \sa isBuffered(), isOpaque(), isRegionReserved()
+*/
+QWSWindowSurface::SurfaceFlags QWSWindowSurface::surfaceFlags() const
+{
+ return d_ptr->flags;
+}
+
+/*!
+ Sets the surface flags describing the contents of this surface, to
+ be the given \a flags.
+
+ \sa surfaceFlags()
+*/
+void QWSWindowSurface::setSurfaceFlags(SurfaceFlags flags)
+{
+ d_ptr->flags = flags;
+}
+
+void QWSWindowSurface::setGeometry(const QRect &rect)
+{
+ QRegion mask = rect;
+
+ const QWidget *win = window();
+ if (win) {
+#ifndef QT_NO_QWS_MANAGER
+ if (win->isWindow()) {
+ QTLWExtra *topextra = win->d_func()->extra->topextra;
+ QWSManager *manager = topextra->qwsManager;
+
+ if (manager) {
+ // The frame geometry is the bounding rect of manager->region,
+ // which could be too much, so we need to clip.
+ mask &= (manager->region() + win->geometry());
+ }
+ }
+#endif
+
+ const QRegion winMask = win->mask();
+ if (!winMask.isEmpty())
+ mask &= winMask.translated(win->geometry().topLeft());
+ }
+
+ setGeometry(rect, mask);
+}
+
+void QWSWindowSurface::setGeometry(const QRect &rect, const QRegion &mask)
+{
+ if (rect == geometry()) // XXX: && mask == prevMask
+ return;
+
+ const bool isResize = rect.size() != geometry().size();
+ const bool needsRepaint = isResize || !isBuffered();
+
+ QWindowSurface::setGeometry(rect);
+
+ const QRegion region = mask & rect;
+ QWidget *win = window();
+ // Only request regions for widgets visible on the screen.
+ // (Added the !win check for compatibility reasons, because
+ // there was no "if (win)" check before).
+ const bool requestQWSRegion = !win || !win->testAttribute(Qt::WA_DontShowOnScreen);
+ if (requestQWSRegion)
+ QWidget::qwsDisplay()->requestRegion(winId(), key(), permanentState(), region);
+
+ if (needsRepaint)
+ invalidateBuffer();
+
+ if (!requestQWSRegion) {
+ // We didn't request a region, hence we won't get a QWSRegionEvent::Allocation
+ // event back from the server so we set the clip directly. We have to
+ // do this after the invalidateBuffer() call above, as it might trigger a
+ // backing store sync, resulting in too many update requests.
+ setClipRegion(region);
+ }
+}
+
+static inline void flushUpdate(QWidget *widget, const QRegion &region,
+ const QPoint &offset)
+{
+#ifdef QT_NO_PAINT_DEBUG
+ Q_UNUSED(widget);
+ Q_UNUSED(region);
+ Q_UNUSED(offset);
+#else
+ static int delay = -1;
+
+ if (delay == -1)
+ delay = qgetenv("QT_FLUSH_UPDATE").toInt() * 10;
+
+ if (delay == 0)
+ return;
+
+ static QWSYellowSurface surface(true);
+ surface.setDelay(delay);
+ surface.flush(widget, region, offset);
+#endif // QT_NO_PAINT_DEBUG
+}
+
+void QWSWindowSurface::flush(QWidget *widget, const QRegion &region,
+ const QPoint &offset)
+{
+ const QWidget *win = window();
+ if (!win)
+ return;
+
+#ifndef QT_NO_GRAPHICSVIEW
+ QWExtra *extra = win->d_func()->extra;
+ if (extra && extra->proxyWidget)
+ return;
+#endif //QT_NO_GRAPHICSVIEW
+
+ Q_UNUSED(offset);
+
+ const bool opaque = isOpaque();
+#ifdef QT_QWS_DISABLE_FLUSHCLIPPING
+ QRegion toFlush = region;
+#else
+ QRegion toFlush = region & d_ptr->clip;
+#endif
+
+ if (!toFlush.isEmpty()) {
+ flushUpdate(widget, toFlush, QPoint(0, 0));
+ QPoint globalZero = win->mapToGlobal(QPoint(0, 0));
+ toFlush.translate(globalZero);
+
+#ifdef QT_QWS_CLIENTBLIT
+ bool needRepaint = true;
+ if (opaque) {
+ QScreen* widgetScreen = getScreen(widget);
+ if (widgetScreen->supportsBlitInClients()) {
+
+ QWSDisplay::grab();
+ if(directRegion().intersected(toFlush) == toFlush) {
+ QPoint translate = -globalZero + painterOffset() + geometry().topLeft();
+ QRegion flushRegion = toFlush.translated(translate);
+ widgetScreen->blit(image(), geometry().topLeft(), flushRegion);
+ widgetScreen->setDirty(toFlush.boundingRect());
+ needRepaint = false;
+ }
+ QWSDisplay::ungrab();
+ }
+ }
+
+ if(needRepaint)
+#endif
+ win->qwsDisplay()->repaintRegion(winId(), win->windowFlags(), opaque, toFlush);
+ }
+}
+
+/*!
+ Move the surface with the given \a offset.
+
+ A subclass may reimplement this function to enable accelerated window move.
+ It must return true if the move was successful and no repaint is necessary,
+ false otherwise.
+
+ The default implementation updates the QWindowSurface geometry and
+ returns true if the surface is buffered; false otherwise.
+
+ This function is called by the window system on the client instance.
+
+ \sa isBuffered()
+*/
+bool QWSWindowSurface::move(const QPoint &offset)
+{
+ QWindowSurface::setGeometry(geometry().translated(offset));
+ return isBuffered();
+}
+
+/*!
+ Move the surface with the given \a offset.
+
+ The new visible region after the window move is given by \a newClip
+ in screen coordinates.
+
+ A subclass may reimplement this function to enable accelerated window move.
+ The returned region indicates the area that still needs to be composed
+ on the screen.
+
+ The default implementation updates the QWindowSurface geometry and
+ returns the union of the old and new geometry.
+
+ This function is called by the window system on the server instance.
+*/
+QRegion QWSWindowSurface::move(const QPoint &offset, const QRegion &newClip)
+{
+ const QRegion oldGeometry = geometry();
+ QWindowSurface::setGeometry(geometry().translated(offset));
+ return oldGeometry + newClip;
+}
+
+void QWSWindowSurface::releaseSurface()
+{
+}
+
+bool QWSMemorySurface::lock(int timeout)
+{
+ Q_UNUSED(timeout);
+#ifndef QT_NO_QWS_MULTIPROCESS
+ if (memlock && !memlock->lock(QWSLock::BackingStore))
+ return false;
+#endif
+#ifndef QT_NO_THREAD
+ threadLock.lock();
+#endif
+ return true;
+}
+
+void QWSMemorySurface::unlock()
+{
+#ifndef QT_NO_THREAD
+ threadLock.unlock();
+#endif
+#ifndef QT_NO_QWS_MULTIPROCESS
+ if (memlock)
+ memlock->unlock(QWSLock::BackingStore);
+#endif
+}
+
+QWSMemorySurface::QWSMemorySurface()
+ : QWSWindowSurface()
+#ifndef QT_NO_QWS_MULTIPROCESS
+ , memlock(0)
+#endif
+{
+ setSurfaceFlags(Buffered);
+}
+
+QWSMemorySurface::QWSMemorySurface(QWidget *w)
+ : QWSWindowSurface(w)
+{
+ SurfaceFlags flags = Buffered;
+ if (isWidgetOpaque(w))
+ flags |= Opaque;
+ setSurfaceFlags(flags);
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+ memlock = QWSDisplay::Data::getClientLock();
+#endif
+}
+
+QWSMemorySurface::~QWSMemorySurface()
+{
+}
+
+
+QImage::Format
+QWSMemorySurface::preferredImageFormat(const QWidget *widget) const
+{
+ QScreen *screen = getScreen(widget);
+ const int depth = screen->depth();
+ const bool opaque = isWidgetOpaque(widget);
+
+ if (!opaque) {
+ if (depth <= 12)
+ return QImage::Format_ARGB4444_Premultiplied;
+ else if (depth <= 15)
+ return QImage::Format_ARGB8555_Premultiplied;
+ else if (depth <= 16)
+ return QImage::Format_ARGB8565_Premultiplied;
+ else if (depth <= 18)
+ return QImage::Format_ARGB6666_Premultiplied;
+ else
+ return QImage::Format_ARGB32_Premultiplied;
+ }
+
+ QImage::Format format = screen->pixelFormat();
+ if (format > QImage::Format_Indexed8) // ### assumes all new image formats supports a QPainter
+ return format;
+
+ if (depth <= 12)
+ return QImage::Format_RGB444;
+ else if (depth <= 15)
+ return QImage::Format_RGB555;
+ else if (depth <= 16)
+ return QImage::Format_RGB16;
+ else if (depth <= 18)
+ return QImage::Format_RGB666;
+ else if (depth <= 24)
+ return QImage::Format_RGB888;
+ else
+ return QImage::Format_ARGB32_Premultiplied;
+}
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+void QWSMemorySurface::setLock(int lockId)
+{
+ if (memlock && memlock->id() == lockId)
+ return;
+ delete memlock;
+ memlock = (lockId == -1 ? 0 : new QWSLock(lockId));
+ return;
+}
+#endif // QT_NO_QWS_MULTIPROCESS
+
+bool QWSMemorySurface::isValid() const
+{
+ if (img.isNull())
+ return true;
+
+ const QWidget *win = window();
+ if (preferredImageFormat(win) != img.format())
+ return false;
+
+ if (isOpaque() != isWidgetOpaque(win)) // XXX: use QWidgetPrivate::isOpaque()
+ return false;
+
+ return true;
+}
+
+// ### copied from qwindowsurface_raster.cpp -- should be cross-platform
+void QWSMemorySurface::beginPaint(const QRegion &rgn)
+{
+ if (!isWidgetOpaque(window())) {
+ QPainter p(&img);
+ p.setCompositionMode(QPainter::CompositionMode_Source);
+ const QVector<QRect> rects = rgn.rects();
+ const QColor blank = Qt::transparent;
+ for (QVector<QRect>::const_iterator it = rects.begin(); it != rects.end(); ++it) {
+ QRect r = *it;
+#ifdef Q_BACKINGSTORE_SUBSURFACES
+ r.translate(painterOffset());
+#endif
+ p.fillRect(r, blank);
+ }
+ }
+ QWSWindowSurface::beginPaint(rgn);
+}
+
+// from qwindowsurface.cpp
+extern void qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &offset);
+
+bool QWSMemorySurface::scroll(const QRegion &area, int dx, int dy)
+{
+ const QVector<QRect> rects = area.rects();
+ for (int i = 0; i < rects.size(); ++i)
+ qt_scrollRectInImage(img, rects.at(i), QPoint(dx, dy));
+
+ return true;
+}
+
+QPoint QWSMemorySurface::painterOffset() const
+{
+ const QWidget *w = window();
+ if (!w)
+ return QPoint();
+
+ if (w->mask().isEmpty())
+ return QWSWindowSurface::painterOffset();
+
+ const QRegion region = w->mask()
+ & w->frameGeometry().translated(-w->geometry().topLeft());
+ return -region.boundingRect().topLeft();
+}
+
+QWSLocalMemSurface::QWSLocalMemSurface()
+ : QWSMemorySurface(), mem(0), memsize(0)
+{
+}
+
+QWSLocalMemSurface::QWSLocalMemSurface(QWidget *w)
+ : QWSMemorySurface(w), mem(0), memsize(0)
+{
+}
+
+QWSLocalMemSurface::~QWSLocalMemSurface()
+{
+ if (memsize)
+ delete[] mem;
+}
+
+void QWSLocalMemSurface::setGeometry(const QRect &rect)
+{
+ QSize size = rect.size();
+
+ QWidget *win = window();
+ if (win && !win->mask().isEmpty()) {
+ const QRegion region = win->mask()
+ & rect.translated(-win->geometry().topLeft());
+ size = region.boundingRect().size();
+ }
+
+ uchar *deleteLater = 0;
+ // In case of a Hide event we need to delete the memory after sending the
+ // event to the server in order to let the server animate the event.
+ if (size.isEmpty()) {
+ deleteLater = mem;
+ mem = 0;
+ }
+
+ if (img.size() != size) {
+ delete[] mem;
+ if (size.isEmpty()) {
+ mem = 0;
+ img = QImage();
+ } else {
+ const QImage::Format format = preferredImageFormat(win);
+ const int bpl = nextMulOf4(bytesPerPixel(format) * size.width());
+ const int memsize = bpl * size.height();
+ mem = new uchar[memsize];
+ img = QImage(mem, size.width(), size.height(), bpl, format);
+ setImageMetrics(img, win);
+ }
+ }
+
+ QWSWindowSurface::setGeometry(rect);
+ delete[] deleteLater;
+}
+
+QByteArray QWSLocalMemSurface::permanentState() const
+{
+ QByteArray array;
+ array.resize(sizeof(uchar*) + 3 * sizeof(int) +
+ sizeof(SurfaceFlags));
+
+ char *ptr = array.data();
+
+ *reinterpret_cast<uchar**>(ptr) = mem;
+ ptr += sizeof(uchar*);
+
+ reinterpret_cast<int*>(ptr)[0] = img.width();
+ reinterpret_cast<int*>(ptr)[1] = img.height();
+ ptr += 2 * sizeof(int);
+
+ *reinterpret_cast<int *>(ptr) = img.format();
+ ptr += sizeof(int);
+
+ *reinterpret_cast<SurfaceFlags*>(ptr) = surfaceFlags();
+
+ return array;
+}
+
+void QWSLocalMemSurface::setPermanentState(const QByteArray &data)
+{
+ int width;
+ int height;
+ QImage::Format format;
+ SurfaceFlags flags;
+
+ const char *ptr = data.constData();
+
+ mem = *reinterpret_cast<uchar* const*>(ptr);
+ ptr += sizeof(uchar*);
+
+ width = reinterpret_cast<const int*>(ptr)[0];
+ height = reinterpret_cast<const int*>(ptr)[1];
+ ptr += 2 * sizeof(int);
+
+ format = QImage::Format(*reinterpret_cast<const int*>(ptr));
+ ptr += sizeof(int);
+
+ flags = *reinterpret_cast<const SurfaceFlags*>(ptr);
+
+ const int bpl = nextMulOf4(bytesPerPixel(format) * width);
+ QWSMemorySurface::img = QImage(mem, width, height, bpl, format);
+ setSurfaceFlags(flags);
+}
+
+void QWSLocalMemSurface::releaseSurface()
+{
+ mem = 0;
+ img = QImage();
+}
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+
+QWSSharedMemSurface::QWSSharedMemSurface()
+ : QWSMemorySurface()
+{
+}
+
+QWSSharedMemSurface::QWSSharedMemSurface(QWidget *widget)
+ : QWSMemorySurface(widget)
+{
+}
+
+QWSSharedMemSurface::~QWSSharedMemSurface()
+{
+ // mem.detach() is done automatically by ~QSharedMemory
+}
+
+bool QWSSharedMemSurface::setMemory(int memId)
+{
+ if (mem.id() == memId)
+ return true;
+
+ mem.detach();
+ if (!mem.attach(memId)) {
+ perror("QWSSharedMemSurface: attaching to shared memory");
+ qCritical("QWSSharedMemSurface: Error attaching to"
+ " shared memory 0x%x", memId);
+ return false;
+ }
+
+ return true;
+}
+
+#ifdef QT_QWS_CLIENTBLIT
+void QWSSharedMemSurface::setDirectRegion(const QRegion &r, int id)
+{
+ QWSMemorySurface::setDirectRegion(r, id);
+ if(mem.address())
+ *(uint *)mem.address() = id;
+}
+
+const QRegion QWSSharedMemSurface::directRegion() const
+{
+ QWSSharedMemory *cmem = const_cast<QWSSharedMemory *>(&mem);
+ if (cmem->address() && ((int*)cmem->address())[0] == directRegionId())
+ return QWSMemorySurface::directRegion();
+ else
+ return QRegion();
+}
+#endif
+
+void QWSSharedMemSurface::setPermanentState(const QByteArray &data)
+{
+ int memId;
+ int width;
+ int height;
+ int lockId;
+ QImage::Format format;
+ SurfaceFlags flags;
+
+ const int *ptr = reinterpret_cast<const int*>(data.constData());
+
+ memId = ptr[0];
+ width = ptr[1];
+ height = ptr[2];
+ lockId = ptr[3];
+ format = QImage::Format(ptr[4]);
+ flags = SurfaceFlags(ptr[5]);
+
+ setSurfaceFlags(flags);
+ setMemory(memId);
+ setLock(lockId);
+
+#ifdef QT_QWS_CLIENTBLIT
+ uchar *base = static_cast<uchar*>(mem.address()) + sizeof(uint);
+#else
+ uchar *base = static_cast<uchar*>(mem.address());
+#endif
+ const int bpl = nextMulOf4(bytesPerPixel(format) * width);
+ QWSMemorySurface::img = QImage(base, width, height, bpl, format);
+}
+
+void QWSSharedMemSurface::setGeometry(const QRect &rect)
+{
+ const QSize size = rect.size();
+ if (img.size() != size) {
+ if (size.isEmpty()) {
+ mem.detach();
+ img = QImage();
+ } else {
+ mem.detach();
+
+ QWidget *win = window();
+ const QImage::Format format = preferredImageFormat(win);
+ const int bpl = nextMulOf4(bytesPerPixel(format) * size.width());
+#ifdef QT_QWS_CLIENTBLIT
+ const int imagesize = bpl * size.height() + sizeof(uint);
+#else
+ const int imagesize = bpl * size.height();
+#endif
+ if (!mem.create(imagesize)) {
+ perror("QWSSharedMemSurface::setGeometry allocating shared memory");
+ qFatal("Error creating shared memory of size %d", imagesize);
+ }
+#ifdef QT_QWS_CLIENTBLIT
+ *((uint *)mem.address()) = 0;
+ uchar *base = static_cast<uchar*>(mem.address()) + sizeof(uint);
+#else
+ uchar *base = static_cast<uchar*>(mem.address());
+#endif
+ img = QImage(base, size.width(), size.height(), bpl, format);
+ setImageMetrics(img, win);
+ }
+ }
+
+ QWSWindowSurface::setGeometry(rect);
+}
+
+QByteArray QWSSharedMemSurface::permanentState() const
+{
+ QByteArray array;
+ array.resize(6 * sizeof(int));
+
+ int *ptr = reinterpret_cast<int*>(array.data());
+
+ ptr[0] = mem.id();
+ ptr[1] = img.width();
+ ptr[2] = img.height();
+ ptr[3] = (memlock ? memlock->id() : -1);
+ ptr[4] = int(img.format());
+ ptr[5] = int(surfaceFlags());
+
+ return array;
+}
+
+void QWSSharedMemSurface::releaseSurface()
+{
+ mem.detach();
+ img = QImage();
+}
+
+#endif // QT_NO_QWS_MULTIPROCESS
+
+#ifndef QT_NO_PAINTONSCREEN
+
+QWSOnScreenSurface::QWSOnScreenSurface(QWidget *w)
+ : QWSMemorySurface(w)
+{
+ attachToScreen(getScreen(w));
+ setSurfaceFlags(Opaque);
+}
+
+QWSOnScreenSurface::QWSOnScreenSurface()
+ : QWSMemorySurface()
+{
+ setSurfaceFlags(Opaque);
+}
+
+void QWSOnScreenSurface::attachToScreen(const QScreen *s)
+{
+ screen = s;
+ uchar *base = screen->base();
+ QImage::Format format = screen->pixelFormat();
+
+ if (format == QImage::Format_Invalid || format == QImage::Format_Indexed8) {
+ //### currently we have no paint engine for indexed image formats
+ qFatal("QWSOnScreenSurface::attachToScreen(): screen depth %d "
+ "not implemented", screen->depth());
+ return;
+ }
+ QWSMemorySurface::img = QImage(base, screen->width(), screen->height(),
+ screen->linestep(), format );
+}
+
+QWSOnScreenSurface::~QWSOnScreenSurface()
+{
+}
+
+QPoint QWSOnScreenSurface::painterOffset() const
+{
+ return geometry().topLeft() + QWSWindowSurface::painterOffset();
+}
+
+bool QWSOnScreenSurface::isValid() const
+{
+ const QWidget *win = window();
+ if (screen != getScreen(win))
+ return false;
+ if (img.isNull())
+ return false;
+ return QScreen::isWidgetPaintOnScreen(win);
+}
+
+QByteArray QWSOnScreenSurface::permanentState() const
+{
+ QByteArray array;
+ array.resize(sizeof(int));
+ int *ptr = reinterpret_cast<int*>(array.data());
+ ptr[0] = QApplication::desktop()->screenNumber(window());
+ return array;
+}
+
+void QWSOnScreenSurface::setPermanentState(const QByteArray &data)
+{
+ const int *ptr = reinterpret_cast<const int*>(data.constData());
+ const int screenNo = ptr[0];
+
+ QScreen *screen = qt_screen;
+ if (screenNo > 0)
+ screen = qt_screen->subScreens().at(screenNo);
+ attachToScreen(screen);
+}
+
+#endif // QT_NO_PAINTONSCREEN
+
+#ifndef QT_NO_PAINT_DEBUG
+
+QWSYellowSurface::QWSYellowSurface(bool isClient)
+ : QWSWindowSurface(), delay(10)
+{
+ if (isClient) {
+ setWinId(QWidget::qwsDisplay()->takeId());
+ QWidget::qwsDisplay()->nameRegion(winId(),
+ QLatin1String("Debug flush paint"),
+ QLatin1String("Silly yellow thing"));
+ QWidget::qwsDisplay()->setAltitude(winId(), 1, true);
+ }
+ setSurfaceFlags(Buffered);
+}
+
+QWSYellowSurface::~QWSYellowSurface()
+{
+}
+
+QByteArray QWSYellowSurface::permanentState() const
+{
+ QByteArray array;
+ array.resize(2 * sizeof(int));
+
+ int *ptr = reinterpret_cast<int*>(array.data());
+ ptr[0] = surfaceSize.width();
+ ptr[1] = surfaceSize.height();
+
+ return array;
+}
+
+void QWSYellowSurface::setPermanentState(const QByteArray &data)
+{
+ const int *ptr = reinterpret_cast<const int*>(data.constData());
+
+ const int width = ptr[0];
+ const int height = ptr[1];
+
+ img = QImage(width, height, QImage::Format_ARGB32);
+ img.fill(qRgba(255,255,31,127));
+}
+
+void QWSYellowSurface::flush(QWidget *widget, const QRegion &region,
+ const QPoint &offset)
+{
+ Q_UNUSED(offset);
+
+ QWSDisplay *display = QWidget::qwsDisplay();
+ QRegion rgn = region;
+
+ if (widget)
+ rgn.translate(widget->mapToGlobal(QPoint(0, 0)));
+
+ surfaceSize = region.boundingRect().size();
+
+ const int id = winId();
+ display->requestRegion(id, key(), permanentState(), rgn);
+ display->setAltitude(id, 1, true);
+ display->repaintRegion(id, 0, false, rgn);
+
+ ::usleep(500 * delay);
+ display->requestRegion(id, key(), permanentState(), QRegion());
+ ::usleep(500 * delay);
+}
+
+#endif // QT_NO_PAINT_DEBUG
+
+#ifndef QT_NO_DIRECTPAINTER
+
+static inline QScreen *getPrimaryScreen()
+{
+ QScreen *screen = QScreen::instance();
+ if (!screen->base()) {
+ QList<QScreen*> subScreens = screen->subScreens();
+ if (subScreens.size() < 1)
+ return 0;
+ screen = subScreens.at(0);
+ }
+ return screen;
+}
+
+QWSDirectPainterSurface::QWSDirectPainterSurface(bool isClient,
+ QDirectPainter::SurfaceFlag flags)
+ : QWSWindowSurface(), flushingRegionEvents(false), doLocking(false)
+{
+ setSurfaceFlags(Opaque);
+ synchronous = (flags == QDirectPainter::ReservedSynchronous);
+
+ if (isClient) {
+ setWinId(QWidget::qwsDisplay()->takeId());
+ QWidget::qwsDisplay()->nameRegion(winId(),
+ QLatin1String("QDirectPainter reserved space"),
+ QLatin1String("reserved"));
+ } else {
+ setWinId(0);
+ }
+ _screen = QScreen::instance();
+ if (!_screen->base()) {
+ QList<QScreen*> subScreens = _screen->subScreens();
+ if (subScreens.size() < 1)
+ _screen = 0;
+ else
+ _screen = subScreens.at(0);
+ }
+}
+
+QWSDirectPainterSurface::~QWSDirectPainterSurface()
+{
+ if (winId() && QWSDisplay::instance()) // make sure not in QApplication destructor
+ QWidget::qwsDisplay()->destroyRegion(winId());
+}
+
+void QWSDirectPainterSurface::setRegion(const QRegion &region)
+{
+ const int id = winId();
+ QWidget::qwsDisplay()->requestRegion(id, key(), permanentState(), region);
+#ifndef QT_NO_QWS_MULTIPROCESS
+ if (synchronous)
+ QWSDisplay::instance()->d->waitForRegionAck(id);
+#endif
+}
+
+void QWSDirectPainterSurface::flush(QWidget *, const QRegion &r, const QPoint &)
+{
+ QWSDisplay::instance()->repaintRegion(winId(), 0, true, r);
+}
+
+QByteArray QWSDirectPainterSurface::permanentState() const
+{
+ QByteArray res;
+ if (isRegionReserved())
+ res.append( 'r');
+ return res;
+}
+
+void QWSDirectPainterSurface::setPermanentState(const QByteArray &ba)
+{
+ if (ba.size() > 0 && ba.at(0) == 'r')
+ setReserved();
+ setSurfaceFlags(surfaceFlags() | Opaque);
+}
+
+void QWSDirectPainterSurface::beginPaint(const QRegion &region)
+{
+ QWSWindowSurface::beginPaint(region);
+#ifndef QT_NO_QWS_MULTIPROCESS
+ if (!synchronous) {
+ flushingRegionEvents = true;
+ QWSDisplay::instance()->d->waitForRegionEvents(winId(), doLocking);
+ flushingRegionEvents = false;
+ }
+#endif
+}
+
+bool QWSDirectPainterSurface::hasPendingRegionEvents() const
+{
+#ifndef QT_NO_QWS_MULTIPROCESS
+ if (synchronous)
+ return false;
+
+ return QWSDisplay::instance()->d->hasPendingRegionEvents();
+#else
+ return false;
+#endif
+}
+
+bool QWSDirectPainterSurface::lock(int timeout)
+{
+#ifndef QT_NO_THREAD
+ threadLock.lock();
+#endif
+ Q_UNUSED(timeout);
+ if (doLocking)
+ QWSDisplay::grab(true);
+ return true;
+}
+
+void QWSDirectPainterSurface::unlock()
+{
+ if (doLocking)
+ QWSDisplay::ungrab();
+#ifndef QT_NO_THREAD
+ threadLock.unlock();
+#endif
+}
+
+#endif // QT_NO_DIRECTPAINTER
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qwindowsurface_qws_p.h b/src/gui/painting/qwindowsurface_qws_p.h
new file mode 100644
index 0000000000..fd56c814a3
--- /dev/null
+++ b/src/gui/painting/qwindowsurface_qws_p.h
@@ -0,0 +1,355 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSURFACE_QWS_P_H
+#define QWINDOWSURFACE_QWS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qwindowsurface_p.h"
+#include <qregion.h>
+#include <qimage.h>
+#include <qdirectpainter_qws.h>
+#include <qmutex.h>
+#include <private/qwssharedmemory_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QScreen;
+class QWSWindowSurfacePrivate;
+
+class Q_GUI_EXPORT QWSWindowSurface : public QWindowSurface
+{
+public:
+ QWSWindowSurface();
+ QWSWindowSurface(QWidget *widget);
+ ~QWSWindowSurface();
+
+ virtual bool isValid() const = 0;
+
+ virtual void setGeometry(const QRect &rect);
+ virtual void setGeometry(const QRect &rect, const QRegion &mask);
+ virtual void flush(QWidget *widget, const QRegion &region,
+ const QPoint &offset);
+
+ virtual bool move(const QPoint &offset);
+ virtual QRegion move(const QPoint &offset, const QRegion &newClip);
+
+ virtual QPoint painterOffset() const; // remove!!!
+
+ virtual void beginPaint(const QRegion &);
+ virtual void endPaint(const QRegion &);
+
+ virtual bool lock(int timeout = -1);
+ virtual void unlock();
+
+ virtual QString key() const = 0;
+
+ // XXX: not good enough
+ virtual QByteArray transientState() const;
+ virtual QByteArray permanentState() const;
+ virtual void setTransientState(const QByteArray &state);
+ virtual void setPermanentState(const QByteArray &state);
+
+ virtual QImage image() const = 0;
+ virtual QPaintDevice *paintDevice() = 0;
+
+ const QRegion clipRegion() const;
+ void setClipRegion(const QRegion &);
+
+#ifdef QT_QWS_CLIENTBLIT
+ virtual const QRegion directRegion() const;
+ virtual int directRegionId() const;
+ virtual void setDirectRegion(const QRegion &, int);
+#endif
+
+ enum SurfaceFlag {
+ RegionReserved = 0x1,
+ Buffered = 0x2,
+ Opaque = 0x4
+ };
+ Q_DECLARE_FLAGS(SurfaceFlags, SurfaceFlag)
+
+ SurfaceFlags surfaceFlags() const;
+
+ inline bool isRegionReserved() const {
+ return surfaceFlags() & RegionReserved;
+ }
+ inline bool isBuffered() const { return surfaceFlags() & Buffered; }
+ inline bool isOpaque() const { return surfaceFlags() & Opaque; }
+
+ int winId() const;
+ virtual void releaseSurface();
+
+protected:
+ void setSurfaceFlags(SurfaceFlags type);
+ void setWinId(int id);
+
+private:
+ friend class QWidgetPrivate;
+
+ void invalidateBuffer();
+
+ QWSWindowSurfacePrivate *d_ptr;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QWSWindowSurface::SurfaceFlags)
+
+class QWSWindowSurfacePrivate
+{
+public:
+ QWSWindowSurfacePrivate();
+
+ void setWinId(int id);
+
+ QWSWindowSurface::SurfaceFlags flags;
+ QRegion clip;
+#ifdef QT_QWS_CLIENTBLIT
+ QRegion direct;
+ int directId;
+#endif
+
+ int winId;
+};
+
+class QWSLock;
+
+class Q_GUI_EXPORT QWSMemorySurface : public QWSWindowSurface
+{
+public:
+ QWSMemorySurface();
+ QWSMemorySurface(QWidget *widget);
+ ~QWSMemorySurface();
+
+ bool isValid() const;
+
+ QPaintDevice *paintDevice() { return &img; }
+ bool scroll(const QRegion &area, int dx, int dy);
+
+ QImage image() const { return img; }
+ QPoint painterOffset() const;
+
+ void beginPaint(const QRegion &rgn);
+
+ bool lock(int timeout = -1);
+ void unlock();
+
+protected:
+ QImage::Format preferredImageFormat(const QWidget *widget) const;
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+ void setLock(int lockId);
+ QWSLock *memlock;
+#endif
+#ifndef QT_NO_THREAD
+ QMutex threadLock;
+#endif
+
+ QImage img;
+};
+
+class Q_GUI_EXPORT QWSLocalMemSurface : public QWSMemorySurface
+{
+public:
+ QWSLocalMemSurface();
+ QWSLocalMemSurface(QWidget *widget);
+ ~QWSLocalMemSurface();
+
+ void setGeometry(const QRect &rect);
+
+ QString key() const { return QLatin1String("mem"); }
+ QByteArray permanentState() const;
+
+ void setPermanentState(const QByteArray &data);
+ virtual void releaseSurface();
+protected:
+ uchar *mem;
+ int memsize;
+};
+
+#ifndef QT_NO_QWS_MULTIPROCESS
+class Q_GUI_EXPORT QWSSharedMemSurface : public QWSMemorySurface
+{
+public:
+ QWSSharedMemSurface();
+ QWSSharedMemSurface(QWidget *widget);
+ ~QWSSharedMemSurface();
+
+ void setGeometry(const QRect &rect);
+
+ QString key() const { return QLatin1String("shm"); }
+ QByteArray permanentState() const;
+
+ void setPermanentState(const QByteArray &data);
+
+#ifdef QT_QWS_CLIENTBLIT
+ virtual void setDirectRegion(const QRegion &, int);
+ virtual const QRegion directRegion() const;
+#endif
+ virtual void releaseSurface();
+
+private:
+ bool setMemory(int memId);
+
+ QWSSharedMemory mem;
+};
+#endif // QT_NO_QWS_MULTIPROCESS
+
+#ifndef QT_NO_PAINTONSCREEN
+class Q_GUI_EXPORT QWSOnScreenSurface : public QWSMemorySurface
+{
+public:
+ QWSOnScreenSurface();
+ QWSOnScreenSurface(QWidget *widget);
+ ~QWSOnScreenSurface();
+
+ bool isValid() const;
+ QPoint painterOffset() const;
+
+ QString key() const { return QLatin1String("OnScreen"); }
+ QByteArray permanentState() const;
+
+ void setPermanentState(const QByteArray &data);
+
+private:
+ void attachToScreen(const QScreen *screen);
+
+ const QScreen *screen;
+};
+#endif // QT_NO_PAINTONSCREEN
+
+#ifndef QT_NO_PAINT_DEBUG
+class Q_GUI_EXPORT QWSYellowSurface : public QWSWindowSurface
+{
+public:
+ QWSYellowSurface(bool isClient = false);
+ ~QWSYellowSurface();
+
+ void setDelay(int msec) { delay = msec; }
+
+ bool isValid() const { return true; }
+
+ void flush(QWidget *widget, const QRegion &region, const QPoint &offset);
+
+ QString key() const { return QLatin1String("Yellow"); }
+ QByteArray permanentState() const;
+
+ void setPermanentState(const QByteArray &data);
+
+ QPaintDevice *paintDevice() { return &img; }
+ QImage image() const { return img; }
+
+private:
+ int delay;
+ QSize surfaceSize; // client side
+ QImage img; // server side
+};
+#endif // QT_NO_PAINT_DEBUG
+
+#ifndef QT_NO_DIRECTPAINTER
+
+class QScreen;
+
+class Q_GUI_EXPORT QWSDirectPainterSurface : public QWSWindowSurface
+{
+public:
+ QWSDirectPainterSurface(bool isClient = false,
+ QDirectPainter::SurfaceFlag flags = QDirectPainter::NonReserved);
+ ~QWSDirectPainterSurface();
+
+ void setReserved() { setSurfaceFlags(RegionReserved); }
+
+ void setGeometry(const QRect &rect) { setRegion(rect); }
+
+ void setRegion(const QRegion &region);
+ QRegion region() const { return clipRegion(); }
+
+ void flush(QWidget*, const QRegion &, const QPoint &);
+
+ bool isValid() const { return false; }
+
+ QString key() const { return QLatin1String("DirectPainter"); }
+ QByteArray permanentState() const;
+
+ void setPermanentState(const QByteArray &);
+
+ QImage image() const { return QImage(); }
+ QPaintDevice *paintDevice() { return 0; }
+
+ // hw: get rid of this
+ WId windowId() const { return static_cast<WId>(winId()); }
+
+ QScreen *screen() const { return _screen; }
+
+ void beginPaint(const QRegion &);
+ bool lock(int timeout = -1);
+ void unlock();
+
+ void setLocking(bool b) { doLocking = b; }
+
+ bool hasPendingRegionEvents() const;
+
+private:
+ QScreen *_screen;
+#ifndef QT_NO_THREAD
+ QMutex threadLock;
+#endif
+
+ friend void qt_directpainter_region(QDirectPainter*, const QRegion&, int);
+ bool flushingRegionEvents;
+ bool synchronous;
+ bool doLocking;
+};
+
+#endif // QT_NO_DIRECTPAINTER
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSURFACE_QWS_P_H
diff --git a/src/gui/painting/qwindowsurface_raster.cpp b/src/gui/painting/qwindowsurface_raster.cpp
new file mode 100644
index 0000000000..9860841640
--- /dev/null
+++ b/src/gui/painting/qwindowsurface_raster.cpp
@@ -0,0 +1,487 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qdebug.h>
+
+#include <qglobal.h> // for Q_WS_WIN define (non-PCH)
+#ifdef Q_WS_WIN
+#include <qlibrary.h>
+#include <qt_windows.h>
+#endif
+
+#include <QtGui/qpaintdevice.h>
+#include <QtGui/qwidget.h>
+
+#include "private/qwindowsurface_raster_p.h"
+#include "private/qnativeimage_p.h"
+#include "private/qwidget_p.h"
+
+#ifdef Q_WS_X11
+#include "private/qpixmap_x11_p.h"
+#include "private/qt_x11_p.h"
+#include "private/qwidget_p.h"
+#include "qx11info_x11.h"
+#endif
+#include "private/qdrawhelper_p.h"
+
+#ifdef Q_WS_MAC
+#include <private/qt_cocoa_helpers_mac_p.h>
+#include <QMainWindow>
+#include <private/qmainwindowlayout_p.h>
+#include <QToolBar>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QRasterWindowSurfacePrivate
+{
+public:
+ QNativeImage *image;
+
+#ifdef Q_WS_X11
+ GC gc;
+#ifndef QT_NO_MITSHM
+ uint needsSync : 1;
+#endif
+#ifndef QT_NO_XRENDER
+ uint translucentBackground : 1;
+#endif
+#endif
+ uint inSetGeometry : 1;
+};
+
+QRasterWindowSurface::QRasterWindowSurface(QWidget *window, bool setDefaultSurface)
+ : QWindowSurface(window, setDefaultSurface), d_ptr(new QRasterWindowSurfacePrivate)
+{
+#ifdef Q_WS_X11
+ d_ptr->gc = XCreateGC(X11->display, window->handle(), 0, 0);
+#ifndef QT_NO_XRENDER
+ d_ptr->translucentBackground = X11->use_xrender
+ && window->x11Info().depth() == 32;
+#endif
+#ifndef QT_NO_MITHSM
+ d_ptr->needsSync = false;
+#endif
+#endif
+ d_ptr->image = 0;
+ d_ptr->inSetGeometry = false;
+
+#ifdef QT_MAC_USE_COCOA
+ needsFlush = false;
+ regionToFlush = QRegion();
+#endif // QT_MAC_USE_COCOA
+}
+
+
+QRasterWindowSurface::~QRasterWindowSurface()
+{
+#ifdef Q_WS_X11
+ XFreeGC(X11->display, d_ptr->gc);
+#endif
+ if (d_ptr->image)
+ delete d_ptr->image;
+}
+
+
+QPaintDevice *QRasterWindowSurface::paintDevice()
+{
+ return &d_ptr->image->image;
+}
+
+#if defined(Q_WS_X11) && !defined(QT_NO_MITSHM)
+void QRasterWindowSurface::syncX()
+{
+ // delay writing to the backbuffer until we know for sure X is done reading from it
+ if (d_ptr->needsSync) {
+ XSync(X11->display, false);
+ d_ptr->needsSync = false;
+ }
+}
+#endif
+
+void QRasterWindowSurface::beginPaint(const QRegion &rgn)
+{
+#if defined(Q_WS_X11) && !defined(QT_NO_MITSHM)
+ syncX();
+#endif
+
+#if (defined(Q_WS_X11) && !defined(QT_NO_XRENDER)) || (defined(Q_WS_WIN) && !defined(Q_WS_WINCE))
+ if (!qt_widget_private(window())->isOpaque && window()->testAttribute(Qt::WA_TranslucentBackground)) {
+#if defined(Q_WS_WIN) && !defined(Q_WS_WINCE)
+ if (d_ptr->image->image.format() != QImage::Format_ARGB32_Premultiplied)
+ prepareBuffer(QImage::Format_ARGB32_Premultiplied, window());
+#endif
+ QPainter p(&d_ptr->image->image);
+ p.setCompositionMode(QPainter::CompositionMode_Source);
+ const QVector<QRect> rects = rgn.rects();
+ const QColor blank = Qt::transparent;
+ for (QVector<QRect>::const_iterator it = rects.begin(); it != rects.end(); ++it) {
+ p.fillRect(*it, blank);
+ }
+ }
+#else
+ Q_UNUSED(rgn);
+#endif
+}
+
+void QRasterWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoint &offset)
+{
+ Q_D(QRasterWindowSurface);
+
+ // Not ready for painting yet, bail out. This can happen in
+ // QWidget::create_sys()
+ if (!d->image || rgn.rectCount() == 0)
+ return;
+
+#ifdef Q_WS_WIN
+ QRect br = rgn.boundingRect();
+
+#ifndef Q_WS_WINCE
+ if (!qt_widget_private(window())->isOpaque
+ && window()->testAttribute(Qt::WA_TranslucentBackground)
+ && (qt_widget_private(window())->data.window_flags & Qt::FramelessWindowHint))
+ {
+ QRect r = window()->frameGeometry();
+ QPoint frameOffset = qt_widget_private(window())->frameStrut().topLeft();
+ QRect dirtyRect = br.translated(offset + frameOffset);
+
+ SIZE size = {r.width(), r.height()};
+ POINT ptDst = {r.x(), r.y()};
+ POINT ptSrc = {0, 0};
+ BLENDFUNCTION blend = {AC_SRC_OVER, 0, (int)(255.0 * window()->windowOpacity()), Q_AC_SRC_ALPHA};
+ RECT dirty = {dirtyRect.x(), dirtyRect.y(),
+ dirtyRect.x() + dirtyRect.width(), dirtyRect.y() + dirtyRect.height()};
+ Q_UPDATELAYEREDWINDOWINFO info = {sizeof(info), NULL, &ptDst, &size, d->image->hdc, &ptSrc, 0, &blend, Q_ULW_ALPHA, &dirty};
+ ptrUpdateLayeredWindowIndirect(window()->internalWinId(), &info);
+ } else
+#endif
+ {
+ QPoint wOffset = qt_qwidget_data(widget)->wrect.topLeft();
+
+ HDC widget_dc = widget->getDC();
+
+ QRect wbr = br.translated(-wOffset);
+ BitBlt(widget_dc, wbr.x(), wbr.y(), wbr.width(), wbr.height(),
+ d->image->hdc, br.x() + offset.x(), br.y() + offset.y(), SRCCOPY);
+ widget->releaseDC(widget_dc);
+ }
+
+#ifndef QT_NO_DEBUG
+ static bool flush = !qgetenv("QT_FLUSH_WINDOWSURFACE").isEmpty();
+ if (flush) {
+ SelectObject(qt_win_display_dc(), GetStockObject(BLACK_BRUSH));
+ Rectangle(qt_win_display_dc(), 0, 0, d->image->width() + 2, d->image->height() + 2);
+ BitBlt(qt_win_display_dc(), 1, 1, d->image->width(), d->image->height(),
+ d->image->hdc, 0, 0, SRCCOPY);
+ }
+#endif
+
+#endif
+
+#ifdef Q_WS_X11
+ extern void *qt_getClipRects(const QRegion &r, int &num); // in qpaintengine_x11.cpp
+ extern QWidgetData* qt_widget_data(QWidget *);
+ QPoint wOffset = qt_qwidget_data(widget)->wrect.topLeft();
+
+ if (widget->window() != window()) {
+ XFreeGC(X11->display, d_ptr->gc);
+ d_ptr->gc = XCreateGC(X11->display, widget->handle(), 0, 0);
+ }
+
+ QRegion wrgn(rgn);
+ if (!wOffset.isNull())
+ wrgn.translate(-wOffset);
+ QRect wbr = wrgn.boundingRect();
+
+ if (wrgn.rectCount() != 1) {
+ int num;
+ XRectangle *rects = (XRectangle *)qt_getClipRects(wrgn, num);
+ XSetClipRectangles(X11->display, d_ptr->gc, 0, 0, rects, num, YXBanded);
+ }
+
+ QRect br = rgn.boundingRect().translated(offset);
+#ifndef QT_NO_MITSHM
+ if (d_ptr->image->xshmpm) {
+ XCopyArea(X11->display, d_ptr->image->xshmpm, widget->handle(), d_ptr->gc,
+ br.x(), br.y(), br.width(), br.height(), wbr.x(), wbr.y());
+ d_ptr->needsSync = true;
+ } else if (d_ptr->image->xshmimg) {
+ const QImage &src = d->image->image;
+ br = br.intersected(src.rect());
+ XShmPutImage(X11->display, widget->handle(), d_ptr->gc, d_ptr->image->xshmimg,
+ br.x(), br.y(), wbr.x(), wbr.y(), br.width(), br.height(), False);
+ d_ptr->needsSync = true;
+ } else
+#endif
+ {
+ const QImage &src = d->image->image;
+ br = br.intersected(src.rect());
+ if (src.format() != QImage::Format_RGB32 || widget->x11Info().depth() < 24) {
+ Q_ASSERT(src.depth() >= 16);
+ const QImage sub_src(src.scanLine(br.y()) + br.x() * (uint(src.depth()) / 8),
+ br.width(), br.height(), src.bytesPerLine(), src.format());
+ QX11PixmapData *data = new QX11PixmapData(QPixmapData::PixmapType);
+ data->xinfo = widget->x11Info();
+ data->fromImage(sub_src, Qt::NoOpaqueDetection);
+ QPixmap pm = QPixmap(data);
+ XCopyArea(X11->display, pm.handle(), widget->handle(), d_ptr->gc, 0 , 0 , br.width(), br.height(), wbr.x(), wbr.y());
+ } else {
+ // qpaintengine_x11.cpp
+ extern void qt_x11_drawImage(const QRect &rect, const QPoint &pos, const QImage &image, Drawable hd, GC gc, Display *dpy, Visual *visual, int depth);
+ qt_x11_drawImage(br, wbr.topLeft(), src, widget->handle(), d_ptr->gc, X11->display, (Visual *)widget->x11Info().visual(), widget->x11Info().depth());
+ }
+ }
+
+ if (wrgn.rectCount() != 1)
+ XSetClipMask(X11->display, d_ptr->gc, XNone);
+#endif // FALCON
+
+#ifdef Q_WS_MAC
+
+ Q_UNUSED(offset);
+
+ // This is mainly done for native components like native "open file" dialog.
+ if (widget->testAttribute(Qt::WA_DontShowOnScreen)) {
+ return;
+ }
+
+#ifdef QT_MAC_USE_COCOA
+
+ this->needsFlush = true;
+ this->regionToFlush += rgn;
+
+ // The actual flushing will be processed in [view drawRect:rect]
+ qt_mac_setNeedsDisplay(widget);
+
+#else
+ // Get a context for the widget.
+ CGContextRef context;
+ CGrafPtr port = GetWindowPort(qt_mac_window_for(widget));
+ QDBeginCGContext(port, &context);
+ CGContextRetain(context);
+ CGContextSaveGState(context);
+
+ // Flip context.
+ CGContextTranslateCTM(context, 0, widget->height());
+ CGContextScaleCTM(context, 1, -1);
+
+ // Clip to region.
+ const QVector<QRect> &rects = rgn.rects();
+ for (int i = 0; i < rects.size(); ++i) {
+ const QRect &rect = rects.at(i);
+ CGContextAddRect(context, CGRectMake(rect.x(), rect.y(), rect.width(), rect.height()));
+ }
+ CGContextClip(context);
+
+ QRect r = rgn.boundingRect();
+ const CGRect area = CGRectMake(r.x(), r.y(), r.width(), r.height());
+ CGImageRef image = CGBitmapContextCreateImage(d->image->cg);
+ CGImageRef subImage = CGImageCreateWithImageInRect(image, area);
+
+ qt_mac_drawCGImage(context, &area, subImage);
+
+ CGImageRelease(subImage);
+ CGImageRelease(image);
+
+ QDEndCGContext(port, &context);
+
+ // Restore context.
+ CGContextRestoreGState(context);
+ CGContextRelease(context);
+#endif // QT_MAC_USE_COCOA
+
+#endif // Q_WS_MAC
+
+#ifdef Q_OS_SYMBIAN
+ Q_UNUSED(widget);
+ Q_UNUSED(rgn);
+ Q_UNUSED(offset);
+#endif
+}
+
+void QRasterWindowSurface::setGeometry(const QRect &rect)
+{
+ QWindowSurface::setGeometry(rect);
+ Q_D(QRasterWindowSurface);
+ d->inSetGeometry = true;
+ if (d->image == 0 || d->image->width() < rect.width() || d->image->height() < rect.height()) {
+#if (defined(Q_WS_X11) && !defined(QT_NO_XRENDER)) || (defined(Q_WS_WIN) && !defined(Q_WS_WINCE))
+#ifndef Q_WS_WIN
+ if (d_ptr->translucentBackground)
+#else
+ if (!qt_widget_private(window())->isOpaque)
+#endif
+ prepareBuffer(QImage::Format_ARGB32_Premultiplied, window());
+ else
+#endif
+ prepareBuffer(QNativeImage::systemFormat(), window());
+ }
+ d->inSetGeometry = false;
+
+#if defined(Q_WS_MAC) && defined(QT_MAC_USE_COCOA)
+ QMainWindow* mWindow = qobject_cast<QMainWindow*>(window());
+ if (mWindow) {
+ QMainWindowLayout *mLayout = qobject_cast<QMainWindowLayout*>(mWindow->layout());
+ QList<QToolBar *> toolbarList = mLayout->qtoolbarsInUnifiedToolbarList;
+
+ for (int i = 0; i < toolbarList.size(); ++i) {
+ QToolBar* toolbar = toolbarList.at(i);
+ if (mLayout->toolBarArea(toolbar) == Qt::TopToolBarArea) {
+ QWidget* tbWidget = (QWidget*) toolbar;
+ if (tbWidget->d_func()->unifiedSurface) {
+ tbWidget->d_func()->unifiedSurface->setGeometry(rect);
+ }
+ }
+ }
+ }
+#endif // Q_WS_MAC && QT_MAC_USE_COCOA
+
+}
+
+// from qwindowsurface.cpp
+extern void qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &offset);
+
+bool QRasterWindowSurface::scroll(const QRegion &area, int dx, int dy)
+{
+#ifdef Q_WS_WIN
+ Q_D(QRasterWindowSurface);
+
+ if (!d->image || !d->image->hdc)
+ return false;
+
+ QRect rect = area.boundingRect();
+ BitBlt(d->image->hdc, rect.x()+dx, rect.y()+dy, rect.width(), rect.height(),
+ d->image->hdc, rect.x(), rect.y(), SRCCOPY);
+
+ return true;
+#else
+ Q_D(QRasterWindowSurface);
+
+ if (!d->image || d->image->image.isNull())
+ return false;
+
+#if defined(Q_WS_X11) && !defined(QT_NO_MITSHM)
+ syncX();
+#endif
+
+ const QVector<QRect> rects = area.rects();
+ for (int i = 0; i < rects.size(); ++i)
+ qt_scrollRectInImage(d->image->image, rects.at(i), QPoint(dx, dy));
+
+ return true;
+#endif
+}
+
+QWindowSurface::WindowSurfaceFeatures QRasterWindowSurface::features() const
+{
+ return QWindowSurface::AllFeatures;
+}
+
+void QRasterWindowSurface::prepareBuffer(QImage::Format format, QWidget *widget)
+{
+ Q_D(QRasterWindowSurface);
+
+ int width = window()->width();
+ int height = window()->height();
+ if (d->image) {
+ width = qMax(d->image->width(), width);
+ height = qMax(d->image->height(), height);
+ }
+
+ if (width == 0 || height == 0) {
+ delete d->image;
+ d->image = 0;
+ return;
+ }
+
+ QNativeImage *oldImage = d->image;
+
+ d->image = new QNativeImage(width, height, format, false, widget);
+
+ if (oldImage && d->inSetGeometry && hasStaticContents()) {
+ // Make sure we use the const version of bits() (no detach).
+ const uchar *src = const_cast<const QImage &>(oldImage->image).bits();
+ uchar *dst = d->image->image.bits();
+
+ const int srcBytesPerLine = oldImage->image.bytesPerLine();
+ const int dstBytesPerLine = d->image->image.bytesPerLine();
+ const int bytesPerPixel = oldImage->image.depth() >> 3;
+
+ QRegion staticRegion(staticContents());
+ // Make sure we're inside the boundaries of the old image.
+ staticRegion &= QRect(0, 0, oldImage->image.width(), oldImage->image.height());
+ const QVector<QRect> &rects = staticRegion.rects();
+ const QRect *srcRect = rects.constData();
+
+ // Copy the static content of the old image into the new one.
+ int numRectsLeft = rects.size();
+ do {
+ const int bytesOffset = srcRect->x() * bytesPerPixel;
+ const int dy = srcRect->y();
+
+ // Adjust src and dst to point to the right offset.
+ const uchar *s = src + dy * srcBytesPerLine + bytesOffset;
+ uchar *d = dst + dy * dstBytesPerLine + bytesOffset;
+ const int numBytes = srcRect->width() * bytesPerPixel;
+
+ int numScanLinesLeft = srcRect->height();
+ do {
+ ::memcpy(d, s, numBytes);
+ d += dstBytesPerLine;
+ s += srcBytesPerLine;
+ } while (--numScanLinesLeft);
+
+ ++srcRect;
+ } while (--numRectsLeft);
+ }
+
+ delete oldImage;
+}
+
+#ifdef QT_MAC_USE_COCOA
+CGContextRef QRasterWindowSurface::imageContext()
+{
+ Q_D(QRasterWindowSurface);
+ return d->image->cg;
+}
+#endif // QT_MAC_USE_COCOA
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qwindowsurface_raster_p.h b/src/gui/painting/qwindowsurface_raster_p.h
new file mode 100644
index 0000000000..06abcd3a4a
--- /dev/null
+++ b/src/gui/painting/qwindowsurface_raster_p.h
@@ -0,0 +1,132 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSURFACE_RASTER_P_H
+#define QWINDOWSURFACE_RASTER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QLibrary class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qglobal.h>
+#include "private/qwindowsurface_p.h"
+
+#ifdef QT_MAC_USE_COCOA
+# include <private/qt_cocoa_helpers_mac_p.h>
+#endif // QT_MAC_USE_COCOA
+
+QT_BEGIN_NAMESPACE
+
+#ifdef Q_WS_WIN
+#define Q_WS_EX_LAYERED 0x00080000 // copied from WS_EX_LAYERED in winuser.h
+#define Q_LWA_ALPHA 0x00000002 // copied from LWA_ALPHA in winuser.h
+#define Q_ULW_ALPHA 0x00000002 // copied from ULW_ALPHA in winuser.h
+#define Q_AC_SRC_ALPHA 0x00000001 // copied from AC_SRC_ALPHA in winuser.h
+
+struct Q_UPDATELAYEREDWINDOWINFO {
+ DWORD cbSize;
+ HDC hdcDst;
+ const POINT *pptDst;
+ const SIZE *psize;
+ HDC hdcSrc;
+ const POINT *pptSrc;
+ COLORREF crKey;
+ const BLENDFUNCTION *pblend;
+ DWORD dwFlags;
+ const RECT *prcDirty;
+};
+
+typedef BOOL (WINAPI *PtrUpdateLayeredWindow)(HWND hwnd, HDC hdcDst, const POINT *pptDst,
+ const SIZE *psize, HDC hdcSrc, const POINT *pptSrc, COLORREF crKey,
+ const BLENDFUNCTION *pblend, DWORD dwflags);
+typedef BOOL (WINAPI *PtrUpdateLayeredWindowIndirect)(HWND hwnd, const Q_UPDATELAYEREDWINDOWINFO *pULWInfo);
+extern PtrUpdateLayeredWindow ptrUpdateLayeredWindow;
+extern PtrUpdateLayeredWindowIndirect ptrUpdateLayeredWindowIndirect;
+#endif
+
+class QPaintDevice;
+class QPoint;
+class QRegion;
+class QRegion;
+class QSize;
+class QWidget;
+class QRasterWindowSurfacePrivate;
+class QNativeImage;
+
+class Q_GUI_EXPORT QRasterWindowSurface : public QWindowSurface
+{
+public:
+ QRasterWindowSurface(QWidget *widget, bool setDefaultSurface = true);
+ ~QRasterWindowSurface();
+
+ QPaintDevice *paintDevice();
+ void flush(QWidget *widget, const QRegion &region, const QPoint &offset);
+ void beginPaint(const QRegion &rgn);
+ void setGeometry(const QRect &rect);
+ bool scroll(const QRegion &area, int dx, int dy);
+ WindowSurfaceFeatures features() const;
+
+#ifdef QT_MAC_USE_COCOA
+ CGContextRef imageContext();
+
+ bool needsFlush;
+ QRegion regionToFlush;
+#endif // QT_MAC_USE_COCOA
+
+private:
+#if defined(Q_WS_X11) && !defined(QT_NO_MITSHM)
+ void syncX();
+#endif
+ void prepareBuffer(QImage::Format format, QWidget *widget);
+ Q_DECLARE_PRIVATE(QRasterWindowSurface)
+ QScopedPointer<QRasterWindowSurfacePrivate> d_ptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSURFACE_RASTER_P_H
diff --git a/src/gui/painting/qwindowsurface_s60.cpp b/src/gui/painting/qwindowsurface_s60.cpp
new file mode 100644
index 0000000000..9fa01edf7d
--- /dev/null
+++ b/src/gui/painting/qwindowsurface_s60.cpp
@@ -0,0 +1,254 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qglobal.h> // for Q_WS_WIN define (non-PCH)
+
+#include <QtGui/qpaintdevice.h>
+#include <private/qwidget_p.h>
+#include <private/qwindowsurface_s60_p.h>
+#include <private/qpixmap_s60_p.h>
+#include <private/qt_s60_p.h>
+#include <private/qapplication_p.h>
+#include <private/qdrawhelper_p.h>
+
+#ifdef QT_GRAPHICSSYSTEM_RUNTIME
+#include <private/qgraphicssystem_runtime_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+struct QS60WindowSurfacePrivate
+{
+ QPixmap device;
+ QList<QImage*> bufferImages;
+};
+
+TDisplayMode displayMode(bool opaque)
+{
+ TDisplayMode mode = S60->screenDevice()->DisplayMode();
+ if (opaque) {
+ mode = EColor16MU;
+ } else {
+ if (QSysInfo::symbianVersion() >= QSysInfo::SV_SF_3)
+ mode = Q_SYMBIAN_ECOLOR16MAP; // Symbian^3 WServ has support for ARGB32_PRE
+ else
+ mode = EColor16MA; // Symbian prior to Symbian^3 sw accelerates EColor16MA
+ }
+ return mode;
+}
+
+bool blitWriteAlpha(QWidgetPrivate *widgetPrivate)
+{
+ QWExtra *extra = widgetPrivate->extraData();
+ return extra ? extra->nativePaintMode == QWExtra::BlitWriteAlpha : false;
+}
+
+QS60WindowSurface::QS60WindowSurface(QWidget* widget)
+ : QWindowSurface(widget), d_ptr(new QS60WindowSurfacePrivate)
+{
+ QWidgetPrivate *widgetPrivate = qt_widget_private(widget);
+ const bool opaque = widgetPrivate->isOpaque && !blitWriteAlpha(widgetPrivate);
+ TDisplayMode mode = displayMode(opaque);
+ // We create empty CFbsBitmap here -> it will be resized in setGeometry
+ CFbsBitmap *bitmap = new CFbsBitmap; // CBase derived object needs check on new
+ Q_CHECK_PTR(bitmap);
+ qt_symbian_throwIfError( bitmap->Create( TSize(0, 0), mode ) );
+
+ QS60PixmapData *data = new QS60PixmapData(QPixmapData::PixmapType);
+ if (data) {
+ data->fromSymbianBitmap(bitmap, true);
+ d_ptr->device = QPixmap(data);
+ }
+}
+
+QS60WindowSurface::~QS60WindowSurface()
+{
+#if defined(QT_GRAPHICSSYSTEM_RUNTIME) && defined(Q_SYMBIAN_SUPPORTS_SURFACES)
+ if(QApplicationPrivate::runtime_graphics_system) {
+ QRuntimeGraphicsSystem *runtimeGraphicsSystem =
+ static_cast<QRuntimeGraphicsSystem*>(QApplicationPrivate::graphics_system);
+ if(runtimeGraphicsSystem->graphicsSystemName() == QLatin1String("openvg")) {
+
+ // Graphics system has been switched from raster to openvg.
+ // Issue empty redraw to clear the UI surface
+
+ QWidget *w = window();
+ if (w->testAttribute(Qt::WA_WState_Created)) {
+ RWindow *const window = static_cast<RWindow *>(w->winId()->DrawableWindow());
+ window->BeginRedraw();
+ window->EndRedraw();
+ }
+ }
+ }
+#endif
+
+ delete d_ptr;
+}
+
+void QS60WindowSurface::beginPaint(const QRegion &rgn)
+{
+#ifdef Q_SYMBIAN_SUPPORTS_SURFACES
+ S60->wsSession().Finish();
+#endif
+
+ QWidgetPrivate *windowPrivate = qt_widget_private(window());
+ if (!windowPrivate->isOpaque || blitWriteAlpha(windowPrivate)) {
+ QS60PixmapData *pixmapData = static_cast<QS60PixmapData *>(d_ptr->device.data_ptr().data());
+
+ TDisplayMode mode = displayMode(false);
+ if (pixmapData->cfbsBitmap->DisplayMode() != mode)
+ pixmapData->convertToDisplayMode(mode);
+
+ pixmapData->beginDataAccess();
+
+ if (!windowPrivate->isOpaque) {
+ QPainter p(&pixmapData->image);
+ p.setCompositionMode(QPainter::CompositionMode_Source);
+ const QVector<QRect> rects = rgn.rects();
+ const QColor blank = Qt::transparent;
+ for (QVector<QRect>::const_iterator it = rects.begin(); it != rects.end(); ++it) {
+ p.fillRect(*it, blank);
+ }
+ }
+
+ pixmapData->endDataAccess();
+ }
+}
+
+void QS60WindowSurface::endPaint(const QRegion &)
+{
+ qDeleteAll(d_ptr->bufferImages);
+ d_ptr->bufferImages.clear();
+}
+
+QImage* QS60WindowSurface::buffer(const QWidget *widget)
+{
+ if (widget->window() != window())
+ return 0;
+
+ QPaintDevice *pdev = paintDevice();
+ if (!pdev)
+ return 0;
+
+ const QPoint off = offset(widget);
+ QImage *img = &(static_cast<QS60PixmapData *>(d_ptr->device.data_ptr().data())->image);
+
+ QRect rect(off, widget->size());
+ rect &= QRect(QPoint(), img->size());
+
+ if (rect.isEmpty())
+ return 0;
+
+ img = new QImage(img->scanLine(rect.y()) + rect.x() * img->depth() / 8,
+ rect.width(), rect.height(),
+ img->bytesPerLine(), img->format());
+ d_ptr->bufferImages.append(img);
+
+ return img;
+}
+
+void QS60WindowSurface::flush(QWidget *widget, const QRegion &region, const QPoint &)
+{
+ QWidget *window = widget->window();
+ Q_ASSERT(window);
+ QTLWExtra *topExtra = window->d_func()->maybeTopData();
+ Q_ASSERT(topExtra);
+ QRect qr = region.boundingRect();
+ if (!topExtra->inExpose) {
+ topExtra->inExpose = true; // Prevent DrawNow() from calling syncBackingStore() again
+ TRect tr = qt_QRect2TRect(qr);
+ widget->winId()->DrawNow(tr);
+ topExtra->inExpose = false;
+ } else {
+ // This handles the case when syncBackingStore updates content outside of the
+ // original drawing rectangle. This might happen if there are pending update()
+ // events at the same time as we get a Draw() from Symbian.
+ QRect drawRect = qt_TRect2QRect(widget->winId()->DrawableWindow()->GetDrawRect());
+ if (!drawRect.contains(qr))
+ widget->winId()->DrawDeferred();
+ }
+}
+
+bool QS60WindowSurface::scroll(const QRegion &area, int dx, int dy)
+{
+ QRect rect = area.boundingRect();
+
+ if (dx == 0 && dy == 0)
+ return false;
+
+ if (d_ptr->device.isNull())
+ return false;
+
+ QS60PixmapData *data = static_cast<QS60PixmapData*>(d_ptr->device.data_ptr().data());
+ data->scroll(dx, dy, rect);
+
+ return true;
+}
+
+QPaintDevice* QS60WindowSurface::paintDevice()
+{
+ return &d_ptr->device;
+}
+
+void QS60WindowSurface::setGeometry(const QRect& rect)
+{
+ if (rect == geometry())
+ return;
+
+ QS60PixmapData *data = static_cast<QS60PixmapData*>(d_ptr->device.data_ptr().data());
+ data->resize(rect.width(), rect.height());
+
+ QWindowSurface::setGeometry(rect);
+}
+
+QWindowSurface::WindowSurfaceFeatures QS60WindowSurface::features() const
+{
+ return QWindowSurface::AllFeatures;
+}
+
+CFbsBitmap* QS60WindowSurface::symbianBitmap() const
+{
+ QS60PixmapData *data = static_cast<QS60PixmapData*>(d_ptr->device.data_ptr().data());
+ return data->cfbsBitmap;
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/gui/painting/qwindowsurface_s60_p.h b/src/gui/painting/qwindowsurface_s60_p.h
new file mode 100644
index 0000000000..25018d86e2
--- /dev/null
+++ b/src/gui/painting/qwindowsurface_s60_p.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSURFACE_S60_P_H
+#define QWINDOWSURFACE_S60_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qglobal.h>
+#include "private/qwindowsurface_p.h"
+
+class CFbsBitmap;
+
+QT_BEGIN_NAMESPACE
+
+struct QS60WindowSurfacePrivate;
+
+class QS60WindowSurface : public QWindowSurface
+{
+public:
+ QS60WindowSurface(QWidget *widget);
+ ~QS60WindowSurface();
+
+ QPaintDevice *paintDevice();
+ void flush(QWidget *widget, const QRegion &region, const QPoint &offset);
+ bool scroll(const QRegion &area, int dx, int dy);
+
+ void beginPaint(const QRegion &);
+ void endPaint(const QRegion &);
+
+ QImage* buffer(const QWidget *widget);
+
+ void setGeometry(const QRect &rect);
+
+ WindowSurfaceFeatures features() const;
+
+ CFbsBitmap *symbianBitmap() const;
+
+private:
+ QS60WindowSurfacePrivate* d_ptr;
+
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSURFACE_S60_P_H
diff --git a/src/gui/painting/qwindowsurface_x11.cpp b/src/gui/painting/qwindowsurface_x11.cpp
new file mode 100644
index 0000000000..deb83b452c
--- /dev/null
+++ b/src/gui/painting/qwindowsurface_x11.cpp
@@ -0,0 +1,265 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtGui/QPaintDevice>
+#include <QtGui/QPainter>
+#include <QtGui/QPixmap>
+#include <QtGui/QWidget>
+
+#include "private/qt_x11_p.h"
+#include "private/qpixmap_x11_p.h"
+#include "private/qwidget_p.h"
+#include "qx11info_x11.h"
+#include "qwindowsurface_x11_p.h"
+
+QT_BEGIN_NAMESPACE
+
+extern void *qt_getClipRects(const QRegion &r, int &num); // in qpaintengine_x11.cpp
+
+struct QX11WindowSurfacePrivate
+{
+ QWidget *widget;
+ QPixmap device;
+#ifndef QT_NO_XRENDER
+ bool translucentBackground;
+#endif
+};
+
+QX11WindowSurface::QX11WindowSurface(QWidget *widget)
+ : QWindowSurface(widget), d_ptr(new QX11WindowSurfacePrivate), gc(0)
+{
+ d_ptr->widget = widget;
+#ifndef QT_NO_XRENDER
+ d_ptr->translucentBackground = X11->use_xrender
+ && widget->x11Info().depth() == 32;
+#endif
+}
+
+
+QX11WindowSurface::~QX11WindowSurface()
+{
+ delete d_ptr;
+ if (gc) {
+ XFreeGC(X11->display, gc);
+ gc = 0;
+ }
+}
+
+QPaintDevice *QX11WindowSurface::paintDevice()
+{
+ return &d_ptr->device;
+}
+
+void QX11WindowSurface::beginPaint(const QRegion &rgn)
+{
+#ifndef QT_NO_XRENDER
+ Q_ASSERT(!d_ptr->device.isNull());
+
+ if (d_ptr->translucentBackground) {
+ if (d_ptr->device.depth() != 32)
+ static_cast<QX11PixmapData *>(d_ptr->device.data_ptr().data())->convertToARGB32();
+ ::Picture src = X11->getSolidFill(d_ptr->device.x11Info().screen(), Qt::transparent);
+ ::Picture dst = d_ptr->device.x11PictureHandle();
+ const QVector<QRect> rects = rgn.rects();
+ const int w = d_ptr->device.width();
+ const int h = d_ptr->device.height();
+ for (QVector<QRect>::const_iterator it = rects.begin(); it != rects.end(); ++it)
+ XRenderComposite(X11->display, PictOpSrc, src, 0, dst,
+ 0, 0, w, h, it->x(), it->y(),
+ it->width(), it->height());
+ }
+#endif
+}
+
+void QX11WindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoint &offset)
+{
+ if (d_ptr->device.isNull())
+ return;
+
+ QPoint wOffset = qt_qwidget_data(widget)->wrect.topLeft();
+ QRegion wrgn(rgn);
+ QRect br = rgn.boundingRect();
+ if (!wOffset.isNull())
+ wrgn.translate(-wOffset);
+ QRect wbr = wrgn.boundingRect();
+
+ int num;
+ XRectangle *rects = (XRectangle *)qt_getClipRects(wrgn, num);
+ if (num <= 0)
+ return;
+// qDebug() << "XSetClipRectangles";
+// for (int i = 0; i < num; ++i)
+// qDebug() << ' ' << i << rects[i].x << rects[i].x << rects[i].y << rects[i].width << rects[i].height;
+ if (num != 1)
+ XSetClipRectangles(X11->display, gc, 0, 0, rects, num, YXBanded);
+ XCopyArea(X11->display, d_ptr->device.handle(), widget->handle(), gc,
+ br.x() + offset.x(), br.y() + offset.y(), br.width(), br.height(), wbr.x(), wbr.y());
+ if (num != 1)
+ XSetClipMask(X11->display, gc, XNone);
+}
+
+void QX11WindowSurface::setGeometry(const QRect &rect)
+{
+ QWindowSurface::setGeometry(rect);
+
+ const QSize size = rect.size();
+
+ if (d_ptr->device.size() == size || size.width() <= 0 || size.height() <= 0)
+ return;
+#ifndef QT_NO_XRENDER
+ if (d_ptr->translucentBackground) {
+ QPixmap::x11SetDefaultScreen(d_ptr->widget->x11Info().screen());
+
+ QX11PixmapData *data = new QX11PixmapData(QPixmapData::PixmapType);
+ data->xinfo = d_ptr->widget->x11Info();
+ data->resize(size.width(), size.height());
+ d_ptr->device = QPixmap(data);
+ } else
+#endif
+ {
+ QPixmap::x11SetDefaultScreen(d_ptr->widget->x11Info().screen());
+
+ QX11PixmapData *oldData = static_cast<QX11PixmapData *>(d_ptr->device.pixmapData());
+
+ if (oldData && !(oldData->flags & QX11PixmapData::Uninitialized) && hasStaticContents()) {
+ // Copy the content of the old pixmap into the new one.
+ QX11PixmapData *newData = new QX11PixmapData(QPixmapData::PixmapType);
+ newData->resize(size.width(), size.height());
+ Q_ASSERT(oldData->d == newData->d);
+
+ QRegion staticRegion(staticContents());
+ // Make sure we're inside the boundaries of the old pixmap.
+ staticRegion &= QRect(0, 0, oldData->w, oldData->h);
+ const QRect boundingRect(staticRegion.boundingRect());
+ const int dx = boundingRect.x();
+ const int dy = boundingRect.y();
+
+ int num;
+ XRectangle *rects = (XRectangle *)qt_getClipRects(staticRegion, num);
+ GC tmpGc = XCreateGC(X11->display, oldData->hd, 0, 0);
+ XSetClipRectangles(X11->display, tmpGc, 0, 0, rects, num, YXBanded);
+ XCopyArea(X11->display, oldData->hd, newData->hd, tmpGc,
+ dx, dy, qMin(boundingRect.width(), size.width()),
+ qMin(boundingRect.height(), size.height()), dx, dy);
+ XFreeGC(X11->display, tmpGc);
+ newData->flags &= ~QX11PixmapData::Uninitialized;
+
+ d_ptr->device = QPixmap(newData);
+ } else {
+ d_ptr->device = QPixmap(size);
+ }
+ }
+
+ if (gc) {
+ XFreeGC(X11->display, gc);
+ gc = 0;
+ }
+ if (!d_ptr->device.isNull()) {
+ gc = XCreateGC(X11->display, d_ptr->device.handle(), 0, 0);
+ XSetGraphicsExposures(X11->display, gc, False);
+ }
+}
+
+bool QX11WindowSurface::scroll(const QRegion &area, int dx, int dy)
+{
+ QRect rect = area.boundingRect();
+
+ if (d_ptr->device.isNull())
+ return false;
+
+ GC gc = XCreateGC(X11->display, d_ptr->device.handle(), 0, 0);
+ XCopyArea(X11->display, d_ptr->device.handle(), d_ptr->device.handle(), gc,
+ rect.x(), rect.y(), rect.width(), rect.height(),
+ rect.x()+dx, rect.y()+dy);
+ XFreeGC(X11->display, gc);
+
+ return true;
+}
+
+QPixmap QX11WindowSurface::grabWidget(const QWidget *widget,
+ const QRect& rect) const
+{
+ if (!widget || d_ptr->device.isNull())
+ return QPixmap();
+
+ QRect srcRect;
+
+ // make sure the rect is inside the widget & clip to widget's rect
+ if (!rect.isEmpty())
+ srcRect = rect & widget->rect();
+ else
+ srcRect = widget->rect();
+
+ if (srcRect.isEmpty())
+ return QPixmap();
+
+ // If it's a child widget we have to translate the coordinates
+ if (widget != window())
+ srcRect.translate(widget->mapTo(window(), QPoint(0, 0)));
+
+ QPixmap::x11SetDefaultScreen(widget->x11Info().screen());
+ QPixmap px(srcRect.width(), srcRect.height());
+
+ GC tmpGc = XCreateGC(X11->display, d_ptr->device.handle(), 0, 0);
+
+ // Copy srcRect from the backing store to the new pixmap
+ XSetGraphicsExposures(X11->display, tmpGc, False);
+ XCopyArea(X11->display, d_ptr->device.handle(), px.handle(), tmpGc,
+ srcRect.x(), srcRect.y(), srcRect.width(), srcRect.height(), 0, 0);
+
+ XFreeGC(X11->display, tmpGc);
+
+ return px;
+}
+
+QWindowSurface::WindowSurfaceFeatures QX11WindowSurface::features() const
+{
+ WindowSurfaceFeatures features = QWindowSurface::PartialUpdates | QWindowSurface::PreservedContents;
+#ifndef QT_NO_XRENDER
+ if (!d_ptr->translucentBackground)
+ features |= QWindowSurface::StaticContents;
+#else
+ features |= QWindowSurface::StaticContents;
+#endif
+ return features;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/painting/qwindowsurface_x11_p.h b/src/gui/painting/qwindowsurface_x11_p.h
new file mode 100644
index 0000000000..df76f986ff
--- /dev/null
+++ b/src/gui/painting/qwindowsurface_x11_p.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSURFACE_X11_P_H
+#define QWINDOWSURFACE_X11_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qglobal.h>
+#include "private/qwindowsurface_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QPaintDevice;
+class QPoint;
+class QRegion;
+class QRegion;
+class QSize;
+class QWidget;
+struct QX11WindowSurfacePrivate;
+
+class QX11WindowSurface : public QWindowSurface
+{
+public:
+ QX11WindowSurface(QWidget *widget);
+ ~QX11WindowSurface();
+
+ QPaintDevice *paintDevice();
+ void flush(QWidget *widget, const QRegion &region, const QPoint &offset);
+
+ void beginPaint(const QRegion &rgn);
+ void setGeometry(const QRect &rect);
+ bool scroll(const QRegion &area, int dx, int dy);
+ QPixmap grabWidget(const QWidget *widget,
+ const QRect& rectangle = QRect()) const;
+ WindowSurfaceFeatures features() const;
+
+private:
+ QX11WindowSurfacePrivate *d_ptr;
+ GC gc;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSURFACE_X11_P_H
diff --git a/src/gui/painting/qwmatrix.h b/src/gui/painting/qwmatrix.h
new file mode 100644
index 0000000000..c4f4c75405
--- /dev/null
+++ b/src/gui/painting/qwmatrix.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWMATRIX_H
+#define QWMATRIX_H
+
+#include <QtGui/qmatrix.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+#if defined(QT3_SUPPORT)
+typedef QMatrix QWMatrix;
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QWMATRIX_H