Fixed <rdar://problem/3704339> Context2D forces integer positions in drawImage
[WebKit-https.git] / WebCore / kwq / KWQPainter.mm
1 /*
2  * Copyright (C) 2003 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 #import "KWQPainter.h"
27
28 #import "KWQAssertions.h"
29 #import "KWQExceptions.h"
30 #import "KWQFontMetrics.h"
31 #import "KWQKHTMLPart.h"
32 #import "KWQPaintDevice.h"
33 #import "KWQPixmap.h"
34 #import "KWQPointArray.h"
35 #import "KWQPrinter.h"
36 #import "KWQPtrStack.h"
37 #import "KWQWidget.h"
38 #import "KWQFoundationExtras.h"
39 #import "WebCoreGraphicsBridge.h"
40 #import "WebCoreImageRenderer.h"
41 #import "WebCoreImageRendererFactory.h"
42 #import "WebCoreTextRenderer.h"
43 #import "WebCoreTextRendererFactory.h"
44
45 // NSColor, NSBezierPath, NSGraphicsContext and WebCoreTextRenderer
46 // calls in this file are all exception-safe, so we don't block
47 // exceptions for those.
48
49 struct QPState {
50     QPState() : paintingDisabled(false) { }
51     QFont font;
52     QPen pen;
53     QBrush brush;
54     QRegion clip;
55     bool paintingDisabled;
56 };
57
58 struct QPainterPrivate {
59     QPainterPrivate() : textRenderer(0), focusRingPath(0), focusRingWidth(0), focusRingOffset(0),
60                         hasFocusRingColor(false) { }
61     ~QPainterPrivate() { KWQRelease(textRenderer); KWQRelease(focusRingPath); }
62     QPState state;
63     QPtrStack<QPState> stack;
64     id <WebCoreTextRenderer> textRenderer;
65     QFont textRendererFont;
66     NSBezierPath *focusRingPath;
67     int focusRingWidth;
68     int focusRingOffset;
69     bool hasFocusRingColor;
70     QColor focusRingColor;
71 };
72
73
74 static CGColorRef CGColorFromNSColor(NSColor *color)
75 {
76     NSColor* deviceColor = [color colorUsingColorSpaceName: @"NSDeviceRGBColorSpace"];
77     float red = [deviceColor redComponent];
78     float green = [deviceColor greenComponent];
79     float blue = [deviceColor blueComponent];
80     float alpha = [deviceColor alphaComponent];
81     const float components[] = { red, green, blue, alpha };
82     
83     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
84     CGColorRef cgColor = CGColorCreate(colorSpace, components);
85     CGColorSpaceRelease(colorSpace);
86     return cgColor;
87 }
88
89 QPainter::QPainter() : data(new QPainterPrivate), _isForPrinting(false), _usesInactiveTextBackgroundColor(false), _drawsFocusRing(true)
90 {
91 }
92
93 QPainter::QPainter(bool forPrinting) : data(new QPainterPrivate), _isForPrinting(forPrinting), _usesInactiveTextBackgroundColor(false), _drawsFocusRing(true)
94 {
95 }
96
97 QPainter::~QPainter()
98 {
99     delete data;
100 }
101
102 QPaintDevice *QPainter::device() const
103 {
104     static QPrinter printer;
105     static QPaintDevice screen;
106     return _isForPrinting ? &printer : &screen;
107 }
108
109 const QFont &QPainter::font() const
110 {
111     return data->state.font;
112 }
113
114 void QPainter::setFont(const QFont &aFont)
115 {
116     data->state.font = aFont;
117 }
118
119 QFontMetrics QPainter::fontMetrics() const
120 {
121     return data->state.font;
122 }
123
124 const QPen &QPainter::pen() const
125 {
126     return data->state.pen;
127 }
128
129 void QPainter::setPen(const QPen &pen)
130 {
131     data->state.pen = pen;
132 }
133
134 void QPainter::setPen(PenStyle style)
135 {
136     data->state.pen.setStyle(style);
137     data->state.pen.setColor(Qt::black);
138     data->state.pen.setWidth(0);
139 }
140
141 void QPainter::setPen(QRgb rgb)
142 {
143     data->state.pen.setStyle(SolidLine);
144     data->state.pen.setColor(rgb);
145     data->state.pen.setWidth(0);
146 }
147
148 void QPainter::setBrush(const QBrush &brush)
149 {
150     data->state.brush = brush;
151 }
152
153 void QPainter::setBrush(BrushStyle style)
154 {
155     data->state.brush.setStyle(style);
156     data->state.brush.setColor(Qt::black);
157 }
158
159 void QPainter::setBrush(QRgb rgb)
160 {
161     data->state.brush.setStyle(SolidPattern);
162     data->state.brush.setColor(rgb);
163 }
164
165 const QBrush &QPainter::brush() const
166 {
167     return data->state.brush;
168 }
169
170 QRect QPainter::xForm(const QRect &aRect) const
171 {
172     // No difference between device and model coords, so the identity transform is ok.
173     return aRect;
174 }
175
176 void QPainter::save()
177 {
178     data->stack.push(new QPState(data->state));
179
180     [NSGraphicsContext saveGraphicsState]; 
181 }
182
183 void QPainter::restore()
184 {
185     if (data->stack.isEmpty()) {
186         ERROR("ERROR void QPainter::restore() stack is empty");
187         return;
188     }
189     QPState *ps = data->stack.pop();
190     data->state = *ps;
191     delete ps;
192
193     [NSGraphicsContext restoreGraphicsState];
194 }
195
196 // Draws a filled rectangle with a stroked border.
197 void QPainter::drawRect(int x, int y, int w, int h)
198 {
199     if (data->state.paintingDisabled)
200         return;
201         
202     if (data->state.brush.style() != NoBrush)
203         _fillRect(x, y, w, h, data->state.brush.color());
204
205     if (data->state.pen.style() != NoPen) {
206         _setColorFromPen();
207         NSFrameRect(NSMakeRect(x, y, w, h));
208     }
209 }
210
211 void QPainter::_setColorFromBrush()
212 {
213     [data->state.brush.color().getNSColor() set];
214 }
215
216 void QPainter::_setColorFromPen()
217 {
218     [data->state.pen.color().getNSColor() set];
219 }
220
221 // This is only used to draw borders.
222 void QPainter::drawLine(int x1, int y1, int x2, int y2)
223 {
224     if (data->state.paintingDisabled)
225         return;
226
227     PenStyle penStyle = data->state.pen.style();
228     if (penStyle == NoPen)
229         return;
230     float width = data->state.pen.width();
231     if (width < 1)
232         width = 1;
233
234     NSPoint p1 = NSMakePoint(x1, y1);
235     NSPoint p2 = NSMakePoint(x2, y2);
236     
237     // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic
238     // works out.  For example, with a border width of 3, KHTML will pass us (y1+y2)/2, e.g.,
239     // (50+53)/2 = 103/2 = 51 when we want 51.5.  It is always true that an even width gave
240     // us a perfect position, but an odd width gave us a position that is off by exactly 0.5.
241     if (penStyle == DotLine || penStyle == DashLine) {
242         if (x1 == x2) {
243             p1.y += width;
244             p2.y -= width;
245         }
246         else {
247             p1.x += width;
248             p2.x -= width;
249         }
250     }
251     
252     if (((int)width)%2) {
253         if (x1 == x2) {
254             // We're a vertical line.  Adjust our x.
255             p1.x += 0.5;
256             p2.x += 0.5;
257         }
258         else {
259             // We're a horizontal line. Adjust our y.
260             p1.y += 0.5;
261             p2.y += 0.5;
262         }
263     }
264     
265     NSBezierPath *path = [[NSBezierPath alloc] init];
266     [path setLineWidth:width];
267
268     int patWidth = 0;
269     switch (penStyle) {
270     case NoPen:
271     case SolidLine:
272         break;
273     case DotLine:
274         patWidth = (int)width;
275         break;
276     case DashLine:
277         patWidth = 3*(int)width;
278         break;
279     }
280
281     _setColorFromPen();
282     
283     NSGraphicsContext *graphicsContext = [NSGraphicsContext currentContext];
284     BOOL flag = [graphicsContext shouldAntialias];
285     [graphicsContext setShouldAntialias: NO];
286     
287     if (patWidth) {
288         // Do a rect fill of our endpoints.  This ensures we always have the
289         // appearance of being a border.  We then draw the actual dotted/dashed line.
290         if (x1 == x2) {
291             _fillRect(p1.x-width/2, p1.y-width, width, width, data->state.pen.color());
292             _fillRect(p2.x-width/2, p2.y, width, width, data->state.pen.color());
293         }
294         else {
295             _fillRect(p1.x-width, p1.y-width/2, width, width, data->state.pen.color());
296             _fillRect(p2.x, p2.y-width/2, width, width, data->state.pen.color());
297         }
298         
299         // Example: 80 pixels with a width of 30 pixels.
300         // Remainder is 20.  The maximum pixels of line we could paint
301         // will be 50 pixels.
302         int distance = ((x1 == x2) ? (y2 - y1) : (x2 - x1)) - 2*(int)width;
303         int remainder = distance%patWidth;
304         int coverage = distance-remainder;
305         int numSegments = coverage/patWidth;
306
307         float patternOffset = 0;
308         // Special case 1px dotted borders for speed.
309         if (patWidth == 1)
310             patternOffset = 1.0;
311         else {
312             bool evenNumberOfSegments = numSegments%2 == 0;
313             if (remainder)
314                 evenNumberOfSegments = !evenNumberOfSegments;
315             if (evenNumberOfSegments) {
316                 if (remainder) {
317                     patternOffset += patWidth - remainder;
318                     patternOffset += remainder/2;
319                 }
320                 else
321                     patternOffset = patWidth/2;
322             }
323             else if (!evenNumberOfSegments) {
324                 if (remainder)
325                     patternOffset = (patWidth - remainder)/2;
326             }
327         }
328         
329         const float dottedLine[2] = { patWidth, patWidth };
330         [path setLineDash:dottedLine count:2 phase:patternOffset];
331     }
332     
333     [path moveToPoint:p1];
334     [path lineToPoint:p2];
335
336     [path stroke];
337     
338     [path release];
339
340     [graphicsContext setShouldAntialias: flag];
341 }
342
343
344 // This method is only used to draw the little circles used in lists.
345 void QPainter::drawEllipse(int x, int y, int w, int h)
346 {
347     // This code can only handle circles, not ellipses. But khtml only
348     // uses it for circles.
349     ASSERT(w == h);
350
351     if (data->state.paintingDisabled)
352         return;
353         
354     CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
355     CGContextBeginPath(context);
356     float r = (float)w / 2;
357     CGContextAddArc(context, x + r, y + r, r, 0, 2*M_PI, true);
358     CGContextClosePath(context);
359
360     if (data->state.brush.style() != NoBrush) {
361         _setColorFromBrush();
362         if (data->state.pen.style() != NoPen) {
363             // stroke and fill
364             _setColorFromPen();
365             uint penWidth = data->state.pen.width();
366             if (penWidth == 0) 
367                 penWidth++;
368             CGContextSetLineWidth(context, penWidth);
369             CGContextDrawPath(context, kCGPathFillStroke);
370         } else {
371             CGContextFillPath(context);
372         }
373     }
374     if (data->state.pen.style() != NoPen) {
375         _setColorFromPen();
376         uint penWidth = data->state.pen.width();
377         if (penWidth == 0) 
378             penWidth++;
379         CGContextSetLineWidth(context, penWidth);
380         CGContextStrokePath(context);
381     }
382 }
383
384
385 void QPainter::drawArc (int x, int y, int w, int h, int a, int alen)
386
387     // Only supports arc on circles.  That's all khtml needs.
388     ASSERT(w == h);
389
390     if (data->state.paintingDisabled)
391         return;
392     
393     if (data->state.pen.style() != NoPen) {
394         CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
395         CGContextBeginPath(context);
396         
397         float r = (float)w / 2;
398         float fa = (float)a / 16;
399         float falen =  fa + (float)alen / 16;
400         CGContextAddArc(context, x + r, y + r, r, -fa * M_PI/180, -falen * M_PI/180, true);
401         
402         _setColorFromPen();
403         CGContextSetLineWidth(context, data->state.pen.width());
404         CGContextStrokePath(context);
405     }
406 }
407
408 void QPainter::drawLineSegments(const QPointArray &points, int index, int nlines)
409 {
410     if (data->state.paintingDisabled)
411         return;
412     
413     _drawPoints (points, 0, index, nlines, false);
414 }
415
416 void QPainter::drawPolyline(const QPointArray &points, int index, int npoints)
417 {
418     _drawPoints (points, 0, index, npoints, false);
419 }
420
421 void QPainter::drawPolygon(const QPointArray &points, bool winding, int index, 
422     int npoints)
423 {
424     _drawPoints (points, winding, index, npoints, true);
425 }
426
427 void QPainter::drawConvexPolygon(const QPointArray &points)
428 {
429     _drawPoints (points, false, 0, -1, true);
430 }
431
432 void QPainter::_drawPoints (const QPointArray &_points, bool winding, int index, int _npoints, bool fill)
433 {
434     if (data->state.paintingDisabled)
435         return;
436         
437     int npoints = _npoints != -1 ? _npoints : _points.size()-index;
438     NSPoint points[npoints];
439     for (int i = 0; i < npoints; i++) {
440         points[i].x = _points[index+i].x();
441         points[i].y = _points[index+i].y();
442     }
443             
444     NSBezierPath *path = [[NSBezierPath alloc] init];
445     [path appendBezierPathWithPoints:&points[0] count:npoints];
446     [path closePath]; // Qt always closes the path.  Determined empirically.
447     
448     if (fill && data->state.brush.style() != NoBrush) {
449         [path setWindingRule:winding ? NSNonZeroWindingRule : NSEvenOddWindingRule];
450         _setColorFromBrush();
451         [path fill];
452     }
453
454     if (data->state.pen.style() != NoPen) {
455         _setColorFromPen();
456         [path setLineWidth:data->state.pen.width()];
457         [path stroke];
458     }
459     
460     [path release];
461 }
462
463
464 void QPainter::drawPixmap(const QPoint &p, const QPixmap &pix)
465 {        
466     drawPixmap(p.x(), p.y(), pix);
467 }
468
469
470 void QPainter::drawPixmap(const QPoint &p, const QPixmap &pix, const QRect &r)
471 {
472     drawPixmap(p.x(), p.y(), pix, r.x(), r.y(), r.width(), r.height());
473 }
474
475 struct CompositeOperator
476 {
477     const char *name;
478     NSCompositingOperation value;
479 };
480
481 #define NUM_COMPOSITE_OPERATORS 14
482 struct CompositeOperator compositeOperators[NUM_COMPOSITE_OPERATORS] = {
483     { "clear", NSCompositeClear },
484     { "copy", NSCompositeCopy },
485     { "source-over", NSCompositeSourceOver },
486     { "source-in", NSCompositeSourceIn },
487     { "source-out", NSCompositeSourceOut },
488     { "source-atop", NSCompositeSourceAtop },
489     { "destination-over", NSCompositeDestinationOver },
490     { "destination-in", NSCompositeDestinationIn },
491     { "destination-out", NSCompositeDestinationOut },
492     { "destination-atop", NSCompositeDestinationAtop },
493     { "xor", NSCompositeXOR },
494     { "darker", NSCompositePlusDarker },
495     { "highlight", NSCompositeHighlight },
496     { "lighter", NSCompositePlusLighter }
497 };
498
499 int QPainter::getCompositeOperation(CGContextRef context)
500 {
501     return (int)[[WebCoreImageRendererFactory sharedFactory] CGCompositeOperationInContext:context];
502 }
503
504 void QPainter::setCompositeOperation (CGContextRef context, QString op)
505 {
506     [[WebCoreImageRendererFactory sharedFactory] setCGCompositeOperationFromString:op.getNSString() inContext:context];
507 }
508
509 void QPainter::setCompositeOperation (CGContextRef context, int op)
510 {
511     [[WebCoreImageRendererFactory sharedFactory] setCGCompositeOperation:op inContext:context];
512 }
513
514 int QPainter::compositeOperatorFromString (QString aString)
515 {
516     NSCompositingOperation op = NSCompositeSourceOver;
517     
518     if (aString.length()) {
519         const char *operatorString = aString.ascii();
520         int i;
521         
522         for (i = 0; i < NUM_COMPOSITE_OPERATORS; i++) {
523             if (strcasecmp (operatorString, compositeOperators[i].name) == 0) {
524                 return compositeOperators[i].value;
525             }
526         }
527     }
528     return (int)op;
529 }
530
531 void QPainter::drawPixmap(const QPoint &p, const QPixmap &pix, const QRect &r, const QString &compositeOperator)
532 {
533     drawPixmap(p.x(), p.y(), pix, r.x(), r.y(), r.width(), r.height(), compositeOperatorFromString(compositeOperator));
534 }
535
536 void QPainter::drawPixmap( int x, int y, const QPixmap &pixmap,
537                            int sx, int sy, int sw, int sh, int compositeOperator, CGContextRef context)
538 {
539     drawPixmap (x, y, sw, sh, pixmap, sx, sy, sw, sh, compositeOperator, context);
540 }
541
542 void QPainter::drawPixmap( int x, int y, int w, int h, const QPixmap &pixmap,
543                            int sx, int sy, int sw, int sh, int compositeOperator, CGContextRef context)
544 {
545     drawFloatPixmap ((float)x, (float)y, (float)w, (float)h, pixmap, (float)sx, (float)sy, (float)sw, (float)sh, compositeOperator, context);
546 }
547
548 void QPainter::drawFloatPixmap( float x, float y, float w, float h, const QPixmap &pixmap,
549                            float sx, float sy, float sw, float sh, int compositeOperator, CGContextRef context)
550 {
551     if (data->state.paintingDisabled)
552         return;
553         
554     if (sw == -1)
555         sw = pixmap.width();
556     if (sh == -1)
557         sh = pixmap.height();
558
559     if (w == -1)
560         w = pixmap.width();
561     if (h == -1)
562         h = pixmap.height();
563
564     NSRect inRect = NSMakeRect(x, y, w, h);
565     NSRect fromRect = NSMakeRect(sx, sy, sw, sh);
566     
567     KWQ_BLOCK_EXCEPTIONS;
568     [pixmap.imageRenderer drawImageInRect:inRect
569                                       fromRect:fromRect compositeOperator:(NSCompositingOperation)compositeOperator context:context];
570     KWQ_UNBLOCK_EXCEPTIONS;
571 }
572
573 void QPainter::drawTiledPixmap( int x, int y, int w, int h,
574                                 const QPixmap &pixmap, int sx, int sy, CGContextRef context)
575 {
576     if (data->state.paintingDisabled)
577         return;
578     
579     KWQ_BLOCK_EXCEPTIONS;
580     [pixmap.imageRenderer tileInRect:NSMakeRect(x, y, w, h) fromPoint:NSMakePoint(sx, sy) context:context];
581     KWQ_UNBLOCK_EXCEPTIONS;
582 }
583
584 void QPainter::_updateRenderer()
585 {
586     if (data->textRenderer == 0 || data->state.font != data->textRendererFont) {
587         data->textRendererFont = data->state.font;
588         id <WebCoreTextRenderer> oldRenderer = data->textRenderer;
589         KWQ_BLOCK_EXCEPTIONS;
590         data->textRenderer = KWQRetain([[WebCoreTextRendererFactory sharedFactory]
591             rendererWithFont:data->textRendererFont.getNSFont()
592             usingPrinterFont:data->textRendererFont.isPrinterFont()]);
593         KWQRelease(oldRenderer);
594         KWQ_UNBLOCK_EXCEPTIONS;
595     }
596 }
597     
598 void QPainter::drawText(int x, int y, int, int, int alignmentFlags, const QString &qstring)
599 {
600     if (data->state.paintingDisabled)
601         return;
602
603     // Avoid allocations, use stack array to pass font families.  Normally these
604     // css fallback lists are small <= 3.
605     CREATE_FAMILY_ARRAY(data->state.font, families);    
606
607     _updateRenderer();
608
609     const UniChar* str = (const UniChar*)qstring.unicode();
610
611     WebCoreTextRun run;
612     WebCoreInitializeTextRun(&run, str, qstring.length(), 0, qstring.length());
613     
614     WebCoreTextStyle style;
615     WebCoreInitializeEmptyTextStyle(&style);
616     style.textColor = data->state.pen.color().getNSColor();
617     style.families = families;
618     
619     if (alignmentFlags & Qt::AlignRight)
620         x -= ROUND_TO_INT([data->textRenderer floatWidthForRun:&run style:&style widths:0]);
621
622     WebCoreTextGeometry geometry;
623     WebCoreInitializeEmptyTextGeometry(&geometry);
624     geometry.point = NSMakePoint(x, y);
625      
626     [data->textRenderer drawRun:&run style:&style geometry:&geometry];
627 }
628
629 void QPainter::drawText(int x, int y, const QChar *str, int len, int from, int to, int toAdd, const QColor &backgroundColor, QPainter::TextDirection d, bool visuallyOrdered, int letterSpacing, int wordSpacing, bool smallCaps)
630 {
631     if (data->state.paintingDisabled || len <= 0)
632         return;
633
634     // Avoid allocations, use stack array to pass font families.  Normally these
635     // css fallback lists are small <= 3.
636     CREATE_FAMILY_ARRAY(data->state.font, families);
637
638     _updateRenderer();
639
640     if (from < 0)
641         from = 0;
642     if (to < 0)
643         to = len;
644         
645     WebCoreTextRun run;
646     WebCoreInitializeTextRun(&run, (const UniChar *)str, len, from, to);    
647     WebCoreTextStyle style;
648     WebCoreInitializeEmptyTextStyle(&style);
649     style.textColor = data->state.pen.color().getNSColor();
650     style.backgroundColor = backgroundColor.isValid() ? backgroundColor.getNSColor() : nil;
651     style.rtl = d == RTL ? true : false;
652     style.visuallyOrdered = visuallyOrdered;
653     style.letterSpacing = letterSpacing;
654     style.wordSpacing = wordSpacing;
655     style.smallCaps = smallCaps;
656     style.families = families;
657     style.padding = toAdd;
658     WebCoreTextGeometry geometry;
659     WebCoreInitializeEmptyTextGeometry(&geometry);
660     geometry.point = NSMakePoint(x, y);
661     
662     [data->textRenderer drawRun:&run style:&style geometry:&geometry];
663 }
664
665 void QPainter::drawHighlightForText(int x, int minX, int maxX, int y, int h, 
666     const QChar *str, int len, int from, int to, int toAdd, const QColor &backgroundColor, 
667     QPainter::TextDirection d, bool visuallyOrdered, int letterSpacing, int wordSpacing, bool smallCaps)
668 {
669     if (data->state.paintingDisabled || len <= 0)
670         return;
671
672     // Avoid allocations, use stack array to pass font families.  Normally these
673     // css fallback lists are small <= 3.
674     CREATE_FAMILY_ARRAY(data->state.font, families);
675
676     _updateRenderer();
677
678     if (from < 0)
679         from = 0;
680     if (to < 0)
681         to = len;
682         
683     WebCoreTextRun run;
684     WebCoreInitializeTextRun(&run, (const UniChar *)str, len, from, to);    
685     WebCoreTextStyle style;
686     WebCoreInitializeEmptyTextStyle(&style);
687     style.textColor = data->state.pen.color().getNSColor();
688     style.backgroundColor = backgroundColor.isValid() ? backgroundColor.getNSColor() : nil;
689     style.rtl = d == RTL ? true : false;
690     style.visuallyOrdered = visuallyOrdered;
691     style.letterSpacing = letterSpacing;
692     style.wordSpacing = wordSpacing;
693     style.smallCaps = smallCaps;
694     style.families = families;    
695     style.padding = toAdd;
696     WebCoreTextGeometry geometry;
697     WebCoreInitializeEmptyTextGeometry(&geometry);
698     geometry.point = NSMakePoint(x, y);
699     geometry.selectionY = y;
700     geometry.selectionHeight = h;
701     geometry.selectionMinX = minX;
702     geometry.selectionMaxX = maxX;
703     geometry.useFontMetricsForSelectionYAndHeight = false;
704     [data->textRenderer drawHighlightForRun:&run style:&style geometry:&geometry];
705 }
706
707 void QPainter::drawLineForText(int x, int y, int yOffset, int width)
708 {
709     if (data->state.paintingDisabled)
710         return;
711     _updateRenderer();
712     [data->textRenderer
713         drawLineForCharacters: NSMakePoint(x, y)
714                yOffset:(float)yOffset
715              withWidth: width
716              withColor:data->state.pen.color().getNSColor()];
717 }
718
719 void QPainter::drawLineForMisspelling(int x, int y, int width)
720 {
721     if (data->state.paintingDisabled)
722         return;
723     _updateRenderer();
724     [data->textRenderer drawLineForMisspelling:NSMakePoint(x, y) withWidth:width];
725 }
726
727 QColor QPainter::selectedTextBackgroundColor() const
728 {
729     NSColor *color = _usesInactiveTextBackgroundColor ? [NSColor secondarySelectedControlColor] : [NSColor selectedTextBackgroundColor];
730     color = [color colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
731     return QColor((int)(255 * [color redComponent]), (int)(255 * [color greenComponent]), (int)(255 * [color blueComponent]));
732 }
733
734 // A fillRect designed to work around buggy behavior in NSRectFill.
735 void QPainter::_fillRect(float x, float y, float w, float h, const QColor& col)
736 {
737     [col.getNSColor() set];
738     NSRectFillUsingOperation(NSMakeRect(x,y,w,h), NSCompositeSourceOver);
739 }
740
741 void QPainter::fillRect(int x, int y, int w, int h, const QBrush &brush)
742 {
743     if (data->state.paintingDisabled)
744         return;
745
746     if (brush.style() == SolidPattern)
747         _fillRect(x, y, w, h, brush.color());
748 }
749
750 void QPainter::addClip(const QRect &rect)
751 {
752     [NSBezierPath clipRect:rect];
753 }
754
755 Qt::RasterOp QPainter::rasterOp() const
756 {
757     return CopyROP;
758 }
759
760 void QPainter::setRasterOp(RasterOp)
761 {
762 }
763
764 void QPainter::setPaintingDisabled(bool f)
765 {
766     data->state.paintingDisabled = f;
767 }
768
769 bool QPainter::paintingDisabled() const
770 {
771     return data->state.paintingDisabled;
772 }
773
774 CGContextRef QPainter::currentContext()
775 {
776     return (CGContextRef)([[NSGraphicsContext currentContext] graphicsPort]);
777 }
778
779 void QPainter::beginTransparencyLayer(float opacity)
780 {
781     [NSGraphicsContext saveGraphicsState];
782     CGContextRef context = (CGContextRef)([[NSGraphicsContext currentContext] graphicsPort]);
783     CGContextSetAlpha(context, opacity);
784     CGContextBeginTransparencyLayer(context, 0);
785 }
786
787 void QPainter::endTransparencyLayer()
788 {
789     CGContextRef context = (CGContextRef)([[NSGraphicsContext currentContext] graphicsPort]);
790     CGContextEndTransparencyLayer(context);
791     [NSGraphicsContext restoreGraphicsState];
792 }
793
794 void QPainter::setShadow(int x, int y, int blur, const QColor& color)
795 {
796     // Check for an invalid color, as this means that the color was not set for the shadow
797     // and we should therefore just use the default shadow color.
798     CGContextRef context = (CGContextRef)([[NSGraphicsContext currentContext] graphicsPort]);
799     if (!color.isValid()) {
800         CGContextSetShadow(context, CGSizeMake(x,-y), blur); // y is flipped.
801     } else {
802         CGColorRef cgColor = CGColorFromNSColor(color.getNSColor());
803         CGContextSetShadowWithColor(context,
804                                     CGSizeMake(x,-y), // y is flipped.
805                                     blur, 
806                                     cgColor);
807         CGColorRelease(cgColor);
808     }
809 }
810
811 void QPainter::clearShadow()
812 {
813     CGContextRef context = (CGContextRef)([[NSGraphicsContext currentContext] graphicsPort]);
814     CGContextSetShadowWithColor(context, CGSizeZero, 0, NULL);
815 }
816
817 void QPainter::initFocusRing(int width, int offset)
818 {
819     if (!_drawsFocusRing)
820         return;
821
822     clearFocusRing();
823     data->focusRingWidth = width;
824     data->hasFocusRingColor = false;
825     data->focusRingOffset = offset;
826     data->focusRingPath = KWQRetainNSRelease([[NSBezierPath alloc] init]);
827     [data->focusRingPath setWindingRule:NSNonZeroWindingRule];
828 }
829
830 void QPainter::initFocusRing(int width, int offset, const QColor &color)
831 {
832     if (!_drawsFocusRing)
833         return;
834
835     initFocusRing(width, offset);
836     data->hasFocusRingColor = true;
837     data->focusRingColor = color;
838 }
839
840 void QPainter::addFocusRingRect(int x, int y, int width, int height)
841 {
842     if (!_drawsFocusRing)
843         return;
844
845     ASSERT(data->focusRingPath);
846     NSRect rect = NSMakeRect(x, y, width, height);
847     int offset = (data->focusRingWidth-1)/2 + data->focusRingOffset;
848     rect = NSInsetRect(rect, -offset, -offset);
849     [data->focusRingPath appendBezierPathWithRect:rect];
850 }
851
852 void QPainter::drawFocusRing()
853 {
854     if (!_drawsFocusRing)
855         return;
856     
857     ASSERT(data->focusRingPath);
858     if (data->state.paintingDisabled)
859         return;
860
861     if ([data->focusRingPath elementCount] == 0) {
862         ERROR("Request to draw focus ring with no control points");
863         return;
864     }
865     
866     NSRect bounds = [data->focusRingPath bounds];
867     if (!NSIsEmptyRect(bounds)) {
868         int radius = (data->focusRingWidth-1)/2;
869         NSColor *color = data->hasFocusRingColor ? data->focusRingColor.getNSColor() : nil;
870         [NSGraphicsContext saveGraphicsState];
871         [[WebCoreGraphicsBridge sharedBridge] setFocusRingStyle:NSFocusRingOnly radius:radius color:color];
872         [data->focusRingPath fill];
873         [NSGraphicsContext restoreGraphicsState];   
874     }
875 }
876
877 void QPainter::clearFocusRing()
878 {
879     if (data->focusRingPath) {
880         KWQRelease(data->focusRingPath);
881         data->focusRingPath = nil;
882     }
883 }