2009-02-19 Dimitri Glazkov <dglazkov@chromium.org>
[WebKit-https.git] / WebCore / platform / graphics / skia / GraphicsContextSkia.cpp
1 /*
2  * Copyright (c) 2006, Google 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 are
6  * met:
7  * 
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  * 
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #include "GraphicsContext.h"
33
34 #include "GraphicsContextPlatformPrivate.h"
35 #include "GraphicsContextPrivate.h"
36 #include "Color.h"
37 #include "FloatRect.h"
38 #include "Gradient.h"
39 #include "ImageBuffer.h"
40 #include "IntRect.h"
41 #include "NativeImageSkia.h"
42 #include "NotImplemented.h"
43 #include "PlatformContextSkia.h"
44 #include "TransformationMatrix.h"
45
46 #include "SkBitmap.h"
47 #include "SkBlurDrawLooper.h"
48 #include "SkCornerPathEffect.h"
49 #include "skia/ext/platform_canvas.h"
50 #include "SkiaUtils.h"
51 #include "SkShader.h"
52
53 #include <math.h>
54 #include <wtf/Assertions.h>
55 #include <wtf/MathExtras.h>
56
57 using namespace std;
58
59 namespace WebCore {
60
61 namespace {
62
63 // "Seatbelt" functions ------------------------------------------------------
64 //
65 // These functions check certain graphics primitives for being "safe".
66 // Skia has historically crashed when sent crazy data. These functions do
67 // additional checking to prevent crashes.
68 //
69 // Ideally, all of these would be fixed in the graphics layer and we would not
70 // have to do any checking. You can uncomment the ENSURE_VALUE_SAFETY_FOR_SKIA
71 // flag to check the graphics layer.
72 #define ENSURE_VALUE_SAFETY_FOR_SKIA
73
74 static bool isCoordinateSkiaSafe(float coord)
75 {
76 #ifdef ENSURE_VALUE_SAFETY_FOR_SKIA
77     // First check for valid floats.
78 #if defined(_MSC_VER)
79     if (!_finite(coord))
80 #else
81     if (!finite(coord))
82 #endif
83         return false;
84
85     // Skia uses 16.16 fixed point and 26.6 fixed point in various places. If
86     // the transformed point exceeds 15 bits, we just declare that it's
87     // unreasonable to catch both of these cases.
88     static const int maxPointMagnitude = 32767;
89     if (coord > maxPointMagnitude || coord < -maxPointMagnitude)
90         return false;
91
92     return true;
93 #else
94     return true;
95 #endif
96 }
97
98 static bool isPointSkiaSafe(const SkMatrix& transform, const SkPoint& pt)
99 {
100 #ifdef ENSURE_VALUE_SAFETY_FOR_SKIA
101     // Now check for points that will overflow. We check the *transformed*
102     // points since this is what will be rasterized.
103     SkPoint xPt;
104     transform.mapPoints(&xPt, &pt, 1);
105     return isCoordinateSkiaSafe(xPt.fX) && isCoordinateSkiaSafe(xPt.fY);
106 #else
107     return true;
108 #endif
109 }
110
111 static bool isRectSkiaSafe(const SkMatrix& transform, const SkRect& rc)
112 {
113 #ifdef ENSURE_VALUE_SAFETY_FOR_SKIA
114     SkPoint topleft = {rc.fLeft, rc.fTop};
115     SkPoint bottomright = {rc.fRight, rc.fBottom};
116     return isPointSkiaSafe(transform, topleft) && isPointSkiaSafe(transform, bottomright);
117 #else
118     return true;
119 #endif
120 }
121
122 bool isPathSkiaSafe(const SkMatrix& transform, const SkPath& path)
123 {
124 #ifdef ENSURE_VALUE_SAFETY_FOR_SKIA
125     SkPoint current_points[4];
126     SkPath::Iter iter(path, false);
127     for (SkPath::Verb verb = iter.next(current_points);
128          verb != SkPath::kDone_Verb;
129          verb = iter.next(current_points)) {
130         switch (verb) {
131         case SkPath::kMove_Verb:
132             // This move will be duplicated in the next verb, so we can ignore.
133             break;
134         case SkPath::kLine_Verb:
135             // iter.next returns 2 points.
136             if (!isPointSkiaSafe(transform, current_points[0])
137                 || !isPointSkiaSafe(transform, current_points[1]))
138                 return false;
139             break;
140         case SkPath::kQuad_Verb:
141             // iter.next returns 3 points.
142             if (!isPointSkiaSafe(transform, current_points[0])
143                 || !isPointSkiaSafe(transform, current_points[1])
144                 || !isPointSkiaSafe(transform, current_points[2]))
145                 return false;
146             break;
147         case SkPath::kCubic_Verb:
148             // iter.next returns 4 points.
149             if (!isPointSkiaSafe(transform, current_points[0])
150                 || !isPointSkiaSafe(transform, current_points[1])
151                 || !isPointSkiaSafe(transform, current_points[2])
152                 || !isPointSkiaSafe(transform, current_points[3]))
153                 return false;
154             break;
155         case SkPath::kClose_Verb:
156         case SkPath::kDone_Verb:
157         default:
158             break;
159         }
160     }
161     return true;
162 #else
163     return true;
164 #endif
165 }
166
167 // Local helper functions ------------------------------------------------------
168
169 void addCornerArc(SkPath* path, const SkRect& rect, const IntSize& size, int startAngle)
170 {
171     SkIRect ir;
172     int rx = SkMin32(SkScalarRound(rect.width()), size.width());
173     int ry = SkMin32(SkScalarRound(rect.height()), size.height());
174
175     ir.set(-rx, -ry, rx, ry);
176     switch (startAngle) {
177     case 0:
178         ir.offset(rect.fRight - ir.fRight, rect.fBottom - ir.fBottom);
179         break;
180     case 90:
181         ir.offset(rect.fLeft - ir.fLeft, rect.fBottom - ir.fBottom);
182         break;
183     case 180:
184         ir.offset(rect.fLeft - ir.fLeft, rect.fTop - ir.fTop);
185         break;
186     case 270:
187         ir.offset(rect.fRight - ir.fRight, rect.fTop - ir.fTop);
188         break;
189     default:
190         ASSERT(0);
191     }
192
193     SkRect r;
194     r.set(ir);
195     path->arcTo(r, SkIntToScalar(startAngle), SkIntToScalar(90), false);
196 }
197
198 inline int fastMod(int value, int max)
199 {
200     int sign = SkExtractSign(value);
201
202     value = SkApplySign(value, sign);
203     if (value >= max)
204         value %= max;
205     return SkApplySign(value, sign);
206 }
207
208 inline float square(float n)
209 {
210     return n * n;
211 }
212
213 }  // namespace
214
215 // -----------------------------------------------------------------------------
216
217 // This may be called with a NULL pointer to create a graphics context that has
218 // no painting.
219 GraphicsContext::GraphicsContext(PlatformGraphicsContext* gc)
220     : m_common(createGraphicsContextPrivate())
221     , m_data(new GraphicsContextPlatformPrivate(gc))
222 {
223     setPaintingDisabled(!gc || !platformContext()->canvas());
224 }
225
226 GraphicsContext::~GraphicsContext()
227 {
228     delete m_data;
229     this->destroyGraphicsContextPrivate(m_common);
230 }
231
232 PlatformGraphicsContext* GraphicsContext::platformContext() const
233 {
234     ASSERT(!paintingDisabled());
235     return m_data->context();
236 }
237
238 // State saving ----------------------------------------------------------------
239
240 void GraphicsContext::savePlatformState()
241 {
242     if (paintingDisabled())
243         return;
244
245     // Save our private State.
246     platformContext()->save();
247 }
248
249 void GraphicsContext::restorePlatformState()
250 {
251     if (paintingDisabled())
252         return;
253
254     // Restore our private State.
255     platformContext()->restore();
256 }
257
258 void GraphicsContext::beginTransparencyLayer(float opacity)
259 {
260     if (paintingDisabled())
261         return;
262
263     // We need the "alpha" layer flag here because the base layer is opaque
264     // (the surface of the page) but layers on top may have transparent parts.
265     // Without explicitly setting the alpha flag, the layer will inherit the
266     // opaque setting of the base and some things won't work properly.
267     platformContext()->canvas()->saveLayerAlpha(
268         0,
269         static_cast<unsigned char>(opacity * 255),
270         static_cast<SkCanvas::SaveFlags>(SkCanvas::kHasAlphaLayer_SaveFlag |
271                                          SkCanvas::kFullColorLayer_SaveFlag));
272 }
273
274 void GraphicsContext::endTransparencyLayer()
275 {
276     if (paintingDisabled())
277         return;
278
279 #if PLATFORM(WIN_OS)
280     platformContext()->canvas()->getTopPlatformDevice().
281         fixupAlphaBeforeCompositing();
282 #endif
283     platformContext()->canvas()->restore();
284 }
285
286 // Graphics primitives ---------------------------------------------------------
287
288 void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness)
289 {
290     if (paintingDisabled())
291         return;
292
293     SkRect r(rect);
294     if (!isRectSkiaSafe(getCTM(), r))
295         return;
296
297     SkPath path;
298     path.addOval(r, SkPath::kCW_Direction);
299     // only perform the inset if we won't invert r
300     if (2 * thickness < rect.width() && 2 * thickness < rect.height()) {
301         r.inset(SkIntToScalar(thickness) ,SkIntToScalar(thickness));
302         path.addOval(r, SkPath::kCCW_Direction);
303     }
304     platformContext()->canvas()->clipPath(path);
305 }
306
307 void GraphicsContext::addPath(const Path& path)
308 {
309     if (paintingDisabled())
310         return;
311     platformContext()->addPath(*path.platformPath());
312 }
313
314 void GraphicsContext::beginPath()
315 {
316     if (paintingDisabled())
317         return;
318     platformContext()->beginPath();
319 }
320
321 void GraphicsContext::clearPlatformShadow()
322 {
323     if (paintingDisabled())
324         return;
325     platformContext()->setDrawLooper(0);
326 }
327
328 void GraphicsContext::clearRect(const FloatRect& rect)
329 {
330     if (paintingDisabled())
331         return;
332
333     SkRect r = rect;
334     if (!isRectSkiaSafe(getCTM(), r))
335         ClipRectToCanvas(*platformContext()->canvas(), r, &r);
336
337     SkPaint paint;
338     platformContext()->setupPaintForFilling(&paint);
339     paint.setPorterDuffXfermode(SkPorterDuff::kClear_Mode);
340     platformContext()->canvas()->drawRect(r, paint);
341 }
342
343 void GraphicsContext::clip(const FloatRect& rect)
344 {
345     if (paintingDisabled())
346         return;
347
348     SkRect r(rect);
349     if (!isRectSkiaSafe(getCTM(), r))
350         return;
351
352     platformContext()->canvas()->clipRect(r);
353 }
354
355 void GraphicsContext::clip(const Path& path)
356 {
357     if (paintingDisabled())
358         return;
359
360     const SkPath& p = *path.platformPath();
361     if (!isPathSkiaSafe(getCTM(), p))
362         return;
363
364     platformContext()->canvas()->clipPath(p);
365 }
366
367 void GraphicsContext::clipOut(const IntRect& rect)
368 {
369     if (paintingDisabled())
370         return;
371
372     SkRect r(rect);
373     if (!isRectSkiaSafe(getCTM(), r))
374         return;
375
376     platformContext()->canvas()->clipRect(r, SkRegion::kDifference_Op);
377 }
378
379 void GraphicsContext::clipOut(const Path& p)
380 {
381     if (paintingDisabled())
382         return;
383
384     const SkPath& path = *p.platformPath();
385     if (!isPathSkiaSafe(getCTM(), path))
386         return;
387
388     platformContext()->canvas()->clipPath(path, SkRegion::kDifference_Op);
389 }
390
391 void GraphicsContext::clipOutEllipseInRect(const IntRect& rect)
392 {
393     if (paintingDisabled())
394         return;
395
396     SkRect oval(rect);
397     if (!isRectSkiaSafe(getCTM(), oval))
398         return;
399
400     SkPath path;
401     path.addOval(oval, SkPath::kCCW_Direction);
402     platformContext()->canvas()->clipPath(path, SkRegion::kDifference_Op);
403 }
404
405 void GraphicsContext::clipPath(WindRule clipRule)
406 {
407     if (paintingDisabled())
408         return;
409
410     SkPath path = platformContext()->currentPathInLocalCoordinates();
411     path.setFillType(clipRule == RULE_EVENODD ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType);
412     platformContext()->canvas()->clipPath(path);
413 }
414
415 void GraphicsContext::clipToImageBuffer(const FloatRect& rect,
416                                         const ImageBuffer* imageBuffer)
417 {
418     if (paintingDisabled())
419         return;
420
421 #if defined(__linux__) || PLATFORM(WIN_OS)
422     platformContext()->beginLayerClippedToImage(rect, imageBuffer);
423 #endif
424 }
425
426 void GraphicsContext::concatCTM(const TransformationMatrix& xform)
427 {
428     if (paintingDisabled())
429         return;
430     platformContext()->canvas()->concat(xform);
431 }
432
433 void GraphicsContext::drawConvexPolygon(size_t numPoints,
434                                         const FloatPoint* points,
435                                         bool shouldAntialias)
436 {
437     if (paintingDisabled())
438         return;
439
440     if (numPoints <= 1)
441         return;
442
443     SkPath path;
444
445     path.incReserve(numPoints);
446     path.moveTo(WebCoreFloatToSkScalar(points[0].x()),
447                 WebCoreFloatToSkScalar(points[0].y()));
448     for (size_t i = 1; i < numPoints; i++) {
449         path.lineTo(WebCoreFloatToSkScalar(points[i].x()),
450                     WebCoreFloatToSkScalar(points[i].y()));
451     }
452
453     if (!isPathSkiaSafe(getCTM(), path))
454         return;
455
456     SkPaint paint;
457     if (fillColor().alpha() > 0) {
458         platformContext()->setupPaintForFilling(&paint);
459         platformContext()->canvas()->drawPath(path, paint);
460     }
461
462     if (strokeStyle() != NoStroke) {
463         paint.reset();
464         platformContext()->setupPaintForStroking(&paint, 0, 0);
465         platformContext()->canvas()->drawPath(path, paint);
466     }
467 }
468
469 // This method is only used to draw the little circles used in lists.
470 void GraphicsContext::drawEllipse(const IntRect& elipseRect)
471 {
472     if (paintingDisabled())
473         return;
474
475     SkRect rect = elipseRect;
476     if (!isRectSkiaSafe(getCTM(), rect))
477         return;
478
479     SkPaint paint;
480     if (fillColor().alpha() > 0) {
481         platformContext()->setupPaintForFilling(&paint);
482         platformContext()->canvas()->drawOval(rect, paint);
483     }
484
485     if (strokeStyle() != NoStroke) {
486         paint.reset();
487         platformContext()->setupPaintForStroking(&paint, &rect, 0);
488         platformContext()->canvas()->drawOval(rect, paint);
489     }
490 }
491
492 void GraphicsContext::drawFocusRing(const Color& color)
493 {
494     if (paintingDisabled())
495         return;
496
497     const Vector<IntRect>& rects = focusRingRects();
498     unsigned rectCount = rects.size();
499     if (0 == rectCount)
500         return;
501
502     SkRegion focusRingRegion;
503     const SkScalar focusRingOutset = WebCoreFloatToSkScalar(0.5);
504     for (unsigned i = 0; i < rectCount; i++) {
505         SkIRect r = rects[i];
506         r.inset(-focusRingOutset, -focusRingOutset);
507         focusRingRegion.op(r, SkRegion::kUnion_Op);
508     }
509
510     SkPath path;
511     SkPaint paint;
512     paint.setAntiAlias(true);
513     paint.setStyle(SkPaint::kStroke_Style);
514
515     paint.setColor(focusRingColor().rgb());
516     paint.setStrokeWidth(focusRingOutset * 2);
517     paint.setPathEffect(new SkCornerPathEffect(focusRingOutset * 2))->unref();
518     focusRingRegion.getBoundaryPath(&path);
519     platformContext()->canvas()->drawPath(path, paint);
520 }
521
522 // This is only used to draw borders.
523 void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
524 {
525     if (paintingDisabled())
526         return;
527
528     StrokeStyle penStyle = strokeStyle();
529     if (penStyle == NoStroke)
530         return;
531
532     SkPaint paint;
533     SkPoint pts[2] = { (SkPoint)point1, (SkPoint)point2 };
534     if (!isPointSkiaSafe(getCTM(), pts[0]) || !isPointSkiaSafe(getCTM(), pts[1]))
535         return;
536
537     // We know these are vertical or horizontal lines, so the length will just
538     // be the sum of the displacement component vectors give or take 1 -
539     // probably worth the speed up of no square root, which also won't be exact.
540     SkPoint disp = pts[1] - pts[0];
541     int length = SkScalarRound(disp.fX + disp.fY);
542     int width = roundf(
543         platformContext()->setupPaintForStroking(&paint, 0, length));
544
545     // "Borrowed" this comment and idea from GraphicsContextCG.cpp
546     // For odd widths, we add in 0.5 to the appropriate x/y so that the float
547     // arithmetic works out.  For example, with a border width of 3, KHTML will
548     // pass us (y1+y2)/2, e.g., (50+53)/2 = 103/2 = 51 when we want 51.5.  It is
549     // always true that an even width gave us a perfect position, but an odd
550     // width gave us a position that is off by exactly 0.5.
551     bool isVerticalLine = pts[0].fX == pts[1].fX;
552
553     if (width & 1) {  // Odd.
554         if (isVerticalLine) {
555             pts[0].fX = pts[0].fX + SK_ScalarHalf;
556             pts[1].fX = pts[0].fX;
557         } else {  // Horizontal line
558             pts[0].fY = pts[0].fY + SK_ScalarHalf;
559             pts[1].fY = pts[0].fY;
560         }
561     }
562     platformContext()->canvas()->drawPoints(SkCanvas::kLines_PointMode, 2, pts, paint);
563 }
564
565 void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint& pt,
566                                                          int width,
567                                                          bool grammar)
568 {
569     if (paintingDisabled())
570         return;
571
572     // Create the pattern we'll use to draw the underline.
573     static SkBitmap* misspellBitmap = 0;
574     if (!misspellBitmap) {
575         // We use a 2-pixel-high misspelling indicator because that seems to be
576         // what WebKit is designed for, and how much room there is in a typical
577         // page for it.
578         const int rowPixels = 32;  // Must be multiple of 4 for pattern below.
579         const int colPixels = 2;
580         misspellBitmap = new SkBitmap;
581         misspellBitmap->setConfig(SkBitmap::kARGB_8888_Config,
582                                    rowPixels, colPixels);
583         misspellBitmap->allocPixels();
584
585         misspellBitmap->eraseARGB(0, 0, 0, 0);
586         const uint32_t lineColor = 0xFFFF0000;  // Opaque red.
587         const uint32_t antiColor = 0x60600000;  // Semitransparent red.
588
589         // Pattern:  X o   o X o   o X
590         //             o X o   o X o
591         uint32_t* row1 = misspellBitmap->getAddr32(0, 0);
592         uint32_t* row2 = misspellBitmap->getAddr32(0, 1);
593         for (int x = 0; x < rowPixels; x++) {
594             switch (x % 4) {
595             case 0:
596                 row1[x] = lineColor;
597                 break;
598             case 1:
599                 row1[x] = antiColor;
600                 row2[x] = antiColor;
601                 break;
602             case 2:
603                 row2[x] = lineColor;
604                 break;
605             case 3:
606                 row1[x] = antiColor;
607                 row2[x] = antiColor;
608                 break;
609             }
610         }
611     }
612
613     // Offset it vertically by 1 so that there's some space under the text.
614     SkScalar originX = SkIntToScalar(pt.x());
615     SkScalar originY = SkIntToScalar(pt.y()) + 1;
616
617     // Make a shader for the bitmap with an origin of the box we'll draw. This
618     // shader is refcounted and will have an initial refcount of 1.
619     SkShader* shader = SkShader::CreateBitmapShader(
620         *misspellBitmap, SkShader::kRepeat_TileMode,
621         SkShader::kRepeat_TileMode);
622     SkMatrix matrix;
623     matrix.reset();
624     matrix.postTranslate(originX, originY);
625     shader->setLocalMatrix(matrix);
626
627     // Assign the shader to the paint & release our reference. The paint will
628     // now own the shader and the shader will be destroyed when the paint goes
629     // out of scope.
630     SkPaint paint;
631     paint.setShader(shader);
632     shader->unref();
633
634     SkRect rect;
635     rect.set(originX,
636              originY,
637              originX + SkIntToScalar(width),
638              originY + SkIntToScalar(misspellBitmap->height()));
639     platformContext()->canvas()->drawRect(rect, paint);
640 }
641
642 void GraphicsContext::drawLineForText(const IntPoint& pt,
643                                       int width,
644                                       bool printing)
645 {
646     if (paintingDisabled())
647         return;
648
649     if (width <= 0)
650         return;
651
652     int thickness = SkMax32(static_cast<int>(strokeThickness()), 1);
653     SkRect r;
654     r.fLeft = SkIntToScalar(pt.x());
655     r.fTop = SkIntToScalar(pt.y());
656     r.fRight = r.fLeft + SkIntToScalar(width);
657     r.fBottom = r.fTop + SkIntToScalar(thickness);
658
659     SkPaint paint;
660     platformContext()->setupPaintForFilling(&paint);
661     // Text lines are drawn using the stroke color.
662     paint.setColor(platformContext()->effectiveStrokeColor());
663     platformContext()->canvas()->drawRect(r, paint);
664 }
665
666 // Draws a filled rectangle with a stroked border.
667 void GraphicsContext::drawRect(const IntRect& rect)
668 {
669     if (paintingDisabled())
670         return;
671
672     SkRect r = rect;
673     if (!isRectSkiaSafe(getCTM(), r))
674         // See the fillRect below.
675         ClipRectToCanvas(*platformContext()->canvas(), r, &r);
676
677     platformContext()->drawRect(r);
678 }
679
680 void GraphicsContext::fillPath()
681 {
682     if (paintingDisabled())
683         return;
684
685     SkPath path = platformContext()->currentPathInLocalCoordinates();
686     if (!isPathSkiaSafe(getCTM(), path))
687       return;
688
689     const GraphicsContextState& state = m_common->state;
690     ColorSpace colorSpace = state.fillColorSpace;
691
692     if (colorSpace == SolidColorSpace && !fillColor().alpha())
693         return;
694
695     path.setFillType(state.fillRule == RULE_EVENODD ?
696         SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType);
697
698     SkPaint paint;
699     platformContext()->setupPaintForFilling(&paint);
700
701     if (colorSpace == PatternColorSpace) {
702         SkShader* pat = state.fillPattern->createPlatformPattern(getCTM());
703         paint.setShader(pat);
704         pat->unref();
705     } else if (colorSpace == GradientColorSpace)
706         paint.setShader(state.fillGradient->platformGradient());
707
708     platformContext()->canvas()->drawPath(path, paint);
709 }
710
711 void GraphicsContext::fillRect(const FloatRect& rect)
712 {
713     if (paintingDisabled())
714         return;
715
716     SkRect r = rect;
717     if (!isRectSkiaSafe(getCTM(), r))
718         // See the other version of fillRect below.
719         ClipRectToCanvas(*platformContext()->canvas(), r, &r);
720
721     const GraphicsContextState& state = m_common->state;
722     ColorSpace colorSpace = state.fillColorSpace;
723
724     if (colorSpace == SolidColorSpace && !fillColor().alpha())
725         return;
726
727     SkPaint paint;
728     platformContext()->setupPaintForFilling(&paint);
729
730     if (colorSpace == PatternColorSpace) {
731         SkShader* pat = state.fillPattern->createPlatformPattern(getCTM());
732         paint.setShader(pat);
733         pat->unref();
734     } else if (colorSpace == GradientColorSpace)
735         paint.setShader(state.fillGradient->platformGradient());
736
737     platformContext()->canvas()->drawRect(r, paint);
738 }
739
740 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color)
741 {
742     if (paintingDisabled())
743         return;
744
745     if (!color.alpha())
746         return;
747
748     SkRect r = rect;
749     if (!isRectSkiaSafe(getCTM(), r)) {
750         // Special case when the rectangle overflows fixed point. This is a
751         // workaround to fix bug 1212844. When the input rectangle is very
752         // large, it can overflow Skia's internal fixed point rect. This
753         // should be fixable in Skia (since the output bitmap isn't that
754         // large), but until that is fixed, we try to handle it ourselves.
755         //
756         // We manually clip the rectangle to the current clip rect. This
757         // will prevent overflow. The rectangle will be transformed to the
758         // canvas' coordinate space before it is converted to fixed point
759         // so we are guaranteed not to overflow after doing this.
760         ClipRectToCanvas(*platformContext()->canvas(), r, &r);
761     }
762
763     SkPaint paint;
764     platformContext()->setupPaintCommon(&paint);
765     paint.setColor(color.rgb());
766     platformContext()->canvas()->drawRect(r, paint);
767 }
768
769 void GraphicsContext::fillRoundedRect(const IntRect& rect,
770                                       const IntSize& topLeft,
771                                       const IntSize& topRight,
772                                       const IntSize& bottomLeft,
773                                       const IntSize& bottomRight,
774                                       const Color& color)
775 {
776     if (paintingDisabled())
777         return;
778
779     SkRect r = rect;
780     if (!isRectSkiaSafe(getCTM(), r))
781         // See fillRect().
782         ClipRectToCanvas(*platformContext()->canvas(), r, &r);
783
784     if (topLeft.width() + topRight.width() > rect.width()
785             || bottomLeft.width() + bottomRight.width() > rect.width()
786             || topLeft.height() + bottomLeft.height() > rect.height()
787             || topRight.height() + bottomRight.height() > rect.height()) {
788         // Not all the radii fit, return a rect. This matches the behavior of
789         // Path::createRoundedRectangle. Without this we attempt to draw a round
790         // shadow for a square box.
791         fillRect(rect, color);
792         return;
793     }
794
795     SkPath path;
796     addCornerArc(&path, r, topRight, 270);
797     addCornerArc(&path, r, bottomRight, 0);
798     addCornerArc(&path, r, bottomLeft, 90);
799     addCornerArc(&path, r, topLeft, 180);
800
801     SkPaint paint;
802     platformContext()->setupPaintForFilling(&paint);
803     platformContext()->canvas()->drawPath(path, paint);
804 }
805
806 TransformationMatrix GraphicsContext::getCTM() const
807 {
808     const SkMatrix& m = platformContext()->canvas()->getTotalMatrix();
809     return TransformationMatrix(SkScalarToDouble(m.getScaleX()),      // a
810                                 SkScalarToDouble(m.getSkewY()),       // b
811                                 SkScalarToDouble(m.getSkewX()),       // c
812                                 SkScalarToDouble(m.getScaleY()),      // d
813                                 SkScalarToDouble(m.getTranslateX()),  // e
814                                 SkScalarToDouble(m.getTranslateY())); // f
815 }
816
817 FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect)
818 {
819     // This logic is copied from GraphicsContextCG, eseidel 5/05/08
820
821     // It is not enough just to round to pixels in device space. The rotation
822     // part of the affine transform matrix to device space can mess with this
823     // conversion if we have a rotating image like the hands of the world clock
824     // widget. We just need the scale, so we get the affine transform matrix and
825     // extract the scale.
826
827     const SkMatrix& deviceMatrix = platformContext()->canvas()->getTotalMatrix();
828     if (deviceMatrix.isIdentity())
829         return rect;
830
831     float deviceScaleX = sqrtf(square(deviceMatrix.getScaleX())
832         + square(deviceMatrix.getSkewY()));
833     float deviceScaleY = sqrtf(square(deviceMatrix.getSkewX())
834         + square(deviceMatrix.getScaleY()));
835
836     FloatPoint deviceOrigin(rect.x() * deviceScaleX, rect.y() * deviceScaleY);
837     FloatPoint deviceLowerRight((rect.x() + rect.width()) * deviceScaleX,
838         (rect.y() + rect.height()) * deviceScaleY);
839
840     deviceOrigin.setX(roundf(deviceOrigin.x()));
841     deviceOrigin.setY(roundf(deviceOrigin.y()));
842     deviceLowerRight.setX(roundf(deviceLowerRight.x()));
843     deviceLowerRight.setY(roundf(deviceLowerRight.y()));
844
845     // Don't let the height or width round to 0 unless either was originally 0
846     if (deviceOrigin.y() == deviceLowerRight.y() && rect.height() != 0)
847         deviceLowerRight.move(0, 1);
848     if (deviceOrigin.x() == deviceLowerRight.x() && rect.width() != 0)
849         deviceLowerRight.move(1, 0);
850
851     FloatPoint roundedOrigin(deviceOrigin.x() / deviceScaleX,
852         deviceOrigin.y() / deviceScaleY);
853     FloatPoint roundedLowerRight(deviceLowerRight.x() / deviceScaleX,
854         deviceLowerRight.y() / deviceScaleY);
855     return FloatRect(roundedOrigin, roundedLowerRight - roundedOrigin);
856 }
857
858 void GraphicsContext::scale(const FloatSize& size)
859 {
860     if (paintingDisabled())
861         return;
862     platformContext()->canvas()->scale(WebCoreFloatToSkScalar(size.width()),
863         WebCoreFloatToSkScalar(size.height()));
864 }
865
866 void GraphicsContext::setAlpha(float alpha)
867 {
868     if (paintingDisabled())
869         return;
870     platformContext()->setAlpha(alpha);
871 }
872
873 void GraphicsContext::setCompositeOperation(CompositeOperator op)
874 {
875     if (paintingDisabled())
876         return;
877     platformContext()->setPorterDuffMode(WebCoreCompositeToSkiaComposite(op));
878 }
879
880 void GraphicsContext::setImageInterpolationQuality(InterpolationQuality)
881 {
882     notImplemented();
883 }
884
885 void GraphicsContext::setLineCap(LineCap cap)
886 {
887     if (paintingDisabled())
888         return;
889     switch (cap) {
890     case ButtCap:
891         platformContext()->setLineCap(SkPaint::kButt_Cap);
892         break;
893     case RoundCap:
894         platformContext()->setLineCap(SkPaint::kRound_Cap);
895         break;
896     case SquareCap:
897         platformContext()->setLineCap(SkPaint::kSquare_Cap);
898         break;
899     default:
900         ASSERT(0);
901         break;
902     }
903 }
904
905 void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
906 {
907     if (paintingDisabled())
908         return;
909
910     // FIXME: This is lifted directly off SkiaSupport, lines 49-74
911     // so it is not guaranteed to work correctly.
912     size_t dashLength = dashes.size();
913     if (!dashLength)
914         return;
915
916     size_t count = (dashLength % 2) == 0 ? dashLength : dashLength * 2;
917     SkScalar* intervals = new SkScalar[count];
918
919     for (unsigned int i = 0; i < count; i++)
920         intervals[i] = dashes[i % dashLength];
921
922     platformContext()->setDashPathEffect(new SkDashPathEffect(intervals, count, dashOffset));
923
924     delete[] intervals;
925 }
926
927 void GraphicsContext::setLineJoin(LineJoin join)
928 {
929     if (paintingDisabled())
930         return;
931     switch (join) {
932     case MiterJoin:
933         platformContext()->setLineJoin(SkPaint::kMiter_Join);
934         break;
935     case RoundJoin:
936         platformContext()->setLineJoin(SkPaint::kRound_Join);
937         break;
938     case BevelJoin:
939         platformContext()->setLineJoin(SkPaint::kBevel_Join);
940         break;
941     default:
942         ASSERT(0);
943         break;
944     }
945 }
946
947 void GraphicsContext::setMiterLimit(float limit)
948 {
949     if (paintingDisabled())
950         return;
951     platformContext()->setMiterLimit(limit);
952 }
953
954 void GraphicsContext::setPlatformFillColor(const Color& color)
955 {
956     if (paintingDisabled())
957         return;
958     platformContext()->setFillColor(color.rgb());
959 }
960
961 void GraphicsContext::setPlatformShadow(const IntSize& size,
962                                         int blurInt,
963                                         const Color& color)
964 {
965     if (paintingDisabled())
966         return;
967
968     double width = size.width();
969     double height = size.height();
970     double blur = blurInt;
971
972     // TODO(tc): This still does not address the issue that shadows
973     // within canvas elements should ignore transforms.
974     if (m_common->state.shadowsIgnoreTransforms)  {
975         // Currently only the GraphicsContext associated with the
976         // CanvasRenderingContext for HTMLCanvasElement have shadows ignore
977         // Transforms. So with this flag set, we know this state is associated
978         // with a CanvasRenderingContext.
979         // CG uses natural orientation for Y axis, but the HTML5 canvas spec
980         // does not.
981         // So we now flip the height since it was flipped in
982         // CanvasRenderingContext in order to work with CG.
983         height = -height;
984     }
985
986     SkColor c;
987     if (color.isValid())
988         c = color.rgb();
989     else
990         c = SkColorSetARGB(0xFF/3, 0, 0, 0);    // "std" apple shadow color.
991
992     // TODO(tc): Should we have a max value for the blur?  CG clamps at 1000.0
993     // for perf reasons.
994     SkDrawLooper* dl = new SkBlurDrawLooper(blur / 2, width, height, c);
995     platformContext()->setDrawLooper(dl);
996     dl->unref();
997 }
998
999 void GraphicsContext::setPlatformStrokeColor(const Color& strokecolor)
1000 {
1001     if (paintingDisabled())
1002         return;
1003
1004     platformContext()->setStrokeColor(strokecolor.rgb());
1005 }
1006
1007 void GraphicsContext::setPlatformStrokeStyle(const StrokeStyle& stroke)
1008 {
1009     if (paintingDisabled())
1010         return;
1011
1012     platformContext()->setStrokeStyle(stroke);
1013 }
1014
1015 void GraphicsContext::setPlatformStrokeThickness(float thickness)
1016 {
1017     if (paintingDisabled())
1018         return;
1019
1020     platformContext()->setStrokeThickness(thickness);
1021 }
1022
1023 void GraphicsContext::setPlatformTextDrawingMode(int mode)
1024 {
1025     if (paintingDisabled())
1026         return;
1027
1028     platformContext()->setTextDrawingMode(mode);
1029 }
1030
1031 void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
1032 {
1033 }
1034
1035 void GraphicsContext::setPlatformShouldAntialias(bool enable)
1036 {
1037     if (paintingDisabled())
1038         return;
1039
1040     platformContext()->setUseAntialiasing(enable);
1041 }
1042
1043 void GraphicsContext::strokeArc(const IntRect& r, int startAngle, int angleSpan)
1044 {
1045     if (paintingDisabled())
1046         return;
1047
1048     SkPaint paint;
1049     SkRect oval = r;
1050     if (strokeStyle() == NoStroke) {
1051         // Stroke using the fill color.
1052         // TODO(brettw) is this really correct? It seems unreasonable.
1053         platformContext()->setupPaintForFilling(&paint);
1054         paint.setStyle(SkPaint::kStroke_Style);
1055         paint.setStrokeWidth(WebCoreFloatToSkScalar(strokeThickness()));
1056     } else
1057         platformContext()->setupPaintForStroking(&paint, 0, 0);
1058
1059     // We do this before converting to scalar, so we don't overflow SkFixed.
1060     startAngle = fastMod(startAngle, 360);
1061     angleSpan = fastMod(angleSpan, 360);
1062
1063     SkPath path;
1064     path.addArc(oval, SkIntToScalar(-startAngle), SkIntToScalar(-angleSpan));
1065     if (!isPathSkiaSafe(getCTM(), path))
1066         return;
1067     platformContext()->canvas()->drawPath(path, paint);
1068 }
1069
1070 void GraphicsContext::strokePath()
1071 {
1072     if (paintingDisabled())
1073         return;
1074
1075     SkPath path = platformContext()->currentPathInLocalCoordinates();
1076     if (!isPathSkiaSafe(getCTM(), path))
1077         return;
1078
1079     const GraphicsContextState& state = m_common->state;
1080     ColorSpace colorSpace = state.strokeColorSpace;
1081
1082     if (colorSpace == SolidColorSpace && !strokeColor().alpha())
1083         return;
1084
1085     SkPaint paint;
1086     platformContext()->setupPaintForStroking(&paint, 0, 0);
1087
1088     if (colorSpace == PatternColorSpace) {
1089         SkShader* pat = state.strokePattern->createPlatformPattern(getCTM());
1090         paint.setShader(pat);
1091         pat->unref();
1092     } else if (colorSpace == GradientColorSpace)
1093         paint.setShader(state.strokeGradient->platformGradient());
1094
1095     platformContext()->canvas()->drawPath(path, paint);
1096 }
1097
1098 void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
1099 {
1100     if (paintingDisabled())
1101         return;
1102
1103     if (!isRectSkiaSafe(getCTM(), rect))
1104         return;
1105
1106     const GraphicsContextState& state = m_common->state;
1107     ColorSpace colorSpace = state.strokeColorSpace;
1108
1109     if (colorSpace == SolidColorSpace && !strokeColor().alpha())
1110         return;
1111
1112     SkPaint paint;
1113     platformContext()->setupPaintForStroking(&paint, 0, 0);
1114     paint.setStrokeWidth(WebCoreFloatToSkScalar(lineWidth));
1115
1116     if (colorSpace == PatternColorSpace) {
1117         SkShader* pat = state.strokePattern->createPlatformPattern(getCTM());
1118         paint.setShader(pat);
1119         pat->unref();
1120     } else if (colorSpace == GradientColorSpace)
1121         paint.setShader(state.strokeGradient->platformGradient());
1122
1123     platformContext()->canvas()->drawRect(rect, paint);
1124 }
1125
1126 void GraphicsContext::rotate(float angleInRadians)
1127 {
1128     if (paintingDisabled())
1129         return;
1130
1131     platformContext()->canvas()->rotate(WebCoreFloatToSkScalar(
1132         angleInRadians * (180.0f / 3.14159265f)));
1133 }
1134
1135 void GraphicsContext::translate(float w, float h)
1136 {
1137     if (paintingDisabled())
1138         return;
1139
1140     platformContext()->canvas()->translate(WebCoreFloatToSkScalar(w),
1141                                            WebCoreFloatToSkScalar(h));
1142 }
1143
1144 }  // namespace WebCore