Use the proper clipping algorithm
[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  *
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
21  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
28  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include "config.h"
34
35 #include "AffineTransform.h"
36 #include "Path.h"
37 #include "Color.h"
38 #include "GraphicsContext.h"
39 #include "Font.h"
40 #include "Pen.h"
41
42 #include <QStack>
43 #include <QPainter>
44 #include <QPolygonF>
45 #include <QPainterPath>
46 #include <QPaintDevice>
47
48 #ifndef M_PI
49 #define M_PI 3.14159265358979323846
50 #endif
51
52 #define notImplemented() qDebug("FIXME: UNIMPLEMENTED: %s:%d (%s)", __FILE__, __LINE__, __FUNCTION__)
53
54 namespace WebCore {
55
56 static inline QPainter::CompositionMode toQtCompositionMode(CompositeOperator op)
57 {
58     switch (op) {
59         case CompositeClear:
60             return QPainter::CompositionMode_Clear;
61         case CompositeCopy:
62             return QPainter::CompositionMode_Source;
63         case CompositeSourceOver:
64             return QPainter::CompositionMode_SourceOver;
65         case CompositeSourceIn:
66             return QPainter::CompositionMode_SourceIn;
67         case CompositeSourceOut:
68             return QPainter::CompositionMode_SourceOut;
69         case CompositeSourceAtop:
70             return QPainter::CompositionMode_SourceAtop;
71         case CompositeDestinationOver:
72             return QPainter::CompositionMode_DestinationOver;
73         case CompositeDestinationIn:
74             return QPainter::CompositionMode_DestinationIn;
75         case CompositeDestinationOut:
76             return QPainter::CompositionMode_DestinationOut;
77         case CompositeDestinationAtop:
78             return QPainter::CompositionMode_DestinationAtop;
79         case CompositeXOR:
80             return QPainter::CompositionMode_Xor;
81         case CompositePlusDarker:
82             return QPainter::CompositionMode_SourceOver;
83         case CompositeHighlight:
84             return QPainter::CompositionMode_SourceOver;
85         case CompositePlusLighter:
86             return QPainter::CompositionMode_SourceOver;
87     }
88
89     return QPainter::CompositionMode_SourceOver;
90 }
91
92 static inline Qt::PenCapStyle toQtLineCap(LineCap lc)
93 {
94     switch (lc) {
95         case ButtCap:
96             return Qt::FlatCap;
97         case RoundCap:
98             return Qt::RoundCap;
99         case SquareCap:
100             return Qt::SquareCap;
101     }
102
103     return Qt::FlatCap;
104 }
105
106 static inline Qt::PenJoinStyle toQtLineJoin(LineJoin lj)
107 {
108     switch (lj) {
109         case MiterJoin:
110 #if QT_VERSION < 0x040200
111             return Qt::MiterJoin;
112 #else
113             return Qt::SvgMiterJoin;
114 #endif
115         case RoundJoin:
116             return Qt::RoundJoin;
117         case BevelJoin:
118             return Qt::BevelJoin;
119     }
120
121     return Qt::MiterJoin;
122 }
123
124 static Qt::PenStyle toQPenStyle(StrokeStyle style)
125 {
126     switch (style) {
127     case NoStroke:
128         return Qt::NoPen;
129         break;
130     case SolidStroke:
131         return Qt::SolidLine;
132         break;
133     case DottedStroke:
134         return Qt::DotLine;
135         break;
136     case DashedStroke:
137         return Qt::DashLine;
138         break;
139     }
140     qWarning("couldn't recognize the pen style");
141     return Qt::NoPen;
142 }
143
144 struct TransparencyLayer
145 {
146     TransparencyLayer(const QPainter& p, int width, int height)
147     {
148         pixmap = new QPixmap(width, height);
149
150         painter = new QPainter(pixmap);
151         painter->setPen(p.pen());
152         painter->setBrush(p.brush());
153         painter->setMatrix(p.matrix());
154 #if QT_VERSION >= 0x040200
155         painter->setOpacity(p.opacity());
156 #endif
157         painter->setFont(p.font());
158         painter->setCompositionMode(p.compositionMode());
159         painter->setClipPath(p.clipPath());
160     }
161
162     TransparencyLayer()
163         : pixmap(0),
164           painter(0)
165     {
166     }
167
168     void cleanup()
169     {
170         delete painter;
171         delete pixmap;
172     }
173
174     QPixmap* pixmap;
175     QPainter* painter;
176     qreal opacity;
177 };
178
179 struct TextShadow
180 {
181     TextShadow()
182         : x(0)
183         , y(0)
184         , blur(0)
185     {
186     }
187
188     bool isNull() { return !x && !y && !blur; }
189
190     int x;
191     int y;
192     int blur;
193
194     Color color;
195 };
196
197 class GraphicsContextPlatformPrivate
198 {
199 public:
200     GraphicsContextPlatformPrivate(QPainter* painter);
201     ~GraphicsContextPlatformPrivate();
202
203     QPainter& p()
204     {
205         if (layers.isEmpty()) {
206             if (redirect)
207                 return *redirect;
208
209             return *painter;
210         } else
211             return *layers.top().painter;
212     }
213
214     QPaintDevice* device;
215
216     QStack<TransparencyLayer> layers;
217     QPainter* redirect;
218
219     IntRect focusRingClip;
220     TextShadow shadow;
221
222     // Only used by SVG for now.
223     QPainterPath currentPath;
224
225 private:
226     QPainter* painter;
227 };
228
229
230 GraphicsContextPlatformPrivate::GraphicsContextPlatformPrivate(QPainter* p)
231 {
232     painter = p;
233     device = painter ? painter->device() : 0;
234     redirect = 0;
235
236     // FIXME: Maybe only enable in SVG mode?
237     if (painter)
238         painter->setRenderHint(QPainter::Antialiasing);
239 }
240
241 GraphicsContextPlatformPrivate::~GraphicsContextPlatformPrivate()
242 {
243 }
244
245 GraphicsContext::GraphicsContext(PlatformGraphicsContext* context)
246     : m_common(createGraphicsContextPrivate())
247     , m_data(new GraphicsContextPlatformPrivate(context))
248 {
249     setPaintingDisabled(!context);
250     if (context) {
251         // Make sure the context starts in sync with our state.
252         setPlatformFillColor(fillColor());
253         setPlatformStrokeColor(strokeColor());
254     }
255 }
256
257 GraphicsContext::~GraphicsContext()
258 {
259     while(!m_data->layers.isEmpty())
260         endTransparencyLayer();
261
262     destroyGraphicsContextPrivate(m_common);
263     delete m_data;
264 }
265
266 PlatformGraphicsContext* GraphicsContext::platformContext() const
267 {
268     return &m_data->p();
269 }
270
271 void GraphicsContext::savePlatformState()
272 {
273     m_data->p().save();
274 }
275
276 void GraphicsContext::restorePlatformState()
277 {
278     m_data->p().restore();
279 }
280
281 /* FIXME: DISABLED WHILE MERGING BACK FROM UNITY
282 void GraphicsContext::drawTextShadow(const TextRun& run, const IntPoint& point, const TextStyle& style)
283 {
284     if (paintingDisabled())
285         return;
286
287     if (m_data->shadow.isNull())
288         return;
289
290     TextShadow* shadow = &m_data->shadow;
291
292     if (shadow->blur <= 0) {
293         Pen p = pen();
294         setPen(shadow->color);
295         font().drawText(this, run, style, IntPoint(point.x() + shadow->x, point.y() + shadow->y));
296         setPen(p);
297     } else {
298         const int thickness = shadow->blur;
299         // FIXME: OPTIMIZE: limit the area to only the actually painted area + 2*thickness
300         const int w = m_data->p().device()->width();
301         const int h = m_data->p().device()->height();
302         const QRgb color = qRgb(255, 255, 255);
303         const QRgb bgColor = qRgb(0, 0, 0);
304         QImage image(QSize(w, h), QImage::Format_ARGB32);
305         image.fill(bgColor);
306         QPainter p;
307
308         Pen curPen = pen();
309         p.begin(&image);
310         setPen(color);
311         m_data->redirect = &p;
312         font().drawText(this, run, style, IntPoint(point.x() + shadow->x, point.y() + shadow->y));
313         m_data->redirect = 0;
314         p.end();
315         setPen(curPen);
316
317         int md = thickness * thickness; // max-dist^2
318
319         // blur map/precalculated shadow-decay
320         float* bmap = (float*) alloca(sizeof(float) * (md + 1));
321         for (int n = 0; n <= md; n++) {
322             float f;
323             f = n / (float) (md + 1);
324             f = 1.0 - f * f;
325             bmap[n] = f;
326         }
327
328         float factor = 0.0; // maximal potential opacity-sum
329         for (int n = -thickness; n <= thickness; n++) {
330             for (int m = -thickness; m <= thickness; m++) {
331                 int d = n * n + m * m;
332                 if (d <= md)
333                     factor += bmap[d];
334             }
335         }
336
337         // alpha map
338         float* amap = (float*) alloca(sizeof(float) * (h * w));
339         memset(amap, 0, h * w * (sizeof(float)));
340
341         for (int j = thickness; j<h-thickness; j++) {
342             for (int i = thickness; i<w-thickness; i++) {
343                 QRgb col = image.pixel(i,j);
344                 if (col == bgColor)
345                     continue;
346
347                 float g = qAlpha(col);
348                 g = g / 255;
349
350                 for (int n = -thickness; n <= thickness; n++) {
351                     for (int m = -thickness; m <= thickness; m++) {
352                         int d = n * n + m * m;
353                         if (d > md)
354                             continue;
355
356                         float f = bmap[d];
357                         amap[(i + m) + (j + n) * w] += (g * f);
358                     }
359                 }
360             }
361         }
362
363         QImage res(QSize(w,h),QImage::Format_ARGB32);
364         int r = shadow->color.red();
365         int g = shadow->color.green();
366         int b = shadow->color.blue();
367         int a1 = shadow->color.alpha();
368
369         // arbitratry factor adjustment to make shadows more solid.
370         factor = 1.333 / factor;
371
372         for (int j = 0; j < h; j++) {
373             for (int i = 0; i < w; i++) {
374                 int a = (int) (amap[i + j * w] * factor * a1);
375                 if (a > 255)
376                     a = 255;
377
378                 res.setPixel(i,j, qRgba(r, g, b, a));
379             }
380         }
381
382         m_data->p().drawImage(0, 0, res, 0, 0, -1, -1, Qt::DiffuseAlphaDither | Qt::ColorOnly | Qt::PreferDither);
383     }
384 }
385 */
386
387 // Draws a filled rectangle with a stroked border.
388 void GraphicsContext::drawRect(const IntRect& rect)
389 {
390     if (paintingDisabled())
391         return;
392
393     m_data->p().drawRect(rect);
394 }
395
396 // FIXME: Now that this is refactored, it should be shared by all contexts.
397 static void adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth,
398                                         const StrokeStyle& penStyle)
399 {
400     // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic
401     // works out.  For example, with a border width of 3, KHTML will pass us (y1+y2)/2, e.g.,
402     // (50+53)/2 = 103/2 = 51 when we want 51.5.  It is always true that an even width gave
403     // us a perfect position, but an odd width gave us a position that is off by exactly 0.5.
404     if (penStyle == DottedStroke || penStyle == DashedStroke) {
405         if (p1.x() == p2.x()) {
406             p1.setY(p1.y() + strokeWidth);
407             p2.setY(p2.y() - strokeWidth);
408         } else {
409             p1.setX(p1.x() + strokeWidth);
410             p2.setX(p2.x() - strokeWidth);
411         }
412     }
413
414     if (((int) strokeWidth) % 2) {
415         if (p1.x() == p2.x()) {
416             // We're a vertical line.  Adjust our x.
417             p1.setX(p1.x() + 0.5);
418             p2.setX(p2.x() + 0.5);
419         } else {
420             // We're a horizontal line. Adjust our y.
421             p1.setY(p1.y() + 0.5);
422             p2.setY(p2.y() + 0.5);
423         }
424     }
425 }
426
427 // This is only used to draw borders.
428 void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
429 {
430     if (paintingDisabled())
431         return;
432
433     FloatPoint p1 = point1;
434     FloatPoint p2 = point2;
435
436     adjustLineToPixelBoundaries(p1, p2, strokeThickness(), strokeStyle());
437     m_data->p().drawLine(p1, p2);
438 }
439
440 // This method is only used to draw the little circles used in lists.
441 void GraphicsContext::drawEllipse(const IntRect& rect)
442 {
443     if (paintingDisabled())
444         return;
445
446     m_data->p().drawEllipse(rect);
447 }
448
449 void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSpan)
450 {
451     if (paintingDisabled() || strokeStyle() == NoStroke || strokeThickness() <= 0.0f || !strokeColor().alpha())
452         return;
453
454     m_data->p().drawArc(rect, startAngle * 16, angleSpan * 16);
455 }
456
457 void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool shouldAntialias)
458 {
459     if (paintingDisabled())
460         return;
461
462     if (npoints <= 1)
463         return;
464
465     QPolygonF polygon(npoints);
466
467     for (size_t i = 0; i < npoints; i++)
468         polygon[i] = points[i];
469
470     m_data->p().save();
471     m_data->p().setRenderHint(QPainter::Antialiasing, shouldAntialias);
472     m_data->p().drawConvexPolygon(polygon);
473     m_data->p().restore();
474 }
475
476 void GraphicsContext::fillRect(const IntRect& rect, const Color& c)
477 {
478     if (paintingDisabled())
479         return;
480
481     m_data->p().fillRect(rect, QColor(c));
482 }
483
484 void GraphicsContext::fillRect(const FloatRect& rect, const Color& c)
485 {
486     if (paintingDisabled())
487         return;
488
489     m_data->p().fillRect(rect, QColor(c));
490 }
491
492 void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color)
493 {
494     if (paintingDisabled() || !color.alpha())
495         return;
496
497     // FIXME: Implement.
498     notImplemented();
499 }
500
501 void GraphicsContext::beginPath()
502 {
503     m_data->currentPath = QPainterPath();
504 }
505
506 void GraphicsContext::addPath(const Path& path)
507 {
508     m_data->currentPath = *(path.platformPath());
509 }
510
511 void GraphicsContext::setFillRule(WindRule rule)
512 {
513     m_data->currentPath.setFillRule(rule == RULE_EVENODD ? Qt::OddEvenFill : Qt::WindingFill);
514 }
515
516 PlatformPath* GraphicsContext::currentPath()
517 {
518     return &m_data->currentPath;
519 }
520
521 void GraphicsContext::clip(const IntRect& rect)
522 {
523     if (paintingDisabled())
524         return;
525
526     if (m_data->p().clipRegion().isEmpty())
527         m_data->p().setClipRect(rect);
528     else m_data->p().setClipRect(rect, Qt::IntersectClip);
529 }
530
531 /**
532  * Focus ring handling is not handled here. Qt style in 
533  * RenderTheme handles drawing focus on widgets which 
534  * need it.
535  */
536 void setFocusRingColorChangeFunction(void (*)()) { }
537 Color focusRingColor() { return 0x00000000; }
538 void GraphicsContext::drawFocusRing(const Color& color)
539 {
540     if (paintingDisabled())
541         return;
542
543     return;
544
545     const Vector<IntRect>& rects = focusRingRects();
546     unsigned rectCount = rects.size();
547
548     QVector<QRect> qrects(rectCount);
549     for (int i = 0; i < rectCount; ++i)
550         qrects[i] = rects[i];
551     m_data->p().save();
552     m_data->p().setClipRect(m_data->focusRingClip);
553     m_data->p().setPen(color);
554     m_data->p().drawRects(qrects);
555     m_data->p().restore();
556 }
557
558 void GraphicsContext::setFocusRingClip(const IntRect& rect)
559 {
560     if (paintingDisabled())
561         return;
562
563     m_data->focusRingClip = rect;
564 }
565
566 void GraphicsContext::clearFocusRingClip()
567 {
568     if (paintingDisabled())
569         return;
570
571     m_data->focusRingClip = IntRect();
572 }
573
574 void GraphicsContext::drawLineForText(const IntPoint& origin, int width, bool printing)
575 {
576     if (paintingDisabled())
577         return;
578
579     IntPoint endPoint = origin + IntSize(width, 0);
580     drawLine(origin, endPoint);
581 }
582
583 void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint&,
584                                                          int width, bool grammar)
585 {
586     if (paintingDisabled())
587         return;
588
589     notImplemented();
590 }
591
592 FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect)
593 {
594     QRectF rect(frect);
595     rect = m_data->p().deviceMatrix().mapRect(rect);
596
597     QRect result = rect.toRect(); //round it
598     return FloatRect(QRectF(result));
599 }
600
601 void GraphicsContext::setShadow(const IntSize& pos, int blur, const Color &color)
602 {
603     if (paintingDisabled())
604         return;
605
606     m_data->shadow.x = pos.width();
607     m_data->shadow.y = pos.height();
608     m_data->shadow.blur = blur;
609     m_data->shadow.color = color;
610 }
611
612 void GraphicsContext::clearShadow()
613 {
614     if (paintingDisabled())
615         return;
616
617     m_data->shadow = TextShadow();
618 }
619
620 void GraphicsContext::beginTransparencyLayer(float opacity)
621 {
622     if (paintingDisabled())
623         return;
624
625     TransparencyLayer layer(m_data->p(),
626                             m_data->device->width(),
627                             m_data->device->height());
628
629     layer.opacity = opacity;
630     m_data->layers.push(layer);
631 }
632
633 void GraphicsContext::endTransparencyLayer()
634 {
635     if (paintingDisabled())
636         return;
637
638     TransparencyLayer layer = m_data->layers.pop();
639     layer.painter->end();
640
641     m_data->p().save();
642 #if QT_VERSION >= 0x040200
643     m_data->p().setOpacity(layer.opacity);
644 #endif
645     m_data->p().drawPixmap(0, 0, *layer.pixmap);
646     m_data->p().restore();
647
648     layer.cleanup();
649 }
650
651 void GraphicsContext::clearRect(const FloatRect& rect)
652 {
653     if (paintingDisabled())
654         return;
655
656     m_data->p().eraseRect(rect);
657 }
658
659 void GraphicsContext::strokeRect(const FloatRect& rect, float width)
660 {
661     if (paintingDisabled())
662         return;
663
664     QPainterPath path;
665     path.addRect(rect);
666     QPen nPen = m_data->p().pen();
667     nPen.setWidthF(width);
668     m_data->p().strokePath(path, nPen);
669 }
670
671 void GraphicsContext::setLineCap(LineCap lc)
672 {
673     if (paintingDisabled())
674         return;
675
676     QPen nPen = m_data->p().pen();
677     nPen.setCapStyle(toQtLineCap(lc));
678     m_data->p().setPen(nPen);
679 }
680
681 void GraphicsContext::setLineJoin(LineJoin lj)
682 {
683     if (paintingDisabled())
684         return;
685
686     QPen nPen = m_data->p().pen();
687     nPen.setJoinStyle(toQtLineJoin(lj));
688     m_data->p().setPen(nPen);
689 }
690
691 void GraphicsContext::setMiterLimit(float limit)
692 {
693     if (paintingDisabled())
694         return;
695
696     QPen nPen = m_data->p().pen();
697     nPen.setMiterLimit(limit);
698     m_data->p().setPen(nPen);
699 }
700
701 void GraphicsContext::setAlpha(float opacity)
702 {
703     if (paintingDisabled())
704         return;
705 #if QT_VERSION >= 0x040200
706     m_data->p().setOpacity(opacity);
707 #endif
708 }
709
710 void GraphicsContext::setCompositeOperation(CompositeOperator op)
711 {
712     if (paintingDisabled())
713         return;
714
715     m_data->p().setCompositionMode(toQtCompositionMode(op));
716 }
717
718 void GraphicsContext::clip(const Path& path)
719 {
720     if (paintingDisabled())
721         return;
722
723     m_data->p().setClipPath(*path.platformPath());
724 }
725
726 void GraphicsContext::translate(float x, float y)
727 {
728     if (paintingDisabled())
729         return;
730
731     m_data->p().translate(x, y);
732 }
733
734 IntPoint GraphicsContext::origin()
735 {
736     if (paintingDisabled())
737         return IntPoint();
738     return IntPoint(qRound(m_data->p().matrix().dx()),
739                     qRound(m_data->p().matrix().dy()));
740 }
741
742 void GraphicsContext::rotate(float radians)
743 {
744     if (paintingDisabled())
745         return;
746
747     m_data->p().rotate(radians);
748 }
749
750 void GraphicsContext::scale(const FloatSize& s)
751 {
752     if (paintingDisabled())
753         return;
754
755     m_data->p().scale(s.width(), s.height());
756 }
757
758 void GraphicsContext::clipOut(const IntRect& rect)
759 {
760     if (paintingDisabled())
761         return;
762         
763     // FIXME: Implement
764     notImplemented();
765 }
766
767 void GraphicsContext::clipOutEllipseInRect(const IntRect& rect)
768 {
769     if (paintingDisabled())
770         return;
771     
772     // FIXME: Implement.
773     notImplemented();
774 }
775
776 void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect,
777                                               int thickness)
778 {
779     if (paintingDisabled())
780         return;
781
782     clip(rect);
783     QPainterPath path;
784
785     // Add outer ellipse
786     path.addEllipse(QRectF(rect.x(), rect.y(), rect.width(), rect.height()));
787
788     // Add inner ellipse.
789     path.addEllipse(QRectF(rect.x() + thickness, rect.y() + thickness,
790                            rect.width() - (thickness * 2), rect.height() - (thickness * 2)));
791
792     path.setFillRule(Qt::OddEvenFill);
793     m_data->p().setClipPath(path, Qt::IntersectClip);
794 }
795
796 void GraphicsContext::addRoundedRectClip(const IntRect& rect, const IntSize& topLeft,
797                                          const IntSize& topRight, const IntSize& bottomLeft,
798                                          const IntSize& bottomRight)
799 {
800     if (paintingDisabled())
801         return;
802
803     // Need sufficient width and height to contain these curves.  Sanity check our top/bottom
804     // values and our width/height values to make sure the curves can all fit.
805     int requiredWidth = qMax(topLeft.width() + topRight.width(), bottomLeft.width() + bottomRight.width());
806     if (requiredWidth > rect.width())
807         return;
808
809     int requiredHeight = qMax(topLeft.height() + bottomLeft.height(), topRight.height() + bottomRight.height());
810     if (requiredHeight > rect.height())
811         return;
812
813     // Clip to our rect.
814     clip(rect);
815
816     // OK, the curves can fit.
817     QPainterPath path;
818
819     // Add the four ellipses to the path.  Technically this really isn't good enough, since we could end up
820     // not clipping the other 3/4 of the ellipse we don't care about.  We're relying on the fact that for
821     // normal use cases these ellipses won't overlap one another (or when they do the curvature of one will
822     // be subsumed by the other).
823     path.addEllipse(QRectF(rect.x(), rect.y(), topLeft.width() * 2, topLeft.height() * 2));
824     path.addEllipse(QRectF(rect.right() - topRight.width() * 2, rect.y(),
825                            topRight.width() * 2, topRight.height() * 2));
826     path.addEllipse(QRectF(rect.x(), rect.bottom() - bottomLeft.height() * 2,
827                            bottomLeft.width() * 2, bottomLeft.height() * 2));
828     path.addEllipse(QRectF(rect.right() - bottomRight.width() * 2,
829                            rect.bottom() - bottomRight.height() * 2,
830                            bottomRight.width() * 2, bottomRight.height() * 2));
831
832     int topLeftRightHeightMax = qMax(topLeft.height(), topRight.height());
833     int bottomLeftRightHeightMax = qMax(bottomLeft.height(), bottomRight.height());
834
835     int topBottomLeftWidthMax = qMax(topLeft.width(), bottomLeft.width());
836     int topBottomRightWidthMax = qMax(topRight.width(), bottomRight.width());
837
838     // Now add five rects (one for each edge rect in between the rounded corners and one for the interior).
839     path.addRect(QRectF(rect.x() + topLeft.width(),
840                         rect.y(),
841                         rect.width() - topLeft.width() - topRight.width(),
842                         topLeftRightHeightMax));
843
844     path.addRect(QRectF(rect.x() + bottomLeft.width(), rect.bottom() - bottomLeftRightHeightMax,
845                         rect.width() - bottomLeft.width() - bottomRight.width(), bottomLeftRightHeightMax));
846
847     path.addRect(QRectF(rect.x(),
848                         rect.y() + topLeft.height(),
849                         topBottomLeftWidthMax,
850                         rect.height() - topLeft.height() - bottomLeft.height()));
851
852     path.addRect(QRectF(rect.right() - topBottomRightWidthMax,
853                         rect.y() + topRight.height(),
854                         topBottomRightWidthMax,
855                         rect.height() - topRight.height() - bottomRight.height()));
856
857     path.addRect(QRectF(rect.x() + topBottomLeftWidthMax,
858                         rect.y() + topLeftRightHeightMax,
859                         rect.width() - topBottomLeftWidthMax - topBottomRightWidthMax,
860                         rect.height() - topLeftRightHeightMax - bottomLeftRightHeightMax));
861
862     path.setFillRule(Qt::WindingFill);
863     m_data->p().setClipPath(path, Qt::IntersectClip);
864 }
865
866 void GraphicsContext::concatCTM(const AffineTransform& transform)
867 {
868     if (paintingDisabled())
869         return;
870
871     m_data->p().setMatrix(transform, true);
872 }
873
874 void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
875 {
876     notImplemented();
877 }
878
879 void GraphicsContext::setPlatformFont(const Font& aFont)
880 {
881     if (paintingDisabled())
882         return;
883     m_data->p().setFont(aFont);
884 }
885
886 void GraphicsContext::setPlatformStrokeColor(const Color& color)
887 {
888     if (paintingDisabled())
889         return;
890     QPen newPen(m_data->p().pen());
891     newPen.setColor(color);
892     m_data->p().setPen(newPen);
893 }
894
895 void GraphicsContext::setPlatformStrokeStyle(const StrokeStyle& strokeStyle)
896 {   
897     if (paintingDisabled())
898         return;
899     QPen newPen(m_data->p().pen());
900     newPen.setStyle(toQPenStyle(strokeStyle));
901     m_data->p().setPen(newPen);
902 }
903
904 void GraphicsContext::setPlatformStrokeThickness(float thickness)
905 {
906     if (paintingDisabled())
907         return;
908     QPen newPen(m_data->p().pen());
909     newPen.setWidthF(thickness);
910     m_data->p().setPen(newPen);
911 }
912
913 void GraphicsContext::setPlatformFillColor(const Color& color)
914 {
915     if (paintingDisabled())
916         return;
917     m_data->p().setBrush(QBrush(color));
918 }
919
920 }
921
922 // vim: ts=4 sw=4 et