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