summaryrefslogtreecommitdiffstats
path: root/src/gui/painting
diff options
context:
space:
mode:
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