3e2d1b1a589f049531e62283819eb6e0af55b27b
[WebKit-https.git] / WebCore / kwq / KWQPainter.mm
1 /*
2  * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #import "KWQPainter.h"
28
29 #import <kxmlcore/Assertions.h>
30 #import <kxmlcore/Vector.h>
31 #import "Brush.h"
32 #import "KWQExceptions.h"
33 #import "KWQFont.h"
34 #import "KWQFoundationExtras.h"
35 #import "Pen.h"
36 #import "Image.h"
37 #import "KWQPrinter.h"
38 #import "KWQRegion.h"
39 #import "KWQFontMetrics.h"
40 #import "WebCoreGraphicsBridge.h"
41 #import "WebCoreImageRenderer.h"
42 #import "WebCoreImageRendererFactory.h"
43 #import "WebCoreTextRenderer.h"
44 #import "WebCoreTextRendererFactory.h"
45
46 #if SVG_SUPPORT
47 #import "kcanvas/device/quartz/KRenderingDeviceQuartz.h"
48 #endif
49
50 namespace WebCore {
51
52 // NSColor, NSBezierPath, NSGraphicsContext and WebCoreTextRenderer
53 // calls in this file are all exception-safe, so we don't block
54 // exceptions for those.
55
56 struct QPState {
57     QPState() : paintingDisabled(false) { }
58     QFont font;
59     Pen pen;
60     WebCore::Brush brush;
61     QRegion clip;
62     bool paintingDisabled;
63 };
64
65 struct QPainterPrivate {
66     QPainterPrivate();
67     ~QPainterPrivate();
68     QPState state;
69     
70     Vector<QPState> stack;
71     id <WebCoreTextRenderer> textRenderer;
72     QFont textRendererFont;
73     NSBezierPath *focusRingPath;
74     int focusRingWidth;
75     int focusRingOffset;
76     bool hasFocusRingColor;
77     Color focusRingColor;
78 #if SVG_SUPPORT
79     KRenderingDevice *renderingDevice;
80 #endif
81 };
82
83 QPainterPrivate::QPainterPrivate() : textRenderer(0), focusRingPath(0), focusRingWidth(0), focusRingOffset(0),
84                         hasFocusRingColor(false)
85 {
86
87 }
88
89 QPainterPrivate::~QPainterPrivate()
90 {
91     KWQRelease(textRenderer);
92     KWQRelease(focusRingPath);
93 }
94
95 static inline void _fillRectXX(float x, float y, float w, float h, const Color& col);
96 QPainter::QPainter() : data(new QPainterPrivate), _isForPrinting(false), _usesInactiveTextBackgroundColor(false), _updatingControlTints(false)
97 {
98 }
99
100 QPainter::QPainter(bool forPrinting) : data(new QPainterPrivate), _isForPrinting(forPrinting), 
101     _usesInactiveTextBackgroundColor(false), _updatingControlTints(false)
102 {
103 }
104
105 QPainter::~QPainter()
106 {
107     delete data;
108 }
109
110 const QFont &QPainter::font() const
111 {
112     return data->state.font;
113 }
114
115 void QPainter::setFont(const QFont &aFont)
116 {
117     data->state.font = aFont;
118 }
119
120 QFontMetrics QPainter::fontMetrics() const
121 {
122     return data->state.font;
123 }
124
125 const Pen &QPainter::pen() const
126 {
127     return data->state.pen;
128 }
129
130 void QPainter::setPen(const Pen &pen)
131 {
132     data->state.pen = pen;
133 }
134
135 void QPainter::setPen(Pen::PenStyle style)
136 {
137     data->state.pen.setStyle(style);
138     data->state.pen.setColor(Color::black);
139     data->state.pen.setWidth(0);
140 }
141
142 void QPainter::setPen(RGBA32 rgb)
143 {
144     data->state.pen.setStyle(Pen::SolidLine);
145     data->state.pen.setColor(rgb);
146     data->state.pen.setWidth(0);
147 }
148
149 void QPainter::setBrush(const WebCore::Brush &brush)
150 {
151     data->state.brush = brush;
152 }
153
154 void QPainter::setBrush(WebCore::Brush::BrushStyle style)
155 {
156     data->state.brush.setStyle(style);
157     data->state.brush.setColor(Color::black);
158 }
159
160 void QPainter::setBrush(RGBA32 rgb)
161 {
162     data->state.brush.setStyle(WebCore::Brush::SolidPattern);
163     data->state.brush.setColor(rgb);
164 }
165
166 const WebCore::Brush& QPainter::brush() const
167 {
168     return data->state.brush;
169 }
170
171 IntRect QPainter::xForm(const IntRect &aRect) const
172 {
173     // No difference between device and model coords, so the identity transform is ok.
174     return aRect;
175 }
176
177 void QPainter::save()
178 {
179     if (data->state.paintingDisabled)
180         return;
181
182     data->stack.append(data->state);
183
184     [NSGraphicsContext saveGraphicsState]; 
185 }
186
187 void QPainter::restore()
188 {
189     if (data->state.paintingDisabled)
190         return;
191
192     if (data->stack.isEmpty()) {
193         ERROR("ERROR void QPainter::restore() stack is empty");
194         return;
195     }
196     data->state = data->stack.last();
197     data->stack.removeLast();
198      
199     [NSGraphicsContext restoreGraphicsState];
200 }
201
202 // Draws a filled rectangle with a stroked border.
203 void QPainter::drawRect(int x, int y, int w, int h)
204 {
205     if (data->state.paintingDisabled)
206         return;
207          
208     if (data->state.brush.style() != WebCore::Brush::NoBrush)
209         _fillRectXX(x, y, w, h, data->state.brush.color());
210
211     if (data->state.pen.style() != Pen::Pen::NoPen) {
212         _setColorFromPen();
213         NSFrameRect(NSMakeRect(x, y, w, h));
214     }
215 }
216
217 void QPainter::_setColorFromBrush()
218 {
219     [nsColor(data->state.brush.color()) set];
220 }
221   
222 void QPainter::_setColorFromPen()
223 {
224     [nsColor(data->state.pen.color()) set];
225 }
226   
227   // This is only used to draw borders.
228 void QPainter::drawLine(int x1, int y1, int x2, int y2)
229 {
230     if (data->state.paintingDisabled)
231         return;
232
233     Pen::PenStyle penStyle = data->state.pen.style();
234     if (penStyle == Pen::Pen::NoPen)
235         return;
236     float width = data->state.pen.width();
237     if (width < 1)
238         width = 1;
239
240     NSPoint p1 = NSMakePoint(x1, y1);
241     NSPoint p2 = NSMakePoint(x2, y2);
242     
243     // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic
244     // works out.  For example, with a border width of 3, KHTML will pass us (y1+y2)/2, e.g.,
245     // (50+53)/2 = 103/2 = 51 when we want 51.5.  It is always true that an even width gave
246     // us a perfect position, but an odd width gave us a position that is off by exactly 0.5.
247     if (penStyle == Pen::DotLine || penStyle == Pen::DashLine) {
248         if (x1 == x2) {
249             p1.y += width;
250             p2.y -= width;
251         }
252         else {
253             p1.x += width;
254             p2.x -= width;
255         }
256     }
257     
258     if (((int)width)%2) {
259         if (x1 == x2) {
260             // We're a vertical line.  Adjust our x.
261             p1.x += 0.5;
262             p2.x += 0.5;
263         }
264         else {
265             // We're a horizontal line. Adjust our y.
266             p1.y += 0.5;
267             p2.y += 0.5;
268         }
269     }
270     
271     NSBezierPath *path = [[NSBezierPath alloc] init];
272     [path setLineWidth:width];
273
274     int patWidth = 0;
275     switch (penStyle) {
276     case Pen::NoPen:
277     case Pen::SolidLine:
278         break;
279     case Pen::DotLine:
280         patWidth = (int)width;
281         break;
282     case Pen::DashLine:
283         patWidth = 3*(int)width;
284         break;
285     }
286
287     _setColorFromPen();
288     
289     NSGraphicsContext *graphicsContext = [NSGraphicsContext currentContext];
290     BOOL flag = [graphicsContext shouldAntialias];
291     [graphicsContext setShouldAntialias: NO];
292     
293     if (patWidth) {
294         // Do a rect fill of our endpoints.  This ensures we always have the
295         // appearance of being a border.  We then draw the actual dotted/dashed line.
296         if (x1 == x2) {
297             _fillRectXX(p1.x-width/2, p1.y-width, width, width, data->state.pen.color());
298             _fillRectXX(p2.x-width/2, p2.y, width, width, data->state.pen.color());
299         }
300         else {
301             _fillRectXX(p1.x-width, p1.y-width/2, width, width, data->state.pen.color());
302             _fillRectXX(p2.x, p2.y-width/2, width, width, data->state.pen.color());
303         }
304         
305         // Example: 80 pixels with a width of 30 pixels.
306         // Remainder is 20.  The maximum pixels of line we could paint
307         // will be 50 pixels.
308         int distance = ((x1 == x2) ? (y2 - y1) : (x2 - x1)) - 2*(int)width;
309         int remainder = distance%patWidth;
310         int coverage = distance-remainder;
311         int numSegments = coverage/patWidth;
312
313         float patternOffset = 0;
314         // Special case 1px dotted borders for speed.
315         if (patWidth == 1)
316             patternOffset = 1.0;
317         else {
318             bool evenNumberOfSegments = numSegments%2 == 0;
319             if (remainder)
320                 evenNumberOfSegments = !evenNumberOfSegments;
321             if (evenNumberOfSegments) {
322                 if (remainder) {
323                     patternOffset += patWidth - remainder;
324                     patternOffset += remainder/2;
325                 }
326                 else
327                     patternOffset = patWidth/2;
328             }
329             else if (!evenNumberOfSegments) {
330                 if (remainder)
331                     patternOffset = (patWidth - remainder)/2;
332             }
333         }
334         
335         const float dottedLine[2] = { patWidth, patWidth };
336         [path setLineDash:dottedLine count:2 phase:patternOffset];
337     }
338     
339     [path moveToPoint:p1];
340     [path lineToPoint:p2];
341
342     [path stroke];
343     
344     [path release];
345
346     [graphicsContext setShouldAntialias: flag];
347 }
348
349
350 // This method is only used to draw the little circles used in lists.
351 void QPainter::drawEllipse(int x, int y, int w, int h)
352 {
353     // FIXME: CG added CGContextAddEllipseinRect in Tiger, so we should be able to quite easily draw an ellipse.
354     // This code can only handle circles, not ellipses. But khtml only
355     // uses it for circles.
356     ASSERT(w == h);
357
358     if (data->state.paintingDisabled)
359         return;
360         
361     CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
362     CGContextBeginPath(context);
363     float r = (float)w / 2;
364     CGContextAddArc(context, x + r, y + r, r, 0, 2*M_PI, true);
365     CGContextClosePath(context);
366
367     if (data->state.brush.style() != WebCore::Brush::NoBrush) {
368         _setColorFromBrush();
369         if (data->state.pen.style() != Pen::NoPen) {
370             // stroke and fill
371             _setColorFromPen();
372             uint penWidth = data->state.pen.width();
373             if (penWidth == 0) 
374                 penWidth++;
375             CGContextSetLineWidth(context, penWidth);
376             CGContextDrawPath(context, kCGPathFillStroke);
377         } else {
378             CGContextFillPath(context);
379         }
380     }
381     if (data->state.pen.style() != Pen::NoPen) {
382         _setColorFromPen();
383         uint penWidth = data->state.pen.width();
384         if (penWidth == 0) 
385             penWidth++;
386         CGContextSetLineWidth(context, penWidth);
387         CGContextStrokePath(context);
388     }
389 }
390
391
392 void QPainter::drawArc (int x, int y, int w, int h, int a, int alen)
393
394     // Only supports arc on circles.  That's all khtml needs.
395     ASSERT(w == h);
396
397     if (data->state.paintingDisabled)
398         return;
399     
400     if (data->state.pen.style() != Pen::NoPen) {
401         CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
402         CGContextBeginPath(context);
403         
404         float r = (float)w / 2;
405         float fa = (float)a / 16;
406         float falen =  fa + (float)alen / 16;
407         CGContextAddArc(context, x + r, y + r, r, -fa * M_PI/180, -falen * M_PI/180, true);
408         
409         _setColorFromPen();
410         CGContextSetLineWidth(context, data->state.pen.width());
411         CGContextStrokePath(context);
412     }
413 }
414
415 void QPainter::drawConvexPolygon(const IntPointArray &points)
416 {
417     if (data->state.paintingDisabled)
418         return;
419
420     int npoints = points.size();
421     if (npoints <= 1)
422         return;
423
424     CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
425
426     CGContextSaveGState(context);
427
428     CGContextSetShouldAntialias(context, false);
429     
430     CGContextBeginPath(context);
431     CGContextMoveToPoint(context, points[0].x(), points[0].y());
432     for (int i = 1; i < npoints; i++)
433         CGContextAddLineToPoint(context, points[i].x(), points[i].y());
434     CGContextClosePath(context);
435
436     if (data->state.brush.style() != WebCore::Brush::NoBrush) {
437         _setColorFromBrush();
438         CGContextEOFillPath(context);
439     }
440
441     if (data->state.pen.style() != Pen::NoPen) {
442         _setColorFromPen();
443         CGContextSetLineWidth(context, data->state.pen.width());
444         CGContextStrokePath(context);
445     }
446
447     CGContextRestoreGState(context);
448 }
449
450 void QPainter::drawImageAtPoint(const Image& image, const IntPoint &p, Image::CompositeOperator compositeOperator)
451 {        
452     drawImage(image, p.x(), p.y(), 0, 0, -1, -1, compositeOperator);
453 }
454
455 void QPainter::drawImageInRect(const Image& image, const IntRect &r, Image::CompositeOperator compositeOperator)
456 {
457     drawImage(image, r.x(), r.y(), r.width(), r.height(), 0, 0, -1, -1, compositeOperator);
458 }
459
460 int QPainter::getCompositeOperation(CGContextRef context)
461 {
462     return (int)[[WebCoreImageRendererFactory sharedFactory] CGCompositeOperationInContext:context];
463 }
464
465 void QPainter::setCompositeOperation (CGContextRef context, const QString &op)
466 {
467     [[WebCoreImageRendererFactory sharedFactory] setCGCompositeOperationFromString:op.getNSString() inContext:context];
468 }
469
470 void QPainter::setCompositeOperation (CGContextRef context, int op)
471 {
472     [[WebCoreImageRendererFactory sharedFactory] setCGCompositeOperation:op inContext:context];
473 }
474
475 void QPainter::drawImage(const Image& image, int x, int y,
476                          int sx, int sy, int sw, int sh, Image::CompositeOperator compositeOperator, void* context)
477 {
478     drawImage(image, x, y, sw, sh, sx, sy, sw, sh, compositeOperator, context);
479 }
480
481 void QPainter::drawImage(const Image& image, int x, int y, int w, int h,
482                          int sx, int sy, int sw, int sh, Image::CompositeOperator compositeOperator, void* context)
483 {
484     drawFloatImage(image, (float)x, (float)y, (float)w, (float)h, (float)sx, (float)sy, (float)sw, (float)sh, compositeOperator, context);
485 }
486
487 void QPainter::drawFloatImage(const Image &image, float x, float y, float w, float h, 
488                               float sx, float sy, float sw, float sh, Image::CompositeOperator compositeOperator, void* context)
489 {
490     if (data->state.paintingDisabled)
491         return;
492
493     float tsw = sw;
494     float tsh = sh;
495     float tw = w;
496     float th = h;
497         
498     if (tsw == -1)
499         tsw = image.width();
500     if (tsh == -1)
501         tsh = image.height();
502
503     if (tw == -1)
504         tw = image.width();
505     if (th == -1)
506         th = image.height();
507
508     image.drawInRect(FloatRect(x, y, tw, th), FloatRect(sx, sy, tsw, tsh), compositeOperator, context);
509 }
510
511 void QPainter::drawTiledImage(const Image& image, int x, int y, int w, int h,
512                               int sx, int sy, void* context)
513 {
514     if (data->state.paintingDisabled)
515         return;
516     
517     image.tileInRect(FloatRect(x, y, w, h), FloatPoint(sx, sy), context);
518 }
519
520 void QPainter::drawScaledAndTiledImage(const Image &image, int x, int y, int w, int h, int sx, int sy, int sw, int sh, 
521                                        Image::TileRule hRule, Image::TileRule vRule, void* context)
522 {
523     if (data->state.paintingDisabled)
524         return;
525
526     if (hRule == Image::StretchTile && vRule == Image::StretchTile)
527         // Just do a scale.
528         return drawImage(image, x, y, w, h, sx, sy, sw, sh, Image::CompositeSourceOver, context);
529
530     image.scaleAndTileInRect(FloatRect(x, y, w, h), FloatRect(sx, sy, sw, sh),
531                              hRule, vRule, context);
532 }
533
534 void QPainter::_updateRenderer()
535 {
536     if (data->textRenderer == 0 || data->state.font != data->textRendererFont) {
537         data->textRendererFont = data->state.font;
538         id <WebCoreTextRenderer> oldRenderer = data->textRenderer;
539         KWQ_BLOCK_EXCEPTIONS;
540         data->textRenderer = KWQRetain([[WebCoreTextRendererFactory sharedFactory]
541             rendererWithFont:data->textRendererFont.getWebCoreFont()]);
542         KWQRelease(oldRenderer);
543         KWQ_UNBLOCK_EXCEPTIONS;
544     }
545 }
546     
547 void QPainter::drawText(int x, int y, int tabWidth, int xpos, int, int, int alignmentFlags, const QString &qstring)
548 {
549     if (data->state.paintingDisabled)
550         return;
551
552     // Avoid allocations, use stack array to pass font families.  Normally these
553     // css fallback lists are small <= 3.
554     CREATE_FAMILY_ARRAY(data->state.font, families);    
555
556     _updateRenderer();
557
558     const UniChar* str = (const UniChar*)qstring.unicode();
559
560     WebCoreTextRun run;
561     WebCoreInitializeTextRun(&run, str, qstring.length(), 0, qstring.length());
562     
563     WebCoreTextStyle style;
564     WebCoreInitializeEmptyTextStyle(&style);
565     style.textColor = nsColor(data->state.pen.color());
566     style.families = families;
567     style.tabWidth = tabWidth;
568     style.xpos = xpos;
569     
570     if (alignmentFlags & Qt::AlignRight)
571         x -= lroundf([data->textRenderer floatWidthForRun:&run style:&style]);
572
573     WebCoreTextGeometry geometry;
574     WebCoreInitializeEmptyTextGeometry(&geometry);
575     geometry.point = NSMakePoint(x, y);
576     [data->textRenderer drawRun:&run style:&style geometry:&geometry];
577 }
578
579 void QPainter::drawText(int x, int y, int tabWidth, int xpos, const QChar *str, int len, int from, int to, int toAdd, const Color &backgroundColor, QPainter::TextDirection d, bool visuallyOrdered, int letterSpacing, int wordSpacing, bool smallCaps)
580 {
581     if (data->state.paintingDisabled || len <= 0)
582         return;
583
584     // Avoid allocations, use stack array to pass font families.  Normally these
585     // css fallback lists are small <= 3.
586     CREATE_FAMILY_ARRAY(data->state.font, families);
587
588     _updateRenderer();
589
590     if (from < 0)
591         from = 0;
592     if (to < 0)
593         to = len;
594         
595     WebCoreTextRun run;
596     WebCoreInitializeTextRun(&run, (const UniChar *)str, len, from, to);    
597     WebCoreTextStyle style;
598     WebCoreInitializeEmptyTextStyle(&style);
599     style.textColor = nsColor(data->state.pen.color());
600     style.backgroundColor = backgroundColor.isValid() ? nsColor(backgroundColor) : nil;
601     style.rtl = d == RTL;
602     style.directionalOverride = visuallyOrdered;
603     style.letterSpacing = letterSpacing;
604     style.wordSpacing = wordSpacing;
605     style.smallCaps = smallCaps;
606     style.families = families;
607     style.padding = toAdd;
608     style.tabWidth = tabWidth;
609     style.xpos = xpos;
610     WebCoreTextGeometry geometry;
611     WebCoreInitializeEmptyTextGeometry(&geometry);
612     geometry.point = NSMakePoint(x, y);
613     [data->textRenderer drawRun:&run style:&style geometry:&geometry];
614 }
615
616 void QPainter::drawHighlightForText(int x, int y, int h, int tabWidth, int xpos,
617     const QChar *str, int len, int from, int to, int toAdd, const Color &backgroundColor, 
618     QPainter::TextDirection d, bool visuallyOrdered, int letterSpacing, int wordSpacing, bool smallCaps)
619 {
620     if (data->state.paintingDisabled || len <= 0)
621         return;
622
623     // Avoid allocations, use stack array to pass font families.  Normally these
624     // css fallback lists are small <= 3.
625     CREATE_FAMILY_ARRAY(data->state.font, families);
626
627     _updateRenderer();
628
629     if (from < 0)
630         from = 0;
631     if (to < 0)
632         to = len;
633         
634     WebCoreTextRun run;
635     WebCoreInitializeTextRun(&run, (const UniChar *)str, len, from, to);    
636     WebCoreTextStyle style;
637     WebCoreInitializeEmptyTextStyle(&style);
638     style.textColor = nsColor(data->state.pen.color());
639     style.backgroundColor = backgroundColor.isValid() ? nsColor(backgroundColor) : nil;
640     style.rtl = d == RTL;
641     style.directionalOverride = visuallyOrdered;
642     style.letterSpacing = letterSpacing;
643     style.wordSpacing = wordSpacing;
644     style.smallCaps = smallCaps;
645     style.families = families;    
646     style.padding = toAdd;
647     style.tabWidth = tabWidth;
648     style.xpos = xpos;
649     WebCoreTextGeometry geometry;
650     WebCoreInitializeEmptyTextGeometry(&geometry);
651     geometry.point = NSMakePoint(x, y);
652     geometry.selectionY = y;
653     geometry.selectionHeight = h;
654     geometry.useFontMetricsForSelectionYAndHeight = false;
655     [data->textRenderer drawHighlightForRun:&run style:&style geometry:&geometry];
656 }
657
658 void QPainter::drawLineForText(int x, int y, int yOffset, int width)
659 {
660     if (data->state.paintingDisabled)
661         return;
662     _updateRenderer();
663     [data->textRenderer
664         drawLineForCharacters: NSMakePoint(x, y)
665                yOffset:(float)yOffset
666                  width: width
667                  color:nsColor(data->state.pen.color())
668              thickness:data->state.pen.width()];
669 }
670
671 void QPainter::drawLineForMisspelling(int x, int y, int width)
672 {
673     if (data->state.paintingDisabled)
674         return;
675     _updateRenderer();
676     [data->textRenderer drawLineForMisspelling:NSMakePoint(x, y) withWidth:width];
677 }
678
679 int QPainter::misspellingLineThickness() const
680 {
681     return [data->textRenderer misspellingLineThickness];
682 }
683
684 static int getBlendedColorComponent(int c, int a)
685 {
686     // We use white.
687     float alpha = (float)(a) / 255;
688     int whiteBlend = 255 - a;
689     c -= whiteBlend;
690     return (int)(c/alpha);
691 }
692
693 Color QPainter::selectedTextBackgroundColor() const
694 {
695     NSColor *color = _usesInactiveTextBackgroundColor ? [NSColor secondarySelectedControlColor] : [NSColor selectedTextBackgroundColor];
696     // this needs to always use device colorspace so it can de-calibrate the color for
697     // Color to possibly recalibrate it
698     color = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace];
699     
700     Color col = Color((int)(255 * [color redComponent]), (int)(255 * [color greenComponent]), (int)(255 * [color blueComponent]));
701     
702     // Attempt to make the selection 60% transparent.  We do this by applying a standard blend and then
703     // seeing if the resultant color is still within the 0-255 range.
704     int alpha = 153;
705     int red = getBlendedColorComponent(col.red(), alpha);
706     int green = getBlendedColorComponent(col.green(), alpha);
707     int blue = getBlendedColorComponent(col.blue(), alpha);
708     if (red >= 0 && red <= 255 && green >= 0 && green <= 255 && blue >= 0 && blue <= 255)
709         return Color(red, green, blue, alpha);
710     return col;
711 }
712
713 // A fillRect helper to work around the fact that NSRectFill uses copy mode, not source over.
714 static inline void _fillRectXX(float x, float y, float w, float h, const Color& col)
715 {
716     [nsColor(col) set];
717     NSRectFillUsingOperation(NSMakeRect(x,y,w,h), NSCompositeSourceOver);
718 }
719
720 void QPainter::fillRect(int x, int y, int w, int h, const WebCore::Brush &brush)
721 {
722     if (data->state.paintingDisabled)
723         return;
724
725     if (brush.style() == WebCore::Brush::SolidPattern)
726         _fillRectXX(x, y, w, h, brush.color());
727 }
728
729 void QPainter::fillRect(const IntRect &rect, const WebCore::Brush &brush)
730 {
731     fillRect(rect.x(), rect.y(), rect.width(), rect.height(), brush);
732 }
733
734 void QPainter::addClip(const IntRect &rect)
735 {
736     if (data->state.paintingDisabled)
737         return;
738
739     [NSBezierPath clipRect:rect];
740 }
741
742 void QPainter::addRoundedRectClip(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight,
743                                   const IntSize& bottomLeft, const IntSize& bottomRight)
744 {
745     if (data->state.paintingDisabled)
746         return;
747
748     // Need sufficient width and height to contain these curves.  Sanity check our top/bottom
749     // values and our width/height values to make sure the curves can all fit.
750     int requiredWidth = kMax(topLeft.width() + topRight.width(), bottomLeft.width() + bottomRight.width());
751     if (requiredWidth > rect.width())
752         return;
753     int requiredHeight = kMax(topLeft.height() + bottomLeft.height(), topRight.height() + bottomRight.height());
754     if (requiredHeight > rect.height())
755         return;
756  
757     // Clip to our rect.
758     addClip(rect);
759
760     // Ok, the curves can fit.
761     CGContextRef context = (CGContextRef)([[NSGraphicsContext currentContext] graphicsPort]);
762     
763     // Add the four ellipses to the path.  Technically this really isn't good enough, since we could end up
764     // not clipping the other 3/4 of the ellipse we don't care about.  We're relying on the fact that for
765     // normal use cases these ellipses won't overlap one another (or when they do the curvature of one will
766     // be subsumed by the other).
767     CGContextAddEllipseInRect(context, CGRectMake(rect.x(), rect.y(), topLeft.width() * 2, topLeft.height() * 2));
768     CGContextAddEllipseInRect(context, CGRectMake(rect.right() - topRight.width() * 2, rect.y(),
769                                                   topRight.width() * 2, topRight.height() * 2));
770     CGContextAddEllipseInRect(context, CGRectMake(rect.x(), rect.bottom() - bottomLeft.height() * 2,
771                                                   bottomLeft.width() * 2, bottomLeft.height() * 2));
772     CGContextAddEllipseInRect(context, CGRectMake(rect.right() - bottomRight.width() * 2,
773                                                   rect.bottom() - bottomRight.height() * 2,
774                                                   bottomRight.width() * 2, bottomRight.height() * 2));
775     
776     // Now add five rects (one for each edge rect in between the rounded corners and one for the interior).
777     CGContextAddRect(context, CGRectMake(rect.x() + topLeft.width(), rect.y(),
778                                          rect.width() - topLeft.width() - topRight.width(),
779                                          kMax(topLeft.height(), topRight.height())));
780     CGContextAddRect(context, CGRectMake(rect.x() + bottomLeft.width(), 
781                                          rect.bottom() - kMax(bottomLeft.height(), bottomRight.height()),
782                                          rect.width() - bottomLeft.width() - bottomRight.width(),
783                                          kMax(bottomLeft.height(), bottomRight.height())));
784     CGContextAddRect(context, CGRectMake(rect.x(), rect.y() + topLeft.height(),
785                                          kMax(topLeft.width(), bottomLeft.width()), rect.height() - topLeft.height() - bottomLeft.height()));
786     CGContextAddRect(context, CGRectMake(rect.right() - kMax(topRight.width(), bottomRight.width()),
787                                          rect.y() + topRight.height(),
788                                          kMax(topRight.width(), bottomRight.width()), rect.height() - topRight.height() - bottomRight.height()));
789     CGContextAddRect(context, CGRectMake(rect.x() + kMax(topLeft.width(), bottomLeft.width()),
790                                          rect.y() + kMax(topLeft.height(), topRight.height()),
791                                          rect.width() - kMax(topLeft.width(), bottomLeft.width()) - kMax(topRight.width(), bottomRight.width()),
792                                          rect.height() - kMax(topLeft.height(), topRight.height()) - kMax(bottomLeft.height(), bottomRight.height())));
793     CGContextClip(context);
794 }
795
796 Qt::RasterOp QPainter::rasterOp() const
797 {
798     return CopyROP;
799 }
800
801 void QPainter::setRasterOp(RasterOp)
802 {
803 }
804
805 void QPainter::setPaintingDisabled(bool f)
806 {
807     data->state.paintingDisabled = f;
808 }
809
810 bool QPainter::paintingDisabled() const
811 {
812     return data->state.paintingDisabled;
813 }
814
815 CGContextRef QPainter::currentContext()
816 {
817     ASSERT(!data->state.paintingDisabled);
818     return (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
819 }
820
821 #if SVG_SUPPORT
822 KRenderingDeviceContext *QPainter::createRenderingDeviceContext()
823 {
824     return new KRenderingDeviceContextQuartz(currentContext());
825 }
826
827 KRenderingDevice *QPainter::renderingDevice()
828 {
829     static KRenderingDevice *sharedRenderingDevice = new KRenderingDeviceQuartz();
830     return sharedRenderingDevice;
831 }
832 #endif
833
834 void QPainter::beginTransparencyLayer(float opacity)
835 {
836     if (data->state.paintingDisabled)
837         return;
838     [NSGraphicsContext saveGraphicsState];
839     CGContextRef context = (CGContextRef)([[NSGraphicsContext currentContext] graphicsPort]);
840     CGContextSetAlpha(context, opacity);
841     CGContextBeginTransparencyLayer(context, 0);
842 }
843
844 void QPainter::endTransparencyLayer()
845 {
846     if (data->state.paintingDisabled)
847         return;
848     CGContextRef context = (CGContextRef)([[NSGraphicsContext currentContext] graphicsPort]);
849     CGContextEndTransparencyLayer(context);
850     [NSGraphicsContext restoreGraphicsState];
851 }
852
853 void QPainter::setShadow(int x, int y, int blur, const Color& color)
854 {
855     if (data->state.paintingDisabled)
856         return;
857     // Check for an invalid color, as this means that the color was not set for the shadow
858     // and we should therefore just use the default shadow color.
859     CGContextRef context = (CGContextRef)([[NSGraphicsContext currentContext] graphicsPort]);
860     if (!color.isValid()) {
861         CGContextSetShadow(context, CGSizeMake(x,-y), blur); // y is flipped.
862     } else {
863         CGColorRef colorCG = cgColor(color);
864         CGContextSetShadowWithColor(context,
865                                     CGSizeMake(x,-y), // y is flipped.
866                                     blur, 
867                                     colorCG);
868         CGColorRelease(colorCG);
869     }
870 }
871
872 void QPainter::clearShadow()
873 {
874     if (data->state.paintingDisabled)
875         return;
876     CGContextRef context = (CGContextRef)([[NSGraphicsContext currentContext] graphicsPort]);
877     CGContextSetShadowWithColor(context, CGSizeZero, 0, NULL);
878 }
879
880 void QPainter::initFocusRing(int width, int offset)
881 {
882     if (data->state.paintingDisabled)
883         return;
884     clearFocusRing();
885     data->focusRingWidth = width;
886     data->hasFocusRingColor = false;
887     data->focusRingOffset = offset;
888     data->focusRingPath = KWQRetainNSRelease([[NSBezierPath alloc] init]);
889     [data->focusRingPath setWindingRule:NSNonZeroWindingRule];
890 }
891
892 void QPainter::initFocusRing(int width, int offset, const Color &color)
893 {
894     if (data->state.paintingDisabled)
895         return;
896     initFocusRing(width, offset);
897     data->hasFocusRingColor = true;
898     data->focusRingColor = color;
899 }
900
901 void QPainter::addFocusRingRect(int x, int y, int width, int height)
902 {
903     if (data->state.paintingDisabled)
904         return;
905     ASSERT(data->focusRingPath);
906     NSRect rect = NSMakeRect(x, y, width, height);
907     int offset = (data->focusRingWidth-1)/2 + data->focusRingOffset;
908     rect = NSInsetRect(rect, -offset, -offset);
909     [data->focusRingPath appendBezierPathWithRect:rect];
910 }
911
912 void QPainter::drawFocusRing()
913 {
914     if (data->state.paintingDisabled)
915         return;
916
917     ASSERT(data->focusRingPath);
918
919     if ([data->focusRingPath elementCount] == 0) {
920         ERROR("Request to draw focus ring with no control points");
921         return;
922     }
923     
924     NSRect bounds = [data->focusRingPath bounds];
925     if (!NSIsEmptyRect(bounds)) {
926         int radius = (data->focusRingWidth-1)/2;
927         NSColor *color = data->hasFocusRingColor ? nsColor(data->focusRingColor) : nil;
928         [NSGraphicsContext saveGraphicsState];
929         [[WebCoreGraphicsBridge sharedBridge] setFocusRingStyle:NSFocusRingOnly radius:radius color:color];
930         [data->focusRingPath fill];
931         [NSGraphicsContext restoreGraphicsState];   
932     }
933 }
934
935 void QPainter::clearFocusRing()
936 {
937     if (data->focusRingPath) {
938         KWQRelease(data->focusRingPath);
939         data->focusRingPath = nil;
940     }
941 }
942
943 }