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