• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  *
12  * All rights reserved.
13  *
14  * Redistribution and use in source and binary forms, with or without
15  * modification, are permitted provided that the following conditions
16  * are met:
17  * 1. Redistributions of source code must retain the above copyright
18  *    notice, this list of conditions and the following disclaimer.
19  * 2. Redistributions in binary form must reproduce the above copyright
20  *    notice, this list of conditions and the following disclaimer in the
21  *    documentation and/or other materials provided with the distribution.
22  *
23  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
24  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
27  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
31  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34  */
35 
36 #include "config.h"
37 #include "GraphicsContext.h"
38 
39 #ifdef Q_WS_WIN
40 #include <windows.h>
41 #endif
42 
43 #include "AffineTransform.h"
44 #include "Color.h"
45 #include "FloatConversion.h"
46 #include "Font.h"
47 #include "GraphicsContextPrivate.h"
48 #include "ImageBuffer.h"
49 #include "NotImplemented.h"
50 #include "Path.h"
51 #include "Pattern.h"
52 #include "Pen.h"
53 
54 #include <QBrush>
55 #include <QDebug>
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 
70 namespace WebCore {
71 
toQtCompositionMode(CompositeOperator op)72 static inline QPainter::CompositionMode 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     }
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     }
119 
120     return Qt::FlatCap;
121 }
122 
toQtLineJoin(LineJoin lj)123 static inline Qt::PenJoinStyle toQtLineJoin(LineJoin lj)
124 {
125     switch (lj) {
126     case MiterJoin:
127         return Qt::SvgMiterJoin;
128     case RoundJoin:
129         return Qt::RoundJoin;
130     case BevelJoin:
131         return Qt::BevelJoin;
132     }
133 
134     return Qt::MiterJoin;
135 }
136 
toQPenStyle(StrokeStyle style)137 static Qt::PenStyle toQPenStyle(StrokeStyle style)
138 {
139     switch (style) {
140     case NoStroke:
141         return Qt::NoPen;
142         break;
143     case SolidStroke:
144         return Qt::SolidLine;
145         break;
146     case DottedStroke:
147         return Qt::DotLine;
148         break;
149     case DashedStroke:
150         return Qt::DashLine;
151         break;
152     }
153     qWarning("couldn't recognize the pen style");
154     return Qt::NoPen;
155 }
156 
toQtFillRule(WindRule rule)157 static inline Qt::FillRule toQtFillRule(WindRule rule)
158 {
159     switch (rule) {
160     case RULE_EVENODD:
161         return Qt::OddEvenFill;
162     case RULE_NONZERO:
163         return Qt::WindingFill;
164     }
165     qDebug("Qt: unrecognized wind rule!");
166     return Qt::OddEvenFill;
167 }
168 
169 struct TransparencyLayer : FastAllocBase {
TransparencyLayerWebCore::TransparencyLayer170     TransparencyLayer(const QPainter* p, const QRect &rect)
171         : pixmap(rect.width(), rect.height())
172     {
173         offset = rect.topLeft();
174         pixmap.fill(Qt::transparent);
175         painter.begin(&pixmap);
176         painter.setRenderHint(QPainter::Antialiasing, p->testRenderHint(QPainter::Antialiasing));
177         painter.translate(-offset);
178         painter.setPen(p->pen());
179         painter.setBrush(p->brush());
180         painter.setTransform(p->transform(), true);
181         painter.setOpacity(p->opacity());
182         painter.setFont(p->font());
183         if (painter.paintEngine()->hasFeature(QPaintEngine::PorterDuff))
184             painter.setCompositionMode(p->compositionMode());
185         painter.setClipPath(p->clipPath());
186     }
187 
TransparencyLayerWebCore::TransparencyLayer188     TransparencyLayer()
189     {
190     }
191 
192     QPixmap pixmap;
193     QPoint offset;
194     QPainter painter;
195     qreal opacity;
196 private:
TransparencyLayerWebCore::TransparencyLayer197     TransparencyLayer(const TransparencyLayer &) {}
operator =WebCore::TransparencyLayer198     TransparencyLayer & operator=(const TransparencyLayer &) { return *this; }
199 };
200 
201 class GraphicsContextPlatformPrivate : public Noncopyable {
202 public:
203     GraphicsContextPlatformPrivate(QPainter* painter);
204     ~GraphicsContextPlatformPrivate();
205 
p()206     inline QPainter* p()
207     {
208         if (layers.isEmpty()) {
209             if (redirect)
210                 return redirect;
211 
212             return painter;
213         }
214         return &layers.top()->painter;
215     }
216 
217     bool antiAliasingForRectsAndLines;
218 
219     QStack<TransparencyLayer*> layers;
220     QPainter* redirect;
221 
222     QBrush solidColor;
223 
224     InterpolationQuality imageInterpolationQuality;
225 
226     // Only used by SVG for now.
227     QPainterPath currentPath;
228 
229 private:
230     QPainter* painter;
231 };
232 
233 
GraphicsContextPlatformPrivate(QPainter * p)234 GraphicsContextPlatformPrivate::GraphicsContextPlatformPrivate(QPainter* p)
235 {
236     painter = p;
237     redirect = 0;
238 
239     solidColor = QBrush(Qt::black);
240 
241     imageInterpolationQuality = InterpolationDefault;
242 
243     if (painter) {
244         // use the default the QPainter was constructed with
245         antiAliasingForRectsAndLines = painter->testRenderHint(QPainter::Antialiasing);
246         // FIXME: Maybe only enable in SVG mode?
247         painter->setRenderHint(QPainter::Antialiasing, true);
248     } else
249         antiAliasingForRectsAndLines = false;
250 }
251 
~GraphicsContextPlatformPrivate()252 GraphicsContextPlatformPrivate::~GraphicsContextPlatformPrivate()
253 {
254 }
255 
GraphicsContext(PlatformGraphicsContext * context)256 GraphicsContext::GraphicsContext(PlatformGraphicsContext* context)
257     : m_common(createGraphicsContextPrivate())
258     , m_data(new GraphicsContextPlatformPrivate(context))
259 {
260     setPaintingDisabled(!context);
261     if (context) {
262         // Make sure the context starts in sync with our state.
263         setPlatformFillColor(fillColor(), DeviceColorSpace);
264         setPlatformStrokeColor(strokeColor(), DeviceColorSpace);
265     }
266 }
267 
~GraphicsContext()268 GraphicsContext::~GraphicsContext()
269 {
270     while (!m_data->layers.isEmpty())
271         endTransparencyLayer();
272 
273     destroyGraphicsContextPrivate(m_common);
274     delete m_data;
275 }
276 
platformContext() const277 PlatformGraphicsContext* GraphicsContext::platformContext() const
278 {
279     return m_data->p();
280 }
281 
getCTM() const282 AffineTransform GraphicsContext::getCTM() const
283 {
284     QTransform matrix(platformContext()->combinedTransform());
285     return AffineTransform(matrix.m11(), matrix.m12(), matrix.m21(),
286                            matrix.m22(), matrix.dx(), matrix.dy());
287 }
288 
savePlatformState()289 void GraphicsContext::savePlatformState()
290 {
291     m_data->p()->save();
292 }
293 
restorePlatformState()294 void GraphicsContext::restorePlatformState()
295 {
296     m_data->p()->restore();
297 
298     if (!m_data->currentPath.isEmpty() && m_common->state.pathTransform.isInvertible()) {
299         QTransform matrix = m_common->state.pathTransform;
300         m_data->currentPath = m_data->currentPath * matrix;
301     }
302 }
303 
304 // Draws a filled rectangle with a stroked border.
drawRect(const IntRect & rect)305 void GraphicsContext::drawRect(const IntRect& rect)
306 {
307     if (paintingDisabled())
308         return;
309 
310     QPainter* p = m_data->p();
311     const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
312     p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines);
313 
314     IntSize shadowSize;
315     int shadowBlur;
316     Color shadowColor;
317     if (getShadow(shadowSize, shadowBlur, shadowColor)) {
318         IntRect shadowRect = rect;
319         shadowRect.move(shadowSize.width(), shadowSize.height());
320         shadowRect.inflate(static_cast<int>(p->pen().widthF()));
321         p->fillRect(shadowRect, QColor(shadowColor));
322     }
323 
324     p->drawRect(rect);
325 
326     p->setRenderHint(QPainter::Antialiasing, antiAlias);
327 }
328 
329 // This is only used to draw borders.
drawLine(const IntPoint & point1,const IntPoint & point2)330 void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
331 {
332     if (paintingDisabled())
333         return;
334 
335     StrokeStyle style = strokeStyle();
336     Color color = strokeColor();
337     if (style == NoStroke || !color.alpha())
338         return;
339 
340     float width = strokeThickness();
341 
342     FloatPoint p1 = point1;
343     FloatPoint p2 = point2;
344     bool isVerticalLine = (p1.x() == p2.x());
345 
346     QPainter* p = m_data->p();
347     const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
348     p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines);
349     adjustLineToPixelBoundaries(p1, p2, width, style);
350 
351     IntSize shadowSize;
352     int shadowBlur;
353     Color shadowColor;
354     if (textDrawingMode() == cTextFill && getShadow(shadowSize, shadowBlur, shadowColor)) {
355         p->save();
356         p->translate(shadowSize.width(), shadowSize.height());
357         p->setPen(shadowColor);
358         p->drawLine(p1, p2);
359         p->restore();
360     }
361 
362     int patWidth = 0;
363     switch (style) {
364     case NoStroke:
365     case SolidStroke:
366         break;
367     case DottedStroke:
368         patWidth = static_cast<int>(width);
369         break;
370     case DashedStroke:
371         patWidth = 3 * static_cast<int>(width);
372         break;
373     }
374 
375     if (patWidth) {
376         p->save();
377 
378         // Do a rect fill of our endpoints.  This ensures we always have the
379         // appearance of being a border.  We then draw the actual dotted/dashed line.
380         if (isVerticalLine) {
381             p->fillRect(FloatRect(p1.x() - width / 2, p1.y() - width, width, width), QColor(color));
382             p->fillRect(FloatRect(p2.x() - width / 2, p2.y(), width, width), QColor(color));
383         } else {
384             p->fillRect(FloatRect(p1.x() - width, p1.y() - width / 2, width, width), QColor(color));
385             p->fillRect(FloatRect(p2.x(), p2.y() - width / 2, width, width), QColor(color));
386         }
387 
388         // Example: 80 pixels with a width of 30 pixels.
389         // Remainder is 20.  The maximum pixels of line we could paint
390         // will be 50 pixels.
391         int distance = (isVerticalLine ? (point2.y() - point1.y()) : (point2.x() - point1.x())) - 2*(int)width;
392         int remainder = distance % patWidth;
393         int coverage = distance - remainder;
394         int numSegments = coverage / patWidth;
395 
396         float patternOffset = 0.0f;
397         // Special case 1px dotted borders for speed.
398         if (patWidth == 1)
399             patternOffset = 1.0f;
400         else {
401             bool evenNumberOfSegments = !(numSegments % 2);
402             if (remainder)
403                 evenNumberOfSegments = !evenNumberOfSegments;
404             if (evenNumberOfSegments) {
405                 if (remainder) {
406                     patternOffset += patWidth - remainder;
407                     patternOffset += remainder / 2;
408                 } else
409                     patternOffset = patWidth / 2;
410             } else {
411                 if (remainder)
412                     patternOffset = (patWidth - remainder)/2;
413             }
414         }
415 
416         QVector<qreal> dashes;
417         dashes << qreal(patWidth) / width << qreal(patWidth) / width;
418 
419         QPen pen = p->pen();
420         pen.setWidthF(width);
421         pen.setCapStyle(Qt::FlatCap);
422         pen.setDashPattern(dashes);
423         pen.setDashOffset(patternOffset / width);
424         p->setPen(pen);
425     }
426 
427     p->drawLine(p1, p2);
428 
429     if (patWidth)
430         p->restore();
431 
432     p->setRenderHint(QPainter::Antialiasing, antiAlias);
433 }
434 
435 // This method is only used to draw the little circles used in lists.
drawEllipse(const IntRect & rect)436 void GraphicsContext::drawEllipse(const IntRect& rect)
437 {
438     if (paintingDisabled())
439         return;
440 
441     m_data->p()->drawEllipse(rect);
442 }
443 
strokeArc(const IntRect & rect,int startAngle,int angleSpan)444 void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSpan)
445 {
446     if (paintingDisabled() || strokeStyle() == NoStroke || strokeThickness() <= 0.0f || !strokeColor().alpha())
447         return;
448 
449     QPainter* p = m_data->p();
450     const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
451     p->setRenderHint(QPainter::Antialiasing, true);
452 
453     IntSize shadowSize;
454     int shadowBlur;
455     Color shadowColor;
456     startAngle *= 16;
457     angleSpan *= 16;
458     if (getShadow(shadowSize, shadowBlur, shadowColor)) {
459         p->save();
460         p->translate(shadowSize.width(), shadowSize.height());
461         QPen pen(p->pen());
462         pen.setColor(shadowColor);
463         p->setPen(pen);
464         p->drawArc(rect, startAngle, angleSpan);
465         p->restore();
466     }
467     p->drawArc(rect, startAngle, angleSpan);
468 
469     p->setRenderHint(QPainter::Antialiasing, antiAlias);
470 }
471 
drawConvexPolygon(size_t npoints,const FloatPoint * points,bool shouldAntialias)472 void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool shouldAntialias)
473 {
474     if (paintingDisabled())
475         return;
476 
477     if (npoints <= 1)
478         return;
479 
480     QPolygonF polygon(npoints);
481 
482     for (size_t i = 0; i < npoints; i++)
483         polygon[i] = points[i];
484 
485     QPainter* p = m_data->p();
486     p->save();
487     p->setRenderHint(QPainter::Antialiasing, shouldAntialias);
488     IntSize shadowSize;
489     int shadowBlur;
490     Color shadowColor;
491     if (getShadow(shadowSize, shadowBlur, shadowColor)) {
492         p->save();
493         p->translate(shadowSize.width(), shadowSize.height());
494         if (p->brush().style() != Qt::NoBrush)
495             p->setBrush(QBrush(shadowColor));
496         QPen pen(p->pen());
497         if (pen.style() != Qt::NoPen) {
498             pen.setColor(shadowColor);
499             p->setPen(pen);
500         }
501         p->drawConvexPolygon(polygon);
502         p->restore();
503     }
504     p->drawConvexPolygon(polygon);
505     p->restore();
506 }
507 
pen()508 QPen GraphicsContext::pen()
509 {
510     if (paintingDisabled())
511         return QPen();
512 
513     QPainter* p = m_data->p();
514     return p->pen();
515 }
516 
drawFilledShadowPath(GraphicsContext * context,QPainter * p,const QPainterPath & path)517 static void inline drawFilledShadowPath(GraphicsContext* context, QPainter* p, const QPainterPath& path)
518 {
519     IntSize shadowSize;
520     int shadowBlur;
521     Color shadowColor;
522     if (context->getShadow(shadowSize, shadowBlur, shadowColor)) {
523         p->translate(shadowSize.width(), shadowSize.height());
524         p->fillPath(path, QBrush(shadowColor));
525         p->translate(-shadowSize.width(), -shadowSize.height());
526     }
527 }
528 
fillPath()529 void GraphicsContext::fillPath()
530 {
531     if (paintingDisabled())
532         return;
533 
534     QPainter* p = m_data->p();
535     QPainterPath path = m_data->currentPath;
536     path.setFillRule(toQtFillRule(fillRule()));
537 
538     if (m_common->state.fillPattern || m_common->state.fillGradient || fillColor().alpha()) {
539         drawFilledShadowPath(this, p, path);
540         if (m_common->state.fillPattern) {
541             AffineTransform affine;
542             p->fillPath(path, QBrush(m_common->state.fillPattern->createPlatformPattern(affine)));
543         } else if (m_common->state.fillGradient) {
544             QBrush brush(*m_common->state.fillGradient->platformGradient());
545             brush.setTransform(m_common->state.fillGradient->gradientSpaceTransform());
546             p->fillPath(path, brush);
547         } else {
548             if (fillColor().alpha())
549                 p->fillPath(path, p->brush());
550         }
551     }
552     m_data->currentPath = QPainterPath();
553 }
554 
strokePath()555 void GraphicsContext::strokePath()
556 {
557     if (paintingDisabled())
558         return;
559 
560     QPainter* p = m_data->p();
561     QPen pen(p->pen());
562     QPainterPath path = m_data->currentPath;
563     path.setFillRule(toQtFillRule(fillRule()));
564 
565     if (m_common->state.strokePattern || m_common->state.strokeGradient || strokeColor().alpha()) {
566         IntSize shadowSize;
567         int shadowBlur;
568         Color shadowColor;
569         if (getShadow(shadowSize, shadowBlur, shadowColor)) {
570             QTransform t(p->worldTransform());
571             p->translate(shadowSize.width(), shadowSize.height());
572             QPen shadowPen(pen);
573             shadowPen.setColor(shadowColor);
574             p->strokePath(path, shadowPen);
575             p->setWorldTransform(t);
576         }
577         if (m_common->state.strokePattern) {
578             AffineTransform affine;
579             pen.setBrush(QBrush(m_common->state.strokePattern->createPlatformPattern(affine)));
580             p->setPen(pen);
581             p->strokePath(path, pen);
582         } else if (m_common->state.strokeGradient) {
583             QBrush brush(*m_common->state.strokeGradient->platformGradient());
584             brush.setTransform(m_common->state.strokeGradient->gradientSpaceTransform());
585             pen.setBrush(brush);
586             p->setPen(pen);
587             p->strokePath(path, pen);
588         } else {
589             if (strokeColor().alpha())
590                 p->strokePath(path, pen);
591         }
592     }
593     m_data->currentPath = QPainterPath();
594 }
595 
drawBorderlessRectShadow(GraphicsContext * context,QPainter * p,const FloatRect & rect)596 static inline void drawBorderlessRectShadow(GraphicsContext* context, QPainter* p, const FloatRect& rect)
597 {
598     IntSize shadowSize;
599     int shadowBlur;
600     Color shadowColor;
601     if (context->getShadow(shadowSize, shadowBlur, shadowColor)) {
602         FloatRect shadowRect(rect);
603         shadowRect.move(shadowSize.width(), shadowSize.height());
604         p->fillRect(shadowRect, QColor(shadowColor));
605     }
606 }
607 
fillRect(const FloatRect & rect)608 void GraphicsContext::fillRect(const FloatRect& rect)
609 {
610     if (paintingDisabled())
611         return;
612 
613     QPainter* p = m_data->p();
614 
615     if (m_common->state.fillPattern || m_common->state.fillGradient || fillColor().alpha()) {
616         drawBorderlessRectShadow(this, p, rect);
617         if (m_common->state.fillPattern) {
618             AffineTransform affine;
619             p->fillRect(rect, QBrush(m_common->state.fillPattern->createPlatformPattern(affine)));
620         } else if (m_common->state.fillGradient) {
621             QBrush brush(*m_common->state.fillGradient->platformGradient());
622             brush.setTransform(m_common->state.fillGradient->gradientSpaceTransform());
623             p->fillRect(rect, brush);
624         } else {
625             if (fillColor().alpha())
626                 p->fillRect(rect, p->brush());
627         }
628     }
629 }
630 
fillRect(const FloatRect & rect,const Color & c,ColorSpace colorSpace)631 void GraphicsContext::fillRect(const FloatRect& rect, const Color& c, ColorSpace colorSpace)
632 {
633     if (paintingDisabled())
634         return;
635 
636     m_data->solidColor.setColor(c);
637     QPainter* p = m_data->p();
638     drawBorderlessRectShadow(this, p, rect);
639     p->fillRect(rect, m_data->solidColor);
640 }
641 
fillRoundedRect(const IntRect & rect,const IntSize & topLeft,const IntSize & topRight,const IntSize & bottomLeft,const IntSize & bottomRight,const Color & color,ColorSpace colorSpace)642 void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color, ColorSpace colorSpace)
643 {
644     if (paintingDisabled() || !color.alpha())
645         return;
646 
647     Path path = Path::createRoundedRectangle(rect, topLeft, topRight, bottomLeft, bottomRight);
648     QPainter* p = m_data->p();
649     drawFilledShadowPath(this, p, path.platformPath());
650     p->fillPath(path.platformPath(), QColor(color));
651 }
652 
beginPath()653 void GraphicsContext::beginPath()
654 {
655     m_data->currentPath = QPainterPath();
656 }
657 
addPath(const Path & path)658 void GraphicsContext::addPath(const Path& path)
659 {
660     QPainterPath newPath = m_data->currentPath;
661     newPath.addPath(path.platformPath());
662     m_data->currentPath = newPath;
663 }
664 
inTransparencyLayer() const665 bool GraphicsContext::inTransparencyLayer() const
666 {
667     return !m_data->layers.isEmpty();
668 }
669 
currentPath()670 PlatformPath* GraphicsContext::currentPath()
671 {
672     return &m_data->currentPath;
673 }
674 
clip(const FloatRect & rect)675 void GraphicsContext::clip(const FloatRect& rect)
676 {
677     if (paintingDisabled())
678         return;
679 
680     m_data->p()->setClipRect(rect, Qt::IntersectClip);
681 }
682 
clipPath(WindRule clipRule)683 void GraphicsContext::clipPath(WindRule clipRule)
684 {
685     if (paintingDisabled())
686         return;
687 
688     QPainter* p = m_data->p();
689     QPainterPath newPath = m_data->currentPath;
690     newPath.setFillRule(clipRule == RULE_EVENODD ? Qt::OddEvenFill : Qt::WindingFill);
691     p->setClipPath(newPath);
692 }
693 
drawFocusRing(const Vector<Path> & paths,int width,int offset,const Color & color)694 void GraphicsContext::drawFocusRing(const Vector<Path>& paths, int width, int offset, const Color& color)
695 {
696     // FIXME: implement
697 }
698 
699 /**
700  * Focus ring handling is not handled here. Qt style in
701  * RenderTheme handles drawing focus on widgets which
702  * need it.
703  */
drawFocusRing(const Vector<IntRect> & rects,int,int,const Color & color)704 void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int /* width */, int /* offset */, const Color& color)
705 {
706     if (paintingDisabled())
707         return;
708 
709     unsigned rectCount = rects.size();
710 
711     if (!rects.size())
712         return;
713 
714     QPainter* p = m_data->p();
715     const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
716     p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines);
717 
718     const QPen oldPen = p->pen();
719     const QBrush oldBrush = p->brush();
720 
721     QPen nPen = p->pen();
722     nPen.setColor(color);
723     p->setBrush(Qt::NoBrush);
724     nPen.setStyle(Qt::DotLine);
725     p->setPen(nPen);
726 #if 0
727     // FIXME How do we do a bounding outline with Qt?
728     QPainterPath path;
729     for (int i = 0; i < rectCount; ++i)
730         path.addRect(QRectF(rects[i]));
731     QPainterPathStroker stroker;
732     QPainterPath newPath = stroker.createStroke(path);
733     p->strokePath(newPath, nPen);
734 #else
735     for (unsigned i = 0; i < rectCount; ++i)
736         p->drawRect(QRectF(rects[i]));
737 #endif
738     p->setPen(oldPen);
739     p->setBrush(oldBrush);
740 
741     p->setRenderHint(QPainter::Antialiasing, antiAlias);
742 }
743 
drawLineForText(const IntPoint & origin,int width,bool)744 void GraphicsContext::drawLineForText(const IntPoint& origin, int width, bool)
745 {
746     if (paintingDisabled())
747         return;
748 
749     IntPoint endPoint = origin + IntSize(width, 0);
750     drawLine(origin, endPoint);
751 }
752 
drawLineForMisspellingOrBadGrammar(const IntPoint &,int,bool)753 void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint&, int, bool)
754 {
755     if (paintingDisabled())
756         return;
757 
758     notImplemented();
759 }
760 
roundToDevicePixels(const FloatRect & frect)761 FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect)
762 {
763     QRectF rect(frect);
764     rect = m_data->p()->deviceMatrix().mapRect(rect);
765 
766     QRect result = rect.toRect(); //round it
767     return FloatRect(QRectF(result));
768 }
769 
setPlatformShadow(const IntSize & size,int,const Color &,ColorSpace)770 void GraphicsContext::setPlatformShadow(const IntSize& size, int, const Color&, ColorSpace)
771 {
772     // Qt doesn't support shadows natively, they are drawn manually in the draw*
773     // functions
774 
775     if (m_common->state.shadowsIgnoreTransforms) {
776         // Meaning that this graphics context is associated with a CanvasRenderingContext
777         // We flip the height since CG and HTML5 Canvas have opposite Y axis
778         m_common->state.shadowSize = IntSize(size.width(), -size.height());
779     }
780 }
781 
clearPlatformShadow()782 void GraphicsContext::clearPlatformShadow()
783 {
784     // Qt doesn't support shadows natively, they are drawn manually in the draw*
785     // functions
786 }
787 
beginTransparencyLayer(float opacity)788 void GraphicsContext::beginTransparencyLayer(float opacity)
789 {
790     if (paintingDisabled())
791         return;
792 
793     int x, y, w, h;
794     x = y = 0;
795     QPainter* p = m_data->p();
796     const QPaintDevice* device = p->device();
797     w = device->width();
798     h = device->height();
799 
800     QRectF clip = p->clipPath().boundingRect();
801     QRectF deviceClip = p->transform().mapRect(clip);
802     x = int(qBound(qreal(0), deviceClip.x(), (qreal)w));
803     y = int(qBound(qreal(0), deviceClip.y(), (qreal)h));
804     w = int(qBound(qreal(0), deviceClip.width(), (qreal)w) + 2);
805     h = int(qBound(qreal(0), deviceClip.height(), (qreal)h) + 2);
806 
807     TransparencyLayer * layer = new TransparencyLayer(m_data->p(), QRect(x, y, w, h));
808 
809     layer->opacity = opacity;
810     m_data->layers.push(layer);
811 }
812 
endTransparencyLayer()813 void GraphicsContext::endTransparencyLayer()
814 {
815     if (paintingDisabled())
816         return;
817 
818     TransparencyLayer* layer = m_data->layers.pop();
819     layer->painter.end();
820 
821     QPainter* p = m_data->p();
822     p->save();
823     p->resetTransform();
824     p->setOpacity(layer->opacity);
825     p->drawPixmap(layer->offset, layer->pixmap);
826     p->restore();
827 
828     delete layer;
829 }
830 
clearRect(const FloatRect & rect)831 void GraphicsContext::clearRect(const FloatRect& rect)
832 {
833     if (paintingDisabled())
834         return;
835 
836     QPainter* p = m_data->p();
837     QPainter::CompositionMode currentCompositionMode = p->compositionMode();
838     if (p->paintEngine()->hasFeature(QPaintEngine::PorterDuff))
839         p->setCompositionMode(QPainter::CompositionMode_Source);
840     p->fillRect(rect, Qt::transparent);
841     if (p->paintEngine()->hasFeature(QPaintEngine::PorterDuff))
842         p->setCompositionMode(currentCompositionMode);
843 }
844 
strokeRect(const FloatRect & rect,float width)845 void GraphicsContext::strokeRect(const FloatRect& rect, float width)
846 {
847     if (paintingDisabled())
848         return;
849 
850     QPainterPath path;
851     path.addRect(rect);
852     setStrokeThickness(width);
853     m_data->currentPath = path;
854 
855     strokePath();
856 }
857 
setLineCap(LineCap lc)858 void GraphicsContext::setLineCap(LineCap lc)
859 {
860     if (paintingDisabled())
861         return;
862 
863     QPainter* p = m_data->p();
864     QPen nPen = p->pen();
865     nPen.setCapStyle(toQtLineCap(lc));
866     p->setPen(nPen);
867 }
868 
setLineDash(const DashArray & dashes,float dashOffset)869 void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
870 {
871     QPainter* p = m_data->p();
872     QPen pen = p->pen();
873     unsigned dashLength = dashes.size();
874     if (dashLength) {
875         QVector<qreal> pattern;
876         unsigned count = dashLength;
877         if (dashLength % 2)
878             count *= 2;
879 
880         float penWidth = narrowPrecisionToFloat(double(pen.widthF()));
881         for (unsigned i = 0; i < count; i++)
882             pattern.append(dashes[i % dashLength] / penWidth);
883 
884         pen.setDashPattern(pattern);
885         pen.setDashOffset(dashOffset);
886     }
887     p->setPen(pen);
888 }
889 
setLineJoin(LineJoin lj)890 void GraphicsContext::setLineJoin(LineJoin lj)
891 {
892     if (paintingDisabled())
893         return;
894 
895     QPainter* p = m_data->p();
896     QPen nPen = p->pen();
897     nPen.setJoinStyle(toQtLineJoin(lj));
898     p->setPen(nPen);
899 }
900 
setMiterLimit(float limit)901 void GraphicsContext::setMiterLimit(float limit)
902 {
903     if (paintingDisabled())
904         return;
905 
906     QPainter* p = m_data->p();
907     QPen nPen = p->pen();
908     nPen.setMiterLimit(limit);
909     p->setPen(nPen);
910 }
911 
setAlpha(float opacity)912 void GraphicsContext::setAlpha(float opacity)
913 {
914     if (paintingDisabled())
915         return;
916     QPainter* p = m_data->p();
917     p->setOpacity(opacity);
918 }
919 
setCompositeOperation(CompositeOperator op)920 void GraphicsContext::setCompositeOperation(CompositeOperator op)
921 {
922     if (paintingDisabled())
923         return;
924 
925     if (m_data->p()->paintEngine()->hasFeature(QPaintEngine::PorterDuff))
926         m_data->p()->setCompositionMode(toQtCompositionMode(op));
927 }
928 
clip(const Path & path)929 void GraphicsContext::clip(const Path& path)
930 {
931     if (paintingDisabled())
932         return;
933 
934     m_data->p()->setClipPath(path.platformPath(), Qt::IntersectClip);
935 }
936 
canvasClip(const Path & path)937 void GraphicsContext::canvasClip(const Path& path)
938 {
939     clip(path);
940 }
941 
clipOut(const Path & path)942 void GraphicsContext::clipOut(const Path& path)
943 {
944     if (paintingDisabled())
945         return;
946 
947     QPainter* p = m_data->p();
948     QPainterPath clippedOut = path.platformPath();
949     QPainterPath newClip;
950     newClip.setFillRule(Qt::OddEvenFill);
951     if (p->hasClipping()) {
952         newClip.addRect(p->clipRegion().boundingRect());
953         newClip.addPath(clippedOut);
954         p->setClipPath(newClip, Qt::IntersectClip);
955     } else {
956         newClip.addRect(p->window());
957         newClip.addPath(clippedOut.intersected(newClip));
958         p->setClipPath(newClip);
959     }
960 }
961 
translate(float x,float y)962 void GraphicsContext::translate(float x, float y)
963 {
964     if (paintingDisabled())
965         return;
966 
967     m_data->p()->translate(x, y);
968 
969     if (!m_data->currentPath.isEmpty()) {
970         QTransform matrix;
971         m_data->currentPath = m_data->currentPath * matrix.translate(-x, -y);
972         m_common->state.pathTransform.translate(x, y);
973     }
974 }
975 
origin()976 IntPoint GraphicsContext::origin()
977 {
978     if (paintingDisabled())
979         return IntPoint();
980     const QTransform &transform = m_data->p()->transform();
981     return IntPoint(qRound(transform.dx()), qRound(transform.dy()));
982 }
983 
rotate(float radians)984 void GraphicsContext::rotate(float radians)
985 {
986     if (paintingDisabled())
987         return;
988 
989     m_data->p()->rotate(180/M_PI*radians);
990 
991     if (!m_data->currentPath.isEmpty()) {
992         QTransform matrix;
993         m_data->currentPath = m_data->currentPath * matrix.rotate(-180/M_PI*radians);
994         m_common->state.pathTransform.rotate(radians);
995     }
996 }
997 
scale(const FloatSize & s)998 void GraphicsContext::scale(const FloatSize& s)
999 {
1000     if (paintingDisabled())
1001         return;
1002 
1003     m_data->p()->scale(s.width(), s.height());
1004 
1005     if (!m_data->currentPath.isEmpty()) {
1006         QTransform matrix;
1007         m_data->currentPath = m_data->currentPath * matrix.scale(1 / s.width(), 1 / s.height());
1008         m_common->state.pathTransform.scaleNonUniform(s.width(), s.height());
1009     }
1010 }
1011 
clipOut(const IntRect & rect)1012 void GraphicsContext::clipOut(const IntRect& rect)
1013 {
1014     if (paintingDisabled())
1015         return;
1016 
1017     QPainter* p = m_data->p();
1018     QPainterPath newClip;
1019     newClip.setFillRule(Qt::OddEvenFill);
1020     if (p->hasClipping()) {
1021         newClip.addRect(p->clipRegion().boundingRect());
1022         newClip.addRect(QRect(rect));
1023         p->setClipPath(newClip, Qt::IntersectClip);
1024     } else {
1025         QRect clipOutRect(rect);
1026         QRect window(p->window());
1027         clipOutRect &= window;
1028         newClip.addRect(window);
1029         newClip.addRect(clipOutRect);
1030         p->setClipPath(newClip);
1031     }
1032 }
1033 
clipOutEllipseInRect(const IntRect & rect)1034 void GraphicsContext::clipOutEllipseInRect(const IntRect& rect)
1035 {
1036     if (paintingDisabled())
1037         return;
1038 
1039     QPainter* p = m_data->p();
1040     QPainterPath newClip;
1041     newClip.setFillRule(Qt::OddEvenFill);
1042     if (p->hasClipping()) {
1043         newClip.addRect(p->clipRegion().boundingRect());
1044         newClip.addEllipse(QRect(rect));
1045         p->setClipPath(newClip, Qt::IntersectClip);
1046     } else {
1047         QRect clipOutRect(rect);
1048         QRect window(p->window());
1049         clipOutRect &= window;
1050         newClip.addRect(window);
1051         newClip.addEllipse(clipOutRect);
1052         p->setClipPath(newClip);
1053     }
1054 }
1055 
clipToImageBuffer(const FloatRect &,const ImageBuffer *)1056 void GraphicsContext::clipToImageBuffer(const FloatRect&, const ImageBuffer*)
1057 {
1058     notImplemented();
1059 }
1060 
addInnerRoundedRectClip(const IntRect & rect,int thickness)1061 void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect,
1062                                               int thickness)
1063 {
1064     if (paintingDisabled())
1065         return;
1066 
1067     clip(rect);
1068     QPainterPath path;
1069 
1070     // Add outer ellipse
1071     path.addEllipse(QRectF(rect.x(), rect.y(), rect.width(), rect.height()));
1072 
1073     // Add inner ellipse.
1074     path.addEllipse(QRectF(rect.x() + thickness, rect.y() + thickness,
1075                            rect.width() - (thickness * 2), rect.height() - (thickness * 2)));
1076 
1077     path.setFillRule(Qt::OddEvenFill);
1078 
1079     QPainter* p = m_data->p();
1080 
1081     const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
1082     p->setRenderHint(QPainter::Antialiasing, true);
1083     p->setClipPath(path, Qt::IntersectClip);
1084     p->setRenderHint(QPainter::Antialiasing, antiAlias);
1085 }
1086 
concatCTM(const AffineTransform & transform)1087 void GraphicsContext::concatCTM(const AffineTransform& transform)
1088 {
1089     if (paintingDisabled())
1090         return;
1091 
1092     m_data->p()->setWorldTransform(transform, true);
1093 
1094     // Transformations to the context shouldn't transform the currentPath.
1095     // We have to undo every change made to the context from the currentPath
1096     // to avoid wrong drawings.
1097     if (!m_data->currentPath.isEmpty() && transform.isInvertible()) {
1098         QTransform matrix = transform.inverse();
1099         m_data->currentPath = m_data->currentPath * matrix;
1100         m_common->state.pathTransform.multiply(transform.toTransformationMatrix());
1101     }
1102 }
1103 
setURLForRect(const KURL &,const IntRect &)1104 void GraphicsContext::setURLForRect(const KURL&, const IntRect&)
1105 {
1106     notImplemented();
1107 }
1108 
setPlatformStrokeColor(const Color & color,ColorSpace colorSpace)1109 void GraphicsContext::setPlatformStrokeColor(const Color& color, ColorSpace colorSpace)
1110 {
1111     if (paintingDisabled())
1112         return;
1113     QPainter* p = m_data->p();
1114     QPen newPen(p->pen());
1115     newPen.setColor(color);
1116     p->setPen(newPen);
1117 }
1118 
setPlatformStrokeStyle(const StrokeStyle & strokeStyle)1119 void GraphicsContext::setPlatformStrokeStyle(const StrokeStyle& strokeStyle)
1120 {
1121     if (paintingDisabled())
1122         return;
1123     QPainter* p = m_data->p();
1124     QPen newPen(p->pen());
1125     newPen.setStyle(toQPenStyle(strokeStyle));
1126     p->setPen(newPen);
1127 }
1128 
setPlatformStrokeThickness(float thickness)1129 void GraphicsContext::setPlatformStrokeThickness(float thickness)
1130 {
1131     if (paintingDisabled())
1132         return;
1133     QPainter* p = m_data->p();
1134     QPen newPen(p->pen());
1135     newPen.setWidthF(thickness);
1136     p->setPen(newPen);
1137 }
1138 
setPlatformFillColor(const Color & color,ColorSpace colorSpace)1139 void GraphicsContext::setPlatformFillColor(const Color& color, ColorSpace colorSpace)
1140 {
1141     if (paintingDisabled())
1142         return;
1143     m_data->solidColor.setColor(color);
1144     m_data->p()->setBrush(m_data->solidColor);
1145 }
1146 
setPlatformShouldAntialias(bool enable)1147 void GraphicsContext::setPlatformShouldAntialias(bool enable)
1148 {
1149     if (paintingDisabled())
1150         return;
1151     m_data->p()->setRenderHint(QPainter::Antialiasing, enable);
1152 }
1153 
1154 #ifdef Q_WS_WIN
1155 
getWindowsContext(const IntRect & dstRect,bool supportAlphaBlend,bool mayCreateBitmap)1156 HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap)
1157 {
1158     // painting through native HDC is only supported for plugin, where mayCreateBitmap is always true
1159     Q_ASSERT(mayCreateBitmap);
1160 
1161     if (dstRect.isEmpty())
1162         return 0;
1163 
1164     // Create a bitmap DC in which to draw.
1165     BITMAPINFO bitmapInfo;
1166     bitmapInfo.bmiHeader.biSize          = sizeof(BITMAPINFOHEADER);
1167     bitmapInfo.bmiHeader.biWidth         = dstRect.width();
1168     bitmapInfo.bmiHeader.biHeight        = dstRect.height();
1169     bitmapInfo.bmiHeader.biPlanes        = 1;
1170     bitmapInfo.bmiHeader.biBitCount      = 32;
1171     bitmapInfo.bmiHeader.biCompression   = BI_RGB;
1172     bitmapInfo.bmiHeader.biSizeImage     = 0;
1173     bitmapInfo.bmiHeader.biXPelsPerMeter = 0;
1174     bitmapInfo.bmiHeader.biYPelsPerMeter = 0;
1175     bitmapInfo.bmiHeader.biClrUsed       = 0;
1176     bitmapInfo.bmiHeader.biClrImportant  = 0;
1177 
1178     void* pixels = 0;
1179     HBITMAP bitmap = ::CreateDIBSection(0, &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0);
1180     if (!bitmap)
1181         return 0;
1182 
1183     HDC displayDC = ::GetDC(0);
1184     HDC bitmapDC = ::CreateCompatibleDC(displayDC);
1185     ::ReleaseDC(0, displayDC);
1186 
1187     ::SelectObject(bitmapDC, bitmap);
1188 
1189     // Fill our buffer with clear if we're going to alpha blend.
1190     if (supportAlphaBlend) {
1191         BITMAP bmpInfo;
1192         GetObject(bitmap, sizeof(bmpInfo), &bmpInfo);
1193         int bufferSize = bmpInfo.bmWidthBytes * bmpInfo.bmHeight;
1194         memset(bmpInfo.bmBits, 0, bufferSize);
1195     }
1196 
1197 #if !OS(WINCE)
1198     // Make sure we can do world transforms.
1199     SetGraphicsMode(bitmapDC, GM_ADVANCED);
1200 
1201     // Apply a translation to our context so that the drawing done will be at (0,0) of the bitmap.
1202     XFORM xform;
1203     xform.eM11 = 1.0f;
1204     xform.eM12 = 0.0f;
1205     xform.eM21 = 0.0f;
1206     xform.eM22 = 1.0f;
1207     xform.eDx = -dstRect.x();
1208     xform.eDy = -dstRect.y();
1209     ::SetWorldTransform(bitmapDC, &xform);
1210 #endif
1211 
1212     return bitmapDC;
1213 }
1214 
releaseWindowsContext(HDC hdc,const IntRect & dstRect,bool supportAlphaBlend,bool mayCreateBitmap)1215 void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap)
1216 {
1217     // painting through native HDC is only supported for plugin, where mayCreateBitmap is always true
1218     Q_ASSERT(mayCreateBitmap);
1219 
1220     if (hdc) {
1221 
1222         if (!dstRect.isEmpty()) {
1223 
1224             HBITMAP bitmap = static_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP));
1225             BITMAP info;
1226             GetObject(bitmap, sizeof(info), &info);
1227             ASSERT(info.bmBitsPixel == 32);
1228 
1229             QPixmap pixmap = QPixmap::fromWinHBITMAP(bitmap, supportAlphaBlend ? QPixmap::PremultipliedAlpha : QPixmap::NoAlpha);
1230             m_data->p()->drawPixmap(dstRect, pixmap);
1231 
1232             ::DeleteObject(bitmap);
1233         }
1234 
1235         ::DeleteDC(hdc);
1236     }
1237 }
1238 #endif
1239 
setImageInterpolationQuality(InterpolationQuality quality)1240 void GraphicsContext::setImageInterpolationQuality(InterpolationQuality quality)
1241 {
1242     m_data->imageInterpolationQuality = quality;
1243 
1244     switch (quality) {
1245     case InterpolationDefault:
1246     case InterpolationNone:
1247     case InterpolationLow:
1248         // use nearest-neigbor
1249         m_data->p()->setRenderHint(QPainter::SmoothPixmapTransform, false);
1250         break;
1251 
1252     case InterpolationMedium:
1253     case InterpolationHigh:
1254     default:
1255         // use the filter
1256         m_data->p()->setRenderHint(QPainter::SmoothPixmapTransform, true);
1257         break;
1258     };
1259 }
1260 
imageInterpolationQuality() const1261 InterpolationQuality GraphicsContext::imageInterpolationQuality() const
1262 {
1263     return m_data->imageInterpolationQuality;
1264 }
1265 
1266 }
1267 
1268 // vim: ts=4 sw=4 et
1269