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