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 
68 namespace WebCore {
69 
toQtCompositionMode(CompositeOperator op)70 static 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 
toQtLineCap(LineCap lc)109 static 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 
toQtLineJoin(LineJoin lj)125 static 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 
toQPenStyle(StrokeStyle style)141 static 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 
toQtFillRule(WindRule rule)162 static 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 
175 class GraphicsContextPlatformPrivate {
176     WTF_MAKE_NONCOPYABLE(GraphicsContextPlatformPrivate); WTF_MAKE_FAST_ALLOCATED;
177 public:
178     GraphicsContextPlatformPrivate(QPainter*, const QColor& initialSolidColor);
179     ~GraphicsContextPlatformPrivate();
180 
p() const181     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 
clipBoundingRect() const204     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 
takeOwnershipOfPlatformContext()213     void takeOwnershipOfPlatformContext() { platformContextIsOwned = true; }
214 
215 private:
216     QPainter* painter;
217     bool platformContextIsOwned;
218 };
219 
GraphicsContextPlatformPrivate(QPainter * p,const QColor & initialSolidColor)220 GraphicsContextPlatformPrivate::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 
~GraphicsContextPlatformPrivate()248 GraphicsContextPlatformPrivate::~GraphicsContextPlatformPrivate()
249 {
250     if (!platformContextIsOwned)
251         return;
252 
253     QPaintDevice* device = painter->device();
254     painter->end();
255     delete painter;
256     delete device;
257 }
258 
platformInit(PlatformGraphicsContext * painter)259 void 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 
platformDestroy()277 void GraphicsContext::platformDestroy()
278 {
279     while (!m_data->layers.isEmpty())
280         endTransparencyLayer();
281 
282     delete m_data;
283 }
284 
platformContext() const285 PlatformGraphicsContext* GraphicsContext::platformContext() const
286 {
287     return m_data->p();
288 }
289 
getCTM() const290 AffineTransform 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 
savePlatformState()297 void 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 
restorePlatformState()305 void 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.
drawRect(const IntRect & rect)322 void 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.
drawLine(const IntPoint & point1,const IntPoint & point2)338 void 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.
drawEllipse(const IntRect & rect)433 void GraphicsContext::drawEllipse(const IntRect& rect)
434 {
435     if (paintingDisabled())
436         return;
437 
438     m_data->p()->drawEllipse(rect);
439 }
440 
drawConvexPolygon(size_t npoints,const FloatPoint * points,bool shouldAntialias)441 void 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 
clipConvexPolygon(size_t numPoints,const FloatPoint * points,bool antialiased)464 void 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 
fillPath(const Path & path)490 void 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 
strokePath(const Path & path)541 void 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 
drawRepeatPattern(QPainter * p,QPixmap * image,const FloatRect & rect,const bool repeatX,const bool repeatY)599 static 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 
fillRect(const FloatRect & rect)666 void 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 
fillRect(const FloatRect & rect,const Color & color,ColorSpace colorSpace)719 void 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 
fillRoundedRect(const IntRect & rect,const IntSize & topLeft,const IntSize & topRight,const IntSize & bottomLeft,const IntSize & bottomRight,const Color & color,ColorSpace colorSpace)744 void 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 
inTransparencyLayer() const770 bool GraphicsContext::inTransparencyLayer() const
771 {
772     return m_data->layerCount;
773 }
774 
contextShadow()775 ContextShadow* GraphicsContext::contextShadow()
776 {
777     return &m_data->shadow;
778 }
779 
clip(const IntRect & rect)780 void GraphicsContext::clip(const IntRect& rect)
781 {
782     if (paintingDisabled())
783         return;
784 
785     m_data->p()->setClipRect(rect, Qt::IntersectClip);
786 }
787 
clip(const FloatRect & rect)788 void GraphicsContext::clip(const FloatRect& rect)
789 {
790     if (paintingDisabled())
791         return;
792 
793     m_data->p()->setClipRect(rect, Qt::IntersectClip);
794 }
795 
clipPath(const Path & path,WindRule clipRule)796 void 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 
drawFocusRingForPath(QPainter * p,const QPainterPath & path,const Color & color,bool antiAliasing)807 void 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 
drawFocusRing(const Path & path,int,int offset,const Color & color)827 void 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  */
drawFocusRing(const Vector<IntRect> & rects,int width,int offset,const Color & color)842 void 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 
drawLineForText(const FloatPoint & origin,float width,bool)865 void 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 
drawLineForTextChecking(const FloatPoint &,float,TextCheckingLineStyle)895 void GraphicsContext::drawLineForTextChecking(const FloatPoint&, float, TextCheckingLineStyle)
896 {
897     if (paintingDisabled())
898         return;
899 
900     notImplemented();
901 }
902 
roundToDevicePixels(const FloatRect & frect,RoundingMode)903 FloatRect 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 
setPlatformShadow(const FloatSize & size,float blur,const Color & color,ColorSpace)931 void 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 
clearPlatformShadow()947 void GraphicsContext::clearPlatformShadow()
948 {
949     m_data->shadow.clear();
950 }
951 
pushTransparencyLayerInternal(const QRect & rect,qreal opacity,QPixmap & alphaMask)952 void 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 
beginTransparencyLayer(float opacity)958 void 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 
endTransparencyLayer()982 void 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 
clearRect(const FloatRect & rect)1006 void 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 
strokeRect(const FloatRect & rect,float lineWidth)1018 void 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 
setLineCap(LineCap lc)1037 void 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 
setLineDash(const DashArray & dashes,float dashOffset)1048 void 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 
setLineJoin(LineJoin lj)1070 void 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 
setMiterLimit(float limit)1081 void 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 
setAlpha(float opacity)1092 void GraphicsContext::setAlpha(float opacity)
1093 {
1094     if (paintingDisabled())
1095         return;
1096     QPainter* p = m_data->p();
1097     p->setOpacity(opacity);
1098 }
1099 
setPlatformCompositeOperation(CompositeOperator op)1100 void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op)
1101 {
1102     if (paintingDisabled())
1103         return;
1104 
1105     m_data->p()->setCompositionMode(toQtCompositionMode(op));
1106 }
1107 
clip(const Path & path)1108 void 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 
canvasClip(const Path & path)1118 void GraphicsContext::canvasClip(const Path& path)
1119 {
1120     clip(path);
1121 }
1122 
clipOut(const Path & path)1123 void 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 
translate(float x,float y)1144 void GraphicsContext::translate(float x, float y)
1145 {
1146     if (paintingDisabled())
1147         return;
1148 
1149     m_data->p()->translate(x, y);
1150 }
1151 
rotate(float radians)1152 void GraphicsContext::rotate(float radians)
1153 {
1154     if (paintingDisabled())
1155         return;
1156 
1157     m_data->p()->rotate(rad2deg(qreal(radians)));
1158 }
1159 
scale(const FloatSize & s)1160 void GraphicsContext::scale(const FloatSize& s)
1161 {
1162     if (paintingDisabled())
1163         return;
1164 
1165     m_data->p()->scale(s.width(), s.height());
1166 }
1167 
clipOut(const IntRect & rect)1168 void 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 
addInnerRoundedRectClip(const IntRect & rect,int thickness)1190 void 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 
concatCTM(const AffineTransform & transform)1216 void GraphicsContext::concatCTM(const AffineTransform& transform)
1217 {
1218     if (paintingDisabled())
1219         return;
1220 
1221     m_data->p()->setWorldTransform(transform, true);
1222 }
1223 
setCTM(const AffineTransform & transform)1224 void GraphicsContext::setCTM(const AffineTransform& transform)
1225 {
1226     if (paintingDisabled())
1227         return;
1228 
1229     m_data->p()->setWorldTransform(transform);
1230 }
1231 
setURLForRect(const KURL &,const IntRect &)1232 void GraphicsContext::setURLForRect(const KURL&, const IntRect&)
1233 {
1234     notImplemented();
1235 }
1236 
setPlatformStrokeColor(const Color & color,ColorSpace colorSpace)1237 void 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 
setPlatformStrokeStyle(StrokeStyle strokeStyle)1249 void 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 
setPlatformStrokeThickness(float thickness)1259 void 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 
setPlatformFillColor(const Color & color,ColorSpace colorSpace)1269 void 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 
setPlatformShouldAntialias(bool enable)1278 void 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 
getWindowsContext(const IntRect & dstRect,bool supportAlphaBlend,bool mayCreateBitmap)1287 HDC 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 
releaseWindowsContext(HDC hdc,const IntRect & dstRect,bool supportAlphaBlend,bool mayCreateBitmap)1346 void 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 
setImageInterpolationQuality(InterpolationQuality quality)1371 void 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 
imageInterpolationQuality() const1395 InterpolationQuality GraphicsContext::imageInterpolationQuality() const
1396 {
1397     return m_data->imageInterpolationQuality;
1398 }
1399 
takeOwnershipOfPlatformContext()1400 void GraphicsContext::takeOwnershipOfPlatformContext()
1401 {
1402     m_data->takeOwnershipOfPlatformContext();
1403 }
1404 
1405 }
1406 
1407 // vim: ts=4 sw=4 et
1408