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