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