2009-03-02 Kim Grönholm <kim.gronholm@nomovok.com>
[WebKit-https.git] / WebCore / platform / graphics / qt / GraphicsContextQt.cpp
1 /*
2  * Copyright (C) 2006 Dirk Mueller <mueller@kde.org>
3  * Copyright (C) 2006 Zack Rusin <zack@kde.org>
4  * Copyright (C) 2006 George Staikos <staikos@kde.org>
5  * Copyright (C) 2006 Simon Hausmann <hausmann@kde.org>
6  * Copyright (C) 2006 Allan Sandfeld Jensen <sandfeld@kde.org>
7  * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org>
8  * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
9  * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
10  * Copyright (C) 2008 Dirk Schulze <vbs85@gmx.de>
11  *
12  * All rights reserved.
13  *
14  * Redistribution and use in source and binary forms, with or without
15  * modification, are permitted provided that the following conditions
16  * are met:
17  * 1. Redistributions of source code must retain the above copyright
18  *    notice, this list of conditions and the following disclaimer.
19  * 2. Redistributions in binary form must reproduce the above copyright
20  *    notice, this list of conditions and the following disclaimer in the
21  *    documentation and/or other materials provided with the distribution.
22  *
23  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
24  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
27  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
31  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34  */
35
36 #include "config.h"
37
38 #ifdef Q_WS_WIN
39 #include <windows.h>
40 #endif
41
42 #include "TransformationMatrix.h"
43 #include "Color.h"
44 #include "FloatConversion.h"
45 #include "Font.h"
46 #include "GraphicsContext.h"
47 #include "GraphicsContextPrivate.h"
48 #include "ImageBuffer.h"
49 #include "Path.h"
50 #include "Pattern.h"
51 #include "Pen.h"
52 #include "NotImplemented.h"
53
54 #include <QDebug>
55 #include <QGradient>
56 #include <QPainter>
57 #include <QPaintDevice>
58 #include <QPaintEngine>
59 #include <QPainterPath>
60 #include <QPixmap>
61 #include <QPolygonF>
62 #include <QStack>
63 #include <QVector>
64
65 #ifndef M_PI
66 #define M_PI 3.14159265358979323846
67 #endif
68
69 namespace WebCore {
70
71 static inline QPainter::CompositionMode toQtCompositionMode(CompositeOperator op)
72 {
73     switch (op) {
74         case CompositeClear:
75             return QPainter::CompositionMode_Clear;
76         case CompositeCopy:
77             return QPainter::CompositionMode_Source;
78         case CompositeSourceOver:
79             return QPainter::CompositionMode_SourceOver;
80         case CompositeSourceIn:
81             return QPainter::CompositionMode_SourceIn;
82         case CompositeSourceOut:
83             return QPainter::CompositionMode_SourceOut;
84         case CompositeSourceAtop:
85             return QPainter::CompositionMode_SourceAtop;
86         case CompositeDestinationOver:
87             return QPainter::CompositionMode_DestinationOver;
88         case CompositeDestinationIn:
89             return QPainter::CompositionMode_DestinationIn;
90         case CompositeDestinationOut:
91             return QPainter::CompositionMode_DestinationOut;
92         case CompositeDestinationAtop:
93             return QPainter::CompositionMode_DestinationAtop;
94         case CompositeXOR:
95             return QPainter::CompositionMode_Xor;
96         case CompositePlusDarker:
97             return QPainter::CompositionMode_SourceOver;
98         case CompositeHighlight:
99             return QPainter::CompositionMode_SourceOver;
100         case CompositePlusLighter:
101             return QPainter::CompositionMode_SourceOver;
102     }
103
104     return QPainter::CompositionMode_SourceOver;
105 }
106
107 static inline Qt::PenCapStyle toQtLineCap(LineCap lc)
108 {
109     switch (lc) {
110         case ButtCap:
111             return Qt::FlatCap;
112         case RoundCap:
113             return Qt::RoundCap;
114         case SquareCap:
115             return Qt::SquareCap;
116     }
117
118     return Qt::FlatCap;
119 }
120
121 static inline Qt::PenJoinStyle toQtLineJoin(LineJoin lj)
122 {
123     switch (lj) {
124         case MiterJoin:
125             return Qt::SvgMiterJoin;
126         case RoundJoin:
127             return Qt::RoundJoin;
128         case BevelJoin:
129             return Qt::BevelJoin;
130     }
131
132     return Qt::MiterJoin;
133 }
134
135 static Qt::PenStyle toQPenStyle(StrokeStyle style)
136 {
137     switch (style) {
138     case NoStroke:
139         return Qt::NoPen;
140         break;
141     case SolidStroke:
142         return Qt::SolidLine;
143         break;
144     case DottedStroke:
145         return Qt::DotLine;
146         break;
147     case DashedStroke:
148         return Qt::DashLine;
149         break;
150     }
151     qWarning("couldn't recognize the pen style");
152     return Qt::NoPen;
153 }
154
155 struct TransparencyLayer
156 {
157     TransparencyLayer(const QPainter* p, const QRect &rect)
158         : pixmap(rect.width(), rect.height())
159     {
160         offset = rect.topLeft();
161         pixmap.fill(Qt::transparent);
162         painter.begin(&pixmap);
163         painter.setRenderHint(QPainter::Antialiasing, p->testRenderHint(QPainter::Antialiasing));
164         painter.translate(-offset);
165         painter.setPen(p->pen());
166         painter.setBrush(p->brush());
167         painter.setTransform(p->transform(), true);
168         painter.setOpacity(p->opacity());
169         painter.setFont(p->font());
170         if (painter.paintEngine()->hasFeature(QPaintEngine::PorterDuff))
171             painter.setCompositionMode(p->compositionMode());
172         painter.setClipPath(p->clipPath());
173     }
174
175     TransparencyLayer()
176     {
177     }
178
179     QPixmap pixmap;
180     QPoint offset;
181     QPainter painter;
182     qreal opacity;
183 private:
184     TransparencyLayer(const TransparencyLayer &) {}
185     TransparencyLayer & operator=(const TransparencyLayer &) { return *this; }
186 };
187
188 class GraphicsContextPlatformPrivate
189 {
190 public:
191     GraphicsContextPlatformPrivate(QPainter* painter);
192     ~GraphicsContextPlatformPrivate();
193
194     inline QPainter* p()
195     {
196         if (layers.isEmpty()) {
197             if (redirect)
198                 return redirect;
199
200             return painter;
201         } else
202             return &layers.top()->painter;
203     }
204
205     bool antiAliasingForRectsAndLines;
206
207     QStack<TransparencyLayer *> layers;
208     QPainter* redirect;
209
210     QBrush solidColor;
211
212     // Only used by SVG for now.
213     QPainterPath currentPath;
214
215 private:
216     QPainter* painter;
217 };
218
219
220 GraphicsContextPlatformPrivate::GraphicsContextPlatformPrivate(QPainter* p)
221 {
222     painter = p;
223     redirect = 0;
224
225     solidColor = QBrush(Qt::black);
226
227     if (painter) {
228         // use the default the QPainter was constructed with
229         antiAliasingForRectsAndLines = painter->testRenderHint(QPainter::Antialiasing);
230         // FIXME: Maybe only enable in SVG mode?
231         painter->setRenderHint(QPainter::Antialiasing, true);
232     } else {
233         antiAliasingForRectsAndLines = false;
234     }
235 }
236
237 GraphicsContextPlatformPrivate::~GraphicsContextPlatformPrivate()
238 {
239 }
240
241 GraphicsContext::GraphicsContext(PlatformGraphicsContext* context)
242     : m_common(createGraphicsContextPrivate())
243     , m_data(new GraphicsContextPlatformPrivate(context))
244 {
245     setPaintingDisabled(!context);
246     if (context) {
247         // Make sure the context starts in sync with our state.
248         setPlatformFillColor(fillColor());
249         setPlatformStrokeColor(strokeColor());
250     }
251 }
252
253 GraphicsContext::~GraphicsContext()
254 {
255     while(!m_data->layers.isEmpty())
256         endTransparencyLayer();
257
258     destroyGraphicsContextPrivate(m_common);
259     delete m_data;
260 }
261
262 PlatformGraphicsContext* GraphicsContext::platformContext() const
263 {
264     return m_data->p();
265 }
266
267 TransformationMatrix GraphicsContext::getCTM() const
268 {
269     QTransform matrix(platformContext()->combinedTransform());
270     return TransformationMatrix(matrix.m11(), matrix.m12(), 0, matrix.m13(), 
271                                 matrix.m21(), matrix.m22(), 0, matrix.m23(),
272                                            0,            0, 1,            0,
273                                 matrix.m31(), matrix.m32(), 0, matrix.m33());
274 }
275
276 void GraphicsContext::savePlatformState()
277 {
278     m_data->p()->save();
279 }
280
281 void GraphicsContext::restorePlatformState()
282 {
283     m_data->p()->restore();
284
285     if (!m_data->currentPath.isEmpty() && m_common->state.pathTransform.isInvertible()) {
286         QTransform matrix = m_common->state.pathTransform;
287         m_data->currentPath = m_data->currentPath * matrix;
288     }
289 }
290
291 /* FIXME: DISABLED WHILE MERGING BACK FROM UNITY
292 void GraphicsContext::drawTextShadow(const TextRun& run, const IntPoint& point, const FontStyle& style)
293 {
294     if (paintingDisabled())
295         return;
296
297     if (m_data->shadow.isNull())
298         return;
299
300     TextShadow* shadow = &m_data->shadow;
301
302     if (shadow->blur <= 0) {
303         Pen p = pen();
304         setPen(shadow->color);
305         font().drawText(this, run, style, IntPoint(point.x() + shadow->x, point.y() + shadow->y));
306         setPen(p);
307     } else {
308         const int thickness = shadow->blur;
309         // FIXME: OPTIMIZE: limit the area to only the actually painted area + 2*thickness
310         const int w = m_data->p()->device()->width();
311         const int h = m_data->p()->device()->height();
312         const QRgb color = qRgb(255, 255, 255);
313         const QRgb bgColor = qRgb(0, 0, 0);
314         QImage image(QSize(w, h), QImage::Format_ARGB32);
315         image.fill(bgColor);
316         QPainter p;
317
318         Pen curPen = pen();
319         p.begin(&image);
320         setPen(color);
321         m_data->redirect = &p;
322         font().drawText(this, run, style, IntPoint(point.x() + shadow->x, point.y() + shadow->y));
323         m_data->redirect = 0;
324         p.end();
325         setPen(curPen);
326
327         int md = thickness * thickness; // max-dist^2
328
329         // blur map/precalculated shadow-decay
330         float* bmap = (float*) alloca(sizeof(float) * (md + 1));
331         for (int n = 0; n <= md; n++) {
332             float f;
333             f = n / (float) (md + 1);
334             f = 1.0 - f * f;
335             bmap[n] = f;
336         }
337
338         float factor = 0.0; // maximal potential opacity-sum
339         for (int n = -thickness; n <= thickness; n++) {
340             for (int m = -thickness; m <= thickness; m++) {
341                 int d = n * n + m * m;
342                 if (d <= md)
343                     factor += bmap[d];
344             }
345         }
346
347         // alpha map
348         float* amap = (float*) alloca(sizeof(float) * (h * w));
349         memset(amap, 0, h * w * (sizeof(float)));
350
351         for (int j = thickness; j<h-thickness; j++) {
352             for (int i = thickness; i<w-thickness; i++) {
353                 QRgb col = image.pixel(i,j);
354                 if (col == bgColor)
355                     continue;
356
357                 float g = qAlpha(col);
358                 g = g / 255;
359
360                 for (int n = -thickness; n <= thickness; n++) {
361                     for (int m = -thickness; m <= thickness; m++) {
362                         int d = n * n + m * m;
363                         if (d > md)
364                             continue;
365
366                         float f = bmap[d];
367                         amap[(i + m) + (j + n) * w] += (g * f);
368                     }
369                 }
370             }
371         }
372
373         QImage res(QSize(w,h),QImage::Format_ARGB32);
374         int r = shadow->color.red();
375         int g = shadow->color.green();
376         int b = shadow->color.blue();
377         int a1 = shadow->color.alpha();
378
379         // arbitratry factor adjustment to make shadows more solid.
380         factor = 1.333 / factor;
381
382         for (int j = 0; j < h; j++) {
383             for (int i = 0; i < w; i++) {
384                 int a = (int) (amap[i + j * w] * factor * a1);
385                 if (a > 255)
386                     a = 255;
387
388                 res.setPixel(i,j, qRgba(r, g, b, a));
389             }
390         }
391
392         m_data->p()->drawImage(0, 0, res, 0, 0, -1, -1, Qt::DiffuseAlphaDither | Qt::ColorOnly | Qt::PreferDither);
393     }
394 }
395 */
396
397 // Draws a filled rectangle with a stroked border.
398 void GraphicsContext::drawRect(const IntRect& rect)
399 {
400     if (paintingDisabled())
401         return;
402
403     QPainter *p = m_data->p();
404     const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
405     p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines);
406
407     p->drawRect(rect);
408
409     p->setRenderHint(QPainter::Antialiasing, antiAlias);
410 }
411
412 // FIXME: Now that this is refactored, it should be shared by all contexts.
413 static void adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth,
414                                         const StrokeStyle& penStyle)
415 {
416     // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic
417     // works out.  For example, with a border width of 3, KHTML will pass us (y1+y2)/2, e.g.,
418     // (50+53)/2 = 103/2 = 51 when we want 51.5.  It is always true that an even width gave
419     // us a perfect position, but an odd width gave us a position that is off by exactly 0.5.
420     if (penStyle == DottedStroke || penStyle == DashedStroke) {
421         if (p1.x() == p2.x()) {
422             p1.setY(p1.y() + strokeWidth);
423             p2.setY(p2.y() - strokeWidth);
424         } else {
425             p1.setX(p1.x() + strokeWidth);
426             p2.setX(p2.x() - strokeWidth);
427         }
428     }
429
430     if (((int) strokeWidth) % 2) {
431         if (p1.x() == p2.x()) {
432             // We're a vertical line.  Adjust our x.
433             p1.setX(p1.x() + 0.5);
434             p2.setX(p2.x() + 0.5);
435         } else {
436             // We're a horizontal line. Adjust our y.
437             p1.setY(p1.y() + 0.5);
438             p2.setY(p2.y() + 0.5);
439         }
440     }
441 }
442
443 // This is only used to draw borders.
444 void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
445 {
446     if (paintingDisabled())
447         return;
448
449     FloatPoint p1 = point1;
450     FloatPoint p2 = point2;
451
452     QPainter *p = m_data->p();
453     const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
454     p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines);
455     adjustLineToPixelBoundaries(p1, p2, strokeThickness(), strokeStyle());
456
457     IntSize shadowSize;
458     int shadowBlur;
459     Color shadowColor;
460     if (textDrawingMode() == cTextFill && getShadow(shadowSize, shadowBlur, shadowColor)) {
461         p->save();
462         p->translate(shadowSize.width(), shadowSize.height());
463         p->setPen(QColor(shadowColor));
464         p->drawLine(p1, p2);
465         p->restore();
466     }
467
468     p->drawLine(p1, p2);
469
470     p->setRenderHint(QPainter::Antialiasing, antiAlias);
471 }
472
473 // This method is only used to draw the little circles used in lists.
474 void GraphicsContext::drawEllipse(const IntRect& rect)
475 {
476     if (paintingDisabled())
477         return;
478
479     m_data->p()->drawEllipse(rect);
480 }
481
482 void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSpan)
483 {
484     if (paintingDisabled() || strokeStyle() == NoStroke || strokeThickness() <= 0.0f || !strokeColor().alpha())
485         return;
486
487     QPainter *p = m_data->p();
488     const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
489     p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines);
490
491     p->drawArc(rect, startAngle * 16, angleSpan * 16);
492
493     p->setRenderHint(QPainter::Antialiasing, antiAlias);
494 }
495
496 void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool shouldAntialias)
497 {
498     if (paintingDisabled())
499         return;
500
501     if (npoints <= 1)
502         return;
503
504     QPolygonF polygon(npoints);
505
506     for (size_t i = 0; i < npoints; i++)
507         polygon[i] = points[i];
508
509     QPainter *p = m_data->p();
510     p->save();
511     p->setRenderHint(QPainter::Antialiasing, shouldAntialias);
512     p->drawConvexPolygon(polygon);
513     p->restore();
514 }
515
516 QPen GraphicsContext::pen()
517 {
518     if (paintingDisabled())
519         return QPen();
520
521     QPainter *p = m_data->p();
522     return p->pen();
523 }
524
525 void GraphicsContext::fillPath()
526 {
527     if (paintingDisabled())
528         return;
529
530     QPainter *p = m_data->p();
531     QPainterPath path = m_data->currentPath;
532
533     switch (m_common->state.fillColorSpace) {
534     case SolidColorSpace:
535         if (fillColor().alpha())
536             p->fillPath(path, p->brush());
537         break;
538     case PatternColorSpace: {
539         TransformationMatrix affine;
540         p->fillPath(path, QBrush(m_common->state.fillPattern->createPlatformPattern(affine)));
541         break;
542     }
543     case GradientColorSpace:
544         QBrush brush(*m_common->state.fillGradient->platformGradient());
545         brush.setTransform(m_common->state.fillGradient->gradientSpaceTransform());
546         p->fillPath(path, brush);
547         break;
548     }
549     m_data->currentPath = QPainterPath();
550 }
551
552 void GraphicsContext::strokePath()
553 {
554     if (paintingDisabled())
555         return;
556
557     QPainter *p = m_data->p();
558     QPen pen = p->pen();
559     QPainterPath path = m_data->currentPath;
560
561     switch (m_common->state.strokeColorSpace) {
562     case SolidColorSpace:
563         if (strokeColor().alpha())
564             p->strokePath(path, pen);
565         break;
566     case PatternColorSpace: {
567         TransformationMatrix affine;
568         pen.setBrush(QBrush(m_common->state.strokePattern->createPlatformPattern(affine)));
569         p->setPen(pen);
570         p->strokePath(path, pen);
571         break;
572     }
573     case GradientColorSpace: {
574         QBrush brush(*m_common->state.strokeGradient->platformGradient());
575         brush.setTransform(m_common->state.strokeGradient->gradientSpaceTransform());
576         pen.setBrush(brush);
577         p->setPen(pen);
578         p->strokePath(path, pen);
579         break;
580     }
581     }
582     m_data->currentPath = QPainterPath();
583 }
584
585 void GraphicsContext::fillRect(const FloatRect& rect)
586 {
587     if (paintingDisabled())
588         return;
589
590     QPainter *p = m_data->p();
591
592     switch (m_common->state.fillColorSpace) {
593     case SolidColorSpace:
594         if (fillColor().alpha())
595             p->fillRect(rect, p->brush());
596         break;
597     case PatternColorSpace: {
598         TransformationMatrix affine;
599         p->fillRect(rect, QBrush(m_common->state.fillPattern->createPlatformPattern(affine)));
600         break;
601     }
602     case GradientColorSpace:
603         QBrush brush(*m_common->state.fillGradient->platformGradient());
604         brush.setTransform(m_common->state.fillGradient->gradientSpaceTransform());
605         p->fillRect(rect, brush);
606         break;
607     }
608     m_data->currentPath = QPainterPath();
609 }
610
611 void GraphicsContext::fillRect(const FloatRect& rect, const Color& c)
612 {
613     if (paintingDisabled())
614         return;
615
616     m_data->solidColor.setColor(QColor(c));
617     m_data->p()->fillRect(rect, m_data->solidColor);
618 }
619
620 void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color)
621 {
622     if (paintingDisabled() || !color.alpha())
623         return;
624
625     Path path = Path::createRoundedRectangle(rect, topLeft, topRight, bottomLeft, bottomRight);
626     m_data->p()->fillPath(*path.platformPath(), QColor(color));
627 }
628
629 void GraphicsContext::beginPath()
630 {
631     m_data->currentPath = QPainterPath();
632 }
633
634 void GraphicsContext::addPath(const Path& path)
635 {
636     QPainterPath newPath = m_data->currentPath;
637     newPath.addPath(*(path.platformPath()));
638     m_data->currentPath = newPath;
639 }
640
641 bool GraphicsContext::inTransparencyLayer() const
642 {
643     return !m_data->layers.isEmpty();
644 }
645
646 PlatformPath* GraphicsContext::currentPath()
647 {
648     return &m_data->currentPath;
649 }
650
651 void GraphicsContext::clip(const FloatRect& rect)
652 {
653     if (paintingDisabled())
654         return;
655
656     m_data->p()->setClipRect(rect, Qt::IntersectClip);
657 }
658
659 void GraphicsContext::clipPath(WindRule clipRule)
660 {
661     if (paintingDisabled())
662         return;
663
664     QPainter *p = m_data->p();
665     QPainterPath newPath = m_data->currentPath;
666     newPath.setFillRule(clipRule == RULE_EVENODD ? Qt::OddEvenFill : Qt::WindingFill);
667     p->setClipPath(newPath);
668 }
669
670 /**
671  * Focus ring handling is not handled here. Qt style in 
672  * RenderTheme handles drawing focus on widgets which 
673  * need it.
674  */
675 Color focusRingColor() { return Color(0, 0, 0); }
676 void GraphicsContext::drawFocusRing(const Color& color)
677 {
678     if (paintingDisabled())
679         return;
680
681     const Vector<IntRect>& rects = focusRingRects();
682     unsigned rectCount = rects.size();
683
684     if (rects.size() == 0)
685         return;
686
687     QPainter *p = m_data->p();
688     const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
689     p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines);
690
691     const QPen oldPen = p->pen();
692     const QBrush oldBrush = p->brush();
693
694     QPen nPen = p->pen();
695     nPen.setColor(color);
696     p->setBrush(Qt::NoBrush);
697     nPen.setStyle(Qt::DotLine);
698     p->setPen(nPen);
699 #if 0
700     // FIXME How do we do a bounding outline with Qt?
701     QPainterPath path;
702     for (int i = 0; i < rectCount; ++i)
703         path.addRect(QRectF(rects[i]));
704     QPainterPathStroker stroker;
705     QPainterPath newPath = stroker.createStroke(path);
706     p->strokePath(newPath, nPen);
707 #else
708     for (int i = 0; i < rectCount; ++i)
709         p->drawRect(QRectF(rects[i]));
710 #endif
711     p->setPen(oldPen);
712     p->setBrush(oldBrush);
713
714     p->setRenderHint(QPainter::Antialiasing, antiAlias);
715 }
716
717 void GraphicsContext::drawLineForText(const IntPoint& origin, int width, bool printing)
718 {
719     if (paintingDisabled())
720         return;
721
722     IntPoint endPoint = origin + IntSize(width, 0);
723     drawLine(origin, endPoint);
724 }
725
726 void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint&,
727                                                          int width, bool grammar)
728 {
729     if (paintingDisabled())
730         return;
731
732     notImplemented();
733 }
734
735 FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect)
736 {
737     QRectF rect(frect);
738     rect = m_data->p()->deviceMatrix().mapRect(rect);
739
740     QRect result = rect.toRect(); //round it
741     return FloatRect(QRectF(result));
742 }
743
744 void GraphicsContext::setPlatformShadow(const IntSize& pos, int blur, const Color &color)
745 {
746     // Qt doesn't support shadows natively, they are drawn manually in the draw*
747     // functions
748 }
749
750 void GraphicsContext::clearPlatformShadow()
751 {
752     // Qt doesn't support shadows natively, they are drawn manually in the draw*
753     // functions
754 }
755
756 void GraphicsContext::beginTransparencyLayer(float opacity)
757 {
758     if (paintingDisabled())
759         return;
760
761     int x, y, w, h;
762     x = y = 0;
763     QPainter *p = m_data->p();
764     const QPaintDevice *device = p->device();
765     w = device->width();
766     h = device->height();
767
768     QRectF clip = p->clipPath().boundingRect();
769     QRectF deviceClip = p->transform().mapRect(clip);
770     x = int(qBound(qreal(0), deviceClip.x(), (qreal)w));
771     y = int(qBound(qreal(0), deviceClip.y(), (qreal)h));
772     w = int(qBound(qreal(0), deviceClip.width(), (qreal)w) + 2);
773     h = int(qBound(qreal(0), deviceClip.height(), (qreal)h) + 2);
774
775     TransparencyLayer * layer = new TransparencyLayer(m_data->p(), QRect(x, y, w, h));
776
777     layer->opacity = opacity;
778     m_data->layers.push(layer);
779 }
780
781 void GraphicsContext::endTransparencyLayer()
782 {
783     if (paintingDisabled())
784         return;
785
786     TransparencyLayer *layer = m_data->layers.pop();
787     layer->painter.end();
788
789     QPainter *p = m_data->p();
790     p->save();
791     p->resetTransform();
792     p->setOpacity(layer->opacity);
793     p->drawPixmap(layer->offset, layer->pixmap);
794     p->restore();
795
796     delete layer;
797 }
798
799 void GraphicsContext::clearRect(const FloatRect& rect)
800 {
801     if (paintingDisabled())
802         return;
803
804     QPainter *p = m_data->p();
805     QPainter::CompositionMode currentCompositionMode = p->compositionMode();
806     if (p->paintEngine()->hasFeature(QPaintEngine::PorterDuff))
807         p->setCompositionMode(QPainter::CompositionMode_Source);
808     p->fillRect(rect, Qt::transparent);
809     if (p->paintEngine()->hasFeature(QPaintEngine::PorterDuff))
810         p->setCompositionMode(currentCompositionMode);
811 }
812
813 void GraphicsContext::strokeRect(const FloatRect& rect, float width)
814 {
815     if (paintingDisabled())
816         return;
817
818     QPainterPath path;
819     path.addRect(rect);
820     setStrokeThickness(width);
821     m_data->currentPath = path;
822
823     strokePath();
824 }
825
826 void GraphicsContext::setLineCap(LineCap lc)
827 {
828     if (paintingDisabled())
829         return;
830
831     QPainter *p = m_data->p();
832     QPen nPen = p->pen();
833     nPen.setCapStyle(toQtLineCap(lc));
834     p->setPen(nPen);
835 }
836
837 void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
838 {
839     QPainter* p = m_data->p();
840     QPen pen = p->pen();
841     unsigned dashLength = dashes.size();
842     if (dashLength) {
843         QVector<qreal> pattern;
844         unsigned count = dashLength;
845         if (dashLength % 2)
846             count *= 2;
847
848         float penWidth = narrowPrecisionToFloat(double(pen.widthF()));
849         for (unsigned i = 0; i < count; i++)
850             pattern.append(dashes[i % dashLength] / penWidth);
851
852         pen.setDashPattern(pattern);
853         pen.setDashOffset(dashOffset);
854     }
855     p->setPen(pen);
856 }
857
858 void GraphicsContext::setLineJoin(LineJoin lj)
859 {
860     if (paintingDisabled())
861         return;
862
863     QPainter *p = m_data->p();
864     QPen nPen = p->pen();
865     nPen.setJoinStyle(toQtLineJoin(lj));
866     p->setPen(nPen);
867 }
868
869 void GraphicsContext::setMiterLimit(float limit)
870 {
871     if (paintingDisabled())
872         return;
873
874     QPainter *p = m_data->p();
875     QPen nPen = p->pen();
876     nPen.setMiterLimit(limit);
877     p->setPen(nPen);
878 }
879
880 void GraphicsContext::setAlpha(float opacity)
881 {
882     if (paintingDisabled())
883         return;
884     QPainter *p = m_data->p();
885     p->setOpacity(opacity);
886 }
887
888 void GraphicsContext::setCompositeOperation(CompositeOperator op)
889 {
890     if (paintingDisabled())
891         return;
892
893     if (m_data->p()->paintEngine()->hasFeature(QPaintEngine::PorterDuff))
894         m_data->p()->setCompositionMode(toQtCompositionMode(op));
895 }
896
897 void GraphicsContext::clip(const Path& path)
898 {
899     if (paintingDisabled())
900         return;
901
902     m_data->p()->setClipPath(*path.platformPath(), Qt::IntersectClip);
903 }
904
905 void GraphicsContext::clipOut(const Path& path)
906 {
907     if (paintingDisabled())
908         return;
909
910     QPainter *p = m_data->p();
911     QRectF clipBounds = p->clipPath().boundingRect();
912     QPainterPath clippedOut = *path.platformPath();
913     QPainterPath newClip;
914     newClip.setFillRule(Qt::OddEvenFill);
915     newClip.addRect(clipBounds);
916     newClip.addPath(clippedOut);
917
918     p->setClipPath(newClip, Qt::IntersectClip);
919 }
920
921 void GraphicsContext::translate(float x, float y)
922 {
923     if (paintingDisabled())
924         return;
925
926     m_data->p()->translate(x, y);
927
928     if (!m_data->currentPath.isEmpty()) {
929         QTransform matrix;
930         m_data->currentPath = m_data->currentPath * matrix.translate(-x, -y);
931         m_common->state.pathTransform.translate(x, y);
932     }
933 }
934
935 IntPoint GraphicsContext::origin()
936 {
937     if (paintingDisabled())
938         return IntPoint();
939     const QTransform &transform = m_data->p()->transform();
940     return IntPoint(qRound(transform.dx()), qRound(transform.dy()));
941 }
942
943 void GraphicsContext::rotate(float radians)
944 {
945     if (paintingDisabled())
946         return;
947
948     m_data->p()->rotate(180/M_PI*radians);
949
950     if (!m_data->currentPath.isEmpty()) {
951         QTransform matrix;
952         m_data->currentPath = m_data->currentPath * matrix.rotate(-180/M_PI*radians);
953         m_common->state.pathTransform.rotate(radians);
954     }
955 }
956
957 void GraphicsContext::scale(const FloatSize& s)
958 {
959     if (paintingDisabled())
960         return;
961
962     m_data->p()->scale(s.width(), s.height());
963
964     if (!m_data->currentPath.isEmpty()) {
965         QTransform matrix;
966         m_data->currentPath = m_data->currentPath * matrix.scale(1 / s.width(), 1 / s.height());
967         m_common->state.pathTransform.scaleNonUniform(s.width(), s.height());
968     }
969 }
970
971 void GraphicsContext::clipOut(const IntRect& rect)
972 {
973     if (paintingDisabled())
974         return;
975
976     QPainter *p = m_data->p();
977     QRectF clipBounds = p->clipPath().boundingRect();
978     QPainterPath newClip;
979     newClip.setFillRule(Qt::OddEvenFill);
980     newClip.addRect(clipBounds);
981     newClip.addRect(QRect(rect));
982
983     p->setClipPath(newClip, Qt::IntersectClip);
984 }
985
986 void GraphicsContext::clipOutEllipseInRect(const IntRect& rect)
987 {
988     if (paintingDisabled())
989         return;
990
991     QPainter *p = m_data->p();
992     QRectF clipBounds = p->clipPath().boundingRect();
993     QPainterPath newClip;
994     newClip.setFillRule(Qt::OddEvenFill);
995     newClip.addRect(clipBounds);
996     newClip.addEllipse(QRect(rect));
997
998     p->setClipPath(newClip, Qt::IntersectClip);
999 }
1000
1001 void GraphicsContext::clipToImageBuffer(const FloatRect&, const ImageBuffer*)
1002 {
1003     notImplemented();
1004 }
1005
1006 void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect,
1007                                               int thickness)
1008 {
1009     if (paintingDisabled())
1010         return;
1011
1012     clip(rect);
1013     QPainterPath path;
1014
1015     // Add outer ellipse
1016     path.addEllipse(QRectF(rect.x(), rect.y(), rect.width(), rect.height()));
1017
1018     // Add inner ellipse.
1019     path.addEllipse(QRectF(rect.x() + thickness, rect.y() + thickness,
1020                            rect.width() - (thickness * 2), rect.height() - (thickness * 2)));
1021
1022     path.setFillRule(Qt::OddEvenFill);
1023     m_data->p()->setClipPath(path, Qt::IntersectClip);
1024 }
1025
1026 void GraphicsContext::concatCTM(const TransformationMatrix& transform)
1027 {
1028     if (paintingDisabled())
1029         return;
1030
1031     m_data->p()->setWorldTransform(transform, true);
1032
1033     // Transformations to the context shouldn't transform the currentPath. 
1034     // We have to undo every change made to the context from the currentPath to avoid wrong drawings.
1035     if (!m_data->currentPath.isEmpty() && transform.isInvertible()) {
1036         QTransform matrix = transform.inverse();
1037         m_data->currentPath = m_data->currentPath * matrix;
1038         m_common->state.pathTransform.multiply(transform);
1039     }
1040 }
1041
1042 void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
1043 {
1044     notImplemented();
1045 }
1046
1047 void GraphicsContext::setPlatformStrokeColor(const Color& color)
1048 {
1049     if (paintingDisabled())
1050         return;
1051     QPainter *p = m_data->p();
1052     QPen newPen(p->pen());
1053     newPen.setColor(color);
1054     p->setPen(newPen);
1055 }
1056
1057 void GraphicsContext::setPlatformStrokeStyle(const StrokeStyle& strokeStyle)
1058 {
1059     if (paintingDisabled())
1060         return;
1061     QPainter *p = m_data->p();
1062     QPen newPen(p->pen());
1063     newPen.setStyle(toQPenStyle(strokeStyle));
1064     p->setPen(newPen);
1065 }
1066
1067 void GraphicsContext::setPlatformStrokeThickness(float thickness)
1068 {
1069     if (paintingDisabled())
1070         return;
1071     QPainter *p = m_data->p();
1072     QPen newPen(p->pen());
1073     newPen.setWidthF(thickness);
1074     p->setPen(newPen);
1075 }
1076
1077 void GraphicsContext::setPlatformFillColor(const Color& color)
1078 {
1079     if (paintingDisabled())
1080         return;
1081     m_data->p()->setBrush(QBrush(color));
1082 }
1083
1084 void GraphicsContext::setPlatformShouldAntialias(bool enable)
1085 {
1086     if (paintingDisabled())
1087         return;
1088     m_data->p()->setRenderHint(QPainter::Antialiasing, enable);
1089 }
1090
1091 #ifdef Q_WS_WIN
1092 #include <windows.h>
1093
1094 HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap)
1095 {
1096     // painting through native HDC is only supported for plugin, where mayCreateBitmap is always true
1097     Q_ASSERT(mayCreateBitmap);
1098
1099     if (dstRect.isEmpty())
1100         return 0;
1101
1102     // Create a bitmap DC in which to draw.
1103     BITMAPINFO bitmapInfo;
1104     bitmapInfo.bmiHeader.biSize          = sizeof(BITMAPINFOHEADER);
1105     bitmapInfo.bmiHeader.biWidth         = dstRect.width();
1106     bitmapInfo.bmiHeader.biHeight        = dstRect.height();
1107     bitmapInfo.bmiHeader.biPlanes        = 1;
1108     bitmapInfo.bmiHeader.biBitCount      = 32;
1109     bitmapInfo.bmiHeader.biCompression   = BI_RGB;
1110     bitmapInfo.bmiHeader.biSizeImage     = 0;
1111     bitmapInfo.bmiHeader.biXPelsPerMeter = 0;
1112     bitmapInfo.bmiHeader.biYPelsPerMeter = 0;
1113     bitmapInfo.bmiHeader.biClrUsed       = 0;
1114     bitmapInfo.bmiHeader.biClrImportant  = 0;
1115
1116     void* pixels = 0;
1117     HBITMAP bitmap = ::CreateDIBSection(NULL, &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0);
1118     if (!bitmap)
1119         return 0;
1120
1121     HDC displayDC = ::GetDC(0);
1122     HDC bitmapDC = ::CreateCompatibleDC(displayDC);
1123     ::ReleaseDC(0, displayDC);
1124
1125     ::SelectObject(bitmapDC, bitmap);
1126
1127     // Fill our buffer with clear if we're going to alpha blend.
1128     if (supportAlphaBlend) {
1129         BITMAP bmpInfo;
1130         GetObject(bitmap, sizeof(bmpInfo), &bmpInfo);
1131         int bufferSize = bmpInfo.bmWidthBytes * bmpInfo.bmHeight;
1132         memset(bmpInfo.bmBits, 0, bufferSize);
1133     }
1134
1135 #if !PLATFORM(WIN_CE)
1136     // Make sure we can do world transforms.
1137     SetGraphicsMode(bitmapDC, GM_ADVANCED);
1138
1139     // Apply a translation to our context so that the drawing done will be at (0,0) of the bitmap.
1140     XFORM xform;
1141     xform.eM11 = 1.0f;
1142     xform.eM12 = 0.0f;
1143     xform.eM21 = 0.0f;
1144     xform.eM22 = 1.0f;
1145     xform.eDx = -dstRect.x();
1146     xform.eDy = -dstRect.y();
1147     ::SetWorldTransform(bitmapDC, &xform);
1148 #endif
1149
1150     return bitmapDC;
1151 }
1152
1153 void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap)
1154 {
1155     // painting through native HDC is only supported for plugin, where mayCreateBitmap is always true
1156     Q_ASSERT(mayCreateBitmap);
1157
1158     if (hdc) {
1159
1160         if (!dstRect.isEmpty()) {
1161
1162             HBITMAP bitmap = static_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP));
1163             BITMAP info;
1164             GetObject(bitmap, sizeof(info), &info);
1165             ASSERT(info.bmBitsPixel == 32);
1166
1167             QPixmap pixmap = QPixmap::fromWinHBITMAP(bitmap, supportAlphaBlend ? QPixmap::PremultipliedAlpha : QPixmap::NoAlpha);
1168             m_data->p()->drawPixmap(dstRect, pixmap);
1169
1170             ::DeleteObject(bitmap);
1171         }
1172
1173         ::DeleteDC(hdc);
1174     }
1175 }
1176 #endif
1177
1178 void GraphicsContext::setImageInterpolationQuality(InterpolationQuality)
1179 {
1180 }
1181
1182 InterpolationQuality GraphicsContext::imageInterpolationQuality() const
1183 {
1184     return InterpolationDefault;
1185 }
1186
1187 }
1188
1189 // vim: ts=4 sw=4 et