78c682f559d783fcc90419557440fc673d039236
[WebKit-https.git] / Source / 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  * Copyright (C) 2010, 2011 Sencha, Inc.
12  * Copyright (C) 2011 Andreas Kling <kling@webkit.org>
13  *
14  * All rights reserved.
15  *
16  * Redistribution and use in source and binary forms, with or without
17  * modification, are permitted provided that the following conditions
18  * are met:
19  * 1. Redistributions of source code must retain the above copyright
20  *    notice, this list of conditions and the following disclaimer.
21  * 2. Redistributions in binary form must reproduce the above copyright
22  *    notice, this list of conditions and the following disclaimer in the
23  *    documentation and/or other materials provided with the distribution.
24  *
25  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
26  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
29  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
32  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
33  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36  */
37
38 #include "config.h"
39 #include "GraphicsContext.h"
40
41 #if OS(WINDOWS)
42 #include <windows.h>
43 #endif
44
45 #include "AffineTransform.h"
46 #include "Color.h"
47 #include "FloatConversion.h"
48 #include "Font.h"
49 #include "ImageBuffer.h"
50 #include "NotImplemented.h"
51 #include "Path.h"
52 #include "Pattern.h"
53 #include "ShadowBlur.h"
54 #include "TransparencyLayer.h"
55
56 #include <QBrush>
57 #include <QGradient>
58 #include <QPaintDevice>
59 #include <QPaintEngine>
60 #include <QPainter>
61 #include <QPainterPath>
62 #include <QPainterPathStroker>
63 #include <QPixmap>
64 #include <QPolygonF>
65 #include <QStack>
66 #include <QVector>
67 #include <wtf/MathExtras.h>
68
69 #if OS(WINDOWS)
70 QT_BEGIN_NAMESPACE
71 Q_GUI_EXPORT QPixmap qt_pixmapFromWinHBITMAP(HBITMAP, int hbitmapFormat = 0);
72 QT_END_NAMESPACE
73
74 enum HBitmapFormat {
75     HBitmapNoAlpha,
76     HBitmapPremultipliedAlpha,
77     HBitmapAlpha
78 };
79 #endif
80
81 namespace WebCore {
82
83 static inline QPainter::CompositionMode toQtCompositionMode(CompositeOperator op)
84 {
85     switch (op) {
86     case CompositeClear:
87         return QPainter::CompositionMode_Clear;
88     case CompositeCopy:
89         return QPainter::CompositionMode_Source;
90     case CompositeSourceOver:
91         return QPainter::CompositionMode_SourceOver;
92     case CompositeSourceIn:
93         return QPainter::CompositionMode_SourceIn;
94     case CompositeSourceOut:
95         return QPainter::CompositionMode_SourceOut;
96     case CompositeSourceAtop:
97         return QPainter::CompositionMode_SourceAtop;
98     case CompositeDestinationOver:
99         return QPainter::CompositionMode_DestinationOver;
100     case CompositeDestinationIn:
101         return QPainter::CompositionMode_DestinationIn;
102     case CompositeDestinationOut:
103         return QPainter::CompositionMode_DestinationOut;
104     case CompositeDestinationAtop:
105         return QPainter::CompositionMode_DestinationAtop;
106     case CompositeXOR:
107         return QPainter::CompositionMode_Xor;
108     case CompositePlusDarker:
109         // there is no exact match, but this is the closest
110         return QPainter::CompositionMode_Darken;
111     case CompositePlusLighter:
112         return QPainter::CompositionMode_Plus;
113     case CompositeDifference:
114         return QPainter::CompositionMode_Difference;
115     default:
116         ASSERT_NOT_REACHED();
117     }
118
119     return QPainter::CompositionMode_SourceOver;
120 }
121
122 static inline Qt::PenCapStyle toQtLineCap(LineCap lc)
123 {
124     switch (lc) {
125     case ButtCap:
126         return Qt::FlatCap;
127     case RoundCap:
128         return Qt::RoundCap;
129     case SquareCap:
130         return Qt::SquareCap;
131     default:
132         ASSERT_NOT_REACHED();
133     }
134
135     return Qt::FlatCap;
136 }
137
138 static inline Qt::PenJoinStyle toQtLineJoin(LineJoin lj)
139 {
140     switch (lj) {
141     case MiterJoin:
142         return Qt::SvgMiterJoin;
143     case RoundJoin:
144         return Qt::RoundJoin;
145     case BevelJoin:
146         return Qt::BevelJoin;
147     default:
148         ASSERT_NOT_REACHED();
149     }
150
151     return Qt::SvgMiterJoin;
152 }
153
154 static Qt::PenStyle toQPenStyle(StrokeStyle style)
155 {
156     switch (style) {
157     case NoStroke:
158         return Qt::NoPen;
159         break;
160     case SolidStroke:
161 #if ENABLE(CSS3_TEXT)
162     case DoubleStroke:
163     case WavyStroke: // FIXME: https://bugs.webkit.org/show_bug.cgi?id=93507 - Needs platform support.
164 #endif
165         return Qt::SolidLine;
166         break;
167     case DottedStroke:
168         return Qt::DotLine;
169         break;
170     case DashedStroke:
171         return Qt::DashLine;
172         break;
173     default:
174         ASSERT_NOT_REACHED();
175     }
176     return Qt::NoPen;
177 }
178
179 static inline Qt::FillRule toQtFillRule(WindRule rule)
180 {
181     switch (rule) {
182     case RULE_EVENODD:
183         return Qt::OddEvenFill;
184     case RULE_NONZERO:
185         return Qt::WindingFill;
186     default:
187         ASSERT_NOT_REACHED();
188     }
189     return Qt::OddEvenFill;
190 }
191
192 static inline void adjustPointsForDottedLine(FloatPoint& p1, FloatPoint& p2, float width, bool isVerticalLine)
193 {
194     if (isVerticalLine) {
195         p1.setY(p1.y() - width / 2);
196         p2.setY(p2.y() + width / 2);
197     } else {
198         p1.setX(p1.x() - width / 2);
199         p2.setX(p2.x() + width / 2);
200     }
201 }
202
203 static inline void drawLineEndpointsForStyle(QPainter *painter, const FloatPoint& p1, const FloatPoint& p2, float width, bool isVerticalLine, StrokeStyle style, Color color)
204 {
205     // Do a rect fill of our endpoints. This ensures we always have the
206     // appearance of being a border.
207     if (style == DashedStroke) {
208         if (isVerticalLine) {
209             painter->fillRect(FloatRect(p1.x() - width / 2, p1.y() - width, width, width), QColor(color));
210             painter->fillRect(FloatRect(p2.x() - width / 2, p2.y(), width, width), QColor(color));
211         } else {
212             painter->fillRect(FloatRect(p1.x() - width, p1.y() - width / 2, width, width), QColor(color));
213             painter->fillRect(FloatRect(p2.x(), p2.y() - width / 2, width, width), QColor(color));
214         }
215     }
216
217     // As per css spec a dotted stroke should be made of circles so we're
218     // drawing circles as endpoints.
219     if (style == DottedStroke) {
220         painter->setPen(Qt::NoPen);
221         painter->setBrush(QColor(color));
222         painter->drawEllipse(p1.x() - width / 2, p1.y() - width / 2, width, width);
223         painter->drawEllipse(p2.x() - width / 2, p2.y() - width / 2, width, width);
224     }
225 }
226
227 class GraphicsContextPlatformPrivate {
228     WTF_MAKE_NONCOPYABLE(GraphicsContextPlatformPrivate); WTF_MAKE_FAST_ALLOCATED;
229 public:
230     GraphicsContextPlatformPrivate(QPainter*, const QColor& initialSolidColor);
231     ~GraphicsContextPlatformPrivate();
232
233     inline QPainter* p() const
234     {
235         if (layers.isEmpty())
236             return painter;
237         return &layers.top()->painter;
238     }
239
240     bool antiAliasingForRectsAndLines;
241
242     QStack<TransparencyLayer*> layers;
243     // Counting real layers. Required by isInTransparencyLayer() calls
244     // For example, layers with valid alphaMask are not real layers
245     int layerCount;
246
247     // reuse this brush for solid color (to prevent expensive QBrush construction)
248     QBrush solidColor;
249
250     InterpolationQuality imageInterpolationQuality;
251     bool initialSmoothPixmapTransformHint;
252
253     ShadowBlur* shadow;
254
255     QRectF clipBoundingRect() const
256     {
257         return p()->clipBoundingRect();
258     }
259
260     void takeOwnershipOfPlatformContext() { platformContextIsOwned = true; }
261
262 private:
263     QPainter* painter;
264     bool platformContextIsOwned;
265 };
266
267 GraphicsContextPlatformPrivate::GraphicsContextPlatformPrivate(QPainter* p, const QColor& initialSolidColor)
268     : antiAliasingForRectsAndLines(false)
269     , layerCount(0)
270     , solidColor(initialSolidColor)
271     , imageInterpolationQuality(InterpolationDefault)
272     , initialSmoothPixmapTransformHint(false)
273     , shadow(new ShadowBlur())
274     , painter(p)
275     , platformContextIsOwned(false)
276 {
277     if (!painter)
278         return;
279
280     // Use the default the QPainter was constructed with.
281     antiAliasingForRectsAndLines = painter->testRenderHint(QPainter::Antialiasing);
282
283     // Used for default image interpolation quality.
284     initialSmoothPixmapTransformHint = painter->testRenderHint(QPainter::SmoothPixmapTransform);
285
286     painter->setRenderHint(QPainter::Antialiasing, true);
287
288 }
289
290 GraphicsContextPlatformPrivate::~GraphicsContextPlatformPrivate()
291 {
292     delete shadow;
293
294     if (!platformContextIsOwned)
295         return;
296
297     QPaintDevice* device = painter->device();
298     painter->end();
299     delete painter;
300     delete device;
301 }
302
303 void GraphicsContext::platformInit(PlatformGraphicsContext* painter)
304 {
305     m_data = new GraphicsContextPlatformPrivate(painter, fillColor());
306
307     setPaintingDisabled(!painter);
308
309     if (!painter)
310         return;
311
312     // solidColor is initialized with the fillColor().
313     painter->setBrush(m_data->solidColor);
314
315     QPen pen(painter->pen());
316     pen.setColor(strokeColor());
317     pen.setJoinStyle(toQtLineJoin(MiterJoin));
318     painter->setPen(pen);
319 }
320
321 void GraphicsContext::platformDestroy()
322 {
323     while (!m_data->layers.isEmpty())
324         endTransparencyLayer();
325
326     delete m_data;
327 }
328
329 PlatformGraphicsContext* GraphicsContext::platformContext() const
330 {
331     return m_data->p();
332 }
333
334 AffineTransform GraphicsContext::getCTM(IncludeDeviceScale) const
335 {
336     if (paintingDisabled())
337         return AffineTransform();
338
339     const QTransform& matrix = platformContext()->combinedTransform();
340     return AffineTransform(matrix.m11(), matrix.m12(), matrix.m21(),
341                            matrix.m22(), matrix.dx(), matrix.dy());
342 }
343
344 void GraphicsContext::savePlatformState()
345 {
346     if (!m_data->layers.isEmpty() && !m_data->layers.top()->alphaMask.isNull())
347         ++m_data->layers.top()->saveCounter;
348     m_data->p()->save();
349 }
350
351 void GraphicsContext::restorePlatformState()
352 {
353     if (!m_data->layers.isEmpty() && !m_data->layers.top()->alphaMask.isNull())
354         if (!--m_data->layers.top()->saveCounter)
355             endPlatformTransparencyLayer();
356
357     m_data->p()->restore();
358
359     m_data->shadow->setShadowValues(FloatSize(m_state.shadowBlur, m_state.shadowBlur), m_state.shadowOffset, m_state.shadowColor, m_state.shadowColorSpace, m_state.shadowsIgnoreTransforms);
360 }
361
362 // Draws a filled rectangle with a stroked border.
363 // This is only used to draw borders (real fill is done via fillRect), and
364 // thus it must not cast any shadow.
365 void GraphicsContext::drawRect(const IntRect& rect)
366 {
367     if (paintingDisabled())
368         return;
369
370     ASSERT(!rect.isEmpty());
371
372     QPainter* p = m_data->p();
373     const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
374     p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines);
375
376     p->drawRect(rect);
377
378     p->setRenderHint(QPainter::Antialiasing, antiAlias);
379 }
380
381 // This is only used to draw borders.
382 // Must not cast any shadow.
383 void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
384 {
385     if (paintingDisabled())
386         return;
387
388     StrokeStyle style = strokeStyle();
389     Color color = strokeColor();
390     if (style == NoStroke)
391         return;
392
393     float width = strokeThickness();
394
395     FloatPoint p1 = point1;
396     FloatPoint p2 = point2;
397     bool isVerticalLine = (p1.x() == p2.x());
398
399     QPainter* p = m_data->p();
400     const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
401     p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines);
402     adjustLineToPixelBoundaries(p1, p2, width, style);
403
404     Qt::PenCapStyle capStyle = Qt::FlatCap;
405     QVector<qreal> dashes;
406     int patWidth = 0;
407
408     switch (style) {
409     case NoStroke:
410     case SolidStroke:
411 #if ENABLE(CSS3_TEXT)
412     case DoubleStroke:
413     case WavyStroke: // FIXME: https://bugs.webkit.org/show_bug.cgi?id=93507 - Needs platform support.
414 #endif
415         break;
416     case DottedStroke: {
417         capStyle = Qt::RoundCap;
418         patWidth = static_cast<int>(width);
419         // The actual length of one line element can not be set to zero and at 0.1 the dots
420         // are still slightly elongated. Setting it to 0.01 will make it look like the
421         // line endings are being stuck together, close enough to look like a circle.
422         // For the distance of the line elements we subtract the small amount again.
423         const qreal lineElementLength = 0.01;
424         dashes << lineElementLength << qreal(2 * patWidth) / width - lineElementLength;
425         adjustPointsForDottedLine(p1, p2, width, isVerticalLine);
426         break;
427     }
428     case DashedStroke:
429         capStyle = Qt::FlatCap;
430         patWidth = 3 * static_cast<int>(width);
431         dashes << qreal(patWidth) / width << qreal(patWidth) / width;
432         break;
433     }
434
435     if (patWidth) {
436         p->save();
437
438         QPen pen = p->pen();
439
440         drawLineEndpointsForStyle(p, p1, p2, width, isVerticalLine, style, color);
441
442         // Example: 80 pixels with a width of 30 pixels.
443         // Remainder is 20.  The maximum pixels of line we could paint
444         // will be 50 pixels.
445         int distance = (isVerticalLine ? (point2.y() - point1.y()) : (point2.x() - point1.x())) - 2*(int)width;
446         int remainder = distance % patWidth;
447         int coverage = distance - remainder;
448         int numSegments = coverage / patWidth;
449
450         float patternOffset = 0.0f;
451         // Special case 1px dotted borders for speed.
452         if (patWidth == 1)
453             patternOffset = 1.0f;
454         else {
455             bool evenNumberOfSegments = !(numSegments % 2);
456             if (remainder)
457                 evenNumberOfSegments = !evenNumberOfSegments;
458             if (evenNumberOfSegments) {
459                 if (remainder) {
460                     patternOffset += patWidth - remainder;
461                     patternOffset += remainder / 2;
462                 } else
463                     patternOffset = patWidth / 2;
464             } else {
465                 if (remainder)
466                     patternOffset = (patWidth - remainder) / 2;
467             }
468         }
469
470         pen.setWidthF(width);
471         pen.setCapStyle(capStyle);
472         pen.setDashPattern(dashes);
473         pen.setDashOffset(patternOffset / width);
474         p->setPen(pen);
475     }
476
477     // Qt interprets geometric units as end-point inclusive, while WebCore interprets geomtric units as endpoint exclusive.
478     // This means we need to subtract one from the endpoint, or the line will be painted one pixel too long.
479     if (p1.x() == p2.x())
480         p->drawLine(p1, p2 - FloatSize(0, 1));
481     else
482         p->drawLine(p1, p2 - FloatSize(1, 0));
483
484     if (patWidth)
485         p->restore();
486
487     p->setRenderHint(QPainter::Antialiasing, antiAlias);
488 }
489
490 // This method is only used to draw the little circles used in lists.
491 void GraphicsContext::drawEllipse(const IntRect& rect)
492 {
493     if (paintingDisabled())
494         return;
495
496     m_data->p()->drawEllipse(rect);
497 }
498
499 void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool shouldAntialias)
500 {
501     if (paintingDisabled())
502         return;
503
504     if (npoints <= 1)
505         return;
506
507     QPolygonF polygon(npoints);
508
509     for (size_t i = 0; i < npoints; i++)
510         polygon[i] = points[i];
511
512     QPainter* p = m_data->p();
513
514     const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
515     p->setRenderHint(QPainter::Antialiasing, shouldAntialias);
516
517     p->drawConvexPolygon(polygon);
518
519     p->setRenderHint(QPainter::Antialiasing, antiAlias);
520 }
521
522 void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* points, bool antialiased)
523 {
524     if (paintingDisabled())
525         return;
526
527     if (numPoints <= 1)
528         return;
529
530     QPainterPath path(points[0]);
531     for (size_t i = 1; i < numPoints; ++i)
532         path.lineTo(points[i]);
533     path.setFillRule(Qt::WindingFill);
534
535     QPainter* p = m_data->p();
536
537     bool painterWasAntialiased = p->testRenderHint(QPainter::Antialiasing);
538
539     if (painterWasAntialiased != antialiased)
540         p->setRenderHint(QPainter::Antialiasing, antialiased);
541
542     p->setClipPath(path, Qt::IntersectClip);
543
544     if (painterWasAntialiased != antialiased)
545         p->setRenderHint(QPainter::Antialiasing, painterWasAntialiased);
546 }
547
548 void GraphicsContext::fillPath(const Path& path)
549 {
550     if (paintingDisabled())
551         return;
552
553     QPainter* p = m_data->p();
554     QPainterPath platformPath = path.platformPath();
555     platformPath.setFillRule(toQtFillRule(fillRule()));
556
557     if (hasShadow()) {
558         ShadowBlur* shadow = shadowBlur();
559         if (shadow->mustUseShadowBlur(this) || m_state.fillPattern || m_state.fillGradient)
560         {
561             GraphicsContext* shadowContext = shadow->beginShadowLayer(this, platformPath.controlPointRect());
562             if (shadowContext) {
563                 QPainter* shadowPainter = shadowContext->platformContext();
564                 if (m_state.fillPattern) {
565                     shadowPainter->fillPath(platformPath, QBrush(m_state.fillPattern->createPlatformPattern()));
566                 } else if (m_state.fillGradient) {
567                     QBrush brush(*m_state.fillGradient->platformGradient());
568                     brush.setTransform(m_state.fillGradient->gradientSpaceTransform());
569                     shadowPainter->fillPath(platformPath, brush);
570                 } else {
571                     shadowPainter->fillPath(platformPath, p->brush());
572                 }
573                 shadow->endShadowLayer(this);
574             }
575         } else {
576             QPointF offset(m_state.shadowOffset.width(), m_state.shadowOffset.height());
577             p->translate(offset);
578             QColor shadowColor = m_state.shadowColor;
579             shadowColor.setAlphaF(shadowColor.alphaF() * p->brush().color().alphaF());
580             p->fillPath(platformPath, shadowColor);
581             p->translate(-offset);
582         }
583     }
584     if (m_state.fillPattern) {
585         p->fillPath(platformPath, QBrush(m_state.fillPattern->createPlatformPattern()));
586     } else if (m_state.fillGradient) {
587         QBrush brush(*m_state.fillGradient->platformGradient());
588         brush.setTransform(m_state.fillGradient->gradientSpaceTransform());
589         p->fillPath(platformPath, brush);
590     } else
591         p->fillPath(platformPath, p->brush());
592 }
593
594 inline static void fillPathStroke(QPainter* painter, QPainterPathStroker& pathStroker, const QPainterPath& platformPath, const QBrush& brush)
595 {
596     QPainterPath stroke = pathStroker.createStroke(platformPath);
597     painter->fillPath(stroke, brush);
598 }
599
600 void GraphicsContext::strokePath(const Path& path)
601 {
602     if (paintingDisabled())
603         return;
604
605     QPainter* p = m_data->p();
606     QPen pen(p->pen());
607     QPainterPath platformPath = path.platformPath();
608     platformPath.setFillRule(toQtFillRule(fillRule()));
609     QPainterPathStroker pathStroker;
610     pathStroker.setJoinStyle(pen.joinStyle());
611     pathStroker.setDashOffset(pen.dashOffset());
612     pathStroker.setMiterLimit(pen.miterLimit());
613     pathStroker.setCapStyle(pen.capStyle());
614     pathStroker.setWidth(pen.widthF());
615
616     if (hasShadow()) {
617         ShadowBlur* shadow = shadowBlur();
618         if (shadow->mustUseShadowBlur(this) || m_state.strokePattern || m_state.strokeGradient)
619         {
620             FloatRect boundingRect = platformPath.controlPointRect();
621             boundingRect.inflate(pen.miterLimit() + pen.widthF());
622             GraphicsContext* shadowContext = shadow->beginShadowLayer(this, boundingRect);
623             if (shadowContext) {
624                 QPainter* shadowPainter = shadowContext->platformContext();
625                 if (m_state.strokeGradient) {
626                     QBrush brush(*m_state.strokeGradient->platformGradient());
627                     brush.setTransform(m_state.strokeGradient->gradientSpaceTransform());
628                     fillPathStroke(shadowPainter, pathStroker, platformPath, brush);
629                 } else
630                     fillPathStroke(shadowPainter, pathStroker, platformPath, pen.brush());
631                 shadow->endShadowLayer(this);
632             }
633         } else {
634             QPointF offset(m_state.shadowOffset.width(), m_state.shadowOffset.height());
635             p->translate(offset);
636             QColor shadowColor = m_state.shadowColor;
637             shadowColor.setAlphaF(shadowColor.alphaF() * pen.color().alphaF());
638             QPen shadowPen(pen);
639             shadowPen.setColor(shadowColor);
640             fillPathStroke(p, pathStroker, platformPath, shadowPen.brush());
641             p->translate(-offset);
642         }
643     }
644
645     if (m_state.strokePattern) {
646         QBrush brush = m_state.strokePattern->createPlatformPattern();
647         fillPathStroke(p, pathStroker, platformPath, brush);
648     } else if (m_state.strokeGradient) {
649         QBrush brush(*m_state.strokeGradient->platformGradient());
650         brush.setTransform(m_state.strokeGradient->gradientSpaceTransform());
651         fillPathStroke(p, pathStroker, platformPath, brush);
652     } else
653         fillPathStroke(p, pathStroker, platformPath, pen.brush());
654 }
655
656 static inline void drawRepeatPattern(QPainter* p, PassRefPtr<Pattern> pattern, const FloatRect& rect)
657 {
658     ASSERT(pattern);
659
660     const QBrush brush = pattern->createPlatformPattern();
661     if (brush.style() != Qt::TexturePattern)
662         return;
663
664     const bool repeatX = pattern->repeatX();
665     const bool repeatY = pattern->repeatY();
666     // Patterns must be painted so that the top left of the first image is anchored at
667     // the origin of the coordinate space
668
669     QRectF targetRect(rect);
670     const int w = brush.texture().width();
671     const int h = brush.texture().height();
672
673     ASSERT(p);
674     QRegion oldClip;
675     if (p->hasClipping())
676         oldClip = p->clipRegion();
677
678     // The only type of transforms supported for the brush are translations.
679     ASSERT(!brush.transform().isRotating());
680
681     QRectF clip = targetRect;
682     QRectF patternRect = brush.transform().mapRect(QRectF(0, 0, w, h));
683     if (!repeatX) {
684         clip.setLeft(patternRect.left());
685         clip.setWidth(patternRect.width());
686     }
687     if (!repeatY) {
688         clip.setTop(patternRect.top());
689         clip.setHeight(patternRect.height());
690     }
691     if (!repeatX || !repeatY)
692         p->setClipRect(clip);
693
694     p->fillRect(targetRect, brush);
695
696     if (!oldClip.isEmpty())
697         p->setClipRegion(oldClip);
698     else if (!repeatX || !repeatY)
699         p->setClipping(false);
700 }
701
702 void GraphicsContext::fillRect(const FloatRect& rect)
703 {
704     if (paintingDisabled())
705         return;
706
707     QPainter* p = m_data->p();
708     QRectF normalizedRect = rect.normalized();
709     ShadowBlur* shadow = shadowBlur();
710
711     if (m_state.fillPattern) {
712         GraphicsContext* shadowContext = hasShadow() ? shadow->beginShadowLayer(this, normalizedRect) : 0;
713         if (shadowContext) {
714             QPainter* shadowPainter = shadowContext->platformContext();
715             drawRepeatPattern(shadowPainter, m_state.fillPattern, normalizedRect);
716             shadow->endShadowLayer(this);
717         }
718         drawRepeatPattern(p, m_state.fillPattern, normalizedRect);
719     } else if (m_state.fillGradient) {
720         QBrush brush(*m_state.fillGradient->platformGradient());
721         brush.setTransform(m_state.fillGradient->gradientSpaceTransform());
722         GraphicsContext* shadowContext = hasShadow() ? shadow->beginShadowLayer(this, normalizedRect) : 0;
723         if (shadowContext) {
724             QPainter* shadowPainter = shadowContext->platformContext();
725             shadowPainter->fillRect(normalizedRect, brush);
726             shadow->endShadowLayer(this);
727         }
728         p->fillRect(normalizedRect, brush);
729     } else {
730         if (hasShadow()) {
731             if (shadow->mustUseShadowBlur(this)) {
732                 GraphicsContext* shadowContext = shadow->beginShadowLayer(this, normalizedRect);
733                 if (shadowContext) {
734                     QPainter* shadowPainter = shadowContext->platformContext();
735                     shadowPainter->fillRect(normalizedRect, p->brush());
736                     shadow->endShadowLayer(this);
737                 }
738             } else {
739                 // Solid rectangle fill with no blur shadow or transformations applied can be done
740                 // faster without using the shadow layer at all.
741                 QColor shadowColor = m_state.shadowColor;
742                 shadowColor.setAlphaF(shadowColor.alphaF() * p->brush().color().alphaF());
743                 p->fillRect(normalizedRect.translated(QPointF(m_state.shadowOffset.width(), m_state.shadowOffset.height())), shadowColor);
744             }
745         }
746
747         p->fillRect(normalizedRect, p->brush());
748     }
749 }
750
751
752 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace)
753 {
754     if (paintingDisabled() || !color.isValid())
755         return;
756
757     QRectF platformRect(rect);
758     QPainter* p = m_data->p();
759     if (hasShadow()) {
760         ShadowBlur* shadow = shadowBlur();
761         if (shadow->mustUseShadowBlur(this)) {
762             shadow->drawRectShadow(this, platformRect, RoundedRect::Radii());
763         } else {
764             QColor shadowColor = m_state.shadowColor;
765             shadowColor.setAlphaF(shadowColor.alphaF() * p->brush().color().alphaF());
766             p->fillRect(platformRect.translated(QPointF(m_state.shadowOffset.width(), m_state.shadowOffset.height())), shadowColor);
767         }
768     }
769     p->fillRect(platformRect, QColor(color));
770 }
771
772 void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color, ColorSpace colorSpace)
773 {
774     if (paintingDisabled() || !color.isValid())
775         return;
776
777     Path path;
778     path.addRoundedRect(rect, topLeft, topRight, bottomLeft, bottomRight);
779     QPainter* p = m_data->p();
780     if (hasShadow()) {
781         ShadowBlur* shadow = shadowBlur();
782         if (shadow->mustUseShadowBlur(this)) {
783             shadow->drawRectShadow(this, rect, RoundedRect::Radii(topLeft, topRight, bottomLeft, bottomRight));
784         } else {
785             const QPointF shadowOffset(m_state.shadowOffset.width(), m_state.shadowOffset.height());
786             p->translate(shadowOffset);
787             p->fillPath(path.platformPath(), QColor(m_state.shadowColor));
788             p->translate(-shadowOffset);
789         }
790     }
791     p->fillPath(path.platformPath(), QColor(color));
792 }
793
794 bool GraphicsContext::isInTransparencyLayer() const
795 {
796     return m_data->layerCount;
797 }
798
799 ShadowBlur* GraphicsContext::shadowBlur()
800 {
801     return m_data->shadow;
802 }
803
804 void GraphicsContext::clip(const IntRect& rect)
805 {
806     if (paintingDisabled())
807         return;
808
809     m_data->p()->setClipRect(rect, Qt::IntersectClip);
810 }
811
812 void GraphicsContext::clip(const FloatRect& rect)
813 {
814     if (paintingDisabled())
815         return;
816
817     m_data->p()->setClipRect(rect, Qt::IntersectClip);
818 }
819 IntRect GraphicsContext::clipBounds() const
820 {
821     QPainter* p = m_data->p();
822     QRectF clipRect;
823
824     if (p->hasClipping())
825         clipRect = m_data->clipBoundingRect();
826     else
827         clipRect = p->transform().inverted().mapRect(p->window());
828
829     return enclosingIntRect(clipRect);
830 }
831
832 void GraphicsContext::clipPath(const Path& path, WindRule clipRule)
833 {
834     if (paintingDisabled())
835         return;
836
837     QPainter* p = m_data->p();
838     QPainterPath platformPath = path.platformPath();
839     platformPath.setFillRule(clipRule == RULE_EVENODD ? Qt::OddEvenFill : Qt::WindingFill);
840     p->setClipPath(platformPath, Qt::IntersectClip);
841 }
842
843 void drawFocusRingForPath(QPainter* p, const QPainterPath& path, const Color& color, bool antiAliasing)
844 {
845     const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
846     p->setRenderHint(QPainter::Antialiasing, antiAliasing);
847
848     const QPen oldPen = p->pen();
849     const QBrush oldBrush = p->brush();
850
851     QPen nPen = p->pen();
852     nPen.setColor(color);
853     p->setBrush(Qt::NoBrush);
854     nPen.setStyle(Qt::DotLine);
855
856     p->strokePath(path, nPen);
857     p->setBrush(oldBrush);
858     p->setPen(oldPen);
859
860     p->setRenderHint(QPainter::Antialiasing, antiAlias);
861 }
862
863 void GraphicsContext::drawFocusRing(const Path& path, int /* width */, int offset, const Color& color)
864 {
865     // FIXME: Use 'offset' for something? http://webkit.org/b/49909
866
867     if (paintingDisabled() || !color.isValid())
868         return;
869
870     drawFocusRingForPath(m_data->p(), path.platformPath(), color, m_data->antiAliasingForRectsAndLines);
871 }
872
873 /**
874  * Focus ring handling for form controls is not handled here. Qt style in
875  * RenderTheme handles drawing focus on widgets which 
876  * need it. It is still handled here for links.
877  */
878 void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color)
879 {
880     if (paintingDisabled() || !color.isValid())
881         return;
882
883     unsigned rectCount = rects.size();
884
885     if (!rects.size())
886         return;
887
888     int radius = (width - 1) / 2;
889     QPainterPath path;
890     for (unsigned i = 0; i < rectCount; ++i) {
891         QRect rect = QRect((rects[i])).adjusted(-offset - radius, -offset - radius, offset + radius, offset + radius);
892         // This is not the most efficient way to add a rect to a path, but if we don't create the tmpPath,
893         // we will end up with ugly lines in between rows of text on anchors with multiple lines.
894         QPainterPath tmpPath;
895         tmpPath.addRoundedRect(rect, radius, radius);
896         path = path.united(tmpPath);
897     }
898     drawFocusRingForPath(m_data->p(), path, color, m_data->antiAliasingForRectsAndLines);
899 }
900
901 void GraphicsContext::drawLineForText(const FloatPoint& origin, float width, bool)
902 {
903     if (paintingDisabled())
904         return;
905
906     FloatPoint startPoint = origin;
907     FloatPoint endPoint = origin + FloatSize(width, 0);
908
909     // If paintengine type is X11 to avoid artifacts
910     // like bug https://bugs.webkit.org/show_bug.cgi?id=42248
911 #if defined(Q_WS_X11)
912     QPainter* p = m_data->p();
913     if (p->paintEngine()->type() == QPaintEngine::X11) {
914         // If stroke thickness is odd we need decrease Y coordinate by 1 pixel,
915         // because inside method adjustLineToPixelBoundaries(...), which
916         // called from drawLine(...), Y coordinate will be increased by 0.5f
917         // and then inside Qt painting engine will be rounded to next greater
918         // integer value.
919         float strokeWidth = strokeThickness();
920         if (static_cast<int>(strokeWidth) % 2) {
921             startPoint.setY(startPoint.y() - 1);
922             endPoint.setY(endPoint.y() - 1);
923         }
924     }
925 #endif // defined(Q_WS_X11)
926
927     // FIXME: Loss of precision here. Might consider rounding.
928     drawLine(IntPoint(startPoint.x(), startPoint.y()), IntPoint(endPoint.x(), endPoint.y()));
929 }
930
931
932 /*
933  *   NOTE: This code is completely based upon the one from
934  *   Source/WebCore/platform/graphics/cairo/DrawErrorUnderline.{h|cpp}
935  *
936  *   Draws an error underline that looks like one of:
937  *
938  *               H       E                H
939  *      /\      /\      /\        /\      /\               -
940  *    A/  \    /  \    /  \     A/  \    /  \              |
941  *     \   \  /    \  /   /D     \   \  /    \             |
942  *      \   \/  C   \/   /        \   \/   C  \            | height = heightSquares * square
943  *       \      /\  F   /          \  F   /\   \           |
944  *        \    /  \    /            \    /  \   \G         |
945  *         \  /    \  /              \  /    \  /          |
946  *          \/      \/                \/      \/           -
947  *          B                         B
948  *          |---|
949  *        unitWidth = (heightSquares - 1) * square
950  *
951  *  The x, y, width, height passed in give the desired bounding box;
952  *  x/width are adjusted to make the underline a integer number of units wide.
953 */
954 static void drawErrorUnderline(QPainter *painter, qreal x, qreal y, qreal width, qreal height)
955 {
956     const qreal heightSquares = 2.5;
957
958     qreal square = height / heightSquares;
959     qreal halfSquare = 0.5 * square;
960
961     qreal unitWidth = (heightSquares - 1.0) * square;
962     int widthUnits = static_cast<int>((width + 0.5 * unitWidth) / unitWidth);
963
964     x += 0.5 * (width - widthUnits * unitWidth);
965     width = widthUnits * unitWidth;
966
967     qreal bottom = y + height;
968     qreal top = y;
969
970     QPainterPath path;
971
972     // Bottom of squiggle.
973     path.moveTo(x - halfSquare, top + halfSquare); // A
974
975     int i = 0;
976     for (i = 0; i < widthUnits; i += 2) {
977         qreal middle = x + (i + 1) * unitWidth;
978         qreal right = x + (i + 2) * unitWidth;
979
980         path.lineTo(middle, bottom); // B
981
982         if (i + 2 == widthUnits)
983             path.lineTo(right + halfSquare, top + halfSquare); // D
984         else if (i + 1 != widthUnits)
985             path.lineTo(right, top + square); // C
986     }
987
988     // Top of squiggle.
989     for (i -= 2; i >= 0; i -= 2) {
990         qreal left = x + i * unitWidth;
991         qreal middle = x + (i + 1) * unitWidth;
992         qreal right = x + (i + 2) * unitWidth;
993
994         if (i + 1 == widthUnits)
995             path.lineTo(middle + halfSquare, bottom - halfSquare); // G
996         else {
997             if (i + 2 == widthUnits)
998                 path.lineTo(right, top); // E
999
1000             path.lineTo(middle, bottom - halfSquare); // F
1001         }
1002
1003         path.lineTo(left, top); // H
1004     }
1005
1006     painter->drawPath(path);
1007 }
1008
1009
1010 void GraphicsContext::drawLineForDocumentMarker(const FloatPoint& origin, float width, DocumentMarkerLineStyle style)
1011 {
1012     if (paintingDisabled())
1013         return;
1014
1015     QPainter* painter = platformContext();
1016     const QPen originalPen = painter->pen();
1017
1018     switch (style) {
1019     case DocumentMarkerSpellingLineStyle:
1020         painter->setPen(Qt::red);
1021         break;
1022     case DocumentMarkerGrammarLineStyle:
1023         painter->setPen(Qt::green);
1024         break;
1025     default:
1026         return;
1027     }
1028
1029     drawErrorUnderline(painter, origin.x(), origin.y(), width, cMisspellingLineThickness);
1030     painter->setPen(originalPen);
1031 }
1032
1033 FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect, RoundingMode)
1034 {
1035     // It is not enough just to round to pixels in device space. The rotation part of the
1036     // affine transform matrix to device space can mess with this conversion if we have a
1037     // rotating image like the hands of the world clock widget. We just need the scale, so
1038     // we get the affine transform matrix and extract the scale.
1039     QPainter* painter = platformContext();
1040     QTransform deviceTransform = painter->deviceTransform();
1041     if (deviceTransform.isIdentity())
1042         return frect;
1043
1044     qreal deviceScaleX = sqrtf(deviceTransform.m11() * deviceTransform.m11() + deviceTransform.m12() * deviceTransform.m12());
1045     qreal deviceScaleY = sqrtf(deviceTransform.m21() * deviceTransform.m21() + deviceTransform.m22() * deviceTransform.m22());
1046
1047     QPoint deviceOrigin(frect.x() * deviceScaleX, frect.y() * deviceScaleY);
1048     QPoint deviceLowerRight(frect.maxX() * deviceScaleX, frect.maxY() * deviceScaleY);
1049
1050     // Don't let the height or width round to 0 unless either was originally 0
1051     if (deviceOrigin.y() == deviceLowerRight.y() && frect.height())
1052         deviceLowerRight.setY(deviceLowerRight.y() + 1);
1053     if (deviceOrigin.x() == deviceLowerRight.x() && frect.width())
1054         deviceLowerRight.setX(deviceLowerRight.x() + 1);
1055
1056     FloatPoint roundedOrigin = FloatPoint(deviceOrigin.x() / deviceScaleX, deviceOrigin.y() / deviceScaleY);
1057     FloatPoint roundedLowerRight = FloatPoint(deviceLowerRight.x() / deviceScaleX, deviceLowerRight.y() / deviceScaleY);
1058     return FloatRect(roundedOrigin, roundedLowerRight - roundedOrigin);
1059 }
1060
1061 void GraphicsContext::setPlatformShadow(const FloatSize& size, float blur, const Color& color, ColorSpace colorSpace)
1062 {
1063     // Qt doesn't support shadows natively, they are drawn manually in the draw*
1064     // functions
1065
1066     if (m_state.shadowsIgnoreTransforms) {
1067         // Meaning that this graphics context is associated with a CanvasRenderingContext
1068         // We flip the height since CG and HTML5 Canvas have opposite Y axis
1069         m_state.shadowOffset = FloatSize(size.width(), -size.height());
1070     }
1071
1072     m_data->shadow->setShadowValues(FloatSize(m_state.shadowBlur, m_state.shadowBlur), m_state.shadowOffset, color, colorSpace, m_state.shadowsIgnoreTransforms);
1073 }
1074
1075 void GraphicsContext::clearPlatformShadow()
1076 {
1077     m_data->shadow->clear();
1078 }
1079
1080 void GraphicsContext::pushTransparencyLayerInternal(const QRect &rect, qreal opacity, QPixmap& alphaMask)
1081 {
1082     QPainter* p = m_data->p();
1083
1084     QTransform deviceTransform = p->transform();
1085     QRect deviceClip = deviceTransform.mapRect(rect);
1086
1087     alphaMask = alphaMask.transformed(deviceTransform);
1088     if (alphaMask.width() != deviceClip.width() || alphaMask.height() != deviceClip.height())
1089         alphaMask = alphaMask.scaled(deviceClip.width(), deviceClip.height());
1090
1091     m_data->layers.push(new TransparencyLayer(p, deviceClip, 1.0, alphaMask));
1092 }
1093
1094 void GraphicsContext::beginPlatformTransparencyLayer(float opacity)
1095 {
1096     if (paintingDisabled())
1097         return;
1098
1099     int x, y, w, h;
1100     x = y = 0;
1101     QPainter* p = m_data->p();
1102     const QPaintDevice* device = p->device();
1103     w = device->width();
1104     h = device->height();
1105
1106     if (p->hasClipping()) {
1107         QRectF clip = m_data->clipBoundingRect();
1108         QRectF deviceClip = p->transform().mapRect(clip);
1109         x = int(qBound(qreal(0), deviceClip.x(), (qreal)w));
1110         y = int(qBound(qreal(0), deviceClip.y(), (qreal)h));
1111         w = int(qBound(qreal(0), deviceClip.width(), (qreal)w) + 2);
1112         h = int(qBound(qreal(0), deviceClip.height(), (qreal)h) + 2);
1113     }
1114
1115     QPixmap emptyAlphaMask;
1116     m_data->layers.push(new TransparencyLayer(p, QRect(x, y, w, h), opacity, emptyAlphaMask));
1117     ++m_data->layerCount;
1118 }
1119
1120 void GraphicsContext::endPlatformTransparencyLayer()
1121 {
1122     if (paintingDisabled())
1123         return;
1124
1125     TransparencyLayer* layer = m_data->layers.pop();
1126     if (!layer->alphaMask.isNull()) {
1127         layer->painter.resetTransform();
1128         layer->painter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
1129         layer->painter.drawPixmap(QPoint(), layer->alphaMask);
1130     } else
1131         --m_data->layerCount; // see the comment for layerCount
1132     layer->painter.end();
1133
1134     QPainter* p = m_data->p();
1135     p->save();
1136     p->resetTransform();
1137     p->setOpacity(layer->opacity);
1138     p->drawPixmap(layer->offset, layer->pixmap);
1139     p->restore();
1140
1141     delete layer;
1142 }
1143
1144 bool GraphicsContext::supportsTransparencyLayers()
1145 {
1146     return true;
1147 }
1148
1149 void GraphicsContext::clearRect(const FloatRect& rect)
1150 {
1151     if (paintingDisabled())
1152         return;
1153
1154     QPainter* p = m_data->p();
1155     QPainter::CompositionMode currentCompositionMode = p->compositionMode();
1156     p->setCompositionMode(QPainter::CompositionMode_Source);
1157     p->fillRect(rect, Qt::transparent);
1158     p->setCompositionMode(currentCompositionMode);
1159 }
1160
1161 void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
1162 {
1163     if (paintingDisabled())
1164         return;
1165
1166     Path path;
1167     path.addRect(rect);
1168
1169     float previousStrokeThickness = strokeThickness();
1170
1171     if (lineWidth != previousStrokeThickness)
1172         setStrokeThickness(lineWidth);
1173
1174     strokePath(path);
1175
1176     if (lineWidth != previousStrokeThickness)
1177         setStrokeThickness(previousStrokeThickness);
1178 }
1179
1180 void GraphicsContext::setLineCap(LineCap lc)
1181 {
1182     if (paintingDisabled())
1183         return;
1184
1185     QPainter* p = m_data->p();
1186     QPen nPen = p->pen();
1187     nPen.setCapStyle(toQtLineCap(lc));
1188     p->setPen(nPen);
1189 }
1190
1191 void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
1192 {
1193     QPainter* p = m_data->p();
1194     QPen pen = p->pen();
1195     unsigned dashLength = dashes.size();
1196     if (dashLength) {
1197         QVector<qreal> pattern;
1198         unsigned count = dashLength;
1199         if (dashLength % 2)
1200             count *= 2;
1201
1202         float penWidth = narrowPrecisionToFloat(double(pen.widthF()));
1203         if (penWidth <= 0.f)
1204             penWidth = 1.f;
1205
1206         for (unsigned i = 0; i < count; i++)
1207             pattern.append(dashes[i % dashLength] / penWidth);
1208
1209         pen.setDashPattern(pattern);
1210         pen.setDashOffset(dashOffset / penWidth);
1211     } else
1212         pen.setStyle(Qt::SolidLine);
1213     p->setPen(pen);
1214 }
1215
1216 void GraphicsContext::setLineJoin(LineJoin lj)
1217 {
1218     if (paintingDisabled())
1219         return;
1220
1221     QPainter* p = m_data->p();
1222     QPen nPen = p->pen();
1223     nPen.setJoinStyle(toQtLineJoin(lj));
1224     p->setPen(nPen);
1225 }
1226
1227 void GraphicsContext::setMiterLimit(float limit)
1228 {
1229     if (paintingDisabled())
1230         return;
1231
1232     QPainter* p = m_data->p();
1233     QPen nPen = p->pen();
1234     nPen.setMiterLimit(limit);
1235     p->setPen(nPen);
1236 }
1237
1238 void GraphicsContext::setAlpha(float opacity)
1239 {
1240     if (paintingDisabled())
1241         return;
1242     QPainter* p = m_data->p();
1243     p->setOpacity(opacity);
1244 }
1245
1246 void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op)
1247 {
1248     if (paintingDisabled())
1249         return;
1250
1251     m_data->p()->setCompositionMode(toQtCompositionMode(op));
1252 }
1253
1254 void GraphicsContext::clip(const Path& path)
1255 {
1256     if (paintingDisabled())
1257         return;
1258
1259     QPainterPath clipPath = path.platformPath();
1260     clipPath.setFillRule(Qt::WindingFill);
1261     m_data->p()->setClipPath(clipPath, Qt::IntersectClip);
1262 }
1263
1264 void GraphicsContext::canvasClip(const Path& path)
1265 {
1266     clip(path);
1267 }
1268
1269 void GraphicsContext::clipOut(const Path& path)
1270 {
1271     if (paintingDisabled())
1272         return;
1273
1274     QPainter* p = m_data->p();
1275     QPainterPath clippedOut = path.platformPath();
1276     QPainterPath newClip;
1277     newClip.setFillRule(Qt::OddEvenFill);
1278     if (p->hasClipping()) {
1279         newClip.addRect(m_data->clipBoundingRect());
1280         newClip.addPath(clippedOut);
1281         p->setClipPath(newClip, Qt::IntersectClip);
1282     } else {
1283         QRect windowRect = p->transform().inverted().mapRect(p->window());
1284         newClip.addRect(windowRect);
1285         newClip.addPath(clippedOut.intersected(newClip));
1286         p->setClipPath(newClip);
1287     }
1288 }
1289
1290 void GraphicsContext::translate(float x, float y)
1291 {
1292     if (paintingDisabled())
1293         return;
1294
1295     m_data->p()->translate(x, y);
1296 }
1297
1298 void GraphicsContext::rotate(float radians)
1299 {
1300     if (paintingDisabled())
1301         return;
1302
1303     QTransform rotation = QTransform().rotateRadians(radians);
1304     m_data->p()->setTransform(rotation, true);
1305 }
1306
1307 void GraphicsContext::scale(const FloatSize& s)
1308 {
1309     if (paintingDisabled())
1310         return;
1311
1312     m_data->p()->scale(s.width(), s.height());
1313 }
1314
1315 void GraphicsContext::clipOut(const IntRect& rect)
1316 {
1317     if (paintingDisabled())
1318         return;
1319
1320     QPainter* p = m_data->p();
1321     QPainterPath newClip;
1322     newClip.setFillRule(Qt::OddEvenFill);
1323     if (p->hasClipping()) {
1324         newClip.addRect(m_data->clipBoundingRect());
1325         newClip.addRect(QRect(rect));
1326         p->setClipPath(newClip, Qt::IntersectClip);
1327     } else {
1328         QRect clipOutRect(rect);
1329         QRect window = p->transform().inverted().mapRect(p->window());
1330         clipOutRect &= window;
1331         newClip.addRect(window);
1332         newClip.addRect(clipOutRect);
1333         p->setClipPath(newClip);
1334     }
1335 }
1336
1337 void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect,
1338                                               int thickness)
1339 {
1340     if (paintingDisabled())
1341         return;
1342
1343     clip(rect);
1344     QPainterPath path;
1345
1346     // Add outer ellipse
1347     path.addEllipse(QRectF(rect.x(), rect.y(), rect.width(), rect.height()));
1348
1349     // Add inner ellipse.
1350     path.addEllipse(QRectF(rect.x() + thickness, rect.y() + thickness,
1351                            rect.width() - (thickness * 2), rect.height() - (thickness * 2)));
1352
1353     path.setFillRule(Qt::OddEvenFill);
1354
1355     QPainter* p = m_data->p();
1356
1357     const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
1358     p->setRenderHint(QPainter::Antialiasing, true);
1359     p->setClipPath(path, Qt::IntersectClip);
1360     p->setRenderHint(QPainter::Antialiasing, antiAlias);
1361 }
1362
1363 void GraphicsContext::concatCTM(const AffineTransform& transform)
1364 {
1365     if (paintingDisabled())
1366         return;
1367
1368     m_data->p()->setWorldTransform(transform, true);
1369 }
1370
1371 void GraphicsContext::setCTM(const AffineTransform& transform)
1372 {
1373     if (paintingDisabled())
1374         return;
1375
1376     m_data->p()->setWorldTransform(transform);
1377 }
1378
1379 #if ENABLE(3D_RENDERING)
1380 TransformationMatrix GraphicsContext::get3DTransform() const
1381 {
1382     if (paintingDisabled())
1383         return TransformationMatrix();
1384
1385     return platformContext()->combinedTransform();
1386 }
1387
1388 void GraphicsContext::concat3DTransform(const TransformationMatrix& transform)
1389 {
1390     if (paintingDisabled())
1391         return;
1392
1393     m_data->p()->setWorldTransform(transform, true);
1394 }
1395
1396 void GraphicsContext::set3DTransform(const TransformationMatrix& transform)
1397 {
1398     if (paintingDisabled())
1399         return;
1400
1401     m_data->p()->setWorldTransform(transform, false);
1402 }
1403 #endif
1404
1405 void GraphicsContext::setURLForRect(const KURL&, const IntRect&)
1406 {
1407     notImplemented();
1408 }
1409
1410 void GraphicsContext::setPlatformStrokeColor(const Color& color, ColorSpace colorSpace)
1411 {
1412     if (paintingDisabled() || !color.isValid())
1413         return;
1414
1415     QPainter* p = m_data->p();
1416     QPen newPen(p->pen());
1417     m_data->solidColor.setColor(color);
1418     newPen.setBrush(m_data->solidColor);
1419     p->setPen(newPen);
1420 }
1421
1422 void GraphicsContext::setPlatformStrokeStyle(StrokeStyle strokeStyle)
1423 {
1424     if (paintingDisabled())
1425         return;
1426     QPainter* p = m_data->p();
1427     QPen newPen(p->pen());
1428     newPen.setStyle(toQPenStyle(strokeStyle));
1429     p->setPen(newPen);
1430 }
1431
1432 void GraphicsContext::setPlatformStrokeThickness(float thickness)
1433 {
1434     if (paintingDisabled())
1435         return;
1436     QPainter* p = m_data->p();
1437     QPen newPen(p->pen());
1438     newPen.setWidthF(thickness);
1439     p->setPen(newPen);
1440 }
1441
1442 void GraphicsContext::setPlatformFillColor(const Color& color, ColorSpace colorSpace)
1443 {
1444     if (paintingDisabled() || !color.isValid())
1445         return;
1446
1447     m_data->solidColor.setColor(color);
1448     m_data->p()->setBrush(m_data->solidColor);
1449 }
1450
1451 void GraphicsContext::setPlatformShouldAntialias(bool enable)
1452 {
1453     if (paintingDisabled())
1454         return;
1455     m_data->p()->setRenderHint(QPainter::Antialiasing, enable);
1456 }
1457
1458 #if OS(WINDOWS)
1459
1460 HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap)
1461 {
1462     // painting through native HDC is only supported for plugin, where mayCreateBitmap is always true
1463     Q_ASSERT(mayCreateBitmap);
1464
1465     if (dstRect.isEmpty())
1466         return 0;
1467
1468     // Create a bitmap DC in which to draw.
1469     BITMAPINFO bitmapInfo;
1470     bitmapInfo.bmiHeader.biSize          = sizeof(BITMAPINFOHEADER);
1471     bitmapInfo.bmiHeader.biWidth         = dstRect.width();
1472     bitmapInfo.bmiHeader.biHeight        = dstRect.height();
1473     bitmapInfo.bmiHeader.biPlanes        = 1;
1474     bitmapInfo.bmiHeader.biBitCount      = 32;
1475     bitmapInfo.bmiHeader.biCompression   = BI_RGB;
1476     bitmapInfo.bmiHeader.biSizeImage     = 0;
1477     bitmapInfo.bmiHeader.biXPelsPerMeter = 0;
1478     bitmapInfo.bmiHeader.biYPelsPerMeter = 0;
1479     bitmapInfo.bmiHeader.biClrUsed       = 0;
1480     bitmapInfo.bmiHeader.biClrImportant  = 0;
1481
1482     void* pixels = 0;
1483     HBITMAP bitmap = ::CreateDIBSection(0, &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0);
1484     if (!bitmap)
1485         return 0;
1486
1487     HDC displayDC = ::GetDC(0);
1488     HDC bitmapDC = ::CreateCompatibleDC(displayDC);
1489     ::ReleaseDC(0, displayDC);
1490
1491     ::SelectObject(bitmapDC, bitmap);
1492
1493     // Fill our buffer with clear if we're going to alpha blend.
1494     if (supportAlphaBlend) {
1495         BITMAP bmpInfo;
1496         GetObject(bitmap, sizeof(bmpInfo), &bmpInfo);
1497         int bufferSize = bmpInfo.bmWidthBytes * bmpInfo.bmHeight;
1498         memset(bmpInfo.bmBits, 0, bufferSize);
1499     }
1500
1501 #if !OS(WINCE)
1502     // Make sure we can do world transforms.
1503     SetGraphicsMode(bitmapDC, GM_ADVANCED);
1504
1505     // Apply a translation to our context so that the drawing done will be at (0,0) of the bitmap.
1506     XFORM xform;
1507     xform.eM11 = 1.0f;
1508     xform.eM12 = 0.0f;
1509     xform.eM21 = 0.0f;
1510     xform.eM22 = 1.0f;
1511     xform.eDx = -dstRect.x();
1512     xform.eDy = -dstRect.y();
1513     ::SetWorldTransform(bitmapDC, &xform);
1514 #endif
1515
1516     return bitmapDC;
1517 }
1518
1519 void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap)
1520 {
1521     // painting through native HDC is only supported for plugin, where mayCreateBitmap is always true
1522     Q_ASSERT(mayCreateBitmap);
1523
1524     if (hdc) {
1525
1526         if (!dstRect.isEmpty()) {
1527
1528             HBITMAP bitmap = static_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP));
1529             BITMAP info;
1530             GetObject(bitmap, sizeof(info), &info);
1531             ASSERT(info.bmBitsPixel == 32);
1532
1533             QPixmap pixmap = qt_pixmapFromWinHBITMAP(bitmap, supportAlphaBlend ? HBitmapPremultipliedAlpha : HBitmapNoAlpha);
1534             m_data->p()->drawPixmap(dstRect, pixmap);
1535
1536             ::DeleteObject(bitmap);
1537         }
1538
1539         ::DeleteDC(hdc);
1540     }
1541 }
1542 #endif
1543
1544 void GraphicsContext::setImageInterpolationQuality(InterpolationQuality quality)
1545 {
1546     m_data->imageInterpolationQuality = quality;
1547
1548     switch (quality) {
1549     case InterpolationNone:
1550     case InterpolationLow:
1551         // use nearest-neigbor
1552         m_data->p()->setRenderHint(QPainter::SmoothPixmapTransform, false);
1553         break;
1554
1555     case InterpolationMedium:
1556     case InterpolationHigh:
1557         // use the filter
1558         m_data->p()->setRenderHint(QPainter::SmoothPixmapTransform, true);
1559         break;
1560
1561     case InterpolationDefault:
1562     default:
1563         m_data->p()->setRenderHint(QPainter::SmoothPixmapTransform, m_data->initialSmoothPixmapTransformHint);
1564         break;
1565     };
1566 }
1567
1568 InterpolationQuality GraphicsContext::imageInterpolationQuality() const
1569 {
1570     return m_data->imageInterpolationQuality;
1571 }
1572
1573 void GraphicsContext::takeOwnershipOfPlatformContext()
1574 {
1575     m_data->takeOwnershipOfPlatformContext();
1576 }
1577
1578 }
1579
1580 // vim: ts=4 sw=4 et