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