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