Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(383)

Side by Side Diff: third_party/WebKit/Source/modules/canvas2d/BaseRenderingContext2D.cpp

Issue 1710633002: Pull up a subset of CanvasRenderingContext2D into BaseRenderingContext2D. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fixup. Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "modules/canvas2d/BaseRenderingContext2D.h"
6
7 #include "core/css/parser/CSSParser.h"
8 #include "core/frame/ImageBitmap.h"
9 #include "core/html/HTMLCanvasElement.h"
10 #include "core/html/HTMLImageElement.h"
11 #include "core/html/HTMLVideoElement.h"
12 #include "modules/canvas2d/CanvasGradient.h"
13 #include "modules/canvas2d/CanvasPattern.h"
14 #include "modules/canvas2d/CanvasStyle.h"
15 #include "modules/canvas2d/Path2D.h"
16 #include "platform/geometry/FloatQuad.h"
17 #include "platform/graphics/Color.h"
18 #include "platform/graphics/ExpensiveCanvasHeuristicParameters.h"
19 #include "platform/graphics/Image.h"
20 #include "platform/graphics/ImageBuffer.h"
21 #include "platform/graphics/StrokeData.h"
22 #include "platform/graphics/skia/SkiaUtils.h"
23 #include "third_party/skia/include/core/SkImageFilter.h"
24
25 namespace blink {
26
27 static const double cDeviceScaleFactor = 1.0; // Canvas is device independent
28
29 BaseRenderingContext2D::BaseRenderingContext2D()
30 : m_clipAntialiasing(NotAntiAliased)
31 {
32 m_stateStack.append(CanvasRenderingContext2DState::create());
33 }
34
35 BaseRenderingContext2D::~BaseRenderingContext2D()
36 {
37 }
38
39 CanvasRenderingContext2DState& BaseRenderingContext2D::modifiableState()
40 {
41 realizeSaves();
42 return *m_stateStack.last();
43 }
44
45 void BaseRenderingContext2D::realizeSaves()
46 {
47 validateStateStack();
48 if (state().hasUnrealizedSaves()) {
49 ASSERT(m_stateStack.size() >= 1);
50 // Reduce the current state's unrealized count by one now,
51 // to reflect the fact we are saving one state.
52 m_stateStack.last()->restore();
53 m_stateStack.append(CanvasRenderingContext2DState::create(state(), Canva sRenderingContext2DState::DontCopyClipList));
54 // Set the new state's unrealized count to 0, because it has no outstand ing saves.
55 // We need to do this explicitly because the copy constructor and operat or= used
56 // by the Vector operations copy the unrealized count from the previous state (in
57 // turn necessary to support correct resizing and unwinding of the stack ).
58 m_stateStack.last()->resetUnrealizedSaveCount();
59 SkCanvas* canvas = drawingCanvas();
60 if (canvas)
61 canvas->save();
62 validateStateStack();
63 }
64 }
65
66 void BaseRenderingContext2D::save()
67 {
68 m_stateStack.last()->save();
69 }
70
71 void BaseRenderingContext2D::restore()
72 {
73 validateStateStack();
74 if (state().hasUnrealizedSaves()) {
75 // We never realized the save, so just record that it was unnecessary.
76 m_stateStack.last()->restore();
77 return;
78 }
79 ASSERT(m_stateStack.size() >= 1);
80 if (m_stateStack.size() <= 1)
81 return;
82 m_path.transform(state().transform());
83 m_stateStack.removeLast();
84 m_stateStack.last()->clearResolvedFilter();
85 m_path.transform(state().transform().inverse());
86 SkCanvas* c = drawingCanvas();
87 if (c)
88 c->restore();
89
90 validateStateStack();
91 }
92
93 static inline void convertCanvasStyleToUnionType(CanvasStyle* style, StringOrCan vasGradientOrCanvasPattern& returnValue)
94 {
95 if (CanvasGradient* gradient = style->canvasGradient()) {
96 returnValue.setCanvasGradient(gradient);
97 return;
98 }
99 if (CanvasPattern* pattern = style->canvasPattern()) {
100 returnValue.setCanvasPattern(pattern);
101 return;
102 }
103 returnValue.setString(style->color());
104 }
105
106 void BaseRenderingContext2D::strokeStyle(StringOrCanvasGradientOrCanvasPattern& returnValue) const
107 {
108 convertCanvasStyleToUnionType(state().strokeStyle(), returnValue);
109 }
110
111 void BaseRenderingContext2D::setStrokeStyle(const StringOrCanvasGradientOrCanvas Pattern& style)
112 {
113 ASSERT(!style.isNull());
114
115 String colorString;
116 CanvasStyle* canvasStyle = nullptr;
117 if (style.isString()) {
118 colorString = style.getAsString();
119 if (colorString == state().unparsedStrokeColor())
120 return;
121 Color parsedColor = 0;
122 if (!parseColorOrCurrentColor(parsedColor, colorString))
123 return;
124 if (state().strokeStyle()->isEquivalentRGBA(parsedColor.rgb())) {
125 modifiableState().setUnparsedStrokeColor(colorString);
126 return;
127 }
128 canvasStyle = CanvasStyle::createFromRGBA(parsedColor.rgb());
129 } else if (style.isCanvasGradient()) {
130 canvasStyle = CanvasStyle::createFromGradient(style.getAsCanvasGradient( ));
131 } else if (style.isCanvasPattern()) {
132 CanvasPattern* canvasPattern = style.getAsCanvasPattern();
133
134 if (originClean() && !canvasPattern->originClean())
135 setOriginTainted();
136
137 canvasStyle = CanvasStyle::createFromPattern(canvasPattern);
138 }
139
140 ASSERT(canvasStyle);
141
142 modifiableState().setStrokeStyle(canvasStyle);
143 modifiableState().setUnparsedStrokeColor(colorString);
144 }
145
146 void BaseRenderingContext2D::fillStyle(StringOrCanvasGradientOrCanvasPattern& re turnValue) const
147 {
148 convertCanvasStyleToUnionType(state().fillStyle(), returnValue);
149 }
150
151 void BaseRenderingContext2D::setFillStyle(const StringOrCanvasGradientOrCanvasPa ttern& style)
152 {
153 ASSERT(!style.isNull());
154 validateStateStack();
155 String colorString;
156 CanvasStyle* canvasStyle = nullptr;
157 if (style.isString()) {
158 colorString = style.getAsString();
159 if (colorString == state().unparsedFillColor())
160 return;
161 Color parsedColor = 0;
162 if (!parseColorOrCurrentColor(parsedColor, colorString))
163 return;
164 if (state().fillStyle()->isEquivalentRGBA(parsedColor.rgb())) {
165 modifiableState().setUnparsedFillColor(colorString);
166 return;
167 }
168 canvasStyle = CanvasStyle::createFromRGBA(parsedColor.rgb());
169 } else if (style.isCanvasGradient()) {
170 canvasStyle = CanvasStyle::createFromGradient(style.getAsCanvasGradient( ));
171 } else if (style.isCanvasPattern()) {
172 CanvasPattern* canvasPattern = style.getAsCanvasPattern();
173
174 if (originClean() && !canvasPattern->originClean())
175 setOriginTainted();
176 if (canvasPattern->pattern()->isTextureBacked())
177 disableDeferral(DisableDeferralReasonUsingTextureBackedPattern);
178 canvasStyle = CanvasStyle::createFromPattern(canvasPattern);
179 }
180
181 ASSERT(canvasStyle);
182 modifiableState().setFillStyle(canvasStyle);
183 modifiableState().setUnparsedFillColor(colorString);
184 }
185
186 double BaseRenderingContext2D::lineWidth() const
187 {
188 return state().lineWidth();
189 }
190
191 void BaseRenderingContext2D::setLineWidth(double width)
192 {
193 if (!std::isfinite(width) || width <= 0)
194 return;
195 if (state().lineWidth() == width)
196 return;
197 modifiableState().setLineWidth(width);
198 }
199
200 String BaseRenderingContext2D::lineCap() const
201 {
202 return lineCapName(state().lineCap());
203 }
204
205 void BaseRenderingContext2D::setLineCap(const String& s)
206 {
207 LineCap cap;
208 if (!parseLineCap(s, cap))
209 return;
210 if (state().lineCap() == cap)
211 return;
212 modifiableState().setLineCap(cap);
213 }
214
215 String BaseRenderingContext2D::lineJoin() const
216 {
217 return lineJoinName(state().lineJoin());
218 }
219
220 void BaseRenderingContext2D::setLineJoin(const String& s)
221 {
222 LineJoin join;
223 if (!parseLineJoin(s, join))
224 return;
225 if (state().lineJoin() == join)
226 return;
227 modifiableState().setLineJoin(join);
228 }
229
230 double BaseRenderingContext2D::miterLimit() const
231 {
232 return state().miterLimit();
233 }
234
235 void BaseRenderingContext2D::setMiterLimit(double limit)
236 {
237 if (!std::isfinite(limit) || limit <= 0)
238 return;
239 if (state().miterLimit() == limit)
240 return;
241 modifiableState().setMiterLimit(limit);
242 }
243
244 double BaseRenderingContext2D::shadowOffsetX() const
245 {
246 return state().shadowOffset().width();
247 }
248
249 void BaseRenderingContext2D::setShadowOffsetX(double x)
250 {
251 if (!std::isfinite(x))
252 return;
253 if (state().shadowOffset().width() == x)
254 return;
255 modifiableState().setShadowOffsetX(x);
256 }
257
258 double BaseRenderingContext2D::shadowOffsetY() const
259 {
260 return state().shadowOffset().height();
261 }
262
263 void BaseRenderingContext2D::setShadowOffsetY(double y)
264 {
265 if (!std::isfinite(y))
266 return;
267 if (state().shadowOffset().height() == y)
268 return;
269 modifiableState().setShadowOffsetY(y);
270 }
271
272 double BaseRenderingContext2D::shadowBlur() const
273 {
274 return state().shadowBlur();
275 }
276
277 void BaseRenderingContext2D::setShadowBlur(double blur)
278 {
279 if (!std::isfinite(blur) || blur < 0)
280 return;
281 if (state().shadowBlur() == blur)
282 return;
283 modifiableState().setShadowBlur(blur);
284 }
285
286 String BaseRenderingContext2D::shadowColor() const
287 {
288 return Color(state().shadowColor()).serialized();
289 }
290
291 void BaseRenderingContext2D::setShadowColor(const String& colorString)
292 {
293 Color color;
294 if (!parseColorOrCurrentColor(color, colorString))
295 return;
296 if (state().shadowColor() == color)
297 return;
298 modifiableState().setShadowColor(color.rgb());
299 }
300
301 const Vector<double>& BaseRenderingContext2D::getLineDash() const
302 {
303 return state().lineDash();
304 }
305
306 static bool lineDashSequenceIsValid(const Vector<double>& dash)
307 {
308 for (size_t i = 0; i < dash.size(); i++) {
309 if (!std::isfinite(dash[i]) || dash[i] < 0)
310 return false;
311 }
312 return true;
313 }
314
315 void BaseRenderingContext2D::setLineDash(const Vector<double>& dash)
316 {
317 if (!lineDashSequenceIsValid(dash))
318 return;
319 modifiableState().setLineDash(dash);
320 }
321
322 double BaseRenderingContext2D::lineDashOffset() const
323 {
324 return state().lineDashOffset();
325 }
326
327 void BaseRenderingContext2D::setLineDashOffset(double offset)
328 {
329 if (!std::isfinite(offset) || state().lineDashOffset() == offset)
330 return;
331 modifiableState().setLineDashOffset(offset);
332 }
333
334 double BaseRenderingContext2D::globalAlpha() const
335 {
336 return state().globalAlpha();
337 }
338
339 void BaseRenderingContext2D::setGlobalAlpha(double alpha)
340 {
341 if (!(alpha >= 0 && alpha <= 1))
342 return;
343 if (state().globalAlpha() == alpha)
344 return;
345 modifiableState().setGlobalAlpha(alpha);
346 }
347
348 String BaseRenderingContext2D::globalCompositeOperation() const
349 {
350 return compositeOperatorName(compositeOperatorFromSkia(state().globalComposi te()), blendModeFromSkia(state().globalComposite()));
351 }
352
353 void BaseRenderingContext2D::setGlobalCompositeOperation(const String& operation )
354 {
355 CompositeOperator op = CompositeSourceOver;
356 WebBlendMode blendMode = WebBlendModeNormal;
357 if (!parseCompositeAndBlendOperator(operation, op, blendMode))
358 return;
359 SkXfermode::Mode xfermode = WebCoreCompositeToSkiaComposite(op, blendMode);
360 if (state().globalComposite() == xfermode)
361 return;
362 modifiableState().setGlobalComposite(xfermode);
363 }
364
365 String BaseRenderingContext2D::filter() const
366 {
367 return state().unparsedFilter();
368 }
369
370 void BaseRenderingContext2D::setFilter(const String& filterString)
371 {
372 if (filterString == state().unparsedFilter())
373 return;
374
375 RefPtrWillBeRawPtr<CSSValue> filterValue = CSSParser::parseSingleValue(CSSPr opertyWebkitFilter, filterString, CSSParserContext(HTMLStandardMode, 0));
376
377 if (!filterValue || filterValue->isInitialValue() || filterValue->isInherite dValue())
378 return;
379
380 modifiableState().setUnparsedFilter(filterString);
381 modifiableState().setFilter(filterValue.release());
382 }
383
384 PassRefPtrWillBeRawPtr<SVGMatrixTearOff> BaseRenderingContext2D::currentTransfor m() const
385 {
386 return SVGMatrixTearOff::create(state().transform());
387 }
388
389 void BaseRenderingContext2D::setCurrentTransform(PassRefPtrWillBeRawPtr<SVGMatri xTearOff> passMatrixTearOff)
390 {
391 RefPtrWillBeRawPtr<SVGMatrixTearOff> matrixTearOff = passMatrixTearOff;
392 const AffineTransform& transform = matrixTearOff->value();
393 setTransform(transform.a(), transform.b(), transform.c(), transform.d(), tra nsform.e(), transform.f());
394 }
395
396 void BaseRenderingContext2D::scale(double sx, double sy)
397 {
398 SkCanvas* c = drawingCanvas();
399 if (!c)
400 return;
401
402 if (!std::isfinite(sx) || !std::isfinite(sy))
403 return;
404
405 AffineTransform newTransform = state().transform();
406 newTransform.scaleNonUniform(sx, sy);
407 if (state().transform() == newTransform)
408 return;
409
410 modifiableState().setTransform(newTransform);
411 if (!state().isTransformInvertible())
412 return;
413
414 c->scale(sx, sy);
415 m_path.transform(AffineTransform().scaleNonUniform(1.0 / sx, 1.0 / sy));
416 }
417
418 void BaseRenderingContext2D::rotate(double angleInRadians)
419 {
420 SkCanvas* c = drawingCanvas();
421 if (!c)
422 return;
423
424 if (!std::isfinite(angleInRadians))
425 return;
426
427 AffineTransform newTransform = state().transform();
428 newTransform.rotateRadians(angleInRadians);
429 if (state().transform() == newTransform)
430 return;
431
432 modifiableState().setTransform(newTransform);
433 if (!state().isTransformInvertible())
434 return;
435 c->rotate(angleInRadians * (180.0 / piFloat));
436 m_path.transform(AffineTransform().rotateRadians(-angleInRadians));
437 }
438
439 void BaseRenderingContext2D::translate(double tx, double ty)
440 {
441 SkCanvas* c = drawingCanvas();
442 if (!c)
443 return;
444 if (!state().isTransformInvertible())
445 return;
446
447 if (!std::isfinite(tx) || !std::isfinite(ty))
448 return;
449
450 AffineTransform newTransform = state().transform();
451 newTransform.translate(tx, ty);
452 if (state().transform() == newTransform)
453 return;
454
455 modifiableState().setTransform(newTransform);
456 if (!state().isTransformInvertible())
457 return;
458 c->translate(tx, ty);
459 m_path.transform(AffineTransform().translate(-tx, -ty));
460 }
461
462 void BaseRenderingContext2D::transform(double m11, double m12, double m21, doubl e m22, double dx, double dy)
463 {
464 SkCanvas* c = drawingCanvas();
465 if (!c)
466 return;
467
468 if (!std::isfinite(m11) || !std::isfinite(m21) || !std::isfinite(dx) || !std ::isfinite(m12) || !std::isfinite(m22) || !std::isfinite(dy))
469 return;
470
471 AffineTransform transform(m11, m12, m21, m22, dx, dy);
472 AffineTransform newTransform = state().transform() * transform;
473 if (state().transform() == newTransform)
474 return;
475
476 modifiableState().setTransform(newTransform);
477 if (!state().isTransformInvertible())
478 return;
479
480 c->concat(affineTransformToSkMatrix(transform));
481 m_path.transform(transform.inverse());
482 }
483
484 void BaseRenderingContext2D::resetTransform()
485 {
486 SkCanvas* c = drawingCanvas();
487 if (!c)
488 return;
489
490 AffineTransform ctm = state().transform();
491 bool invertibleCTM = state().isTransformInvertible();
492 // It is possible that CTM is identity while CTM is not invertible.
493 // When CTM becomes non-invertible, realizeSaves() can make CTM identity.
494 if (ctm.isIdentity() && invertibleCTM)
495 return;
496
497 // resetTransform() resolves the non-invertible CTM state.
498 modifiableState().resetTransform();
499 c->setMatrix(affineTransformToSkMatrix(baseTransform()));
500
501 if (invertibleCTM)
502 m_path.transform(ctm);
503 // When else, do nothing because all transform methods didn't update m_path when CTM became non-invertible.
504 // It means that resetTransform() restores m_path just before CTM became non -invertible.
505 }
506
507 void BaseRenderingContext2D::setTransform(double m11, double m12, double m21, do uble m22, double dx, double dy)
508 {
509 SkCanvas* c = drawingCanvas();
510 if (!c)
511 return;
512
513 if (!std::isfinite(m11) || !std::isfinite(m21) || !std::isfinite(dx) || !std ::isfinite(m12) || !std::isfinite(m22) || !std::isfinite(dy))
514 return;
515
516 resetTransform();
517 transform(m11, m12, m21, m22, dx, dy);
518 }
519
520 void BaseRenderingContext2D::beginPath()
521 {
522 m_path.clear();
523 }
524
525 static bool validateRectForCanvas(double& x, double& y, double& width, double& h eight)
526 {
527 if (!std::isfinite(x) || !std::isfinite(y) || !std::isfinite(width) || !std: :isfinite(height))
528 return false;
529
530 if (!width && !height)
531 return false;
532
533 if (width < 0) {
534 width = -width;
535 x -= width;
536 }
537
538 if (height < 0) {
539 height = -height;
540 y -= height;
541 }
542
543 return true;
544 }
545
546 void BaseRenderingContext2D::drawForText(const Font& font, const TextRunPaintInf o& textRunPaintInfo, const FloatPoint& location, CanvasRenderingContext2DState:: PaintType paintType)
547 {
548 draw(
549 [&font, this, &textRunPaintInfo, &location](SkCanvas* c, const SkPaint* paint) // draw lambda
550 {
551 font.drawBidiText(c, textRunPaintInfo, location, Font::UseFallbackIf FontNotReady, cDeviceScaleFactor, *paint);
552 },
553 [](const SkIRect& rect) // overdraw test lambda
554 {
555 return false;
556 },
557 textRunPaintInfo.bounds, paintType);
558 }
559
560 static bool isFullCanvasCompositeMode(SkXfermode::Mode op)
561 {
562 // See 4.8.11.1.3 Compositing
563 // CompositeSourceAtop and CompositeDestinationOut are not listed here as th e platforms already
564 // implement the specification's behavior.
565 return op == SkXfermode::kSrcIn_Mode || op == SkXfermode::kSrcOut_Mode || op == SkXfermode::kDstIn_Mode || op == SkXfermode::kDstATop_Mode;
566 }
567
568 template<typename DrawFunc>
569 void BaseRenderingContext2D::compositedDraw(const DrawFunc& drawFunc, SkCanvas* c, CanvasRenderingContext2DState::PaintType paintType, CanvasRenderingContext2DS tate::ImageType imageType)
570 {
571 SkImageFilter* filter = stateGetFilter();
572 ASSERT(isFullCanvasCompositeMode(state().globalComposite()) || filter);
573 SkMatrix ctm = c->getTotalMatrix();
574 c->resetMatrix();
575 SkPaint compositePaint;
576 compositePaint.setXfermodeMode(state().globalComposite());
577 if (state().shouldDrawShadows()) {
578 // unroll into two independently composited passes if drawing shadows
579 SkPaint shadowPaint = *state().getPaint(paintType, DrawShadowOnly, image Type);
580 int saveCount = c->getSaveCount();
581 if (filter) {
582 SkPaint filterPaint;
583 filterPaint.setImageFilter(filter);
584 // TODO(junov): crbug.com/502921 We could use primitive bounds if we knew that the filter
585 // does not affect transparent black regions.
586 c->saveLayer(nullptr, &shadowPaint);
587 c->saveLayer(nullptr, &filterPaint);
588 SkPaint foregroundPaint = *state().getPaint(paintType, DrawForegroun dOnly, imageType);
589 c->setMatrix(ctm);
590 drawFunc(c, &foregroundPaint);
591 } else {
592 ASSERT(isFullCanvasCompositeMode(state().globalComposite()));
593 c->saveLayer(nullptr, &compositePaint);
594 shadowPaint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
595 c->setMatrix(ctm);
596 drawFunc(c, &shadowPaint);
597 }
598 c->restoreToCount(saveCount);
599 }
600
601 compositePaint.setImageFilter(filter);
602 // TODO(junov): crbug.com/502921 We could use primitive bounds if we knew th at the filter
603 // does not affect transparent black regions *and* !isFullCanvasCompositeMod e
604 c->saveLayer(nullptr, &compositePaint);
605 SkPaint foregroundPaint = *state().getPaint(paintType, DrawForegroundOnly, i mageType);
606 foregroundPaint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
607 c->setMatrix(ctm);
608 drawFunc(c, &foregroundPaint);
609 c->restore();
610 c->setMatrix(ctm);
611 }
612
613 template<typename DrawFunc, typename ContainsFunc>
614 bool BaseRenderingContext2D::draw(const DrawFunc& drawFunc, const ContainsFunc& drawCoversClipBounds, const SkRect& bounds, CanvasRenderingContext2DState::Paint Type paintType, CanvasRenderingContext2DState::ImageType imageType)
615 {
616 if (!state().isTransformInvertible())
617 return false;
618
619 SkIRect clipBounds;
620 if (!drawingCanvas() || !drawingCanvas()->getClipDeviceBounds(&clipBounds))
621 return false;
622
623 // If gradient size is zero, then paint nothing.
624 CanvasStyle* style = state().style(paintType);
625 if (style) {
626 CanvasGradient* gradient = style->canvasGradient();
627 if (gradient && gradient->gradient()->isZeroSize())
628 return false;
629 }
630
631 if (isFullCanvasCompositeMode(state().globalComposite()) || stateHasFilter() ) {
632 compositedDraw(drawFunc, drawingCanvas(), paintType, imageType);
633 didDraw(clipBounds);
634 } else if (state().globalComposite() == SkXfermode::kSrc_Mode) {
635 clearCanvas(); // takes care of checkOverdraw()
636 const SkPaint* paint = state().getPaint(paintType, DrawForegroundOnly, i mageType);
637 drawFunc(drawingCanvas(), paint);
638 didDraw(clipBounds);
639 } else {
640 SkIRect dirtyRect;
641 if (computeDirtyRect(bounds, clipBounds, &dirtyRect)) {
642 const SkPaint* paint = state().getPaint(paintType, DrawShadowAndFore ground, imageType);
643 if (paintType != CanvasRenderingContext2DState::StrokePaintType && d rawCoversClipBounds(clipBounds))
644 checkOverdraw(bounds, paint, imageType, ClipFill);
645 drawFunc(drawingCanvas(), paint);
646 didDraw(dirtyRect);
647 }
648 }
649 return true;
650 }
651
652 static bool isPathExpensive(const Path& path)
653 {
654 const SkPath& skPath = path.skPath();
655 if (ExpensiveCanvasHeuristicParameters::ConcavePathsAreExpensive && !skPath. isConvex())
656 return true;
657
658 if (skPath.countPoints() > ExpensiveCanvasHeuristicParameters::ExpensivePath PointCount)
659 return true;
660
661 return false;
662 }
663
664 void BaseRenderingContext2D::drawPathInternal(const Path& path, CanvasRenderingC ontext2DState::PaintType paintType, SkPath::FillType fillType)
665 {
666 if (path.isEmpty())
667 return;
668
669 SkPath skPath = path.skPath();
670 FloatRect bounds = path.boundingRect();
671 skPath.setFillType(fillType);
672
673 if (paintType == CanvasRenderingContext2DState::StrokePaintType)
674 inflateStrokeRect(bounds);
675
676 if (!drawingCanvas())
677 return;
678
679 if (draw(
680 [&skPath, this](SkCanvas* c, const SkPaint* paint) // draw lambda
681 {
682 c->drawPath(skPath, *paint);
683 },
684 [](const SkIRect& rect) // overdraw test lambda
685 {
686 return false;
687 }, bounds, paintType)) {
688 if (isPathExpensive(path)) {
689 ImageBuffer* buffer = imageBuffer();
690 if (buffer)
691 buffer->setHasExpensiveOp();
692 }
693 }
694 }
695
696 static SkPath::FillType parseWinding(const String& windingRuleString)
697 {
698 if (windingRuleString == "nonzero")
699 return SkPath::kWinding_FillType;
700 if (windingRuleString == "evenodd")
701 return SkPath::kEvenOdd_FillType;
702
703 ASSERT_NOT_REACHED();
704 return SkPath::kEvenOdd_FillType;
705 }
706
707 void BaseRenderingContext2D::fill(const String& windingRuleString)
708 {
709 drawPathInternal(m_path, CanvasRenderingContext2DState::FillPaintType, parse Winding(windingRuleString));
710 }
711
712 void BaseRenderingContext2D::fill(Path2D* domPath, const String& windingRuleStri ng)
713 {
714 drawPathInternal(domPath->path(), CanvasRenderingContext2DState::FillPaintTy pe, parseWinding(windingRuleString));
715 }
716
717 void BaseRenderingContext2D::stroke()
718 {
719 drawPathInternal(m_path, CanvasRenderingContext2DState::StrokePaintType);
720 }
721
722 void BaseRenderingContext2D::stroke(Path2D* domPath)
723 {
724 drawPathInternal(domPath->path(), CanvasRenderingContext2DState::StrokePaint Type);
725 }
726
727 void BaseRenderingContext2D::fillRect(double x, double y, double width, double h eight)
728 {
729 if (!validateRectForCanvas(x, y, width, height))
730 return;
731
732 if (!drawingCanvas())
733 return;
734
735 SkRect rect = SkRect::MakeXYWH(x, y, width, height);
736 draw(
737 [&rect, this](SkCanvas* c, const SkPaint* paint) // draw lambda
738 {
739 c->drawRect(rect, *paint);
740 },
741 [&rect, this](const SkIRect& clipBounds) // overdraw test lambda
742 {
743 return rectContainsTransformedRect(rect, clipBounds);
744 }, rect, CanvasRenderingContext2DState::FillPaintType);
745 }
746
747 static void strokeRectOnCanvas(const FloatRect& rect, SkCanvas* canvas, const Sk Paint* paint)
748 {
749 ASSERT(paint->getStyle() == SkPaint::kStroke_Style);
750 if ((rect.width() > 0) != (rect.height() > 0)) {
751 // When stroking, we must skip the zero-dimension segments
752 SkPath path;
753 path.moveTo(rect.x(), rect.y());
754 path.lineTo(rect.maxX(), rect.maxY());
755 path.close();
756 canvas->drawPath(path, *paint);
757 return;
758 }
759 canvas->drawRect(rect, *paint);
760 }
761
762 void BaseRenderingContext2D::strokeRect(double x, double y, double width, double height)
763 {
764 if (!validateRectForCanvas(x, y, width, height))
765 return;
766
767 if (!drawingCanvas())
768 return;
769
770 SkRect rect = SkRect::MakeXYWH(x, y, width, height);
771 FloatRect bounds = rect;
772 inflateStrokeRect(bounds);
773 draw(
774 [&rect, this](SkCanvas* c, const SkPaint* paint) // draw lambda
775 {
776 strokeRectOnCanvas(rect, c, paint);
777 },
778 [](const SkIRect& clipBounds) // overdraw test lambda
779 {
780 return false;
781 }, bounds, CanvasRenderingContext2DState::StrokePaintType);
782 }
783
784 void BaseRenderingContext2D::clipInternal(const Path& path, const String& windin gRuleString)
785 {
786 SkCanvas* c = drawingCanvas();
787 if (!c) {
788 return;
789 }
790 if (!state().isTransformInvertible()) {
791 return;
792 }
793
794 SkPath skPath = path.skPath();
795 skPath.setFillType(parseWinding(windingRuleString));
796 modifiableState().clipPath(skPath, m_clipAntialiasing);
797 c->clipPath(skPath, SkRegion::kIntersect_Op, m_clipAntialiasing == AntiAlias ed);
798 if (ExpensiveCanvasHeuristicParameters::ComplexClipsAreExpensive && !skPath. isRect(0) && hasImageBuffer()) {
799 imageBuffer()->setHasExpensiveOp();
800 }
801 }
802
803 void BaseRenderingContext2D::clip(const String& windingRuleString)
804 {
805 clipInternal(m_path, windingRuleString);
806 }
807
808 void BaseRenderingContext2D::clip(Path2D* domPath, const String& windingRuleStri ng)
809 {
810 clipInternal(domPath->path(), windingRuleString);
811 }
812
813 bool BaseRenderingContext2D::isPointInPath(const double x, const double y, const String& windingRuleString)
814 {
815 return isPointInPathInternal(m_path, x, y, windingRuleString);
816 }
817
818 bool BaseRenderingContext2D::isPointInPath(Path2D* domPath, const double x, cons t double y, const String& windingRuleString)
819 {
820 return isPointInPathInternal(domPath->path(), x, y, windingRuleString);
821 }
822
823 bool BaseRenderingContext2D::isPointInPathInternal(const Path& path, const doubl e x, const double y, const String& windingRuleString)
824 {
825 SkCanvas* c = drawingCanvas();
826 if (!c)
827 return false;
828 if (!state().isTransformInvertible())
829 return false;
830
831 FloatPoint point(x, y);
832 if (!std::isfinite(point.x()) || !std::isfinite(point.y()))
833 return false;
834 AffineTransform ctm = state().transform();
835 FloatPoint transformedPoint = ctm.inverse().mapPoint(point);
836
837 return path.contains(transformedPoint, SkFillTypeToWindRule(parseWinding(win dingRuleString)));
838 }
839
840 bool BaseRenderingContext2D::isPointInStroke(const double x, const double y)
841 {
842 return isPointInStrokeInternal(m_path, x, y);
843 }
844
845 bool BaseRenderingContext2D::isPointInStroke(Path2D* domPath, const double x, co nst double y)
846 {
847 return isPointInStrokeInternal(domPath->path(), x, y);
848 }
849
850 bool BaseRenderingContext2D::isPointInStrokeInternal(const Path& path, const dou ble x, const double y)
851 {
852 SkCanvas* c = drawingCanvas();
853 if (!c)
854 return false;
855 if (!state().isTransformInvertible())
856 return false;
857
858 FloatPoint point(x, y);
859 if (!std::isfinite(point.x()) || !std::isfinite(point.y()))
860 return false;
861 AffineTransform ctm = state().transform();
862 FloatPoint transformedPoint = ctm.inverse().mapPoint(point);
863
864 StrokeData strokeData;
865 strokeData.setThickness(state().lineWidth());
866 strokeData.setLineCap(state().lineCap());
867 strokeData.setLineJoin(state().lineJoin());
868 strokeData.setMiterLimit(state().miterLimit());
869 Vector<float> lineDash(state().lineDash().size());
870 std::copy(state().lineDash().begin(), state().lineDash().end(), lineDash.beg in());
871 strokeData.setLineDash(lineDash, state().lineDashOffset());
872 return path.strokeContains(transformedPoint, strokeData);
873 }
874
875 void BaseRenderingContext2D::clearRect(double x, double y, double width, double height)
876 {
877 if (!validateRectForCanvas(x, y, width, height))
878 return;
879
880 SkCanvas* c = drawingCanvas();
881 if (!c)
882 return;
883 if (!state().isTransformInvertible())
884 return;
885
886 SkIRect clipBounds;
887 if (!c->getClipDeviceBounds(&clipBounds))
888 return;
889
890 SkPaint clearPaint;
891 clearPaint.setXfermodeMode(SkXfermode::kClear_Mode);
892 clearPaint.setStyle(SkPaint::kFill_Style);
893 FloatRect rect(x, y, width, height);
894
895 if (rectContainsTransformedRect(rect, clipBounds)) {
896 checkOverdraw(rect, &clearPaint, CanvasRenderingContext2DState::NoImage, ClipFill);
897 if (drawingCanvas())
898 drawingCanvas()->drawRect(rect, clearPaint);
899 didDraw(clipBounds);
900 } else {
901 SkIRect dirtyRect;
902 if (computeDirtyRect(rect, clipBounds, &dirtyRect)) {
903 c->drawRect(rect, clearPaint);
904 didDraw(dirtyRect);
905 }
906 }
907 }
908
909 static inline FloatRect normalizeRect(const FloatRect& rect)
910 {
911 return FloatRect(std::min(rect.x(), rect.maxX()),
912 std::min(rect.y(), rect.maxY()),
913 std::max(rect.width(), -rect.width()),
914 std::max(rect.height(), -rect.height()));
915 }
916
917 static inline void clipRectsToImageRect(const FloatRect& imageRect, FloatRect* s rcRect, FloatRect* dstRect)
918 {
919 if (imageRect.contains(*srcRect))
920 return;
921
922 // Compute the src to dst transform
923 FloatSize scale(dstRect->size().width() / srcRect->size().width(), dstRect-> size().height() / srcRect->size().height());
924 FloatPoint scaledSrcLocation = srcRect->location();
925 scaledSrcLocation.scale(scale.width(), scale.height());
926 FloatSize offset = dstRect->location() - scaledSrcLocation;
927
928 srcRect->intersect(imageRect);
929
930 // To clip the destination rectangle in the same proportion, transform the c lipped src rect
931 *dstRect = *srcRect;
932 dstRect->scale(scale.width(), scale.height());
933 dstRect->move(offset);
934 }
935
936 static inline CanvasImageSource* toImageSourceInternal(const CanvasImageSourceUn ion& value)
937 {
938 if (value.isHTMLImageElement())
939 return value.getAsHTMLImageElement().get();
940 if (value.isHTMLVideoElement())
941 return value.getAsHTMLVideoElement().get();
942 if (value.isHTMLCanvasElement())
943 return value.getAsHTMLCanvasElement().get();
944 if (value.isImageBitmap())
945 return value.getAsImageBitmap().get();
946 ASSERT_NOT_REACHED();
947 return nullptr;
948 }
949
950 void BaseRenderingContext2D::drawImage(const CanvasImageSourceUnion& imageSource , double x, double y, ExceptionState& exceptionState)
951 {
952 CanvasImageSource* imageSourceInternal = toImageSourceInternal(imageSource);
953 FloatSize sourceRectSize = imageSourceInternal->elementSize();
954 FloatSize destRectSize = imageSourceInternal->defaultDestinationSize();
955 drawImage(imageSourceInternal, 0, 0, sourceRectSize.width(), sourceRectSize. height(), x, y, destRectSize.width(), destRectSize.height(), exceptionState);
956 }
957
958 void BaseRenderingContext2D::drawImage(const CanvasImageSourceUnion& imageSource ,
959 double x, double y, double width, double height, ExceptionState& exceptionSt ate)
960 {
961 CanvasImageSource* imageSourceInternal = toImageSourceInternal(imageSource);
962 FloatSize sourceRectSize = imageSourceInternal->elementSize();
963 drawImage(imageSourceInternal, 0, 0, sourceRectSize.width(), sourceRectSize. height(), x, y, width, height, exceptionState);
964 }
965
966 void BaseRenderingContext2D::drawImage(const CanvasImageSourceUnion& imageSource ,
967 double sx, double sy, double sw, double sh,
968 double dx, double dy, double dw, double dh, ExceptionState& exceptionState)
969 {
970 CanvasImageSource* imageSourceInternal = toImageSourceInternal(imageSource);
971 drawImage(imageSourceInternal, sx, sy, sw, sh, dx, dy, dw, dh, exceptionStat e);
972 }
973
974 bool BaseRenderingContext2D::shouldDrawImageAntialiased(const FloatRect& destRec t) const
975 {
976 if (!state().shouldAntialias())
977 return false;
978 SkCanvas* c = drawingCanvas();
979 ASSERT(c);
980
981 const SkMatrix &ctm = c->getTotalMatrix();
982 // Don't disable anti-aliasing if we're rotated or skewed.
983 if (!ctm.rectStaysRect())
984 return true;
985 // Check if the dimensions of the destination are "small" (less than one
986 // device pixel). To prevent sudden drop-outs. Since we know that
987 // kRectStaysRect_Mask is set, the matrix either has scale and no skew or
988 // vice versa. We can query the kAffine_Mask flag to determine which case
989 // it is.
990 // FIXME: This queries the CTM while drawing, which is generally
991 // discouraged. Always drawing with AA can negatively impact performance
992 // though - that's why it's not always on.
993 SkScalar widthExpansion, heightExpansion;
994 if (ctm.getType() & SkMatrix::kAffine_Mask)
995 widthExpansion = ctm[SkMatrix::kMSkewY], heightExpansion = ctm[SkMatrix: :kMSkewX];
996 else
997 widthExpansion = ctm[SkMatrix::kMScaleX], heightExpansion = ctm[SkMatrix ::kMScaleY];
998 return destRect.width() * fabs(widthExpansion) < 1 || destRect.height() * fa bs(heightExpansion) < 1;
999 }
1000
1001 void BaseRenderingContext2D::drawImageInternal(SkCanvas* c, CanvasImageSource* i mageSource, Image* image, const FloatRect& srcRect, const FloatRect& dstRect, co nst SkPaint* paint)
1002 {
1003 int initialSaveCount = c->getSaveCount();
1004 SkPaint imagePaint = *paint;
1005
1006 if (paint->getImageFilter()) {
1007 SkMatrix invCtm;
1008 if (!c->getTotalMatrix().invert(&invCtm)) {
1009 // There is an earlier check for invertibility, but the arithmetic
1010 // in AffineTransform is not exactly identical, so it is possible
1011 // for SkMatrix to find the transform to be non-invertible at this s tage.
1012 // crbug.com/504687
1013 return;
1014 }
1015 SkRect bounds = dstRect;
1016 SkPaint layerPaint;
1017 layerPaint.setXfermode(paint->getXfermode());
1018 SkAutoTUnref<SkImageFilter> localFilter(paint->getImageFilter()->newWith LocalMatrix(invCtm));
1019 layerPaint.setImageFilter(localFilter);
1020 c->saveLayer(&bounds, &layerPaint);
1021 imagePaint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
1022 imagePaint.setImageFilter(nullptr);
1023 }
1024
1025 if (!imageSource->isVideoElement()) {
1026 imagePaint.setAntiAlias(shouldDrawImageAntialiased(dstRect));
1027 image->draw(c, imagePaint, dstRect, srcRect, DoNotRespectImageOrientatio n, Image::DoNotClampImageToSourceRect);
1028 } else {
1029 c->save();
1030 c->clipRect(dstRect);
1031 c->translate(dstRect.x(), dstRect.y());
1032 c->scale(dstRect.width() / srcRect.width(), dstRect.height() / srcRect.h eight());
1033 c->translate(-srcRect.x(), -srcRect.y());
1034 HTMLVideoElement* video = static_cast<HTMLVideoElement*>(imageSource);
1035 video->paintCurrentFrame(c, IntRect(IntPoint(), IntSize(video->videoWidt h(), video->videoHeight())), &imagePaint);
1036 }
1037
1038 c->restoreToCount(initialSaveCount);
1039 }
1040
1041 bool shouldDisableDeferral(CanvasImageSource* imageSource, DisableDeferralReason * reason)
1042 {
1043 ASSERT(reason);
1044 ASSERT(*reason == DisableDeferralReasonUnknown);
1045
1046 if (imageSource->isVideoElement()) {
1047 *reason = DisableDeferralReasonDrawImageOfVideo;
1048 return true;
1049 }
1050 if (imageSource->isCanvasElement()) {
1051 HTMLCanvasElement* canvas = static_cast<HTMLCanvasElement*>(imageSource) ;
1052 if (canvas->isAnimated2D()) {
1053 *reason = DisableDeferralReasonDrawImageOfAnimated2dCanvas;
1054 return true;
1055 }
1056 }
1057 return false;
1058 }
1059
1060 void BaseRenderingContext2D::drawImage(CanvasImageSource* imageSource,
1061 double sx, double sy, double sw, double sh,
1062 double dx, double dy, double dw, double dh, ExceptionState& exceptionState)
1063 {
1064 if (!drawingCanvas())
1065 return;
1066
1067 RefPtr<Image> image;
1068 SourceImageStatus sourceImageStatus = InvalidSourceImageStatus;
1069 if (!imageSource->isVideoElement()) {
1070 AccelerationHint hint = imageBuffer()->isAccelerated() ? PreferAccelerat ion : PreferNoAcceleration;
1071 image = imageSource->getSourceImageForCanvas(&sourceImageStatus, hint, S napshotReasonDrawImage);
1072 if (sourceImageStatus == UndecodableSourceImageStatus)
1073 exceptionState.throwDOMException(InvalidStateError, "The HTMLImageEl ement provided is in the 'broken' state.");
1074 if (!image || !image->width() || !image->height())
1075 return;
1076 } else {
1077 if (!static_cast<HTMLVideoElement*>(imageSource)->hasAvailableVideoFrame ())
1078 return;
1079 }
1080
1081 if (!std::isfinite(dx) || !std::isfinite(dy) || !std::isfinite(dw) || !std:: isfinite(dh)
1082 || !std::isfinite(sx) || !std::isfinite(sy) || !std::isfinite(sw) || !st d::isfinite(sh)
1083 || !dw || !dh || !sw || !sh)
1084 return;
1085
1086 FloatRect srcRect = normalizeRect(FloatRect(sx, sy, sw, sh));
1087 FloatRect dstRect = normalizeRect(FloatRect(dx, dy, dw, dh));
1088
1089 clipRectsToImageRect(FloatRect(FloatPoint(), imageSource->elementSize()), &s rcRect, &dstRect);
1090
1091 imageSource->adjustDrawRects(&srcRect, &dstRect);
1092
1093 if (srcRect.isEmpty())
1094 return;
1095
1096 DisableDeferralReason reason = DisableDeferralReasonUnknown;
1097 if (shouldDisableDeferral(imageSource, &reason) || image->isTextureBacked())
1098 disableDeferral(reason);
1099
1100 validateStateStack();
1101
1102 draw(
1103 [this, &imageSource, &image, &srcRect, dstRect](SkCanvas* c, const SkPai nt* paint) // draw lambda
1104 {
1105 drawImageInternal(c, imageSource, image.get(), srcRect, dstRect, pai nt);
1106 },
1107 [this, &dstRect](const SkIRect& clipBounds) // overdraw test lambda
1108 {
1109 return rectContainsTransformedRect(dstRect, clipBounds);
1110 }, dstRect, CanvasRenderingContext2DState::ImagePaintType,
1111 imageSource->isOpaque() ? CanvasRenderingContext2DState::OpaqueImage : C anvasRenderingContext2DState::NonOpaqueImage);
1112
1113 validateStateStack();
1114
1115 bool isExpensive = false;
1116
1117 if (ExpensiveCanvasHeuristicParameters::SVGImageSourcesAreExpensive && image Source->isSVGSource())
1118 isExpensive = true;
1119
1120 if (imageSource->elementSize().width() * imageSource->elementSize().height() > width() * height() * ExpensiveCanvasHeuristicParameters::ExpensiveImageSizeRa tio)
1121 isExpensive = true;
1122
1123 if (isExpensive) {
1124 ImageBuffer* buffer = imageBuffer();
1125 if (buffer)
1126 buffer->setHasExpensiveOp();
1127 }
1128
1129 if (imageSource->isCanvasElement() && static_cast<HTMLCanvasElement*>(imageS ource)->is3D()) {
1130 // WebGL to 2D canvas: must flush graphics context to prevent a race
1131 // FIXME: crbug.com/516331 Fix the underlying synchronization issue so t his flush can be eliminated.
1132 imageBuffer()->flushGpu(FlushReasonDrawImageOfWebGL);
1133 }
1134
1135 if (originClean() && wouldTaintOrigin(imageSource))
1136 setOriginTainted();
1137 }
1138
1139 void BaseRenderingContext2D::clearCanvas()
1140 {
1141 FloatRect canvasRect(0, 0, width(), height());
1142 checkOverdraw(canvasRect, 0, CanvasRenderingContext2DState::NoImage, ClipFil l);
1143 SkCanvas* c = drawingCanvas();
1144 if (c)
1145 c->clear(hasAlpha() ? SK_ColorTRANSPARENT : SK_ColorBLACK);
1146 }
1147
1148 bool BaseRenderingContext2D::rectContainsTransformedRect(const FloatRect& rect, const SkIRect& transformedRect) const
1149 {
1150 FloatQuad quad(rect);
1151 FloatQuad transformedQuad(FloatRect(transformedRect.x(), transformedRect.y() , transformedRect.width(), transformedRect.height()));
1152 return state().transform().mapQuad(quad).containsQuad(transformedQuad);
1153 }
1154
1155 CanvasGradient* BaseRenderingContext2D::createLinearGradient(double x0, double y 0, double x1, double y1)
1156 {
1157 CanvasGradient* gradient = CanvasGradient::create(FloatPoint(x0, y0), FloatP oint(x1, y1));
1158 return gradient;
1159 }
1160
1161 CanvasGradient* BaseRenderingContext2D::createRadialGradient(double x0, double y 0, double r0, double x1, double y1, double r1, ExceptionState& exceptionState)
1162 {
1163 if (r0 < 0 || r1 < 0) {
1164 exceptionState.throwDOMException(IndexSizeError, String::format("The %s provided is less than 0.", r0 < 0 ? "r0" : "r1"));
1165 return nullptr;
1166 }
1167
1168 CanvasGradient* gradient = CanvasGradient::create(FloatPoint(x0, y0), r0, Fl oatPoint(x1, y1), r1);
1169 return gradient;
1170 }
1171
1172 CanvasPattern* BaseRenderingContext2D::createPattern(const CanvasImageSourceUnio n& imageSource, const String& repetitionType, ExceptionState& exceptionState)
1173 {
1174 Pattern::RepeatMode repeatMode = CanvasPattern::parseRepetitionType(repetiti onType, exceptionState);
1175 if (exceptionState.hadException())
1176 return nullptr;
1177
1178 SourceImageStatus status;
1179 CanvasImageSource* imageSourceInternal = toImageSourceInternal(imageSource);
1180 RefPtr<Image> imageForRendering = imageSourceInternal->getSourceImageForCanv as(&status, PreferNoAcceleration, SnapshotReasonCreatePattern);
1181
1182 switch (status) {
1183 case NormalSourceImageStatus:
1184 break;
1185 case ZeroSizeCanvasSourceImageStatus:
1186 exceptionState.throwDOMException(InvalidStateError, String::format("The canvas %s is 0.", imageSourceInternal->elementSize().width() ? "height" : "width "));
1187 return nullptr;
1188 case UndecodableSourceImageStatus:
1189 exceptionState.throwDOMException(InvalidStateError, "Source image is in the 'broken' state.");
1190 return nullptr;
1191 case InvalidSourceImageStatus:
1192 imageForRendering = Image::nullImage();
1193 break;
1194 case IncompleteSourceImageStatus:
1195 return nullptr;
1196 default:
1197 ASSERT_NOT_REACHED();
1198 return nullptr;
1199 }
1200 ASSERT(imageForRendering);
1201
1202 bool originClean = !wouldTaintOrigin(imageSourceInternal);
1203
1204 return CanvasPattern::create(imageForRendering.release(), repeatMode, origin Clean);
1205 }
1206
1207 bool BaseRenderingContext2D::computeDirtyRect(const FloatRect& localRect, SkIRec t* dirtyRect)
1208 {
1209 SkIRect clipBounds;
1210 if (!drawingCanvas()->getClipDeviceBounds(&clipBounds))
1211 return false;
1212 return computeDirtyRect(localRect, clipBounds, dirtyRect);
1213 }
1214
1215 bool BaseRenderingContext2D::computeDirtyRect(const FloatRect& localRect, const SkIRect& transformedClipBounds, SkIRect* dirtyRect)
1216 {
1217 FloatRect canvasRect = state().transform().mapRect(localRect);
1218
1219 if (alphaChannel(state().shadowColor())) {
1220 FloatRect shadowRect(canvasRect);
1221 shadowRect.move(state().shadowOffset());
1222 shadowRect.inflate(state().shadowBlur());
1223 canvasRect.unite(shadowRect);
1224 }
1225
1226 SkIRect canvasIRect;
1227 static_cast<SkRect>(canvasRect).roundOut(&canvasIRect);
1228 if (!canvasIRect.intersect(transformedClipBounds))
1229 return false;
1230
1231 if (dirtyRect)
1232 *dirtyRect = canvasIRect;
1233
1234 return true;
1235 }
1236
1237 void BaseRenderingContext2D::inflateStrokeRect(FloatRect& rect) const
1238 {
1239 // Fast approximation of the stroke's bounding rect.
1240 // This yields a slightly oversized rect but is very fast
1241 // compared to Path::strokeBoundingRect().
1242 static const double root2 = sqrtf(2);
1243 double delta = state().lineWidth() / 2;
1244 if (state().lineJoin() == MiterJoin)
1245 delta *= state().miterLimit();
1246 else if (state().lineCap() == SquareCap)
1247 delta *= root2;
1248
1249 rect.inflate(delta);
1250 }
1251
1252 bool BaseRenderingContext2D::imageSmoothingEnabled() const
1253 {
1254 return state().imageSmoothingEnabled();
1255 }
1256
1257 void BaseRenderingContext2D::setImageSmoothingEnabled(bool enabled)
1258 {
1259 if (enabled == state().imageSmoothingEnabled())
1260 return;
1261
1262 modifiableState().setImageSmoothingEnabled(enabled);
1263 }
1264
1265 String BaseRenderingContext2D::imageSmoothingQuality() const
1266 {
1267 return state().imageSmoothingQuality();
1268 }
1269
1270 void BaseRenderingContext2D::setImageSmoothingQuality(const String& quality)
1271 {
1272 if (quality == state().imageSmoothingQuality())
1273 return;
1274
1275 modifiableState().setImageSmoothingQuality(quality);
1276 }
1277
1278 void BaseRenderingContext2D::checkOverdraw(const SkRect& rect, const SkPaint* pa int, CanvasRenderingContext2DState::ImageType imageType, DrawType drawType)
1279 {
1280 SkCanvas* c = drawingCanvas();
1281 if (!c || !imageBuffer()->isRecording())
1282 return;
1283
1284 SkRect deviceRect;
1285 if (drawType == UntransformedUnclippedFill) {
1286 deviceRect = rect;
1287 } else {
1288 ASSERT(drawType == ClipFill);
1289 if (state().hasComplexClip())
1290 return;
1291
1292 SkIRect skIBounds;
1293 if (!c->getClipDeviceBounds(&skIBounds))
1294 return;
1295 deviceRect = SkRect::Make(skIBounds);
1296 }
1297
1298 const SkImageInfo& imageInfo = c->imageInfo();
1299 if (!deviceRect.contains(SkRect::MakeWH(imageInfo.width(), imageInfo.height( ))))
1300 return;
1301
1302 bool isSourceOver = true;
1303 unsigned alpha = 0xFF;
1304 if (paint) {
1305 if (paint->getLooper() || paint->getImageFilter() || paint->getMaskFilte r())
1306 return;
1307
1308 SkXfermode* xfermode = paint->getXfermode();
1309 if (xfermode) {
1310 SkXfermode::Mode mode;
1311 if (xfermode->asMode(&mode)) {
1312 isSourceOver = mode == SkXfermode::kSrcOver_Mode;
1313 if (!isSourceOver && mode != SkXfermode::kSrc_Mode && mode != Sk Xfermode::kClear_Mode)
1314 return; // The code below only knows how to handle Src, SrcO ver, and Clear
1315 } else {
1316 // unknown xfermode
1317 ASSERT_NOT_REACHED();
1318 return;
1319 }
1320 }
1321
1322 alpha = paint->getAlpha();
1323
1324 if (isSourceOver && imageType == CanvasRenderingContext2DState::NoImage) {
1325 SkShader* shader = paint->getShader();
1326 if (shader) {
1327 if (shader->isOpaque() && alpha == 0xFF)
1328 imageBuffer()->willOverwriteCanvas();
1329 return;
1330 }
1331 }
1332 }
1333
1334 if (isSourceOver) {
1335 // With source over, we need to certify that alpha == 0xFF for all pixel s
1336 if (imageType == CanvasRenderingContext2DState::NonOpaqueImage)
1337 return;
1338 if (alpha < 0xFF)
1339 return;
1340 }
1341
1342 imageBuffer()->willOverwriteCanvas();
1343 }
1344
1345 DEFINE_TRACE(BaseRenderingContext2D)
1346 {
1347 visitor->trace(m_stateStack);
1348 }
1349
1350 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698