• 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 
38 #ifdef Q_WS_WIN
39 #include <windows.h>
40 #endif
41 
42 #include "TransformationMatrix.h"
43 #include "Color.h"
44 #include "FloatConversion.h"
45 #include "Font.h"
46 #include "GraphicsContext.h"
47 #include "GraphicsContextPrivate.h"
48 #include "ImageBuffer.h"
49 #include "Path.h"
50 #include "Pattern.h"
51 #include "Pen.h"
52 #include "NotImplemented.h"
53 
54 #include <QBrush>
55 #include <QDebug>
56 #include <QGradient>
57 #include <QPainter>
58 #include <QPaintDevice>
59 #include <QPaintEngine>
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 {
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 {
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         } else
214             return &layers.top()->painter;
215     }
216 
217     bool antiAliasingForRectsAndLines;
218 
219     QStack<TransparencyLayer*> layers;
220     QPainter* redirect;
221 
222     QBrush solidColor;
223 
224     // Only used by SVG for now.
225     QPainterPath currentPath;
226 
227 private:
228     QPainter* painter;
229 };
230 
231 
GraphicsContextPlatformPrivate(QPainter * p)232 GraphicsContextPlatformPrivate::GraphicsContextPlatformPrivate(QPainter* p)
233 {
234     painter = p;
235     redirect = 0;
236 
237     solidColor = QBrush(Qt::black);
238 
239     if (painter) {
240         // use the default the QPainter was constructed with
241         antiAliasingForRectsAndLines = painter->testRenderHint(QPainter::Antialiasing);
242         // FIXME: Maybe only enable in SVG mode?
243         painter->setRenderHint(QPainter::Antialiasing, true);
244     } else
245         antiAliasingForRectsAndLines = false;
246 }
247 
~GraphicsContextPlatformPrivate()248 GraphicsContextPlatformPrivate::~GraphicsContextPlatformPrivate()
249 {
250 }
251 
GraphicsContext(PlatformGraphicsContext * context)252 GraphicsContext::GraphicsContext(PlatformGraphicsContext* context)
253     : m_common(createGraphicsContextPrivate())
254     , m_data(new GraphicsContextPlatformPrivate(context))
255 {
256     setPaintingDisabled(!context);
257     if (context) {
258         // Make sure the context starts in sync with our state.
259         setPlatformFillColor(fillColor());
260         setPlatformStrokeColor(strokeColor());
261     }
262 }
263 
~GraphicsContext()264 GraphicsContext::~GraphicsContext()
265 {
266     while (!m_data->layers.isEmpty())
267         endTransparencyLayer();
268 
269     destroyGraphicsContextPrivate(m_common);
270     delete m_data;
271 }
272 
platformContext() const273 PlatformGraphicsContext* GraphicsContext::platformContext() const
274 {
275     return m_data->p();
276 }
277 
getCTM() const278 TransformationMatrix GraphicsContext::getCTM() const
279 {
280     QTransform matrix(platformContext()->combinedTransform());
281     return TransformationMatrix(matrix.m11(), matrix.m12(), 0, matrix.m13(),
282                                 matrix.m21(), matrix.m22(), 0, matrix.m23(),
283                                            0,            0, 1,            0,
284                                 matrix.m31(), matrix.m32(), 0, matrix.m33());
285 }
286 
savePlatformState()287 void GraphicsContext::savePlatformState()
288 {
289     m_data->p()->save();
290 }
291 
restorePlatformState()292 void GraphicsContext::restorePlatformState()
293 {
294     m_data->p()->restore();
295 
296     if (!m_data->currentPath.isEmpty() && m_common->state.pathTransform.isInvertible()) {
297         QTransform matrix = m_common->state.pathTransform;
298         m_data->currentPath = m_data->currentPath * matrix;
299     }
300 }
301 
302 /* FIXME: DISABLED WHILE MERGING BACK FROM UNITY
303 void GraphicsContext::drawTextShadow(const TextRun& run, const IntPoint& point, const FontStyle& style)
304 {
305     if (paintingDisabled())
306         return;
307 
308     if (m_data->shadow.isNull())
309         return;
310 
311     TextShadow* shadow = &m_data->shadow;
312 
313     if (shadow->blur <= 0) {
314         Pen p = pen();
315         setPen(shadow->color);
316         font().drawText(this, run, style, IntPoint(point.x() + shadow->x, point.y() + shadow->y));
317         setPen(p);
318     } else {
319         const int thickness = shadow->blur;
320         // FIXME: OPTIMIZE: limit the area to only the actually painted area + 2*thickness
321         const int w = m_data->p()->device()->width();
322         const int h = m_data->p()->device()->height();
323         const QRgb color = qRgb(255, 255, 255);
324         const QRgb bgColor = qRgb(0, 0, 0);
325         QImage image(QSize(w, h), QImage::Format_ARGB32);
326         image.fill(bgColor);
327         QPainter p;
328 
329         Pen curPen = pen();
330         p.begin(&image);
331         setPen(color);
332         m_data->redirect = &p;
333         font().drawText(this, run, style, IntPoint(point.x() + shadow->x, point.y() + shadow->y));
334         m_data->redirect = 0;
335         p.end();
336         setPen(curPen);
337 
338         int md = thickness * thickness; // max-dist^2
339 
340         // blur map/precalculated shadow-decay
341         float* bmap = (float*) alloca(sizeof(float) * (md + 1));
342         for (int n = 0; n <= md; n++) {
343             float f;
344             f = n / (float) (md + 1);
345             f = 1.0 - f * f;
346             bmap[n] = f;
347         }
348 
349         float factor = 0.0; // maximal potential opacity-sum
350         for (int n = -thickness; n <= thickness; n++) {
351             for (int m = -thickness; m <= thickness; m++) {
352                 int d = n * n + m * m;
353                 if (d <= md)
354                     factor += bmap[d];
355             }
356         }
357 
358         // alpha map
359         float* amap = (float*) alloca(sizeof(float) * (h * w));
360         memset(amap, 0, h * w * (sizeof(float)));
361 
362         for (int j = thickness; j<h-thickness; j++) {
363             for (int i = thickness; i<w-thickness; i++) {
364                 QRgb col = image.pixel(i,j);
365                 if (col == bgColor)
366                     continue;
367 
368                 float g = qAlpha(col);
369                 g = g / 255;
370 
371                 for (int n = -thickness; n <= thickness; n++) {
372                     for (int m = -thickness; m <= thickness; m++) {
373                         int d = n * n + m * m;
374                         if (d > md)
375                             continue;
376 
377                         float f = bmap[d];
378                         amap[(i + m) + (j + n) * w] += (g * f);
379                     }
380                 }
381             }
382         }
383 
384         QImage res(QSize(w,h),QImage::Format_ARGB32);
385         int r = shadow->color.red();
386         int g = shadow->color.green();
387         int b = shadow->color.blue();
388         int a1 = shadow->color.alpha();
389 
390         // arbitratry factor adjustment to make shadows more solid.
391         factor = 1.333 / factor;
392 
393         for (int j = 0; j < h; j++) {
394             for (int i = 0; i < w; i++) {
395                 int a = (int) (amap[i + j * w] * factor * a1);
396                 if (a > 255)
397                     a = 255;
398 
399                 res.setPixel(i,j, qRgba(r, g, b, a));
400             }
401         }
402 
403         m_data->p()->drawImage(0, 0, res, 0, 0, -1, -1, Qt::DiffuseAlphaDither | Qt::ColorOnly | Qt::PreferDither);
404     }
405 }
406 */
407 
408 // Draws a filled rectangle with a stroked border.
drawRect(const IntRect & rect)409 void GraphicsContext::drawRect(const IntRect& rect)
410 {
411     if (paintingDisabled())
412         return;
413 
414     QPainter *p = m_data->p();
415     const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
416     p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines);
417 
418     p->drawRect(rect);
419 
420     p->setRenderHint(QPainter::Antialiasing, antiAlias);
421 }
422 
423 // FIXME: Now that this is refactored, it should be shared by all contexts.
adjustLineToPixelBoundaries(FloatPoint & p1,FloatPoint & p2,float strokeWidth,const StrokeStyle & penStyle)424 static void adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth,
425                                         const StrokeStyle& penStyle)
426 {
427     // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic
428     // works out.  For example, with a border width of 3, KHTML will pass us (y1+y2)/2, e.g.,
429     // (50+53)/2 = 103/2 = 51 when we want 51.5.  It is always true that an even width gave
430     // us a perfect position, but an odd width gave us a position that is off by exactly 0.5.
431     if (penStyle == DottedStroke || penStyle == DashedStroke) {
432         if (p1.x() == p2.x()) {
433             p1.setY(p1.y() + strokeWidth);
434             p2.setY(p2.y() - strokeWidth);
435         } else {
436             p1.setX(p1.x() + strokeWidth);
437             p2.setX(p2.x() - strokeWidth);
438         }
439     }
440 
441     if (((int) strokeWidth) % 2) {
442         if (p1.x() == p2.x()) {
443             // We're a vertical line.  Adjust our x.
444             p1.setX(p1.x() + 0.5);
445             p2.setX(p2.x() + 0.5);
446         } else {
447             // We're a horizontal line. Adjust our y.
448             p1.setY(p1.y() + 0.5);
449             p2.setY(p2.y() + 0.5);
450         }
451     }
452 }
453 
454 // This is only used to draw borders.
drawLine(const IntPoint & point1,const IntPoint & point2)455 void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
456 {
457     if (paintingDisabled())
458         return;
459 
460     StrokeStyle style = strokeStyle();
461     Color color = strokeColor();
462     if (style == NoStroke || !color.alpha())
463         return;
464 
465     float width = strokeThickness();
466 
467     FloatPoint p1 = point1;
468     FloatPoint p2 = point2;
469     bool isVerticalLine = (p1.x() == p2.x());
470 
471     QPainter *p = m_data->p();
472     const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
473     p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines);
474     adjustLineToPixelBoundaries(p1, p2, width, style);
475 
476     IntSize shadowSize;
477     int shadowBlur;
478     Color shadowColor;
479     if (textDrawingMode() == cTextFill && getShadow(shadowSize, shadowBlur, shadowColor)) {
480         p->save();
481         p->translate(shadowSize.width(), shadowSize.height());
482         p->setPen(QColor(shadowColor));
483         p->drawLine(p1, p2);
484         p->restore();
485     }
486 
487     int patWidth = 0;
488     switch (style) {
489         case NoStroke:
490         case SolidStroke:
491             break;
492         case DottedStroke:
493             patWidth = (int)width;
494             break;
495         case DashedStroke:
496             patWidth = 3 * (int)width;
497             break;
498     }
499 
500     if (patWidth) {
501         p->save();
502 
503         // Do a rect fill of our endpoints.  This ensures we always have the
504         // appearance of being a border.  We then draw the actual dotted/dashed line.
505         if (isVerticalLine) {
506             p->fillRect(FloatRect(p1.x() - width / 2, p1.y() - width, width, width), QColor(color));
507             p->fillRect(FloatRect(p2.x() - width / 2, p2.y(), width, width), QColor(color));
508         } else {
509             p->fillRect(FloatRect(p1.x() - width, p1.y() - width / 2, width, width), QColor(color));
510             p->fillRect(FloatRect(p2.x(), p2.y() - width / 2, width, width), QColor(color));
511         }
512 
513         // Example: 80 pixels with a width of 30 pixels.
514         // Remainder is 20.  The maximum pixels of line we could paint
515         // will be 50 pixels.
516         int distance = (isVerticalLine ? (point2.y() - point1.y()) : (point2.x() - point1.x())) - 2*(int)width;
517         int remainder = distance % patWidth;
518         int coverage = distance - remainder;
519         int numSegments = coverage / patWidth;
520 
521         float patternOffset = 0.0f;
522         // Special case 1px dotted borders for speed.
523         if (patWidth == 1)
524             patternOffset = 1.0f;
525         else {
526             bool evenNumberOfSegments = numSegments % 2 == 0;
527             if (remainder)
528                 evenNumberOfSegments = !evenNumberOfSegments;
529             if (evenNumberOfSegments) {
530                 if (remainder) {
531                     patternOffset += patWidth - remainder;
532                     patternOffset += remainder / 2;
533                 } else
534                     patternOffset = patWidth / 2;
535             } else {
536                 if (remainder)
537                     patternOffset = (patWidth - remainder)/2;
538             }
539         }
540 
541         QVector<qreal> dashes;
542         dashes << qreal(patWidth) / width << qreal(patWidth) / width;
543 
544         QPen pen = p->pen();
545         pen.setWidthF(width);
546         pen.setCapStyle(Qt::FlatCap);
547         pen.setDashPattern(dashes);
548         pen.setDashOffset(patternOffset / width);
549         p->setPen(pen);
550     }
551 
552     p->drawLine(p1, p2);
553 
554     if (patWidth)
555         p->restore();
556 
557     p->setRenderHint(QPainter::Antialiasing, antiAlias);
558 }
559 
560 // This method is only used to draw the little circles used in lists.
drawEllipse(const IntRect & rect)561 void GraphicsContext::drawEllipse(const IntRect& rect)
562 {
563     if (paintingDisabled())
564         return;
565 
566     m_data->p()->drawEllipse(rect);
567 }
568 
strokeArc(const IntRect & rect,int startAngle,int angleSpan)569 void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSpan)
570 {
571     if (paintingDisabled() || strokeStyle() == NoStroke || strokeThickness() <= 0.0f || !strokeColor().alpha())
572         return;
573 
574     QPainter *p = m_data->p();
575     const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
576     p->setRenderHint(QPainter::Antialiasing, true);
577 
578     p->drawArc(rect, startAngle * 16, angleSpan * 16);
579 
580     p->setRenderHint(QPainter::Antialiasing, antiAlias);
581 }
582 
drawConvexPolygon(size_t npoints,const FloatPoint * points,bool shouldAntialias)583 void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool shouldAntialias)
584 {
585     if (paintingDisabled())
586         return;
587 
588     if (npoints <= 1)
589         return;
590 
591     QPolygonF polygon(npoints);
592 
593     for (size_t i = 0; i < npoints; i++)
594         polygon[i] = points[i];
595 
596     QPainter *p = m_data->p();
597     p->save();
598     p->setRenderHint(QPainter::Antialiasing, shouldAntialias);
599     p->drawConvexPolygon(polygon);
600     p->restore();
601 }
602 
pen()603 QPen GraphicsContext::pen()
604 {
605     if (paintingDisabled())
606         return QPen();
607 
608     QPainter *p = m_data->p();
609     return p->pen();
610 }
611 
fillPath()612 void GraphicsContext::fillPath()
613 {
614     if (paintingDisabled())
615         return;
616 
617     QPainter *p = m_data->p();
618     QPainterPath path = m_data->currentPath;
619     path.setFillRule(toQtFillRule(fillRule()));
620 
621     switch (m_common->state.fillColorSpace) {
622     case SolidColorSpace:
623         if (fillColor().alpha())
624             p->fillPath(path, p->brush());
625         break;
626     case PatternColorSpace: {
627         TransformationMatrix affine;
628         p->fillPath(path, QBrush(m_common->state.fillPattern->createPlatformPattern(affine)));
629         break;
630     }
631     case GradientColorSpace:
632         QBrush brush(*m_common->state.fillGradient->platformGradient());
633         brush.setTransform(m_common->state.fillGradient->gradientSpaceTransform());
634         p->fillPath(path, brush);
635         break;
636     }
637     m_data->currentPath = QPainterPath();
638 }
639 
strokePath()640 void GraphicsContext::strokePath()
641 {
642     if (paintingDisabled())
643         return;
644 
645     QPainter *p = m_data->p();
646     QPen pen = p->pen();
647     QPainterPath path = m_data->currentPath;
648     path.setFillRule(toQtFillRule(fillRule()));
649 
650     switch (m_common->state.strokeColorSpace) {
651     case SolidColorSpace:
652         if (strokeColor().alpha())
653             p->strokePath(path, pen);
654         break;
655     case PatternColorSpace: {
656         TransformationMatrix affine;
657         pen.setBrush(QBrush(m_common->state.strokePattern->createPlatformPattern(affine)));
658         p->setPen(pen);
659         p->strokePath(path, pen);
660         break;
661     }
662     case GradientColorSpace: {
663         QBrush brush(*m_common->state.strokeGradient->platformGradient());
664         brush.setTransform(m_common->state.strokeGradient->gradientSpaceTransform());
665         pen.setBrush(brush);
666         p->setPen(pen);
667         p->strokePath(path, pen);
668         break;
669     }
670     }
671     m_data->currentPath = QPainterPath();
672 }
673 
fillRect(const FloatRect & rect)674 void GraphicsContext::fillRect(const FloatRect& rect)
675 {
676     if (paintingDisabled())
677         return;
678 
679     QPainter *p = m_data->p();
680 
681     switch (m_common->state.fillColorSpace) {
682     case SolidColorSpace:
683         if (fillColor().alpha())
684             p->fillRect(rect, p->brush());
685         break;
686     case PatternColorSpace: {
687         TransformationMatrix affine;
688         p->fillRect(rect, QBrush(m_common->state.fillPattern->createPlatformPattern(affine)));
689         break;
690     }
691     case GradientColorSpace:
692         QBrush brush(*m_common->state.fillGradient->platformGradient());
693         brush.setTransform(m_common->state.fillGradient->gradientSpaceTransform());
694         p->fillRect(rect, brush);
695         break;
696     }
697     m_data->currentPath = QPainterPath();
698 }
699 
fillRect(const FloatRect & rect,const Color & c)700 void GraphicsContext::fillRect(const FloatRect& rect, const Color& c)
701 {
702     if (paintingDisabled())
703         return;
704 
705     m_data->solidColor.setColor(QColor(c));
706     m_data->p()->fillRect(rect, m_data->solidColor);
707 }
708 
fillRoundedRect(const IntRect & rect,const IntSize & topLeft,const IntSize & topRight,const IntSize & bottomLeft,const IntSize & bottomRight,const Color & color)709 void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color)
710 {
711     if (paintingDisabled() || !color.alpha())
712         return;
713 
714     Path path = Path::createRoundedRectangle(rect, topLeft, topRight, bottomLeft, bottomRight);
715     m_data->p()->fillPath(*path.platformPath(), QColor(color));
716 }
717 
beginPath()718 void GraphicsContext::beginPath()
719 {
720     m_data->currentPath = QPainterPath();
721 }
722 
addPath(const Path & path)723 void GraphicsContext::addPath(const Path& path)
724 {
725     QPainterPath newPath = m_data->currentPath;
726     newPath.addPath(*(path.platformPath()));
727     m_data->currentPath = newPath;
728 }
729 
inTransparencyLayer() const730 bool GraphicsContext::inTransparencyLayer() const
731 {
732     return !m_data->layers.isEmpty();
733 }
734 
currentPath()735 PlatformPath* GraphicsContext::currentPath()
736 {
737     return &m_data->currentPath;
738 }
739 
clip(const FloatRect & rect)740 void GraphicsContext::clip(const FloatRect& rect)
741 {
742     if (paintingDisabled())
743         return;
744 
745     m_data->p()->setClipRect(rect, Qt::IntersectClip);
746 }
747 
clipPath(WindRule clipRule)748 void GraphicsContext::clipPath(WindRule clipRule)
749 {
750     if (paintingDisabled())
751         return;
752 
753     QPainter *p = m_data->p();
754     QPainterPath newPath = m_data->currentPath;
755     newPath.setFillRule(clipRule == RULE_EVENODD ? Qt::OddEvenFill : Qt::WindingFill);
756     p->setClipPath(newPath);
757 }
758 
759 /**
760  * Focus ring handling is not handled here. Qt style in
761  * RenderTheme handles drawing focus on widgets which
762  * need it.
763  */
drawFocusRing(const Color & color)764 void GraphicsContext::drawFocusRing(const Color& color)
765 {
766     if (paintingDisabled())
767         return;
768 
769     const Vector<IntRect>& rects = focusRingRects();
770     unsigned rectCount = rects.size();
771 
772     if (rects.size() == 0)
773         return;
774 
775     QPainter *p = m_data->p();
776     const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
777     p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines);
778 
779     const QPen oldPen = p->pen();
780     const QBrush oldBrush = p->brush();
781 
782     QPen nPen = p->pen();
783     nPen.setColor(color);
784     p->setBrush(Qt::NoBrush);
785     nPen.setStyle(Qt::DotLine);
786     p->setPen(nPen);
787 #if 0
788     // FIXME How do we do a bounding outline with Qt?
789     QPainterPath path;
790     for (int i = 0; i < rectCount; ++i)
791         path.addRect(QRectF(rects[i]));
792     QPainterPathStroker stroker;
793     QPainterPath newPath = stroker.createStroke(path);
794     p->strokePath(newPath, nPen);
795 #else
796     for (int i = 0; i < rectCount; ++i)
797         p->drawRect(QRectF(rects[i]));
798 #endif
799     p->setPen(oldPen);
800     p->setBrush(oldBrush);
801 
802     p->setRenderHint(QPainter::Antialiasing, antiAlias);
803 }
804 
drawLineForText(const IntPoint & origin,int width,bool printing)805 void GraphicsContext::drawLineForText(const IntPoint& origin, int width, bool printing)
806 {
807     if (paintingDisabled())
808         return;
809 
810     IntPoint endPoint = origin + IntSize(width, 0);
811     drawLine(origin, endPoint);
812 }
813 
drawLineForMisspellingOrBadGrammar(const IntPoint &,int width,bool grammar)814 void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint&,
815                                                          int width, bool grammar)
816 {
817     if (paintingDisabled())
818         return;
819 
820     notImplemented();
821 }
822 
roundToDevicePixels(const FloatRect & frect)823 FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect)
824 {
825     QRectF rect(frect);
826     rect = m_data->p()->deviceMatrix().mapRect(rect);
827 
828     QRect result = rect.toRect(); //round it
829     return FloatRect(QRectF(result));
830 }
831 
setPlatformShadow(const IntSize & pos,int blur,const Color & color)832 void GraphicsContext::setPlatformShadow(const IntSize& pos, int blur, const Color &color)
833 {
834     // Qt doesn't support shadows natively, they are drawn manually in the draw*
835     // functions
836 }
837 
clearPlatformShadow()838 void GraphicsContext::clearPlatformShadow()
839 {
840     // Qt doesn't support shadows natively, they are drawn manually in the draw*
841     // functions
842 }
843 
beginTransparencyLayer(float opacity)844 void GraphicsContext::beginTransparencyLayer(float opacity)
845 {
846     if (paintingDisabled())
847         return;
848 
849     int x, y, w, h;
850     x = y = 0;
851     QPainter *p = m_data->p();
852     const QPaintDevice *device = p->device();
853     w = device->width();
854     h = device->height();
855 
856     QRectF clip = p->clipPath().boundingRect();
857     QRectF deviceClip = p->transform().mapRect(clip);
858     x = int(qBound(qreal(0), deviceClip.x(), (qreal)w));
859     y = int(qBound(qreal(0), deviceClip.y(), (qreal)h));
860     w = int(qBound(qreal(0), deviceClip.width(), (qreal)w) + 2);
861     h = int(qBound(qreal(0), deviceClip.height(), (qreal)h) + 2);
862 
863     TransparencyLayer * layer = new TransparencyLayer(m_data->p(), QRect(x, y, w, h));
864 
865     layer->opacity = opacity;
866     m_data->layers.push(layer);
867 }
868 
endTransparencyLayer()869 void GraphicsContext::endTransparencyLayer()
870 {
871     if (paintingDisabled())
872         return;
873 
874     TransparencyLayer *layer = m_data->layers.pop();
875     layer->painter.end();
876 
877     QPainter *p = m_data->p();
878     p->save();
879     p->resetTransform();
880     p->setOpacity(layer->opacity);
881     p->drawPixmap(layer->offset, layer->pixmap);
882     p->restore();
883 
884     delete layer;
885 }
886 
clearRect(const FloatRect & rect)887 void GraphicsContext::clearRect(const FloatRect& rect)
888 {
889     if (paintingDisabled())
890         return;
891 
892     QPainter *p = m_data->p();
893     QPainter::CompositionMode currentCompositionMode = p->compositionMode();
894     if (p->paintEngine()->hasFeature(QPaintEngine::PorterDuff))
895         p->setCompositionMode(QPainter::CompositionMode_Source);
896     p->fillRect(rect, Qt::transparent);
897     if (p->paintEngine()->hasFeature(QPaintEngine::PorterDuff))
898         p->setCompositionMode(currentCompositionMode);
899 }
900 
strokeRect(const FloatRect & rect,float width)901 void GraphicsContext::strokeRect(const FloatRect& rect, float width)
902 {
903     if (paintingDisabled())
904         return;
905 
906     QPainterPath path;
907     path.addRect(rect);
908     setStrokeThickness(width);
909     m_data->currentPath = path;
910 
911     strokePath();
912 }
913 
setLineCap(LineCap lc)914 void GraphicsContext::setLineCap(LineCap lc)
915 {
916     if (paintingDisabled())
917         return;
918 
919     QPainter *p = m_data->p();
920     QPen nPen = p->pen();
921     nPen.setCapStyle(toQtLineCap(lc));
922     p->setPen(nPen);
923 }
924 
setLineDash(const DashArray & dashes,float dashOffset)925 void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
926 {
927     QPainter* p = m_data->p();
928     QPen pen = p->pen();
929     unsigned dashLength = dashes.size();
930     if (dashLength) {
931         QVector<qreal> pattern;
932         unsigned count = dashLength;
933         if (dashLength % 2)
934             count *= 2;
935 
936         float penWidth = narrowPrecisionToFloat(double(pen.widthF()));
937         for (unsigned i = 0; i < count; i++)
938             pattern.append(dashes[i % dashLength] / penWidth);
939 
940         pen.setDashPattern(pattern);
941         pen.setDashOffset(dashOffset);
942     }
943     p->setPen(pen);
944 }
945 
setLineJoin(LineJoin lj)946 void GraphicsContext::setLineJoin(LineJoin lj)
947 {
948     if (paintingDisabled())
949         return;
950 
951     QPainter *p = m_data->p();
952     QPen nPen = p->pen();
953     nPen.setJoinStyle(toQtLineJoin(lj));
954     p->setPen(nPen);
955 }
956 
setMiterLimit(float limit)957 void GraphicsContext::setMiterLimit(float limit)
958 {
959     if (paintingDisabled())
960         return;
961 
962     QPainter *p = m_data->p();
963     QPen nPen = p->pen();
964     nPen.setMiterLimit(limit);
965     p->setPen(nPen);
966 }
967 
setAlpha(float opacity)968 void GraphicsContext::setAlpha(float opacity)
969 {
970     if (paintingDisabled())
971         return;
972     QPainter *p = m_data->p();
973     p->setOpacity(opacity);
974 }
975 
setCompositeOperation(CompositeOperator op)976 void GraphicsContext::setCompositeOperation(CompositeOperator op)
977 {
978     if (paintingDisabled())
979         return;
980 
981     if (m_data->p()->paintEngine()->hasFeature(QPaintEngine::PorterDuff))
982         m_data->p()->setCompositionMode(toQtCompositionMode(op));
983 }
984 
clip(const Path & path)985 void GraphicsContext::clip(const Path& path)
986 {
987     if (paintingDisabled())
988         return;
989 
990     m_data->p()->setClipPath(*path.platformPath(), Qt::IntersectClip);
991 }
992 
clipOut(const Path & path)993 void GraphicsContext::clipOut(const Path& path)
994 {
995     if (paintingDisabled())
996         return;
997 
998     QPainter *p = m_data->p();
999     QRectF clipBounds = p->clipPath().boundingRect();
1000     QPainterPath clippedOut = *path.platformPath();
1001     QPainterPath newClip;
1002     newClip.setFillRule(Qt::OddEvenFill);
1003     newClip.addRect(clipBounds);
1004     newClip.addPath(clippedOut);
1005 
1006     p->setClipPath(newClip, Qt::IntersectClip);
1007 }
1008 
translate(float x,float y)1009 void GraphicsContext::translate(float x, float y)
1010 {
1011     if (paintingDisabled())
1012         return;
1013 
1014     m_data->p()->translate(x, y);
1015 
1016     if (!m_data->currentPath.isEmpty()) {
1017         QTransform matrix;
1018         m_data->currentPath = m_data->currentPath * matrix.translate(-x, -y);
1019         m_common->state.pathTransform.translate(x, y);
1020     }
1021 }
1022 
origin()1023 IntPoint GraphicsContext::origin()
1024 {
1025     if (paintingDisabled())
1026         return IntPoint();
1027     const QTransform &transform = m_data->p()->transform();
1028     return IntPoint(qRound(transform.dx()), qRound(transform.dy()));
1029 }
1030 
rotate(float radians)1031 void GraphicsContext::rotate(float radians)
1032 {
1033     if (paintingDisabled())
1034         return;
1035 
1036     m_data->p()->rotate(180/M_PI*radians);
1037 
1038     if (!m_data->currentPath.isEmpty()) {
1039         QTransform matrix;
1040         m_data->currentPath = m_data->currentPath * matrix.rotate(-180/M_PI*radians);
1041         m_common->state.pathTransform.rotate(radians);
1042     }
1043 }
1044 
scale(const FloatSize & s)1045 void GraphicsContext::scale(const FloatSize& s)
1046 {
1047     if (paintingDisabled())
1048         return;
1049 
1050     m_data->p()->scale(s.width(), s.height());
1051 
1052     if (!m_data->currentPath.isEmpty()) {
1053         QTransform matrix;
1054         m_data->currentPath = m_data->currentPath * matrix.scale(1 / s.width(), 1 / s.height());
1055         m_common->state.pathTransform.scaleNonUniform(s.width(), s.height());
1056     }
1057 }
1058 
clipOut(const IntRect & rect)1059 void GraphicsContext::clipOut(const IntRect& rect)
1060 {
1061     if (paintingDisabled())
1062         return;
1063 
1064     QPainter *p = m_data->p();
1065     QRectF clipBounds = p->clipPath().boundingRect();
1066     QPainterPath newClip;
1067     newClip.setFillRule(Qt::OddEvenFill);
1068     newClip.addRect(clipBounds);
1069     newClip.addRect(QRect(rect));
1070 
1071     p->setClipPath(newClip, Qt::IntersectClip);
1072 }
1073 
clipOutEllipseInRect(const IntRect & rect)1074 void GraphicsContext::clipOutEllipseInRect(const IntRect& rect)
1075 {
1076     if (paintingDisabled())
1077         return;
1078 
1079     QPainter *p = m_data->p();
1080     QRectF clipBounds = p->clipPath().boundingRect();
1081     QPainterPath newClip;
1082     newClip.setFillRule(Qt::OddEvenFill);
1083     newClip.addRect(clipBounds);
1084     newClip.addEllipse(QRect(rect));
1085 
1086     p->setClipPath(newClip, Qt::IntersectClip);
1087 }
1088 
clipToImageBuffer(const FloatRect &,const ImageBuffer *)1089 void GraphicsContext::clipToImageBuffer(const FloatRect&, const ImageBuffer*)
1090 {
1091     notImplemented();
1092 }
1093 
addInnerRoundedRectClip(const IntRect & rect,int thickness)1094 void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect,
1095                                               int thickness)
1096 {
1097     if (paintingDisabled())
1098         return;
1099 
1100     clip(rect);
1101     QPainterPath path;
1102 
1103     // Add outer ellipse
1104     path.addEllipse(QRectF(rect.x(), rect.y(), rect.width(), rect.height()));
1105 
1106     // Add inner ellipse.
1107     path.addEllipse(QRectF(rect.x() + thickness, rect.y() + thickness,
1108                            rect.width() - (thickness * 2), rect.height() - (thickness * 2)));
1109 
1110     path.setFillRule(Qt::OddEvenFill);
1111 
1112     QPainter *p = m_data->p();
1113 
1114     const bool antiAlias = p->testRenderHint(QPainter::Antialiasing);
1115     p->setRenderHint(QPainter::Antialiasing, true);
1116     p->setClipPath(path, Qt::IntersectClip);
1117     p->setRenderHint(QPainter::Antialiasing, antiAlias);
1118 }
1119 
concatCTM(const TransformationMatrix & transform)1120 void GraphicsContext::concatCTM(const TransformationMatrix& transform)
1121 {
1122     if (paintingDisabled())
1123         return;
1124 
1125     m_data->p()->setWorldTransform(transform, true);
1126 
1127     // Transformations to the context shouldn't transform the currentPath.
1128     // We have to undo every change made to the context from the currentPath
1129     // to avoid wrong drawings.
1130     if (!m_data->currentPath.isEmpty() && transform.isInvertible()) {
1131         QTransform matrix = transform.inverse();
1132         m_data->currentPath = m_data->currentPath * matrix;
1133         m_common->state.pathTransform.multiply(transform);
1134     }
1135 }
1136 
setURLForRect(const KURL & link,const IntRect & destRect)1137 void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
1138 {
1139     notImplemented();
1140 }
1141 
setPlatformStrokeColor(const Color & color)1142 void GraphicsContext::setPlatformStrokeColor(const Color& color)
1143 {
1144     if (paintingDisabled())
1145         return;
1146     QPainter *p = m_data->p();
1147     QPen newPen(p->pen());
1148     newPen.setColor(color);
1149     p->setPen(newPen);
1150 }
1151 
setPlatformStrokeStyle(const StrokeStyle & strokeStyle)1152 void GraphicsContext::setPlatformStrokeStyle(const StrokeStyle& strokeStyle)
1153 {
1154     if (paintingDisabled())
1155         return;
1156     QPainter *p = m_data->p();
1157     QPen newPen(p->pen());
1158     newPen.setStyle(toQPenStyle(strokeStyle));
1159     p->setPen(newPen);
1160 }
1161 
setPlatformStrokeThickness(float thickness)1162 void GraphicsContext::setPlatformStrokeThickness(float thickness)
1163 {
1164     if (paintingDisabled())
1165         return;
1166     QPainter *p = m_data->p();
1167     QPen newPen(p->pen());
1168     newPen.setWidthF(thickness);
1169     p->setPen(newPen);
1170 }
1171 
setPlatformFillColor(const Color & color)1172 void GraphicsContext::setPlatformFillColor(const Color& color)
1173 {
1174     if (paintingDisabled())
1175         return;
1176     m_data->p()->setBrush(QBrush(color));
1177 }
1178 
setPlatformShouldAntialias(bool enable)1179 void GraphicsContext::setPlatformShouldAntialias(bool enable)
1180 {
1181     if (paintingDisabled())
1182         return;
1183     m_data->p()->setRenderHint(QPainter::Antialiasing, enable);
1184 }
1185 
1186 #ifdef Q_WS_WIN
1187 #include <windows.h>
1188 
getWindowsContext(const IntRect & dstRect,bool supportAlphaBlend,bool mayCreateBitmap)1189 HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap)
1190 {
1191     // painting through native HDC is only supported for plugin, where mayCreateBitmap is always true
1192     Q_ASSERT(mayCreateBitmap);
1193 
1194     if (dstRect.isEmpty())
1195         return 0;
1196 
1197     // Create a bitmap DC in which to draw.
1198     BITMAPINFO bitmapInfo;
1199     bitmapInfo.bmiHeader.biSize          = sizeof(BITMAPINFOHEADER);
1200     bitmapInfo.bmiHeader.biWidth         = dstRect.width();
1201     bitmapInfo.bmiHeader.biHeight        = dstRect.height();
1202     bitmapInfo.bmiHeader.biPlanes        = 1;
1203     bitmapInfo.bmiHeader.biBitCount      = 32;
1204     bitmapInfo.bmiHeader.biCompression   = BI_RGB;
1205     bitmapInfo.bmiHeader.biSizeImage     = 0;
1206     bitmapInfo.bmiHeader.biXPelsPerMeter = 0;
1207     bitmapInfo.bmiHeader.biYPelsPerMeter = 0;
1208     bitmapInfo.bmiHeader.biClrUsed       = 0;
1209     bitmapInfo.bmiHeader.biClrImportant  = 0;
1210 
1211     void* pixels = 0;
1212     HBITMAP bitmap = ::CreateDIBSection(0, &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0);
1213     if (!bitmap)
1214         return 0;
1215 
1216     HDC displayDC = ::GetDC(0);
1217     HDC bitmapDC = ::CreateCompatibleDC(displayDC);
1218     ::ReleaseDC(0, displayDC);
1219 
1220     ::SelectObject(bitmapDC, bitmap);
1221 
1222     // Fill our buffer with clear if we're going to alpha blend.
1223     if (supportAlphaBlend) {
1224         BITMAP bmpInfo;
1225         GetObject(bitmap, sizeof(bmpInfo), &bmpInfo);
1226         int bufferSize = bmpInfo.bmWidthBytes * bmpInfo.bmHeight;
1227         memset(bmpInfo.bmBits, 0, bufferSize);
1228     }
1229 
1230 #if !PLATFORM(WINCE)
1231     // Make sure we can do world transforms.
1232     SetGraphicsMode(bitmapDC, GM_ADVANCED);
1233 
1234     // Apply a translation to our context so that the drawing done will be at (0,0) of the bitmap.
1235     XFORM xform;
1236     xform.eM11 = 1.0f;
1237     xform.eM12 = 0.0f;
1238     xform.eM21 = 0.0f;
1239     xform.eM22 = 1.0f;
1240     xform.eDx = -dstRect.x();
1241     xform.eDy = -dstRect.y();
1242     ::SetWorldTransform(bitmapDC, &xform);
1243 #endif
1244 
1245     return bitmapDC;
1246 }
1247 
releaseWindowsContext(HDC hdc,const IntRect & dstRect,bool supportAlphaBlend,bool mayCreateBitmap)1248 void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap)
1249 {
1250     // painting through native HDC is only supported for plugin, where mayCreateBitmap is always true
1251     Q_ASSERT(mayCreateBitmap);
1252 
1253     if (hdc) {
1254 
1255         if (!dstRect.isEmpty()) {
1256 
1257             HBITMAP bitmap = static_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP));
1258             BITMAP info;
1259             GetObject(bitmap, sizeof(info), &info);
1260             ASSERT(info.bmBitsPixel == 32);
1261 
1262             QPixmap pixmap = QPixmap::fromWinHBITMAP(bitmap, supportAlphaBlend ? QPixmap::PremultipliedAlpha : QPixmap::NoAlpha);
1263             m_data->p()->drawPixmap(dstRect, pixmap);
1264 
1265             ::DeleteObject(bitmap);
1266         }
1267 
1268         ::DeleteDC(hdc);
1269     }
1270 }
1271 #endif
1272 
setImageInterpolationQuality(InterpolationQuality)1273 void GraphicsContext::setImageInterpolationQuality(InterpolationQuality)
1274 {
1275 }
1276 
imageInterpolationQuality() const1277 InterpolationQuality GraphicsContext::imageInterpolationQuality() const
1278 {
1279     return InterpolationDefault;
1280 }
1281 
1282 }
1283 
1284 // vim: ts=4 sw=4 et
1285