root/trunk/BAL/Graphics/WebCore/Qt/BCGraphicsContextQt.cpp

Revision 1387, 38.1 kB (checked in by gpenin, 7 months ago)

merge with webkit revision 55226

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