<rdar://problem/9018212> Underline thickness is not uniform under non-integral scale...
[WebKit-https.git] / Source / 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 "AffineTransform.h"
35 #include "Color.h"
36 #include "FloatRect.h"
37 #include "GLES2Canvas.h"
38 #include "Gradient.h"
39 #include "GraphicsContextPlatformPrivate.h"
40 #include "ImageBuffer.h"
41 #include "IntRect.h"
42 #include "NativeImageSkia.h"
43 #include "NotImplemented.h"
44 #include "PlatformContextSkia.h"
45
46 #include "SkBitmap.h"
47 #include "SkBlurDrawLooper.h"
48 #include "SkCornerPathEffect.h"
49 #include "SkShader.h"
50 #include "SkiaUtils.h"
51 #include "skia/ext/platform_canvas.h"
52
53 #include <math.h>
54 #include <wtf/Assertions.h>
55 #include <wtf/MathExtras.h>
56 #include <wtf/UnusedParam.h>
57
58 using namespace std;
59
60 namespace WebCore {
61
62 namespace {
63
64 inline int fastMod(int value, int max)
65 {
66     int sign = SkExtractSign(value);
67
68     value = SkApplySign(value, sign);
69     if (value >= max)
70         value %= max;
71     return SkApplySign(value, sign);
72 }
73
74 inline float square(float n)
75 {
76     return n * n;
77 }
78
79 }  // namespace
80
81 // "Seatbelt" functions ------------------------------------------------------
82 //
83 // These functions check certain graphics primitives for being "safe".
84 // Skia has historically crashed when sent crazy data. These functions do
85 // additional checking to prevent crashes.
86 //
87 // Ideally, all of these would be fixed in the graphics layer and we would not
88 // have to do any checking. You can uncomment the ENSURE_VALUE_SAFETY_FOR_SKIA
89 // flag to check the graphics layer.
90
91 // Disabling these checks (20/01/2010), since we think we've fixed all the Skia
92 // bugs.  Leaving the code in for now, so we can revert easily if necessary.
93 // #define ENSURE_VALUE_SAFETY_FOR_SKIA
94
95 #ifdef ENSURE_VALUE_SAFETY_FOR_SKIA
96 static bool isCoordinateSkiaSafe(float coord)
97 {
98     // First check for valid floats.
99 #if defined(_MSC_VER)
100     if (!_finite(coord))
101 #else
102     if (!finite(coord))
103 #endif
104         return false;
105
106     // Skia uses 16.16 fixed point and 26.6 fixed point in various places. If
107     // the transformed point exceeds 15 bits, we just declare that it's
108     // unreasonable to catch both of these cases.
109     static const int maxPointMagnitude = 32767;
110     if (coord > maxPointMagnitude || coord < -maxPointMagnitude)
111         return false;
112
113     return true;
114 }
115 #endif
116
117 static bool isPointSkiaSafe(const SkMatrix& transform, const SkPoint& pt)
118 {
119 #ifdef ENSURE_VALUE_SAFETY_FOR_SKIA
120     // Now check for points that will overflow. We check the *transformed*
121     // points since this is what will be rasterized.
122     SkPoint xPt;
123     transform.mapPoints(&xPt, &pt, 1);
124     return isCoordinateSkiaSafe(xPt.fX) && isCoordinateSkiaSafe(xPt.fY);
125 #else
126     return true;
127 #endif
128 }
129
130 static bool isRectSkiaSafe(const SkMatrix& transform, const SkRect& rc)
131 {
132 #ifdef ENSURE_VALUE_SAFETY_FOR_SKIA
133     SkPoint topleft = {rc.fLeft, rc.fTop};
134     SkPoint bottomright = {rc.fRight, rc.fBottom};
135     return isPointSkiaSafe(transform, topleft) && isPointSkiaSafe(transform, bottomright);
136 #else
137     return true;
138 #endif
139 }
140
141 bool isPathSkiaSafe(const SkMatrix& transform, const SkPath& path)
142 {
143 #ifdef ENSURE_VALUE_SAFETY_FOR_SKIA
144     SkPoint current_points[4];
145     SkPath::Iter iter(path, false);
146     for (SkPath::Verb verb = iter.next(current_points);
147          verb != SkPath::kDone_Verb;
148          verb = iter.next(current_points)) {
149         switch (verb) {
150         case SkPath::kMove_Verb:
151             // This move will be duplicated in the next verb, so we can ignore.
152             break;
153         case SkPath::kLine_Verb:
154             // iter.next returns 2 points.
155             if (!isPointSkiaSafe(transform, current_points[0])
156                 || !isPointSkiaSafe(transform, current_points[1]))
157                 return false;
158             break;
159         case SkPath::kQuad_Verb:
160             // iter.next returns 3 points.
161             if (!isPointSkiaSafe(transform, current_points[0])
162                 || !isPointSkiaSafe(transform, current_points[1])
163                 || !isPointSkiaSafe(transform, current_points[2]))
164                 return false;
165             break;
166         case SkPath::kCubic_Verb:
167             // iter.next returns 4 points.
168             if (!isPointSkiaSafe(transform, current_points[0])
169                 || !isPointSkiaSafe(transform, current_points[1])
170                 || !isPointSkiaSafe(transform, current_points[2])
171                 || !isPointSkiaSafe(transform, current_points[3]))
172                 return false;
173             break;
174         case SkPath::kClose_Verb:
175         case SkPath::kDone_Verb:
176         default:
177             break;
178         }
179     }
180     return true;
181 #else
182     return true;
183 #endif
184 }
185
186 // Local helper functions ------------------------------------------------------
187
188 void addCornerArc(SkPath* path, const SkRect& rect, const IntSize& size, int startAngle)
189 {
190     SkIRect ir;
191     int rx = SkMin32(SkScalarRound(rect.width()), size.width());
192     int ry = SkMin32(SkScalarRound(rect.height()), size.height());
193
194     ir.set(-rx, -ry, rx, ry);
195     switch (startAngle) {
196     case 0:
197         ir.offset(rect.fRight - ir.fRight, rect.fBottom - ir.fBottom);
198         break;
199     case 90:
200         ir.offset(rect.fLeft - ir.fLeft, rect.fBottom - ir.fBottom);
201         break;
202     case 180:
203         ir.offset(rect.fLeft - ir.fLeft, rect.fTop - ir.fTop);
204         break;
205     case 270:
206         ir.offset(rect.fRight - ir.fRight, rect.fTop - ir.fTop);
207         break;
208     default:
209         ASSERT(0);
210     }
211
212     SkRect r;
213     r.set(ir);
214     path->arcTo(r, SkIntToScalar(startAngle), SkIntToScalar(90), false);
215 }
216
217 // -----------------------------------------------------------------------------
218
219 // This may be called with a NULL pointer to create a graphics context that has
220 // no painting.
221 void GraphicsContext::platformInit(PlatformGraphicsContext* gc)
222 {
223     m_data = new GraphicsContextPlatformPrivate(gc);
224     setPaintingDisabled(!gc || !platformContext()->canvas());
225 }
226
227 void GraphicsContext::platformDestroy()
228 {
229     delete m_data;
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     if (platformContext()->useGPU())
246         platformContext()->gpuCanvas()->save();
247
248     // Save our private State.
249     platformContext()->save();
250 }
251
252 void GraphicsContext::restorePlatformState()
253 {
254     if (paintingDisabled())
255         return;
256
257     if (platformContext()->useGPU())
258         platformContext()->gpuCanvas()->restore();
259
260     // Restore our private State.
261     platformContext()->restore();
262 }
263
264 void GraphicsContext::beginTransparencyLayer(float opacity)
265 {
266     if (paintingDisabled())
267         return;
268
269     // We need the "alpha" layer flag here because the base layer is opaque
270     // (the surface of the page) but layers on top may have transparent parts.
271     // Without explicitly setting the alpha flag, the layer will inherit the
272     // opaque setting of the base and some things won't work properly.
273     platformContext()->canvas()->saveLayerAlpha(
274         0,
275         static_cast<unsigned char>(opacity * 255),
276         static_cast<SkCanvas::SaveFlags>(SkCanvas::kHasAlphaLayer_SaveFlag |
277                                          SkCanvas::kFullColorLayer_SaveFlag));
278 }
279
280 void GraphicsContext::endTransparencyLayer()
281 {
282     if (paintingDisabled())
283         return;
284     platformContext()->canvas()->restore();
285 }
286
287 // Graphics primitives ---------------------------------------------------------
288
289 void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness)
290 {
291     if (paintingDisabled())
292         return;
293
294     SkRect r(rect);
295     if (!isRectSkiaSafe(getCTM(), r))
296         return;
297
298     platformContext()->prepareForSoftwareDraw();
299     SkPath path;
300     path.addOval(r, SkPath::kCW_Direction);
301     // only perform the inset if we won't invert r
302     if (2 * thickness < rect.width() && 2 * thickness < rect.height()) {
303         // Adding one to the thickness doesn't make the border too thick as
304         // it's painted over afterwards. But without this adjustment the
305         // border appears a little anemic after anti-aliasing.
306         r.inset(SkIntToScalar(thickness + 1), SkIntToScalar(thickness + 1));
307         path.addOval(r, SkPath::kCCW_Direction);
308     }
309     platformContext()->clipPathAntiAliased(path);
310 }
311
312 void GraphicsContext::clearPlatformShadow()
313 {
314     if (paintingDisabled())
315         return;
316     platformContext()->setDrawLooper(0);
317 }
318
319 void GraphicsContext::clearRect(const FloatRect& rect)
320 {
321     if (paintingDisabled())
322         return;
323
324     if (platformContext()->useGPU() && !platformContext()->canvasClipApplied()) {
325         platformContext()->prepareForHardwareDraw();
326         platformContext()->gpuCanvas()->clearRect(rect);
327         return;
328     }
329
330     // Force a readback here (if we're using the GPU), since clearRect() is
331     // incompatible with mixed-mode rendering.
332     platformContext()->syncSoftwareCanvas();
333
334     SkRect r = rect;
335     if (!isRectSkiaSafe(getCTM(), r))
336         ClipRectToCanvas(*platformContext()->canvas(), r, &r);
337
338     SkPaint paint;
339     platformContext()->setupPaintForFilling(&paint);
340     paint.setXfermodeMode(SkXfermode::kClear_Mode);
341     platformContext()->canvas()->drawRect(r, paint);
342 }
343
344 void GraphicsContext::clip(const FloatRect& rect)
345 {
346     if (paintingDisabled())
347         return;
348
349     SkRect r(rect);
350     if (!isRectSkiaSafe(getCTM(), r))
351         return;
352
353     platformContext()->prepareForSoftwareDraw();
354     platformContext()->canvas()->clipRect(r);
355 }
356
357 void GraphicsContext::clip(const Path& path)
358 {
359     if (paintingDisabled())
360         return;
361
362     const SkPath& p = *path.platformPath();
363     if (!isPathSkiaSafe(getCTM(), p))
364         return;
365
366     platformContext()->prepareForSoftwareDraw();
367     platformContext()->clipPathAntiAliased(p);
368 }
369
370 void GraphicsContext::canvasClip(const Path& path)
371 {
372     if (paintingDisabled())
373         return;
374
375     if (platformContext()->useGPU())
376         platformContext()->gpuCanvas()->clipPath(path);
377
378     const SkPath& p = *path.platformPath();
379     if (!isPathSkiaSafe(getCTM(), p))
380         return;
381
382     platformContext()->canvasClipPath(p);
383 }
384
385 void GraphicsContext::clipOut(const IntRect& rect)
386 {
387     if (paintingDisabled())
388         return;
389
390     SkRect r(rect);
391     if (!isRectSkiaSafe(getCTM(), r))
392         return;
393
394     platformContext()->canvas()->clipRect(r, SkRegion::kDifference_Op);
395 }
396
397 void GraphicsContext::clipOut(const Path& p)
398 {
399     if (paintingDisabled())
400         return;
401
402     if (platformContext()->useGPU())
403         platformContext()->gpuCanvas()->clipOut(p);
404
405     const SkPath& path = *p.platformPath();
406     if (!isPathSkiaSafe(getCTM(), path))
407         return;
408
409     platformContext()->canvas()->clipPath(path, SkRegion::kDifference_Op);
410 }
411
412 void GraphicsContext::clipPath(const Path& pathToClip, WindRule clipRule)
413 {
414     if (paintingDisabled())
415         return;
416
417     if (platformContext()->useGPU())
418         platformContext()->gpuCanvas()->clipPath(pathToClip);
419
420     SkPath path = *pathToClip.platformPath();
421     if (!isPathSkiaSafe(getCTM(), path))
422         return;
423
424     path.setFillType(clipRule == RULE_EVENODD ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType);
425     platformContext()->clipPathAntiAliased(path);
426 }
427
428 void GraphicsContext::concatCTM(const AffineTransform& affine)
429 {
430     if (paintingDisabled())
431         return;
432
433     if (platformContext()->useGPU())
434         platformContext()->gpuCanvas()->concatCTM(affine);
435
436     platformContext()->canvas()->concat(affine);
437 }
438
439 void GraphicsContext::setCTM(const AffineTransform& affine)
440 {
441     if (paintingDisabled())
442         return;
443
444     if (platformContext()->useGPU())
445         platformContext()->gpuCanvas()->setCTM(affine);
446
447     platformContext()->canvas()->setMatrix(affine);
448 }
449
450 void GraphicsContext::drawConvexPolygon(size_t numPoints,
451                                         const FloatPoint* points,
452                                         bool shouldAntialias)
453 {
454     if (paintingDisabled())
455         return;
456
457     if (numPoints <= 1)
458         return;
459
460     platformContext()->prepareForSoftwareDraw();
461
462     SkPath path;
463
464     path.incReserve(numPoints);
465     path.moveTo(WebCoreFloatToSkScalar(points[0].x()),
466                 WebCoreFloatToSkScalar(points[0].y()));
467     for (size_t i = 1; i < numPoints; i++) {
468         path.lineTo(WebCoreFloatToSkScalar(points[i].x()),
469                     WebCoreFloatToSkScalar(points[i].y()));
470     }
471
472     if (!isPathSkiaSafe(getCTM(), path))
473         return;
474
475     SkPaint paint;
476     platformContext()->setupPaintForFilling(&paint);
477     platformContext()->canvas()->drawPath(path, paint);
478
479     if (strokeStyle() != NoStroke) {
480         paint.reset();
481         platformContext()->setupPaintForStroking(&paint, 0, 0);
482         platformContext()->canvas()->drawPath(path, paint);
483     }
484 }
485
486 void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* points, bool antialiased)
487 {
488     if (paintingDisabled())
489         return;
490
491     if (numPoints <= 1)
492         return;
493
494     // FIXME: IMPLEMENT!!
495 }
496
497 // This method is only used to draw the little circles used in lists.
498 void GraphicsContext::drawEllipse(const IntRect& elipseRect)
499 {
500     if (paintingDisabled())
501         return;
502
503     SkRect rect = elipseRect;
504     if (!isRectSkiaSafe(getCTM(), rect))
505         return;
506
507     platformContext()->prepareForSoftwareDraw();
508     SkPaint paint;
509     platformContext()->setupPaintForFilling(&paint);
510     platformContext()->canvas()->drawOval(rect, paint);
511
512     if (strokeStyle() != NoStroke) {
513         paint.reset();
514         platformContext()->setupPaintForStroking(&paint, &rect, 0);
515         platformContext()->canvas()->drawOval(rect, paint);
516     }
517 }
518
519 void GraphicsContext::drawFocusRing(const Path& path, int width, int offset, const Color& color)
520 {
521     // FIXME: implement
522 }
523
524 void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int /* width */, int /* offset */, const Color& color)
525 {
526     if (paintingDisabled())
527         return;
528
529     unsigned rectCount = rects.size();
530     if (!rectCount)
531         return;
532
533     platformContext()->prepareForSoftwareDraw();
534     SkRegion focusRingRegion;
535     const SkScalar focusRingOutset = WebCoreFloatToSkScalar(0.5);
536     for (unsigned i = 0; i < rectCount; i++) {
537         SkIRect r = rects[i];
538         r.inset(-focusRingOutset, -focusRingOutset);
539         focusRingRegion.op(r, SkRegion::kUnion_Op);
540     }
541
542     SkPath path;
543     SkPaint paint;
544     paint.setAntiAlias(true);
545     paint.setStyle(SkPaint::kStroke_Style);
546
547     paint.setColor(color.rgb());
548     paint.setStrokeWidth(focusRingOutset * 2);
549     paint.setPathEffect(new SkCornerPathEffect(focusRingOutset * 2))->unref();
550     focusRingRegion.getBoundaryPath(&path);
551     platformContext()->canvas()->drawPath(path, paint);
552 }
553
554 // This is only used to draw borders.
555 void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
556 {
557     if (paintingDisabled())
558         return;
559
560     StrokeStyle penStyle = strokeStyle();
561     if (penStyle == NoStroke)
562         return;
563
564     SkPaint paint;
565     if (!isPointSkiaSafe(getCTM(), point1) || !isPointSkiaSafe(getCTM(), point2))
566         return;
567
568     platformContext()->prepareForSoftwareDraw();
569
570     FloatPoint p1 = point1;
571     FloatPoint p2 = point2;
572     bool isVerticalLine = (p1.x() == p2.x());
573     int width = roundf(strokeThickness());
574
575     // We know these are vertical or horizontal lines, so the length will just
576     // be the sum of the displacement component vectors give or take 1 -
577     // probably worth the speed up of no square root, which also won't be exact.
578     FloatSize disp = p2 - p1;
579     int length = SkScalarRound(disp.width() + disp.height());
580     platformContext()->setupPaintForStroking(&paint, 0, length);
581
582     if (strokeStyle() == DottedStroke || strokeStyle() == DashedStroke) {
583         // Do a rect fill of our endpoints.  This ensures we always have the
584         // appearance of being a border.  We then draw the actual dotted/dashed line.
585
586         SkRect r1, r2;
587         r1.set(p1.x(), p1.y(), p1.x() + width, p1.y() + width);
588         r2.set(p2.x(), p2.y(), p2.x() + width, p2.y() + width);
589
590         if (isVerticalLine) {
591             r1.offset(-width / 2, 0);
592             r2.offset(-width / 2, -width);
593         } else {
594             r1.offset(0, -width / 2);
595             r2.offset(-width, -width / 2);
596         }
597         SkPaint fillPaint;
598         fillPaint.setColor(paint.getColor());
599         platformContext()->canvas()->drawRect(r1, fillPaint);
600         platformContext()->canvas()->drawRect(r2, fillPaint);
601     }
602
603     adjustLineToPixelBoundaries(p1, p2, width, penStyle);
604     SkPoint pts[2] = { (SkPoint)p1, (SkPoint)p2 };
605
606     platformContext()->canvas()->drawPoints(SkCanvas::kLines_PointMode, 2, pts, paint);
607 }
608
609 void GraphicsContext::drawLineForTextChecking(const FloatPoint& pt, float width, TextCheckingLineStyle style)
610 {
611     if (paintingDisabled())
612         return;
613
614     platformContext()->prepareForSoftwareDraw();
615
616     // Create the pattern we'll use to draw the underline.
617     static SkBitmap* misspellBitmap = 0;
618     if (!misspellBitmap) {
619         // We use a 2-pixel-high misspelling indicator because that seems to be
620         // what WebKit is designed for, and how much room there is in a typical
621         // page for it.
622         const int rowPixels = 32;  // Must be multiple of 4 for pattern below.
623         const int colPixels = 2;
624         misspellBitmap = new SkBitmap;
625         misspellBitmap->setConfig(SkBitmap::kARGB_8888_Config,
626                                    rowPixels, colPixels);
627         misspellBitmap->allocPixels();
628
629         misspellBitmap->eraseARGB(0, 0, 0, 0);
630         const uint32_t lineColor = 0xFFFF0000;  // Opaque red.
631         const uint32_t antiColor = 0x60600000;  // Semitransparent red.
632
633         // Pattern:  X o   o X o   o X
634         //             o X o   o X o
635         uint32_t* row1 = misspellBitmap->getAddr32(0, 0);
636         uint32_t* row2 = misspellBitmap->getAddr32(0, 1);
637         for (int x = 0; x < rowPixels; x++) {
638             switch (x % 4) {
639             case 0:
640                 row1[x] = lineColor;
641                 break;
642             case 1:
643                 row1[x] = antiColor;
644                 row2[x] = antiColor;
645                 break;
646             case 2:
647                 row2[x] = lineColor;
648                 break;
649             case 3:
650                 row1[x] = antiColor;
651                 row2[x] = antiColor;
652                 break;
653             }
654         }
655     }
656
657     // Offset it vertically by 1 so that there's some space under the text.
658     SkScalar originX = WebCoreFloatToSkScalar(pt.x());
659     SkScalar originY = WebCoreFloatToSkScalar(pt.y()) + 1;
660
661     // Make a shader for the bitmap with an origin of the box we'll draw. This
662     // shader is refcounted and will have an initial refcount of 1.
663     SkShader* shader = SkShader::CreateBitmapShader(
664         *misspellBitmap, SkShader::kRepeat_TileMode,
665         SkShader::kRepeat_TileMode);
666     SkMatrix matrix;
667     matrix.reset();
668     matrix.postTranslate(originX, originY);
669     shader->setLocalMatrix(matrix);
670
671     // Assign the shader to the paint & release our reference. The paint will
672     // now own the shader and the shader will be destroyed when the paint goes
673     // out of scope.
674     SkPaint paint;
675     paint.setShader(shader);
676     shader->unref();
677
678     SkRect rect;
679     rect.set(originX,
680              originY,
681              originX + WebCoreFloatToSkScalar(width),
682              originY + SkIntToScalar(misspellBitmap->height()));
683     platformContext()->canvas()->drawRect(rect, paint);
684 }
685
686 void GraphicsContext::drawLineForText(const FloatPoint& pt,
687                                       float width,
688                                       bool printing)
689 {
690     if (paintingDisabled())
691         return;
692
693     if (width <= 0)
694         return;
695
696     platformContext()->prepareForSoftwareDraw();
697
698     int thickness = SkMax32(static_cast<int>(strokeThickness()), 1);
699     SkRect r;
700     r.fLeft = WebCoreFloatToSkScalar(pt.x());
701     r.fTop = WebCoreFloatToSkScalar(pt.y());
702     r.fRight = r.fLeft + WebCoreFloatToSkScalar(width);
703     r.fBottom = r.fTop + SkIntToScalar(thickness);
704
705     SkPaint paint;
706     platformContext()->setupPaintForFilling(&paint);
707     // Text lines are drawn using the stroke color.
708     paint.setColor(platformContext()->effectiveStrokeColor());
709     platformContext()->canvas()->drawRect(r, paint);
710 }
711
712 // Draws a filled rectangle with a stroked border.
713 void GraphicsContext::drawRect(const IntRect& rect)
714 {
715     if (paintingDisabled())
716         return;
717
718     platformContext()->prepareForSoftwareDraw();
719
720     SkRect r = rect;
721     if (!isRectSkiaSafe(getCTM(), r)) {
722         // See the fillRect below.
723         ClipRectToCanvas(*platformContext()->canvas(), r, &r);
724     }
725
726     platformContext()->drawRect(r);
727 }
728
729 void GraphicsContext::fillPath(const Path& pathToFill)
730 {
731     if (paintingDisabled())
732         return;
733
734     // FIXME: add support to GLES2Canvas for more than just solid fills.
735     if (platformContext()->useGPU() && platformContext()->canAccelerate()) {
736         platformContext()->prepareForHardwareDraw();
737         platformContext()->gpuCanvas()->fillPath(pathToFill);
738         return;
739     }
740
741     SkPath path = *pathToFill.platformPath();
742     if (!isPathSkiaSafe(getCTM(), path))
743       return;
744
745     platformContext()->prepareForSoftwareDraw();
746
747     const GraphicsContextState& state = m_state;
748     path.setFillType(state.fillRule == RULE_EVENODD ?
749         SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType);
750
751     SkPaint paint;
752     platformContext()->setupPaintForFilling(&paint);
753
754     platformContext()->canvas()->drawPath(path, paint);
755 }
756
757 void GraphicsContext::fillRect(const FloatRect& rect)
758 {
759     if (paintingDisabled())
760         return;
761
762     SkRect r = rect;
763     if (!isRectSkiaSafe(getCTM(), r)) {
764         // See the other version of fillRect below.
765         ClipRectToCanvas(*platformContext()->canvas(), r, &r);
766     }
767
768     if (platformContext()->useGPU() && platformContext()->canAccelerate()) {
769         platformContext()->prepareForHardwareDraw();
770         platformContext()->gpuCanvas()->fillRect(rect);
771         return;
772     }
773
774     platformContext()->save();
775
776     platformContext()->prepareForSoftwareDraw();
777
778     SkPaint paint;
779     platformContext()->setupPaintForFilling(&paint);
780     platformContext()->canvas()->drawRect(r, paint);
781
782     platformContext()->restore();
783 }
784
785 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace)
786 {
787     if (paintingDisabled())
788         return;
789
790     if (platformContext()->useGPU() && platformContext()->canAccelerate()) {
791         platformContext()->prepareForHardwareDraw();
792         platformContext()->gpuCanvas()->fillRect(rect, color, colorSpace);
793         return;
794     }
795
796     platformContext()->prepareForSoftwareDraw();
797
798     SkRect r = rect;
799     if (!isRectSkiaSafe(getCTM(), r)) {
800         // Special case when the rectangle overflows fixed point. This is a
801         // workaround to fix bug 1212844. When the input rectangle is very
802         // large, it can overflow Skia's internal fixed point rect. This
803         // should be fixable in Skia (since the output bitmap isn't that
804         // large), but until that is fixed, we try to handle it ourselves.
805         //
806         // We manually clip the rectangle to the current clip rect. This
807         // will prevent overflow. The rectangle will be transformed to the
808         // canvas' coordinate space before it is converted to fixed point
809         // so we are guaranteed not to overflow after doing this.
810         ClipRectToCanvas(*platformContext()->canvas(), r, &r);
811     }
812
813     SkPaint paint;
814     platformContext()->setupPaintCommon(&paint);
815     paint.setColor(color.rgb());
816     platformContext()->canvas()->drawRect(r, paint);
817 }
818
819 void GraphicsContext::fillRoundedRect(const IntRect& rect,
820                                       const IntSize& topLeft,
821                                       const IntSize& topRight,
822                                       const IntSize& bottomLeft,
823                                       const IntSize& bottomRight,
824                                       const Color& color,
825                                       ColorSpace colorSpace)
826 {
827     if (paintingDisabled())
828         return;
829
830     platformContext()->prepareForSoftwareDraw();
831
832     SkRect r = rect;
833     if (!isRectSkiaSafe(getCTM(), r))
834         // See fillRect().
835         ClipRectToCanvas(*platformContext()->canvas(), r, &r);
836
837     if (topLeft.width() + topRight.width() > rect.width()
838             || bottomLeft.width() + bottomRight.width() > rect.width()
839             || topLeft.height() + bottomLeft.height() > rect.height()
840             || topRight.height() + bottomRight.height() > rect.height()) {
841         // Not all the radii fit, return a rect. This matches the behavior of
842         // Path::createRoundedRectangle. Without this we attempt to draw a round
843         // shadow for a square box.
844         fillRect(rect, color, colorSpace);
845         return;
846     }
847
848     SkPath path;
849     addCornerArc(&path, r, topRight, 270);
850     addCornerArc(&path, r, bottomRight, 0);
851     addCornerArc(&path, r, bottomLeft, 90);
852     addCornerArc(&path, r, topLeft, 180);
853
854     SkPaint paint;
855     platformContext()->setupPaintForFilling(&paint);
856     paint.setColor(color.rgb());
857     platformContext()->canvas()->drawPath(path, paint);
858 }
859
860 AffineTransform GraphicsContext::getCTM() const
861 {
862     const SkMatrix& m = platformContext()->canvas()->getTotalMatrix();
863     return AffineTransform(SkScalarToDouble(m.getScaleX()),
864                            SkScalarToDouble(m.getSkewY()),
865                            SkScalarToDouble(m.getSkewX()),
866                            SkScalarToDouble(m.getScaleY()),
867                            SkScalarToDouble(m.getTranslateX()),
868                            SkScalarToDouble(m.getTranslateY()));
869 }
870
871 FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect, RoundingMode)
872 {
873     return rect;
874 }
875
876 void GraphicsContext::scale(const FloatSize& size)
877 {
878     if (paintingDisabled())
879         return;
880
881     if (platformContext()->useGPU())
882         platformContext()->gpuCanvas()->scale(size);
883
884     platformContext()->canvas()->scale(WebCoreFloatToSkScalar(size.width()),
885         WebCoreFloatToSkScalar(size.height()));
886 }
887
888 void GraphicsContext::setAlpha(float alpha)
889 {
890     if (paintingDisabled())
891         return;
892
893     if (platformContext()->useGPU())
894         platformContext()->gpuCanvas()->setAlpha(alpha);
895
896     platformContext()->setAlpha(alpha);
897 }
898
899 void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op)
900 {
901     if (paintingDisabled())
902         return;
903
904     if (platformContext()->useGPU())
905         platformContext()->gpuCanvas()->setCompositeOperation(op);
906
907     platformContext()->setXfermodeMode(WebCoreCompositeToSkiaComposite(op));
908 }
909
910 InterpolationQuality GraphicsContext::imageInterpolationQuality() const
911 {
912     return platformContext()->interpolationQuality();
913 }
914
915 void GraphicsContext::setImageInterpolationQuality(InterpolationQuality q)
916 {
917     platformContext()->setInterpolationQuality(q);
918 }
919
920 void GraphicsContext::setLineCap(LineCap cap)
921 {
922     if (paintingDisabled())
923         return;
924     switch (cap) {
925     case ButtCap:
926         platformContext()->setLineCap(SkPaint::kButt_Cap);
927         break;
928     case RoundCap:
929         platformContext()->setLineCap(SkPaint::kRound_Cap);
930         break;
931     case SquareCap:
932         platformContext()->setLineCap(SkPaint::kSquare_Cap);
933         break;
934     default:
935         ASSERT(0);
936         break;
937     }
938 }
939
940 void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
941 {
942     if (paintingDisabled())
943         return;
944
945     // FIXME: This is lifted directly off SkiaSupport, lines 49-74
946     // so it is not guaranteed to work correctly.
947     size_t dashLength = dashes.size();
948     if (!dashLength) {
949         // If no dash is set, revert to solid stroke
950         // FIXME: do we need to set NoStroke in some cases?
951         platformContext()->setStrokeStyle(SolidStroke);
952         platformContext()->setDashPathEffect(0);
953         return;
954     }
955
956     size_t count = !(dashLength % 2) ? dashLength : dashLength * 2;
957     SkScalar* intervals = new SkScalar[count];
958
959     for (unsigned int i = 0; i < count; i++)
960         intervals[i] = dashes[i % dashLength];
961
962     platformContext()->setDashPathEffect(new SkDashPathEffect(intervals, count, dashOffset));
963
964     delete[] intervals;
965 }
966
967 void GraphicsContext::setLineJoin(LineJoin join)
968 {
969     if (paintingDisabled())
970         return;
971     switch (join) {
972     case MiterJoin:
973         platformContext()->setLineJoin(SkPaint::kMiter_Join);
974         break;
975     case RoundJoin:
976         platformContext()->setLineJoin(SkPaint::kRound_Join);
977         break;
978     case BevelJoin:
979         platformContext()->setLineJoin(SkPaint::kBevel_Join);
980         break;
981     default:
982         ASSERT(0);
983         break;
984     }
985 }
986
987 void GraphicsContext::setMiterLimit(float limit)
988 {
989     if (paintingDisabled())
990         return;
991     platformContext()->setMiterLimit(limit);
992 }
993
994 void GraphicsContext::setPlatformFillColor(const Color& color, ColorSpace colorSpace)
995 {
996     if (paintingDisabled())
997         return;
998
999     if (platformContext()->useGPU())
1000         platformContext()->gpuCanvas()->setFillColor(color, colorSpace);
1001
1002     platformContext()->setFillColor(color.rgb());
1003 }
1004
1005 void GraphicsContext::setPlatformFillGradient(Gradient* gradient)
1006 {
1007     if (paintingDisabled())
1008         return;
1009
1010     platformContext()->setFillShader(gradient->platformGradient());
1011 }
1012
1013 void GraphicsContext::setPlatformFillPattern(Pattern* pattern)
1014 {
1015     if (paintingDisabled())
1016         return;
1017
1018     platformContext()->setFillShader(pattern->platformPattern(getCTM()));
1019 }
1020
1021 void GraphicsContext::setPlatformShadow(const FloatSize& size,
1022                                         float blurFloat,
1023                                         const Color& color,
1024                                         ColorSpace colorSpace)
1025 {
1026     if (paintingDisabled())
1027         return;
1028
1029     if (platformContext()->useGPU()) {
1030         GLES2Canvas* canvas = platformContext()->gpuCanvas();
1031         canvas->setShadowOffset(size);
1032         canvas->setShadowBlur(blurFloat);
1033         canvas->setShadowColor(color, colorSpace);
1034         canvas->setShadowsIgnoreTransforms(m_state.shadowsIgnoreTransforms);
1035     }
1036
1037     // Detect when there's no effective shadow and clear the looper.
1038     if (!size.width() && !size.height() && !blurFloat) {
1039         platformContext()->setDrawLooper(0);
1040         return;
1041     }
1042
1043     double width = size.width();
1044     double height = size.height();
1045     double blur = blurFloat;
1046
1047     uint32_t blurFlags = SkBlurDrawLooper::kHighQuality_BlurFlag | 
1048         SkBlurDrawLooper::kOverrideColor_BlurFlag;
1049
1050     if (m_state.shadowsIgnoreTransforms)  {
1051         // Currently only the GraphicsContext associated with the
1052         // CanvasRenderingContext for HTMLCanvasElement have shadows ignore
1053         // Transforms. So with this flag set, we know this state is associated
1054         // with a CanvasRenderingContext.
1055         blurFlags |= SkBlurDrawLooper::kIgnoreTransform_BlurFlag;
1056         
1057         // CG uses natural orientation for Y axis, but the HTML5 canvas spec
1058         // does not.
1059         // So we now flip the height since it was flipped in
1060         // CanvasRenderingContext in order to work with CG.
1061         height = -height;
1062     }
1063
1064     SkColor c;
1065     if (color.isValid())
1066         c = color.rgb();
1067     else
1068         c = SkColorSetARGB(0xFF/3, 0, 0, 0);    // "std" apple shadow color.
1069
1070     // TODO(tc): Should we have a max value for the blur?  CG clamps at 1000.0
1071     // for perf reasons.
1072     SkDrawLooper* dl = new SkBlurDrawLooper(blur / 2, width, height, c, blurFlags);
1073     platformContext()->setDrawLooper(dl);
1074     dl->unref();
1075 }
1076
1077 void GraphicsContext::setPlatformStrokeColor(const Color& strokecolor, ColorSpace colorSpace)
1078 {
1079     if (paintingDisabled())
1080         return;
1081
1082     platformContext()->setStrokeColor(strokecolor.rgb());
1083 }
1084
1085 void GraphicsContext::setPlatformStrokeStyle(StrokeStyle stroke)
1086 {
1087     if (paintingDisabled())
1088         return;
1089
1090     platformContext()->setStrokeStyle(stroke);
1091 }
1092
1093 void GraphicsContext::setPlatformStrokeThickness(float thickness)
1094 {
1095     if (paintingDisabled())
1096         return;
1097
1098     platformContext()->setStrokeThickness(thickness);
1099 }
1100
1101 void GraphicsContext::setPlatformStrokeGradient(Gradient* gradient)
1102 {
1103     if (paintingDisabled())
1104         return;
1105
1106     platformContext()->setStrokeShader(gradient->platformGradient());
1107 }
1108
1109 void GraphicsContext::setPlatformStrokePattern(Pattern* pattern)
1110 {
1111     if (paintingDisabled())
1112         return;
1113
1114     platformContext()->setStrokeShader(pattern->platformPattern(getCTM()));
1115 }
1116
1117 void GraphicsContext::setPlatformTextDrawingMode(TextDrawingModeFlags mode)
1118 {
1119     if (paintingDisabled())
1120         return;
1121
1122     platformContext()->setTextDrawingMode(mode);
1123 }
1124
1125 void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
1126 {
1127 }
1128
1129 void GraphicsContext::setPlatformShouldAntialias(bool enable)
1130 {
1131     if (paintingDisabled())
1132         return;
1133
1134     platformContext()->setUseAntialiasing(enable);
1135 }
1136
1137 void GraphicsContext::strokeArc(const IntRect& r, int startAngle, int angleSpan)
1138 {
1139     if (paintingDisabled())
1140         return;
1141
1142     platformContext()->prepareForSoftwareDraw();
1143
1144     SkPaint paint;
1145     SkRect oval = r;
1146     if (strokeStyle() == NoStroke) {
1147         // Stroke using the fill color.
1148         // TODO(brettw) is this really correct? It seems unreasonable.
1149         platformContext()->setupPaintForFilling(&paint);
1150         paint.setStyle(SkPaint::kStroke_Style);
1151         paint.setStrokeWidth(WebCoreFloatToSkScalar(strokeThickness()));
1152     } else
1153         platformContext()->setupPaintForStroking(&paint, 0, 0);
1154
1155     // We do this before converting to scalar, so we don't overflow SkFixed.
1156     startAngle = fastMod(startAngle, 360);
1157     angleSpan = fastMod(angleSpan, 360);
1158
1159     SkPath path;
1160     path.addArc(oval, SkIntToScalar(-startAngle), SkIntToScalar(-angleSpan));
1161     if (!isPathSkiaSafe(getCTM(), path))
1162         return;
1163     platformContext()->canvas()->drawPath(path, paint);
1164 }
1165
1166 void GraphicsContext::strokePath(const Path& pathToStroke)
1167 {
1168     if (paintingDisabled())
1169         return;
1170
1171     SkPath path = *pathToStroke.platformPath();
1172     if (!isPathSkiaSafe(getCTM(), path))
1173         return;
1174
1175     platformContext()->prepareForSoftwareDraw();
1176
1177     SkPaint paint;
1178     platformContext()->setupPaintForStroking(&paint, 0, 0);
1179     platformContext()->canvas()->drawPath(path, paint);
1180 }
1181
1182 void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
1183 {
1184     if (paintingDisabled())
1185         return;
1186
1187     if (!isRectSkiaSafe(getCTM(), rect))
1188         return;
1189
1190     platformContext()->prepareForSoftwareDraw();
1191
1192     SkPaint paint;
1193     platformContext()->setupPaintForStroking(&paint, 0, 0);
1194     paint.setStrokeWidth(WebCoreFloatToSkScalar(lineWidth));
1195     platformContext()->canvas()->drawRect(rect, paint);
1196 }
1197
1198 void GraphicsContext::rotate(float angleInRadians)
1199 {
1200     if (paintingDisabled())
1201         return;
1202
1203     if (platformContext()->useGPU())
1204         platformContext()->gpuCanvas()->rotate(angleInRadians);
1205
1206     platformContext()->canvas()->rotate(WebCoreFloatToSkScalar(
1207         angleInRadians * (180.0f / 3.14159265f)));
1208 }
1209
1210 void GraphicsContext::translate(float w, float h)
1211 {
1212     if (paintingDisabled())
1213         return;
1214
1215     if (platformContext()->useGPU())
1216         platformContext()->gpuCanvas()->translate(w, h);
1217
1218     platformContext()->canvas()->translate(WebCoreFloatToSkScalar(w),
1219                                            WebCoreFloatToSkScalar(h));
1220 }
1221
1222 void GraphicsContext::syncSoftwareCanvas()
1223 {
1224     platformContext()->syncSoftwareCanvas();
1225 }
1226
1227 void GraphicsContext::setSharedGraphicsContext3D(SharedGraphicsContext3D* context, DrawingBuffer* framebuffer, const IntSize& size)
1228 {
1229     platformContext()->setSharedGraphicsContext3D(context, framebuffer, size);
1230 }
1231
1232 void GraphicsContext::markDirtyRect(const IntRect& rect)
1233 {
1234     platformContext()->markDirtyRect(rect);
1235 }
1236
1237 }  // namespace WebCore