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