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 "third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h"
6 
7 #include <algorithm>
8 #include <cmath>
9 #include <memory>
10 
11 #include "base/metrics/histogram_functions.h"
12 #include "base/numerics/checked_math.h"
13 #include "third_party/blink/public/common/features.h"
14 #include "third_party/blink/renderer/core/css/cssom/css_url_image_value.h"
15 #include "third_party/blink/renderer/core/css/parser/css_parser.h"
16 #include "third_party/blink/renderer/core/html/canvas/text_metrics.h"
17 #include "third_party/blink/renderer/core/html/html_image_element.h"
18 #include "third_party/blink/renderer/core/html/media/html_video_element.h"
19 #include "third_party/blink/renderer/core/imagebitmap/image_bitmap.h"
20 #include "third_party/blink/renderer/core/svg/svg_image_element.h"
21 #include "third_party/blink/renderer/modules/canvas/canvas2d/canvas_pattern.h"
22 #include "third_party/blink/renderer/modules/canvas/canvas2d/path_2d.h"
23 #include "third_party/blink/renderer/platform/geometry/float_quad.h"
24 #include "third_party/blink/renderer/platform/graphics/bitmap_image.h"
25 #include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
26 #include "third_party/blink/renderer/platform/graphics/stroke_data.h"
27 #include "third_party/blink/renderer/platform/heap/heap.h"
28 
29 namespace blink {
30 
31 const char BaseRenderingContext2D::kDefaultFont[] = "10px sans-serif";
32 const char BaseRenderingContext2D::kInheritDirectionString[] = "inherit";
33 const char BaseRenderingContext2D::kRtlDirectionString[] = "rtl";
34 const char BaseRenderingContext2D::kLtrDirectionString[] = "ltr";
35 const double BaseRenderingContext2D::kCDeviceScaleFactor = 1.0;
36 
BaseRenderingContext2D()37 BaseRenderingContext2D::BaseRenderingContext2D()
38     : clip_antialiasing_(kNotAntiAliased), origin_tainted_by_content_(false) {
39   state_stack_.push_back(MakeGarbageCollected<CanvasRenderingContext2DState>());
40 }
41 
42 BaseRenderingContext2D::~BaseRenderingContext2D() = default;
43 
ModifiableState()44 CanvasRenderingContext2DState& BaseRenderingContext2D::ModifiableState() {
45   RealizeSaves();
46   return *state_stack_.back();
47 }
48 
RealizeSaves()49 void BaseRenderingContext2D::RealizeSaves() {
50   ValidateStateStack();
51   if (GetState().HasUnrealizedSaves()) {
52     DCHECK_GE(state_stack_.size(), 1u);
53     // GetOrCreatePaintCanvas() can call RestoreMatrixClipStack which syncs
54     // canvas to state_stack_. Get the canvas before adjusting state_stack_ to
55     // ensure canvas is synced prior to adjusting state_stack_.
56     cc::PaintCanvas* canvas = GetOrCreatePaintCanvas();
57 
58     // Reduce the current state's unrealized count by one now,
59     // to reflect the fact we are saving one state.
60     state_stack_.back()->Restore();
61     state_stack_.push_back(MakeGarbageCollected<CanvasRenderingContext2DState>(
62         GetState(), CanvasRenderingContext2DState::kDontCopyClipList));
63     // Set the new state's unrealized count to 0, because it has no outstanding
64     // saves.
65     // We need to do this explicitly because the copy constructor and operator=
66     // used by the Vector operations copy the unrealized count from the previous
67     // state (in turn necessary to support correct resizing and unwinding of the
68     // stack).
69     state_stack_.back()->ResetUnrealizedSaveCount();
70     if (canvas)
71       canvas->save();
72     ValidateStateStack();
73   }
74 }
75 
save()76 void BaseRenderingContext2D::save() {
77   if (isContextLost())
78     return;
79   state_stack_.back()->Save();
80   ValidateStateStack();
81 }
82 
restore()83 void BaseRenderingContext2D::restore() {
84   if (isContextLost())
85     return;
86   ValidateStateStack();
87   if (GetState().HasUnrealizedSaves()) {
88     // We never realized the save, so just record that it was unnecessary.
89     state_stack_.back()->Restore();
90     return;
91   }
92   DCHECK_GE(state_stack_.size(), 1u);
93   if (state_stack_.size() <= 1)
94     return;
95   // Verify that the current state's transform is invertible.
96   if (GetState().IsTransformInvertible())
97     path_.Transform(GetState().Transform());
98 
99   state_stack_.pop_back();
100   state_stack_.back()->ClearResolvedFilter();
101 
102   if (GetState().IsTransformInvertible())
103     path_.Transform(GetState().Transform().Inverse());
104 
105   cc::PaintCanvas* c = GetOrCreatePaintCanvas();
106 
107   if (c)
108     c->restore();
109 
110   ValidateStateStack();
111 }
112 
RestoreMatrixClipStack(cc::PaintCanvas * c) const113 void BaseRenderingContext2D::RestoreMatrixClipStack(cc::PaintCanvas* c) const {
114   if (!c)
115     return;
116   HeapVector<Member<CanvasRenderingContext2DState>>::const_iterator curr_state;
117   DCHECK(state_stack_.begin() < state_stack_.end());
118   for (curr_state = state_stack_.begin(); curr_state < state_stack_.end();
119        curr_state++) {
120     c->setMatrix(SkMatrix::I());
121     if (curr_state->Get()) {
122       curr_state->Get()->PlaybackClips(c);
123       c->setMatrix(AffineTransformToSkMatrix(curr_state->Get()->Transform()));
124     }
125     c->save();
126   }
127   c->restore();
128   ValidateStateStackWithCanvas(c);
129 }
130 
UnwindStateStack()131 void BaseRenderingContext2D::UnwindStateStack() {
132   if (size_t stack_size = state_stack_.size()) {
133     if (cc::PaintCanvas* sk_canvas = GetPaintCanvas()) {
134       while (--stack_size)
135         sk_canvas->restore();
136     }
137   }
138 }
139 
Reset()140 void BaseRenderingContext2D::Reset() {
141   ValidateStateStack();
142   UnwindStateStack();
143   state_stack_.resize(1);
144   state_stack_.front() = MakeGarbageCollected<CanvasRenderingContext2DState>();
145   path_.Clear();
146   if (cc::PaintCanvas* c = GetPaintCanvas()) {
147     // The canvas should always have an initial/unbalanced save frame, which
148     // we use to reset the top level matrix and clip here.
149     DCHECK_EQ(c->getSaveCount(), 2);
150     c->restore();
151     c->save();
152     DCHECK(c->getTotalMatrix().isIdentity());
153 #if DCHECK_IS_ON()
154     SkIRect clip_bounds;
155     DCHECK(c->getDeviceClipBounds(&clip_bounds));
156     DCHECK(clip_bounds == c->imageInfo().bounds());
157 #endif
158   }
159   ValidateStateStack();
160   origin_tainted_by_content_ = false;
161 }
162 
ConvertCanvasStyleToUnionType(CanvasStyle * style,StringOrCanvasGradientOrCanvasPattern & return_value)163 static inline void ConvertCanvasStyleToUnionType(
164     CanvasStyle* style,
165     StringOrCanvasGradientOrCanvasPattern& return_value) {
166   if (CanvasGradient* gradient = style->GetCanvasGradient()) {
167     return_value.SetCanvasGradient(gradient);
168     return;
169   }
170   if (CanvasPattern* pattern = style->GetCanvasPattern()) {
171     return_value.SetCanvasPattern(pattern);
172     return;
173   }
174   return_value.SetString(style->GetColor());
175 }
176 
strokeStyle(StringOrCanvasGradientOrCanvasPattern & return_value) const177 void BaseRenderingContext2D::strokeStyle(
178     StringOrCanvasGradientOrCanvasPattern& return_value) const {
179   ConvertCanvasStyleToUnionType(GetState().StrokeStyle(), return_value);
180 }
181 
setStrokeStyle(const StringOrCanvasGradientOrCanvasPattern & style)182 void BaseRenderingContext2D::setStrokeStyle(
183     const StringOrCanvasGradientOrCanvasPattern& style) {
184   DCHECK(!style.IsNull());
185 
186   String color_string;
187   CanvasStyle* canvas_style = nullptr;
188   if (style.IsString()) {
189     color_string = style.GetAsString();
190     if (color_string == GetState().UnparsedStrokeColor())
191       return;
192     Color parsed_color = 0;
193     if (!ParseColorOrCurrentColor(parsed_color, color_string))
194       return;
195     if (GetState().StrokeStyle()->IsEquivalentRGBA(parsed_color.Rgb())) {
196       ModifiableState().SetUnparsedStrokeColor(color_string);
197       return;
198     }
199     canvas_style = MakeGarbageCollected<CanvasStyle>(parsed_color.Rgb());
200   } else if (style.IsCanvasGradient()) {
201     canvas_style =
202         MakeGarbageCollected<CanvasStyle>(style.GetAsCanvasGradient());
203   } else if (style.IsCanvasPattern()) {
204     CanvasPattern* canvas_pattern = style.GetAsCanvasPattern();
205 
206     if (!origin_tainted_by_content_ && !canvas_pattern->OriginClean())
207       SetOriginTaintedByContent();
208 
209     canvas_style = MakeGarbageCollected<CanvasStyle>(canvas_pattern);
210   }
211 
212   DCHECK(canvas_style);
213 
214   ModifiableState().SetStrokeStyle(canvas_style);
215   ModifiableState().SetUnparsedStrokeColor(color_string);
216   ModifiableState().ClearResolvedFilter();
217 }
218 
fillStyle(StringOrCanvasGradientOrCanvasPattern & return_value) const219 void BaseRenderingContext2D::fillStyle(
220     StringOrCanvasGradientOrCanvasPattern& return_value) const {
221   ConvertCanvasStyleToUnionType(GetState().FillStyle(), return_value);
222 }
223 
setFillStyle(const StringOrCanvasGradientOrCanvasPattern & style)224 void BaseRenderingContext2D::setFillStyle(
225     const StringOrCanvasGradientOrCanvasPattern& style) {
226   DCHECK(!style.IsNull());
227   ValidateStateStack();
228   String color_string;
229   CanvasStyle* canvas_style = nullptr;
230   if (style.IsString()) {
231     color_string = style.GetAsString();
232     if (color_string == GetState().UnparsedFillColor())
233       return;
234     Color parsed_color = 0;
235     if (!ParseColorOrCurrentColor(parsed_color, color_string))
236       return;
237     if (GetState().FillStyle()->IsEquivalentRGBA(parsed_color.Rgb())) {
238       ModifiableState().SetUnparsedFillColor(color_string);
239       return;
240     }
241     canvas_style = MakeGarbageCollected<CanvasStyle>(parsed_color.Rgb());
242   } else if (style.IsCanvasGradient()) {
243     canvas_style =
244         MakeGarbageCollected<CanvasStyle>(style.GetAsCanvasGradient());
245   } else if (style.IsCanvasPattern()) {
246     CanvasPattern* canvas_pattern = style.GetAsCanvasPattern();
247 
248     if (!origin_tainted_by_content_ && !canvas_pattern->OriginClean()) {
249       SetOriginTaintedByContent();
250     }
251     canvas_style = MakeGarbageCollected<CanvasStyle>(canvas_pattern);
252   }
253 
254   DCHECK(canvas_style);
255   ModifiableState().SetFillStyle(canvas_style);
256   ModifiableState().SetUnparsedFillColor(color_string);
257   ModifiableState().ClearResolvedFilter();
258 }
259 
lineWidth() const260 double BaseRenderingContext2D::lineWidth() const {
261   return GetState().LineWidth();
262 }
263 
setLineWidth(double width)264 void BaseRenderingContext2D::setLineWidth(double width) {
265   if (!std::isfinite(width) || width <= 0)
266     return;
267   if (GetState().LineWidth() == width)
268     return;
269   ModifiableState().SetLineWidth(clampTo<float>(width));
270 }
271 
lineCap() const272 String BaseRenderingContext2D::lineCap() const {
273   return LineCapName(GetState().GetLineCap());
274 }
275 
setLineCap(const String & s)276 void BaseRenderingContext2D::setLineCap(const String& s) {
277   LineCap cap;
278   if (!ParseLineCap(s, cap))
279     return;
280   if (GetState().GetLineCap() == cap)
281     return;
282   ModifiableState().SetLineCap(cap);
283 }
284 
lineJoin() const285 String BaseRenderingContext2D::lineJoin() const {
286   return LineJoinName(GetState().GetLineJoin());
287 }
288 
setLineJoin(const String & s)289 void BaseRenderingContext2D::setLineJoin(const String& s) {
290   LineJoin join;
291   if (!ParseLineJoin(s, join))
292     return;
293   if (GetState().GetLineJoin() == join)
294     return;
295   ModifiableState().SetLineJoin(join);
296 }
297 
miterLimit() const298 double BaseRenderingContext2D::miterLimit() const {
299   return GetState().MiterLimit();
300 }
301 
setMiterLimit(double limit)302 void BaseRenderingContext2D::setMiterLimit(double limit) {
303   if (!std::isfinite(limit) || limit <= 0)
304     return;
305   if (GetState().MiterLimit() == limit)
306     return;
307   ModifiableState().SetMiterLimit(clampTo<float>(limit));
308 }
309 
shadowOffsetX() const310 double BaseRenderingContext2D::shadowOffsetX() const {
311   return GetState().ShadowOffset().Width();
312 }
313 
setShadowOffsetX(double x)314 void BaseRenderingContext2D::setShadowOffsetX(double x) {
315   if (!std::isfinite(x))
316     return;
317   if (GetState().ShadowOffset().Width() == x)
318     return;
319   ModifiableState().SetShadowOffsetX(clampTo<float>(x));
320 }
321 
shadowOffsetY() const322 double BaseRenderingContext2D::shadowOffsetY() const {
323   return GetState().ShadowOffset().Height();
324 }
325 
setShadowOffsetY(double y)326 void BaseRenderingContext2D::setShadowOffsetY(double y) {
327   if (!std::isfinite(y))
328     return;
329   if (GetState().ShadowOffset().Height() == y)
330     return;
331   ModifiableState().SetShadowOffsetY(clampTo<float>(y));
332 }
333 
shadowBlur() const334 double BaseRenderingContext2D::shadowBlur() const {
335   return GetState().ShadowBlur();
336 }
337 
setShadowBlur(double blur)338 void BaseRenderingContext2D::setShadowBlur(double blur) {
339   if (!std::isfinite(blur) || blur < 0)
340     return;
341   if (GetState().ShadowBlur() == blur)
342     return;
343   ModifiableState().SetShadowBlur(clampTo<float>(blur));
344 }
345 
shadowColor() const346 String BaseRenderingContext2D::shadowColor() const {
347   return Color(GetState().ShadowColor()).Serialized();
348 }
349 
setShadowColor(const String & color_string)350 void BaseRenderingContext2D::setShadowColor(const String& color_string) {
351   Color color;
352   if (!ParseColorOrCurrentColor(color, color_string))
353     return;
354   if (GetState().ShadowColor() == color)
355     return;
356   ModifiableState().SetShadowColor(color.Rgb());
357 }
358 
getLineDash() const359 const Vector<double>& BaseRenderingContext2D::getLineDash() const {
360   return GetState().LineDash();
361 }
362 
LineDashSequenceIsValid(const Vector<double> & dash)363 static bool LineDashSequenceIsValid(const Vector<double>& dash) {
364   return std::all_of(dash.begin(), dash.end(),
365                      [](double d) { return std::isfinite(d) && d >= 0; });
366 }
367 
setLineDash(const Vector<double> & dash)368 void BaseRenderingContext2D::setLineDash(const Vector<double>& dash) {
369   if (!LineDashSequenceIsValid(dash))
370     return;
371   ModifiableState().SetLineDash(dash);
372 }
373 
lineDashOffset() const374 double BaseRenderingContext2D::lineDashOffset() const {
375   return GetState().LineDashOffset();
376 }
377 
setLineDashOffset(double offset)378 void BaseRenderingContext2D::setLineDashOffset(double offset) {
379   if (!std::isfinite(offset) || GetState().LineDashOffset() == offset)
380     return;
381   ModifiableState().SetLineDashOffset(clampTo<float>(offset));
382 }
383 
globalAlpha() const384 double BaseRenderingContext2D::globalAlpha() const {
385   return GetState().GlobalAlpha();
386 }
387 
setGlobalAlpha(double alpha)388 void BaseRenderingContext2D::setGlobalAlpha(double alpha) {
389   if (!(alpha >= 0 && alpha <= 1))
390     return;
391   if (GetState().GlobalAlpha() == alpha)
392     return;
393   ModifiableState().SetGlobalAlpha(alpha);
394 }
395 
globalCompositeOperation() const396 String BaseRenderingContext2D::globalCompositeOperation() const {
397   return CompositeOperatorName(
398       CompositeOperatorFromSkBlendMode(GetState().GlobalComposite()),
399       BlendModeFromSkBlendMode(GetState().GlobalComposite()));
400 }
401 
setGlobalCompositeOperation(const String & operation)402 void BaseRenderingContext2D::setGlobalCompositeOperation(
403     const String& operation) {
404   CompositeOperator op = kCompositeSourceOver;
405   BlendMode blend_mode = BlendMode::kNormal;
406   if (!ParseCompositeAndBlendMode(operation, op, blend_mode))
407     return;
408   SkBlendMode sk_blend_mode = WebCoreCompositeToSkiaComposite(op, blend_mode);
409   if (GetState().GlobalComposite() == sk_blend_mode)
410     return;
411   ModifiableState().SetGlobalComposite(sk_blend_mode);
412 }
413 
filter() const414 String BaseRenderingContext2D::filter() const {
415   return GetState().UnparsedFilter();
416 }
417 
setFilter(const ExecutionContext * execution_context,const String & filter_string)418 void BaseRenderingContext2D::setFilter(
419     const ExecutionContext* execution_context,
420     const String& filter_string) {
421   if (filter_string == GetState().UnparsedFilter())
422     return;
423 
424   const CSSValue* filter_value = CSSParser::ParseSingleValue(
425       CSSPropertyID::kFilter, filter_string,
426       MakeGarbageCollected<CSSParserContext>(
427           kHTMLStandardMode, execution_context->GetSecureContextMode()));
428 
429   if (!filter_value || filter_value->IsCSSWideKeyword())
430     return;
431 
432   ModifiableState().SetUnparsedFilter(filter_string);
433   ModifiableState().SetFilter(filter_value);
434   SnapshotStateForFilter();
435 }
436 
scale(double sx,double sy)437 void BaseRenderingContext2D::scale(double sx, double sy) {
438   cc::PaintCanvas* c = GetOrCreatePaintCanvas();
439   if (!c)
440     return;
441 
442   if (!std::isfinite(sx) || !std::isfinite(sy))
443     return;
444 
445   AffineTransform new_transform = GetState().Transform();
446   float fsx = clampTo<float>(sx);
447   float fsy = clampTo<float>(sy);
448   new_transform.ScaleNonUniform(fsx, fsy);
449   if (GetState().Transform() == new_transform)
450     return;
451 
452   ModifiableState().SetTransform(new_transform);
453   if (!GetState().IsTransformInvertible())
454     return;
455 
456   c->scale(fsx, fsy);
457   path_.Transform(AffineTransform().ScaleNonUniform(1.0 / fsx, 1.0 / fsy));
458 }
459 
rotate(double angle_in_radians)460 void BaseRenderingContext2D::rotate(double angle_in_radians) {
461   cc::PaintCanvas* c = GetOrCreatePaintCanvas();
462   if (!c)
463     return;
464 
465   if (!std::isfinite(angle_in_radians))
466     return;
467 
468   AffineTransform new_transform = GetState().Transform();
469   new_transform.RotateRadians(angle_in_radians);
470   if (GetState().Transform() == new_transform)
471     return;
472 
473   ModifiableState().SetTransform(new_transform);
474   if (!GetState().IsTransformInvertible())
475     return;
476   c->rotate(clampTo<float>(angle_in_radians * (180.0 / kPiFloat)));
477   path_.Transform(AffineTransform().RotateRadians(-angle_in_radians));
478 }
479 
translate(double tx,double ty)480 void BaseRenderingContext2D::translate(double tx, double ty) {
481   cc::PaintCanvas* c = GetOrCreatePaintCanvas();
482   if (!c)
483     return;
484   if (!GetState().IsTransformInvertible())
485     return;
486 
487   if (!std::isfinite(tx) || !std::isfinite(ty))
488     return;
489 
490   AffineTransform new_transform = GetState().Transform();
491   // clamp to float to avoid float cast overflow when used as SkScalar
492   float ftx = clampTo<float>(tx);
493   float fty = clampTo<float>(ty);
494   new_transform.Translate(ftx, fty);
495   if (GetState().Transform() == new_transform)
496     return;
497 
498   ModifiableState().SetTransform(new_transform);
499   if (!GetState().IsTransformInvertible())
500     return;
501   c->translate(ftx, fty);
502   path_.Transform(AffineTransform().Translate(-ftx, -fty));
503 }
504 
transform(double m11,double m12,double m21,double m22,double dx,double dy)505 void BaseRenderingContext2D::transform(double m11,
506                                        double m12,
507                                        double m21,
508                                        double m22,
509                                        double dx,
510                                        double dy) {
511   cc::PaintCanvas* c = GetOrCreatePaintCanvas();
512   if (!c)
513     return;
514 
515   if (!std::isfinite(m11) || !std::isfinite(m21) || !std::isfinite(dx) ||
516       !std::isfinite(m12) || !std::isfinite(m22) || !std::isfinite(dy))
517     return;
518 
519   // clamp to float to avoid float cast overflow when used as SkScalar
520   float fm11 = clampTo<float>(m11);
521   float fm12 = clampTo<float>(m12);
522   float fm21 = clampTo<float>(m21);
523   float fm22 = clampTo<float>(m22);
524   float fdx = clampTo<float>(dx);
525   float fdy = clampTo<float>(dy);
526 
527   AffineTransform transform(fm11, fm12, fm21, fm22, fdx, fdy);
528   AffineTransform new_transform = GetState().Transform() * transform;
529   if (GetState().Transform() == new_transform)
530     return;
531 
532   ModifiableState().SetTransform(new_transform);
533   if (!GetState().IsTransformInvertible())
534     return;
535 
536   c->concat(AffineTransformToSkMatrix(transform));
537   path_.Transform(transform.Inverse());
538 }
539 
resetTransform()540 void BaseRenderingContext2D::resetTransform() {
541   cc::PaintCanvas* c = GetOrCreatePaintCanvas();
542   if (!c)
543     return;
544 
545   AffineTransform ctm = GetState().Transform();
546   bool invertible_ctm = GetState().IsTransformInvertible();
547   // It is possible that CTM is identity while CTM is not invertible.
548   // When CTM becomes non-invertible, realizeSaves() can make CTM identity.
549   if (ctm.IsIdentity() && invertible_ctm)
550     return;
551 
552   // resetTransform() resolves the non-invertible CTM state.
553   ModifiableState().ResetTransform();
554   c->setMatrix(AffineTransformToSkMatrix(AffineTransform()));
555 
556   if (invertible_ctm)
557     path_.Transform(ctm);
558   // When else, do nothing because all transform methods didn't update m_path
559   // when CTM became non-invertible.
560   // It means that resetTransform() restores m_path just before CTM became
561   // non-invertible.
562 }
563 
setTransform(double m11,double m12,double m21,double m22,double dx,double dy)564 void BaseRenderingContext2D::setTransform(double m11,
565                                           double m12,
566                                           double m21,
567                                           double m22,
568                                           double dx,
569                                           double dy) {
570   if (!std::isfinite(m11) || !std::isfinite(m21) || !std::isfinite(dx) ||
571       !std::isfinite(m12) || !std::isfinite(m22) || !std::isfinite(dy))
572     return;
573 
574   resetTransform();
575   // clamp to float to avoid float cast overflow when used as SkScalar
576   float fm11 = clampTo<float>(m11);
577   float fm12 = clampTo<float>(m12);
578   float fm21 = clampTo<float>(m21);
579   float fm22 = clampTo<float>(m22);
580   float fdx = clampTo<float>(dx);
581   float fdy = clampTo<float>(dy);
582 
583   transform(fm11, fm12, fm21, fm22, fdx, fdy);
584 }
585 
setTransform(DOMMatrix2DInit * transform,ExceptionState & exception_state)586 void BaseRenderingContext2D::setTransform(DOMMatrix2DInit* transform,
587                                           ExceptionState& exception_state) {
588   DOMMatrixReadOnly* m =
589       DOMMatrixReadOnly::fromMatrix2D(transform, exception_state);
590 
591   if (!m)
592     return;
593 
594   setTransform(m->m11(), m->m12(), m->m21(), m->m22(), m->m41(), m->m42());
595 }
596 
getTransform()597 DOMMatrix* BaseRenderingContext2D::getTransform() {
598   const AffineTransform& t = GetState().Transform();
599   DOMMatrix* m = DOMMatrix::Create();
600   m->setA(t.A());
601   m->setB(t.B());
602   m->setC(t.C());
603   m->setD(t.D());
604   m->setE(t.E());
605   m->setF(t.F());
606   return m;
607 }
608 
beginPath()609 void BaseRenderingContext2D::beginPath() {
610   path_.Clear();
611 }
612 
IsFullCanvasCompositeMode(SkBlendMode op)613 bool BaseRenderingContext2D::IsFullCanvasCompositeMode(SkBlendMode op) {
614   // See 4.8.11.1.3 Compositing
615   // CompositeSourceAtop and CompositeDestinationOut are not listed here as the
616   // platforms already implement the specification's behavior.
617   return op == SkBlendMode::kSrcIn || op == SkBlendMode::kSrcOut ||
618          op == SkBlendMode::kDstIn || op == SkBlendMode::kDstATop;
619 }
620 
DrawPathInternal(const Path & path,CanvasRenderingContext2DState::PaintType paint_type,SkPathFillType fill_type)621 void BaseRenderingContext2D::DrawPathInternal(
622     const Path& path,
623     CanvasRenderingContext2DState::PaintType paint_type,
624     SkPathFillType fill_type) {
625   if (path.IsEmpty())
626     return;
627 
628   SkPath sk_path = path.GetSkPath();
629   FloatRect bounds = path.BoundingRect();
630   if (std::isnan(bounds.X()) || std::isnan(bounds.Y()) ||
631       std::isnan(bounds.Width()) || std::isnan(bounds.Height()))
632     return;
633   sk_path.setFillType(fill_type);
634 
635   if (paint_type == CanvasRenderingContext2DState::kStrokePaintType)
636     InflateStrokeRect(bounds);
637 
638   if (!GetOrCreatePaintCanvas())
639     return;
640 
641   Draw([&sk_path](cc::PaintCanvas* c, const PaintFlags* flags)  // draw lambda
642        { c->drawPath(sk_path, *flags); },
643        [](const SkIRect& rect)  // overdraw test lambda
644        { return false; },
645        bounds, paint_type);
646 }
647 
ParseWinding(const String & winding_rule_string)648 static SkPathFillType ParseWinding(const String& winding_rule_string) {
649   if (winding_rule_string == "nonzero")
650     return SkPathFillType::kWinding;
651   if (winding_rule_string == "evenodd")
652     return SkPathFillType::kEvenOdd;
653 
654   NOTREACHED();
655   return SkPathFillType::kEvenOdd;
656 }
657 
fill(const String & winding_rule_string)658 void BaseRenderingContext2D::fill(const String& winding_rule_string) {
659   DrawPathInternal(path_, CanvasRenderingContext2DState::kFillPaintType,
660                    ParseWinding(winding_rule_string));
661 }
662 
fill(Path2D * dom_path,const String & winding_rule_string)663 void BaseRenderingContext2D::fill(Path2D* dom_path,
664                                   const String& winding_rule_string) {
665   DrawPathInternal(dom_path->GetPath(),
666                    CanvasRenderingContext2DState::kFillPaintType,
667                    ParseWinding(winding_rule_string));
668 }
669 
stroke()670 void BaseRenderingContext2D::stroke() {
671   DrawPathInternal(path_, CanvasRenderingContext2DState::kStrokePaintType);
672 }
673 
stroke(Path2D * dom_path)674 void BaseRenderingContext2D::stroke(Path2D* dom_path) {
675   DrawPathInternal(dom_path->GetPath(),
676                    CanvasRenderingContext2DState::kStrokePaintType);
677 }
678 
fillRect(double x,double y,double width,double height)679 void BaseRenderingContext2D::fillRect(double x,
680                                       double y,
681                                       double width,
682                                       double height) {
683   if (!ValidateRectForCanvas(x, y, width, height))
684     return;
685 
686   if (!GetOrCreatePaintCanvas())
687     return;
688 
689   // clamp to float to avoid float cast overflow when used as SkScalar
690   AdjustRectForCanvas(x, y, width, height);
691   float fx = clampTo<float>(x);
692   float fy = clampTo<float>(y);
693   float fwidth = clampTo<float>(width);
694   float fheight = clampTo<float>(height);
695 
696   // We are assuming that if the pattern is not accelerated and the current
697   // canvas is accelerated, the texture of the pattern will not be able to be
698   // moved to the texture of the canvas receiving the pattern (because if the
699   // pattern was unaccelerated is because it was not possible to hold that image
700   // in an accelerated texture - that is, into the GPU). That's why we disable
701   // the acceleration to be sure that it will work.
702   if (IsAccelerated() && GetState().HasPattern() &&
703       !GetState().PatternIsAccelerated())
704     DisableAcceleration();
705   SkRect rect = SkRect::MakeXYWH(fx, fy, fwidth, fheight);
706   Draw([&rect](cc::PaintCanvas* c, const PaintFlags* flags)  // draw lambda
707        { c->drawRect(rect, *flags); },
708        [&rect, this](const SkIRect& clip_bounds)  // overdraw test lambda
709        { return RectContainsTransformedRect(rect, clip_bounds); },
710        rect, CanvasRenderingContext2DState::kFillPaintType);
711 }
712 
StrokeRectOnCanvas(const FloatRect & rect,cc::PaintCanvas * canvas,const PaintFlags * flags)713 static void StrokeRectOnCanvas(const FloatRect& rect,
714                                cc::PaintCanvas* canvas,
715                                const PaintFlags* flags) {
716   DCHECK_EQ(flags->getStyle(), PaintFlags::kStroke_Style);
717   if ((rect.Width() > 0) != (rect.Height() > 0)) {
718     // When stroking, we must skip the zero-dimension segments
719     SkPath path;
720     path.moveTo(rect.X(), rect.Y());
721     path.lineTo(rect.MaxX(), rect.MaxY());
722     path.close();
723     canvas->drawPath(path, *flags);
724     return;
725   }
726   canvas->drawRect(rect, *flags);
727 }
728 
strokeRect(double x,double y,double width,double height)729 void BaseRenderingContext2D::strokeRect(double x,
730                                         double y,
731                                         double width,
732                                         double height) {
733   if (!ValidateRectForCanvas(x, y, width, height))
734     return;
735 
736   if (!GetOrCreatePaintCanvas())
737     return;
738 
739   // clamp to float to avoid float cast overflow when used as SkScalar
740   AdjustRectForCanvas(x, y, width, height);
741   float fx = clampTo<float>(x);
742   float fy = clampTo<float>(y);
743   float fwidth = clampTo<float>(width);
744   float fheight = clampTo<float>(height);
745 
746   SkRect rect = SkRect::MakeXYWH(fx, fy, fwidth, fheight);
747   FloatRect bounds = rect;
748   InflateStrokeRect(bounds);
749 
750   if (!ValidateRectForCanvas(bounds.X(), bounds.Y(), bounds.Width(),
751                              bounds.Height()))
752     return;
753 
754   Draw([&rect](cc::PaintCanvas* c, const PaintFlags* flags)  // draw lambda
755        { StrokeRectOnCanvas(rect, c, flags); },
756        [](const SkIRect& clip_bounds)  // overdraw test lambda
757        { return false; },
758        bounds, CanvasRenderingContext2DState::kStrokePaintType);
759 }
760 
ClipInternal(const Path & path,const String & winding_rule_string)761 void BaseRenderingContext2D::ClipInternal(const Path& path,
762                                           const String& winding_rule_string) {
763   cc::PaintCanvas* c = GetOrCreatePaintCanvas();
764   if (!c) {
765     return;
766   }
767   if (!GetState().IsTransformInvertible()) {
768     return;
769   }
770 
771   SkPath sk_path = path.GetSkPath();
772   sk_path.setFillType(ParseWinding(winding_rule_string));
773   ModifiableState().ClipPath(sk_path, clip_antialiasing_);
774   c->clipPath(sk_path, SkClipOp::kIntersect,
775               clip_antialiasing_ == kAntiAliased);
776 }
777 
clip(const String & winding_rule_string)778 void BaseRenderingContext2D::clip(const String& winding_rule_string) {
779   ClipInternal(path_, winding_rule_string);
780 }
781 
clip(Path2D * dom_path,const String & winding_rule_string)782 void BaseRenderingContext2D::clip(Path2D* dom_path,
783                                   const String& winding_rule_string) {
784   ClipInternal(dom_path->GetPath(), winding_rule_string);
785 }
786 
isPointInPath(const double x,const double y,const String & winding_rule_string)787 bool BaseRenderingContext2D::isPointInPath(const double x,
788                                            const double y,
789                                            const String& winding_rule_string) {
790   return IsPointInPathInternal(path_, x, y, winding_rule_string);
791 }
792 
isPointInPath(Path2D * dom_path,const double x,const double y,const String & winding_rule_string)793 bool BaseRenderingContext2D::isPointInPath(Path2D* dom_path,
794                                            const double x,
795                                            const double y,
796                                            const String& winding_rule_string) {
797   return IsPointInPathInternal(dom_path->GetPath(), x, y, winding_rule_string);
798 }
799 
IsPointInPathInternal(const Path & path,const double x,const double y,const String & winding_rule_string)800 bool BaseRenderingContext2D::IsPointInPathInternal(
801     const Path& path,
802     const double x,
803     const double y,
804     const String& winding_rule_string) {
805   cc::PaintCanvas* c = GetOrCreatePaintCanvas();
806   if (!c)
807     return false;
808   if (!GetState().IsTransformInvertible())
809     return false;
810 
811   if (!std::isfinite(x) || !std::isfinite(y))
812     return false;
813   FloatPoint point(clampTo<float>(x), clampTo<float>(y));
814   AffineTransform ctm = GetState().Transform();
815   FloatPoint transformed_point = ctm.Inverse().MapPoint(point);
816 
817   return path.Contains(transformed_point,
818                        SkFillTypeToWindRule(ParseWinding(winding_rule_string)));
819 }
820 
isPointInStroke(const double x,const double y)821 bool BaseRenderingContext2D::isPointInStroke(const double x, const double y) {
822   return IsPointInStrokeInternal(path_, x, y);
823 }
824 
isPointInStroke(Path2D * dom_path,const double x,const double y)825 bool BaseRenderingContext2D::isPointInStroke(Path2D* dom_path,
826                                              const double x,
827                                              const double y) {
828   return IsPointInStrokeInternal(dom_path->GetPath(), x, y);
829 }
830 
IsPointInStrokeInternal(const Path & path,const double x,const double y)831 bool BaseRenderingContext2D::IsPointInStrokeInternal(const Path& path,
832                                                      const double x,
833                                                      const double y) {
834   cc::PaintCanvas* c = GetOrCreatePaintCanvas();
835   if (!c)
836     return false;
837   if (!GetState().IsTransformInvertible())
838     return false;
839 
840   if (!std::isfinite(x) || !std::isfinite(y))
841     return false;
842   FloatPoint point(clampTo<float>(x), clampTo<float>(y));
843   AffineTransform ctm = GetState().Transform();
844   FloatPoint transformed_point = ctm.Inverse().MapPoint(point);
845 
846   StrokeData stroke_data;
847   stroke_data.SetThickness(GetState().LineWidth());
848   stroke_data.SetLineCap(GetState().GetLineCap());
849   stroke_data.SetLineJoin(GetState().GetLineJoin());
850   stroke_data.SetMiterLimit(GetState().MiterLimit());
851   Vector<float> line_dash(GetState().LineDash().size());
852   std::copy(GetState().LineDash().begin(), GetState().LineDash().end(),
853             line_dash.begin());
854   stroke_data.SetLineDash(line_dash, GetState().LineDashOffset());
855   return path.StrokeContains(transformed_point, stroke_data);
856 }
857 
clearRect(double x,double y,double width,double height)858 void BaseRenderingContext2D::clearRect(double x,
859                                        double y,
860                                        double width,
861                                        double height) {
862   if (!ValidateRectForCanvas(x, y, width, height))
863     return;
864 
865   cc::PaintCanvas* c = GetOrCreatePaintCanvas();
866   if (!c)
867     return;
868   if (!GetState().IsTransformInvertible())
869     return;
870 
871   SkIRect clip_bounds;
872   if (!c->getDeviceClipBounds(&clip_bounds))
873     return;
874 
875   PaintFlags clear_flags;
876   clear_flags.setBlendMode(SkBlendMode::kClear);
877   clear_flags.setStyle(PaintFlags::kFill_Style);
878 
879   // clamp to float to avoid float cast overflow when used as SkScalar
880   AdjustRectForCanvas(x, y, width, height);
881   float fx = clampTo<float>(x);
882   float fy = clampTo<float>(y);
883   float fwidth = clampTo<float>(width);
884   float fheight = clampTo<float>(height);
885 
886   FloatRect rect(fx, fy, fwidth, fheight);
887   if (RectContainsTransformedRect(rect, clip_bounds)) {
888     CheckOverdraw(rect, &clear_flags, CanvasRenderingContext2DState::kNoImage,
889                   kClipFill);
890     c = GetPaintCanvas();  // Check overdraw may have swapped the PaintCanvas
891     c->drawRect(rect, clear_flags);
892     DidDraw(clip_bounds);
893   } else {
894     SkIRect dirty_rect;
895     if (ComputeDirtyRect(rect, clip_bounds, &dirty_rect)) {
896       c->drawRect(rect, clear_flags);
897       DidDraw(dirty_rect);
898     }
899   }
900 }
901 
NormalizeRect(const FloatRect & rect)902 static inline FloatRect NormalizeRect(const FloatRect& rect) {
903   return FloatRect(std::min(rect.X(), rect.MaxX()),
904                    std::min(rect.Y(), rect.MaxY()),
905                    std::max(rect.Width(), -rect.Width()),
906                    std::max(rect.Height(), -rect.Height()));
907 }
908 
ClipRectsToImageRect(const FloatRect & image_rect,FloatRect * src_rect,FloatRect * dst_rect)909 static inline void ClipRectsToImageRect(const FloatRect& image_rect,
910                                         FloatRect* src_rect,
911                                         FloatRect* dst_rect) {
912   if (image_rect.Contains(*src_rect))
913     return;
914 
915   // Compute the src to dst transform
916   FloatSize scale(dst_rect->Size().Width() / src_rect->Size().Width(),
917                   dst_rect->Size().Height() / src_rect->Size().Height());
918   FloatPoint scaled_src_location = src_rect->Location();
919   scaled_src_location.Scale(scale.Width(), scale.Height());
920   FloatSize offset = dst_rect->Location() - scaled_src_location;
921 
922   src_rect->Intersect(image_rect);
923 
924   // To clip the destination rectangle in the same proportion, transform the
925   // clipped src rect
926   *dst_rect = *src_rect;
927   dst_rect->Scale(scale.Width(), scale.Height());
928   dst_rect->Move(offset);
929 }
930 
ToImageSourceInternal(const CanvasImageSourceUnion & value,ExceptionState & exception_state)931 static inline CanvasImageSource* ToImageSourceInternal(
932     const CanvasImageSourceUnion& value,
933     ExceptionState& exception_state) {
934   if (value.IsCSSImageValue()) {
935     return value.GetAsCSSImageValue();
936   }
937   if (value.IsHTMLImageElement())
938     return value.GetAsHTMLImageElement();
939   if (value.IsHTMLVideoElement()) {
940     HTMLVideoElement* video = value.GetAsHTMLVideoElement();
941     video->VideoWillBeDrawnToCanvas();
942     return video;
943   }
944   if (value.IsSVGImageElement())
945     return value.GetAsSVGImageElement();
946   if (value.IsHTMLCanvasElement()) {
947     if (static_cast<HTMLCanvasElement*>(value.GetAsHTMLCanvasElement())
948             ->Size()
949             .IsEmpty()) {
950       exception_state.ThrowDOMException(
951           DOMExceptionCode::kInvalidStateError,
952           String::Format("The image argument is a canvas element with a width "
953                          "or height of 0."));
954       return nullptr;
955     }
956     return value.GetAsHTMLCanvasElement();
957   }
958   if (value.IsImageBitmap()) {
959     if (static_cast<ImageBitmap*>(value.GetAsImageBitmap())->IsNeutered()) {
960       exception_state.ThrowDOMException(
961           DOMExceptionCode::kInvalidStateError,
962           String::Format("The image source is detached"));
963       return nullptr;
964     }
965     return value.GetAsImageBitmap();
966   }
967   if (value.IsOffscreenCanvas()) {
968     if (static_cast<OffscreenCanvas*>(value.GetAsOffscreenCanvas())
969             ->IsNeutered()) {
970       exception_state.ThrowDOMException(
971           DOMExceptionCode::kInvalidStateError,
972           String::Format("The image source is detached"));
973       return nullptr;
974     }
975     if (static_cast<OffscreenCanvas*>(value.GetAsOffscreenCanvas())
976             ->Size()
977             .IsEmpty()) {
978       exception_state.ThrowDOMException(
979           DOMExceptionCode::kInvalidStateError,
980           String::Format("The image argument is an OffscreenCanvas element "
981                          "with a width or height of 0."));
982       return nullptr;
983     }
984     return value.GetAsOffscreenCanvas();
985   }
986   NOTREACHED();
987   return nullptr;
988 }
989 
drawImage(ScriptState * script_state,const CanvasImageSourceUnion & image_source,double x,double y,ExceptionState & exception_state)990 void BaseRenderingContext2D::drawImage(
991     ScriptState* script_state,
992     const CanvasImageSourceUnion& image_source,
993     double x,
994     double y,
995     ExceptionState& exception_state) {
996   CanvasImageSource* image_source_internal =
997       ToImageSourceInternal(image_source, exception_state);
998   if (!image_source_internal)
999     return;
1000   RespectImageOrientationEnum respect_orientation = RespectImageOrientation();
1001   FloatSize default_object_size(Width(), Height());
1002   FloatSize source_rect_size = image_source_internal->ElementSize(
1003       default_object_size, respect_orientation);
1004   FloatSize dest_rect_size = image_source_internal->DefaultDestinationSize(
1005       default_object_size, respect_orientation);
1006   drawImage(script_state, image_source_internal, 0, 0, source_rect_size.Width(),
1007             source_rect_size.Height(), x, y, dest_rect_size.Width(),
1008             dest_rect_size.Height(), exception_state);
1009 }
1010 
drawImage(ScriptState * script_state,const CanvasImageSourceUnion & image_source,double x,double y,double width,double height,ExceptionState & exception_state)1011 void BaseRenderingContext2D::drawImage(
1012     ScriptState* script_state,
1013     const CanvasImageSourceUnion& image_source,
1014     double x,
1015     double y,
1016     double width,
1017     double height,
1018     ExceptionState& exception_state) {
1019   CanvasImageSource* image_source_internal =
1020       ToImageSourceInternal(image_source, exception_state);
1021   if (!image_source_internal)
1022     return;
1023   FloatSize default_object_size(this->Width(), this->Height());
1024   FloatSize source_rect_size = image_source_internal->ElementSize(
1025       default_object_size, RespectImageOrientation());
1026   drawImage(script_state, image_source_internal, 0, 0, source_rect_size.Width(),
1027             source_rect_size.Height(), x, y, width, height, exception_state);
1028 }
1029 
drawImage(ScriptState * script_state,const CanvasImageSourceUnion & image_source,double sx,double sy,double sw,double sh,double dx,double dy,double dw,double dh,ExceptionState & exception_state)1030 void BaseRenderingContext2D::drawImage(
1031     ScriptState* script_state,
1032     const CanvasImageSourceUnion& image_source,
1033     double sx,
1034     double sy,
1035     double sw,
1036     double sh,
1037     double dx,
1038     double dy,
1039     double dw,
1040     double dh,
1041     ExceptionState& exception_state) {
1042   CanvasImageSource* image_source_internal =
1043       ToImageSourceInternal(image_source, exception_state);
1044   if (!image_source_internal)
1045     return;
1046   drawImage(script_state, image_source_internal, sx, sy, sw, sh, dx, dy, dw, dh,
1047             exception_state);
1048 }
1049 
ShouldDrawImageAntialiased(const FloatRect & dest_rect) const1050 bool BaseRenderingContext2D::ShouldDrawImageAntialiased(
1051     const FloatRect& dest_rect) const {
1052   if (!GetState().ShouldAntialias())
1053     return false;
1054   cc::PaintCanvas* c = GetPaintCanvas();
1055   DCHECK(c);
1056 
1057   const SkMatrix& ctm = c->getTotalMatrix();
1058   // Don't disable anti-aliasing if we're rotated or skewed.
1059   if (!ctm.rectStaysRect())
1060     return true;
1061   // Check if the dimensions of the destination are "small" (less than one
1062   // device pixel). To prevent sudden drop-outs. Since we know that
1063   // kRectStaysRect_Mask is set, the matrix either has scale and no skew or
1064   // vice versa. We can query the kAffine_Mask flag to determine which case
1065   // it is.
1066   // FIXME: This queries the CTM while drawing, which is generally
1067   // discouraged. Always drawing with AA can negatively impact performance
1068   // though - that's why it's not always on.
1069   SkScalar width_expansion, height_expansion;
1070   if (ctm.getType() & SkMatrix::kAffine_Mask) {
1071     width_expansion = ctm[SkMatrix::kMSkewY];
1072     height_expansion = ctm[SkMatrix::kMSkewX];
1073   } else {
1074     width_expansion = ctm[SkMatrix::kMScaleX];
1075     height_expansion = ctm[SkMatrix::kMScaleY];
1076   }
1077   return dest_rect.Width() * fabs(width_expansion) < 1 ||
1078          dest_rect.Height() * fabs(height_expansion) < 1;
1079 }
1080 
DrawImageInternal(cc::PaintCanvas * c,CanvasImageSource * image_source,Image * image,const FloatRect & src_rect,const FloatRect & dst_rect,const PaintFlags * flags)1081 void BaseRenderingContext2D::DrawImageInternal(cc::PaintCanvas* c,
1082                                                CanvasImageSource* image_source,
1083                                                Image* image,
1084                                                const FloatRect& src_rect,
1085                                                const FloatRect& dst_rect,
1086                                                const PaintFlags* flags) {
1087   int initial_save_count = c->getSaveCount();
1088   PaintFlags image_flags = *flags;
1089 
1090   if (flags->getImageFilter()) {
1091     SkMatrix ctm = c->getTotalMatrix();
1092     SkMatrix inv_ctm;
1093     if (!ctm.invert(&inv_ctm)) {
1094       // There is an earlier check for invertibility, but the arithmetic
1095       // in AffineTransform is not exactly identical, so it is possible
1096       // for SkMatrix to find the transform to be non-invertible at this stage.
1097       // crbug.com/504687
1098       return;
1099     }
1100     SkRect bounds = dst_rect;
1101     ctm.mapRect(&bounds);
1102     if (!bounds.isFinite()) {
1103       // There is an earlier check for the correctness of the bounds, but it is
1104       // possible that after applying the matrix transformation we get a faulty
1105       // set of bounds, so we want to catch this asap and avoid sending a draw
1106       // command. crbug.com/1039125
1107       // We want to do this before the save command is sent.
1108       return;
1109     }
1110     c->save();
1111     c->concat(inv_ctm);
1112 
1113     PaintFlags layer_flags;
1114     layer_flags.setBlendMode(flags->getBlendMode());
1115     layer_flags.setImageFilter(flags->getImageFilter());
1116 
1117     c->saveLayer(&bounds, &layer_flags);
1118     c->concat(ctm);
1119     image_flags.setBlendMode(SkBlendMode::kSrcOver);
1120     image_flags.setImageFilter(nullptr);
1121   }
1122 
1123   if (!image_source->IsVideoElement()) {
1124     // We always use the image-orientation property on the canvas element
1125     // because the alternative would result in complex rules depending on
1126     // the source of the image.
1127     RespectImageOrientationEnum respect_orientation = RespectImageOrientation();
1128     FloatRect corrected_src_rect = src_rect;
1129     if (respect_orientation == kRespectImageOrientation &&
1130         !image->HasDefaultOrientation()) {
1131       corrected_src_rect = image->CorrectSrcRectForImageOrientation(
1132           image->SizeAsFloat(kRespectImageOrientation), src_rect);
1133     }
1134     image_flags.setAntiAlias(ShouldDrawImageAntialiased(dst_rect));
1135     image->Draw(c, image_flags, dst_rect, corrected_src_rect,
1136                 respect_orientation, Image::kDoNotClampImageToSourceRect,
1137                 Image::kSyncDecode);
1138   } else {
1139     c->save();
1140     c->clipRect(dst_rect);
1141     c->translate(dst_rect.X(), dst_rect.Y());
1142     c->scale(dst_rect.Width() / src_rect.Width(),
1143              dst_rect.Height() / src_rect.Height());
1144     c->translate(-src_rect.X(), -src_rect.Y());
1145     HTMLVideoElement* video = static_cast<HTMLVideoElement*>(image_source);
1146     video->PaintCurrentFrame(
1147         c,
1148         IntRect(IntPoint(), IntSize(video->videoWidth(), video->videoHeight())),
1149         &image_flags);
1150   };
1151 
1152   c->restoreToCount(initial_save_count);
1153 }
1154 
SetOriginTaintedByContent()1155 void BaseRenderingContext2D::SetOriginTaintedByContent() {
1156   SetOriginTainted();
1157   origin_tainted_by_content_ = true;
1158   for (auto& state : state_stack_)
1159     state->ClearResolvedFilter();
1160 }
1161 
drawImage(ScriptState * script_state,CanvasImageSource * image_source,double sx,double sy,double sw,double sh,double dx,double dy,double dw,double dh,ExceptionState & exception_state)1162 void BaseRenderingContext2D::drawImage(ScriptState* script_state,
1163                                        CanvasImageSource* image_source,
1164                                        double sx,
1165                                        double sy,
1166                                        double sw,
1167                                        double sh,
1168                                        double dx,
1169                                        double dy,
1170                                        double dw,
1171                                        double dh,
1172                                        ExceptionState& exception_state) {
1173   if (!GetOrCreatePaintCanvas())
1174     return;
1175 
1176   base::TimeTicks start_time = base::TimeTicks::Now();
1177 
1178   scoped_refptr<Image> image;
1179   FloatSize default_object_size(Width(), Height());
1180   SourceImageStatus source_image_status = kInvalidSourceImageStatus;
1181   if (!image_source->IsVideoElement()) {
1182     AccelerationHint hint =
1183         IsAccelerated() ? kPreferAcceleration : kPreferNoAcceleration;
1184     image = image_source->GetSourceImageForCanvas(&source_image_status, hint,
1185                                                   default_object_size);
1186     if (source_image_status == kUndecodableSourceImageStatus) {
1187       exception_state.ThrowDOMException(
1188           DOMExceptionCode::kInvalidStateError,
1189           "The HTMLImageElement provided is in the 'broken' state.");
1190     }
1191     if (!image || !image->width() || !image->height())
1192       return;
1193   } else {
1194     if (!static_cast<HTMLVideoElement*>(image_source)->HasAvailableVideoFrame())
1195       return;
1196   }
1197 
1198   if (!std::isfinite(dx) || !std::isfinite(dy) || !std::isfinite(dw) ||
1199       !std::isfinite(dh) || !std::isfinite(sx) || !std::isfinite(sy) ||
1200       !std::isfinite(sw) || !std::isfinite(sh) || !dw || !dh || !sw || !sh)
1201     return;
1202 
1203   // clamp to float to avoid float cast overflow when used as SkScalar
1204   float fsx = clampTo<float>(sx);
1205   float fsy = clampTo<float>(sy);
1206   float fsw = clampTo<float>(sw);
1207   float fsh = clampTo<float>(sh);
1208   float fdx = clampTo<float>(dx);
1209   float fdy = clampTo<float>(dy);
1210   float fdw = clampTo<float>(dw);
1211   float fdh = clampTo<float>(dh);
1212 
1213   FloatRect src_rect = NormalizeRect(FloatRect(fsx, fsy, fsw, fsh));
1214   FloatRect dst_rect = NormalizeRect(FloatRect(fdx, fdy, fdw, fdh));
1215   FloatSize image_size =
1216       image_source->ElementSize(default_object_size, RespectImageOrientation());
1217 
1218   ClipRectsToImageRect(FloatRect(FloatPoint(), image_size), &src_rect,
1219                        &dst_rect);
1220 
1221   if (src_rect.IsEmpty())
1222     return;
1223 
1224   ValidateStateStack();
1225 
1226   WillDrawImage(image_source);
1227 
1228   ValidateStateStack();
1229 
1230   if (!origin_tainted_by_content_ && WouldTaintOrigin(image_source))
1231     SetOriginTaintedByContent();
1232 
1233   Draw(
1234       [this, &image_source, &image, &src_rect, dst_rect](
1235           cc::PaintCanvas* c, const PaintFlags* flags)  // draw lambda
1236       {
1237         DrawImageInternal(c, image_source, image.get(), src_rect, dst_rect,
1238                           flags);
1239       },
1240       [this, &dst_rect](const SkIRect& clip_bounds)  // overdraw test lambda
1241       { return RectContainsTransformedRect(dst_rect, clip_bounds); },
1242       dst_rect, CanvasRenderingContext2DState::kImagePaintType,
1243       image_source->IsOpaque()
1244           ? CanvasRenderingContext2DState::kOpaqueImage
1245           : CanvasRenderingContext2DState::kNonOpaqueImage);
1246 
1247   ValidateStateStack();
1248   bool source_is_canvas = false;
1249   if (!IsPaint2D()) {
1250     std::string image_source_name;
1251     if (image_source->IsCanvasElement()) {
1252       image_source_name = "Canvas";
1253       source_is_canvas = true;
1254     } else if (image_source->IsCSSImageValue()) {
1255       image_source_name = "CssImage";
1256     } else if (image_source->IsImageElement()) {
1257       image_source_name = "ImageElement";
1258     } else if (image_source->IsImageBitmap()) {
1259       image_source_name = "ImageBitmap";
1260     } else if (image_source->IsOffscreenCanvas()) {
1261       image_source_name = "OffscreenCanvas";
1262       source_is_canvas = true;
1263     } else if (image_source->IsSVGSource()) {
1264       image_source_name = "SVG";
1265     } else if (image_source->IsVideoElement()) {
1266       image_source_name = "Video";
1267     } else {  // Unknown source.
1268       image_source_name = "Unknown";
1269     }
1270 
1271     std::string duration_histogram_name =
1272         "Blink.Canvas.DrawImage.Duration." + image_source_name;
1273     std::string size_histogram_name =
1274         "Blink.Canvas.DrawImage.SqrtNumberOfPixels." + image_source_name;
1275 
1276     if (CanCreateCanvas2dResourceProvider() && IsAccelerated()) {
1277       if (source_is_canvas)
1278         size_histogram_name.append(".GPU");
1279       duration_histogram_name.append(".GPU");
1280     } else {
1281       if (source_is_canvas)
1282         size_histogram_name.append(".CPU");
1283       duration_histogram_name.append(".CPU");
1284     }
1285 
1286     base::TimeDelta elapsed = base::TimeTicks::Now() - start_time;
1287 
1288     // TODO(crbug.com/983261) Change this to use UmaHistogramMicrosecondsTimes.
1289     base::UmaHistogramMicrosecondsTimesUnderTenMilliseconds(
1290         duration_histogram_name, elapsed);
1291 
1292     float sqrt_pixels_float =
1293         std::sqrt(dst_rect.Width()) * std::sqrt(dst_rect.Height());
1294     // If sqrt_pixels_float overflows as int CheckedNumeric will store it
1295     // as invalid, then ValueOrDefault will return the maximum int.
1296     base::CheckedNumeric<int> sqrt_pixels = sqrt_pixels_float;
1297     base::UmaHistogramCustomCounts(
1298         size_histogram_name,
1299         sqrt_pixels.ValueOrDefault(std::numeric_limits<int>::max()), 1, 5000,
1300         50);
1301   }
1302 }
1303 
ClearCanvas()1304 void BaseRenderingContext2D::ClearCanvas() {
1305   FloatRect canvas_rect(0, 0, Width(), Height());
1306   CheckOverdraw(canvas_rect, nullptr, CanvasRenderingContext2DState::kNoImage,
1307                 kClipFill);
1308   cc::PaintCanvas* c = GetOrCreatePaintCanvas();
1309   if (c)
1310     c->clear(HasAlpha() ? SK_ColorTRANSPARENT : SK_ColorBLACK);
1311 }
1312 
RectContainsTransformedRect(const FloatRect & rect,const SkIRect & transformed_rect) const1313 bool BaseRenderingContext2D::RectContainsTransformedRect(
1314     const FloatRect& rect,
1315     const SkIRect& transformed_rect) const {
1316   FloatQuad quad(rect);
1317   FloatQuad transformed_quad(
1318       FloatRect(transformed_rect.x(), transformed_rect.y(),
1319                 transformed_rect.width(), transformed_rect.height()));
1320   return GetState().Transform().MapQuad(quad).ContainsQuad(transformed_quad);
1321 }
1322 
createLinearGradient(double x0,double y0,double x1,double y1)1323 CanvasGradient* BaseRenderingContext2D::createLinearGradient(double x0,
1324                                                              double y0,
1325                                                              double x1,
1326                                                              double y1) {
1327   if (!std::isfinite(x0) || !std::isfinite(y0) || !std::isfinite(x1) ||
1328       !std::isfinite(y1))
1329     return nullptr;
1330 
1331   // clamp to float to avoid float cast overflow
1332   float fx0 = clampTo<float>(x0);
1333   float fy0 = clampTo<float>(y0);
1334   float fx1 = clampTo<float>(x1);
1335   float fy1 = clampTo<float>(y1);
1336 
1337   auto* gradient = MakeGarbageCollected<CanvasGradient>(FloatPoint(fx0, fy0),
1338                                                         FloatPoint(fx1, fy1));
1339   return gradient;
1340 }
1341 
createRadialGradient(double x0,double y0,double r0,double x1,double y1,double r1,ExceptionState & exception_state)1342 CanvasGradient* BaseRenderingContext2D::createRadialGradient(
1343     double x0,
1344     double y0,
1345     double r0,
1346     double x1,
1347     double y1,
1348     double r1,
1349     ExceptionState& exception_state) {
1350   if (r0 < 0 || r1 < 0) {
1351     exception_state.ThrowDOMException(
1352         DOMExceptionCode::kIndexSizeError,
1353         String::Format("The %s provided is less than 0.",
1354                        r0 < 0 ? "r0" : "r1"));
1355     return nullptr;
1356   }
1357 
1358   if (!std::isfinite(x0) || !std::isfinite(y0) || !std::isfinite(r0) ||
1359       !std::isfinite(x1) || !std::isfinite(y1) || !std::isfinite(r1))
1360     return nullptr;
1361 
1362   // clamp to float to avoid float cast overflow
1363   float fx0 = clampTo<float>(x0);
1364   float fy0 = clampTo<float>(y0);
1365   float fr0 = clampTo<float>(r0);
1366   float fx1 = clampTo<float>(x1);
1367   float fy1 = clampTo<float>(y1);
1368   float fr1 = clampTo<float>(r1);
1369 
1370   auto* gradient = MakeGarbageCollected<CanvasGradient>(
1371       FloatPoint(fx0, fy0), fr0, FloatPoint(fx1, fy1), fr1);
1372   return gradient;
1373 }
1374 
createPattern(ScriptState * script_state,const CanvasImageSourceUnion & image_source,const String & repetition_type,ExceptionState & exception_state)1375 CanvasPattern* BaseRenderingContext2D::createPattern(
1376     ScriptState* script_state,
1377     const CanvasImageSourceUnion& image_source,
1378     const String& repetition_type,
1379     ExceptionState& exception_state) {
1380   CanvasImageSource* image_source_internal =
1381       ToImageSourceInternal(image_source, exception_state);
1382   if (!image_source_internal) {
1383     return nullptr;
1384   }
1385 
1386   return createPattern(script_state, image_source_internal, repetition_type,
1387                        exception_state);
1388 }
1389 
createPattern(ScriptState * script_state,CanvasImageSource * image_source,const String & repetition_type,ExceptionState & exception_state)1390 CanvasPattern* BaseRenderingContext2D::createPattern(
1391     ScriptState* script_state,
1392     CanvasImageSource* image_source,
1393     const String& repetition_type,
1394     ExceptionState& exception_state) {
1395   if (!image_source) {
1396     return nullptr;
1397   }
1398 
1399   Pattern::RepeatMode repeat_mode =
1400       CanvasPattern::ParseRepetitionType(repetition_type, exception_state);
1401   if (exception_state.HadException())
1402     return nullptr;
1403 
1404   SourceImageStatus status;
1405 
1406   FloatSize default_object_size(Width(), Height());
1407   scoped_refptr<Image> image_for_rendering =
1408       image_source->GetSourceImageForCanvas(&status, kPreferNoAcceleration,
1409                                             default_object_size);
1410 
1411   switch (status) {
1412     case kNormalSourceImageStatus:
1413       break;
1414     case kZeroSizeCanvasSourceImageStatus:
1415       exception_state.ThrowDOMException(
1416           DOMExceptionCode::kInvalidStateError,
1417           String::Format("The canvas %s is 0.",
1418                          image_source
1419                                  ->ElementSize(default_object_size,
1420                                                RespectImageOrientation())
1421                                  .Width()
1422                              ? "height"
1423                              : "width"));
1424       return nullptr;
1425     case kUndecodableSourceImageStatus:
1426       exception_state.ThrowDOMException(
1427           DOMExceptionCode::kInvalidStateError,
1428           "Source image is in the 'broken' state.");
1429       return nullptr;
1430     case kInvalidSourceImageStatus:
1431       image_for_rendering = BitmapImage::Create();
1432       break;
1433     case kIncompleteSourceImageStatus:
1434       return nullptr;
1435     default:
1436       NOTREACHED();
1437       return nullptr;
1438   }
1439   DCHECK(image_for_rendering);
1440 
1441   bool origin_clean = !WouldTaintOrigin(image_source);
1442 
1443   return MakeGarbageCollected<CanvasPattern>(std::move(image_for_rendering),
1444                                              repeat_mode, origin_clean);
1445 }
1446 
ComputeDirtyRect(const FloatRect & local_rect,SkIRect * dirty_rect)1447 bool BaseRenderingContext2D::ComputeDirtyRect(const FloatRect& local_rect,
1448                                               SkIRect* dirty_rect) {
1449   SkIRect clip_bounds;
1450   if (!GetOrCreatePaintCanvas() ||
1451       !GetPaintCanvas()->getDeviceClipBounds(&clip_bounds))
1452     return false;
1453   return ComputeDirtyRect(local_rect, clip_bounds, dirty_rect);
1454 }
1455 
ComputeDirtyRect(const FloatRect & local_rect,const SkIRect & transformed_clip_bounds,SkIRect * dirty_rect)1456 bool BaseRenderingContext2D::ComputeDirtyRect(
1457     const FloatRect& local_rect,
1458     const SkIRect& transformed_clip_bounds,
1459     SkIRect* dirty_rect) {
1460   FloatRect canvas_rect = GetState().Transform().MapRect(local_rect);
1461 
1462   if (AlphaChannel(GetState().ShadowColor())) {
1463     FloatRect shadow_rect(canvas_rect);
1464     shadow_rect.Move(GetState().ShadowOffset());
1465     shadow_rect.Inflate(clampTo<float>(GetState().ShadowBlur()));
1466     canvas_rect.Unite(shadow_rect);
1467   }
1468 
1469   SkIRect canvas_i_rect;
1470   static_cast<SkRect>(canvas_rect).roundOut(&canvas_i_rect);
1471   if (!canvas_i_rect.intersect(transformed_clip_bounds))
1472     return false;
1473 
1474   if (dirty_rect)
1475     *dirty_rect = canvas_i_rect;
1476 
1477   return true;
1478 }
1479 
1480 ImageDataColorSettings*
GetColorSettingsAsImageDataColorSettings() const1481 BaseRenderingContext2D::GetColorSettingsAsImageDataColorSettings() const {
1482   ImageDataColorSettings* color_settings = ImageDataColorSettings::Create();
1483   color_settings->setColorSpace(ColorSpaceAsString());
1484   if (PixelFormat() == CanvasPixelFormat::kF16)
1485     color_settings->setStorageFormat(kFloat32ArrayStorageFormatName);
1486   return color_settings;
1487 }
1488 
createImageData(ImageData * image_data,ExceptionState & exception_state) const1489 ImageData* BaseRenderingContext2D::createImageData(
1490     ImageData* image_data,
1491     ExceptionState& exception_state) const {
1492   ImageData* result = nullptr;
1493   ImageDataColorSettings* color_settings =
1494       GetColorSettingsAsImageDataColorSettings();
1495   result = ImageData::Create(image_data->Size(), color_settings);
1496   if (!result)
1497     exception_state.ThrowRangeError("Out of memory at ImageData creation");
1498   return result;
1499 }
1500 
createImageData(int sw,int sh,ExceptionState & exception_state) const1501 ImageData* BaseRenderingContext2D::createImageData(
1502     int sw,
1503     int sh,
1504     ExceptionState& exception_state) const {
1505   if (!sw || !sh) {
1506     exception_state.ThrowDOMException(
1507         DOMExceptionCode::kIndexSizeError,
1508         String::Format("The source %s is 0.", sw ? "height" : "width"));
1509     return nullptr;
1510   }
1511 
1512   IntSize size(abs(sw), abs(sh));
1513   ImageData* result = nullptr;
1514   ImageDataColorSettings* color_settings =
1515       GetColorSettingsAsImageDataColorSettings();
1516   result = ImageData::Create(size, color_settings);
1517 
1518   if (!result)
1519     exception_state.ThrowRangeError("Out of memory at ImageData creation");
1520   return result;
1521 }
1522 
createImageData(unsigned width,unsigned height,ImageDataColorSettings * color_settings,ExceptionState & exception_state) const1523 ImageData* BaseRenderingContext2D::createImageData(
1524     unsigned width,
1525     unsigned height,
1526     ImageDataColorSettings* color_settings,
1527     ExceptionState& exception_state) const {
1528   return ImageData::CreateImageData(width, height, color_settings,
1529                                     exception_state);
1530 }
1531 
createImageData(ImageDataArray & data_array,unsigned width,unsigned height,ExceptionState & exception_state) const1532 ImageData* BaseRenderingContext2D::createImageData(
1533     ImageDataArray& data_array,
1534     unsigned width,
1535     unsigned height,
1536     ExceptionState& exception_state) const {
1537   return ImageData::CreateImageData(data_array, width, height,
1538                                     ImageDataColorSettings::Create(),
1539                                     exception_state);
1540 }
1541 
createImageData(ImageDataArray & data_array,unsigned width,unsigned height,ImageDataColorSettings * color_settings,ExceptionState & exception_state) const1542 ImageData* BaseRenderingContext2D::createImageData(
1543     ImageDataArray& data_array,
1544     unsigned width,
1545     unsigned height,
1546     ImageDataColorSettings* color_settings,
1547     ExceptionState& exception_state) const {
1548   return ImageData::CreateImageData(data_array, width, height, color_settings,
1549                                     exception_state);
1550 }
1551 
getImageData(int sx,int sy,int sw,int sh,ExceptionState & exception_state)1552 ImageData* BaseRenderingContext2D::getImageData(
1553     int sx,
1554     int sy,
1555     int sw,
1556     int sh,
1557     ExceptionState& exception_state) {
1558   if (!base::CheckMul(sw, sh).IsValid<int>()) {
1559     exception_state.ThrowRangeError("Out of memory at ImageData creation");
1560     return nullptr;
1561   }
1562 
1563   base::TimeTicks start_time = base::TimeTicks::Now();
1564 
1565   if (!OriginClean()) {
1566     exception_state.ThrowSecurityError(
1567         "The canvas has been tainted by cross-origin data.");
1568   } else if (!sw || !sh) {
1569     exception_state.ThrowDOMException(
1570         DOMExceptionCode::kIndexSizeError,
1571         String::Format("The source %s is 0.", sw ? "height" : "width"));
1572   }
1573 
1574   if (exception_state.HadException())
1575     return nullptr;
1576 
1577   if (sw < 0) {
1578     if (!base::CheckAdd(sx, sw).IsValid<int>()) {
1579       exception_state.ThrowRangeError("Out of memory at ImageData creation");
1580       return nullptr;
1581     }
1582     sx += sw;
1583     sw = base::saturated_cast<int>(base::SafeUnsignedAbs(sw));
1584   }
1585   if (sh < 0) {
1586     if (!base::CheckAdd(sy, sh).IsValid<int>()) {
1587       exception_state.ThrowRangeError("Out of memory at ImageData creation");
1588       return nullptr;
1589     }
1590     sy += sh;
1591     sh = base::saturated_cast<int>(base::SafeUnsignedAbs(sh));
1592   }
1593 
1594   if (!base::CheckAdd(sx, sw).IsValid<int>() ||
1595       !base::CheckAdd(sy, sh).IsValid<int>()) {
1596     exception_state.ThrowRangeError("Out of memory at ImageData creation");
1597     return nullptr;
1598   }
1599 
1600   IntRect image_data_rect(sx, sy, sw, sh);
1601   bool hasResourceProvider = CanCreateCanvas2dResourceProvider();
1602   ImageDataColorSettings* color_settings =
1603       GetColorSettingsAsImageDataColorSettings();
1604   if (!hasResourceProvider || isContextLost()) {
1605     ImageData* result =
1606         ImageData::Create(image_data_rect.Size(), color_settings);
1607     if (!result)
1608       exception_state.ThrowRangeError("Out of memory at ImageData creation");
1609     return result;
1610   }
1611 
1612   const CanvasColorParams& color_params = ColorParams();
1613   // Deferred offscreen canvases might have recorded commands, make sure
1614   // that those get drawn here
1615   FinalizeFrame();
1616   scoped_refptr<StaticBitmapImage> snapshot = GetImage(kPreferNoAcceleration);
1617 
1618   // GetImagedata is faster in Unaccelerated canvases
1619   if (IsAccelerated())
1620     DisableAcceleration();
1621 
1622   size_t size_in_bytes;
1623   if (!StaticBitmapImage::GetSizeInBytes(image_data_rect, color_params)
1624            .AssignIfValid(&size_in_bytes) ||
1625       size_in_bytes > v8::TypedArray::kMaxLength) {
1626     exception_state.ThrowRangeError("Out of memory at ImageData creation");
1627     return nullptr;
1628   }
1629 
1630   bool may_have_stray_area =
1631       IsAccelerated()  // GPU readback may fail silently.
1632       || StaticBitmapImage::MayHaveStrayArea(snapshot, image_data_rect);
1633   ArrayBufferContents::InitializationPolicy initialization_policy =
1634       may_have_stray_area ? ArrayBufferContents::kZeroInitialize
1635                           : ArrayBufferContents::kDontInitialize;
1636 
1637   ArrayBufferContents contents(
1638       size_in_bytes, 1, ArrayBufferContents::kNotShared, initialization_policy);
1639   if (contents.DataLength() != size_in_bytes) {
1640     exception_state.ThrowRangeError("Out of memory at ImageData creation");
1641     return nullptr;
1642   }
1643 
1644   if (!StaticBitmapImage::CopyToByteArray(
1645           snapshot,
1646           base::span<uint8_t>(reinterpret_cast<uint8_t*>(contents.Data()),
1647                               contents.DataLength()),
1648           image_data_rect, color_params)) {
1649     exception_state.ThrowRangeError("Failed to copy image data");
1650     return nullptr;
1651   }
1652 
1653   // Convert pixels to proper storage format if needed
1654   if (PixelFormat() != CanvasColorParams::GetNativeCanvasPixelFormat()) {
1655     ImageDataStorageFormat storage_format =
1656         ImageData::GetImageDataStorageFormat(color_settings->storageFormat());
1657     NotShared<DOMArrayBufferView> array_buffer_view =
1658         ImageData::ConvertPixelsFromCanvasPixelFormatToImageDataStorageFormat(
1659             contents, PixelFormat(), storage_format);
1660     return ImageData::Create(image_data_rect.Size(), array_buffer_view,
1661                              color_settings);
1662   }
1663   if (size_in_bytes > std::numeric_limits<unsigned int>::max()) {
1664     exception_state.ThrowRangeError(
1665         "Buffer size exceeds maximum heap object size.");
1666     return nullptr;
1667   }
1668   DOMArrayBuffer* array_buffer = DOMArrayBuffer::Create(std::move(contents));
1669 
1670   ImageData* imageData = ImageData::Create(
1671       image_data_rect.Size(),
1672       NotShared<DOMUint8ClampedArray>(DOMUint8ClampedArray::Create(
1673           array_buffer, 0, static_cast<unsigned int>(size_in_bytes))),
1674       color_settings);
1675 
1676   if (!IsPaint2D()) {
1677     int scaled_time = getScaledElapsedTime(
1678         image_data_rect.Width(), image_data_rect.Height(), start_time);
1679     if (CanCreateCanvas2dResourceProvider() && IsAccelerated()) {
1680       base::UmaHistogramCounts1000(
1681           "Blink.Canvas.GetImageDataScaledDuration.GPU", scaled_time);
1682     } else {
1683       base::UmaHistogramCounts1000(
1684           "Blink.Canvas.GetImageDataScaledDuration.CPU", scaled_time);
1685     }
1686   }
1687 
1688   return imageData;
1689 }
1690 
getScaledElapsedTime(float width,float height,base::TimeTicks start_time)1691 int BaseRenderingContext2D::getScaledElapsedTime(float width,
1692                                                  float height,
1693                                                  base::TimeTicks start_time) {
1694   base::TimeDelta elapsed_time = base::TimeTicks::Now() - start_time;
1695   float sqrt_pixels = std::sqrt(width) * std::sqrt(height);
1696   float scaled_time_float = elapsed_time.InMicrosecondsF() * 10.0f /
1697                             (sqrt_pixels == 0 ? 1.0f : sqrt_pixels);
1698 
1699   // If scaled_time_float overflows as integer, CheckedNumeric will store it
1700   // as invalid, then ValueOrDefault will return the maximum int.
1701   base::CheckedNumeric<int> checked_scaled_time = scaled_time_float;
1702   return checked_scaled_time.ValueOrDefault(std::numeric_limits<int>::max());
1703 }
1704 
putImageData(ImageData * data,int dx,int dy,ExceptionState & exception_state)1705 void BaseRenderingContext2D::putImageData(ImageData* data,
1706                                           int dx,
1707                                           int dy,
1708                                           ExceptionState& exception_state) {
1709   putImageData(data, dx, dy, 0, 0, data->width(), data->height(),
1710                exception_state);
1711 }
1712 
putImageData(ImageData * data,int dx,int dy,int dirty_x,int dirty_y,int dirty_width,int dirty_height,ExceptionState & exception_state)1713 void BaseRenderingContext2D::putImageData(ImageData* data,
1714                                           int dx,
1715                                           int dy,
1716                                           int dirty_x,
1717                                           int dirty_y,
1718                                           int dirty_width,
1719                                           int dirty_height,
1720                                           ExceptionState& exception_state) {
1721   if (!base::CheckMul(dirty_width, dirty_height).IsValid<int>()) {
1722     return;
1723   }
1724   base::TimeTicks start_time = base::TimeTicks::Now();
1725 
1726   if (data->BufferBase()->IsDetached()) {
1727     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
1728                                       "The source data has been detached.");
1729     return;
1730   }
1731 
1732   bool hasResourceProvider = CanCreateCanvas2dResourceProvider();
1733   if (!hasResourceProvider)
1734     return;
1735 
1736   if (dirty_width < 0) {
1737     if (dirty_x < 0) {
1738       dirty_x = dirty_width = 0;
1739     } else {
1740       dirty_x += dirty_width;
1741       dirty_width =
1742           base::saturated_cast<int>(base::SafeUnsignedAbs(dirty_width));
1743     }
1744   }
1745 
1746   if (dirty_height < 0) {
1747     if (dirty_y < 0) {
1748       dirty_y = dirty_height = 0;
1749     } else {
1750       dirty_y += dirty_height;
1751       dirty_height =
1752           base::saturated_cast<int>(base::SafeUnsignedAbs(dirty_height));
1753     }
1754   }
1755 
1756   IntRect dest_rect(dirty_x, dirty_y, dirty_width, dirty_height);
1757   dest_rect.Intersect(IntRect(0, 0, data->width(), data->height()));
1758   IntSize dest_offset(static_cast<int>(dx), static_cast<int>(dy));
1759   dest_rect.Move(dest_offset);
1760   dest_rect.Intersect(IntRect(0, 0, Width(), Height()));
1761   if (dest_rect.IsEmpty())
1762     return;
1763 
1764   IntRect source_rect(dest_rect);
1765   source_rect.Move(-dest_offset);
1766 
1767   CheckOverdraw(dest_rect, nullptr, CanvasRenderingContext2DState::kNoImage,
1768                 kUntransformedUnclippedFill);
1769 
1770   // Color / format convert ImageData to context 2D settings if needed. Color /
1771   // format conversion is not needed only if context 2D and ImageData are both
1772   // in sRGB color space and use uint8 pixel storage format. We use RGBA pixel
1773   // order for both ImageData and CanvasResourceProvider, therefore no
1774   // additional swizzling is needed.
1775   CanvasColorParams data_color_params = data->GetCanvasColorParams();
1776   CanvasColorParams context_color_params =
1777       CanvasColorParams(ColorParams().ColorSpace(), PixelFormat(), kNonOpaque);
1778   if (data_color_params.NeedsColorConversion(context_color_params) ||
1779       PixelFormat() == CanvasPixelFormat::kF16) {
1780     size_t data_length;
1781     if (!base::CheckMul(data->Size().Area(),
1782                         context_color_params.BytesPerPixel())
1783              .AssignIfValid(&data_length))
1784       return;
1785     std::unique_ptr<uint8_t[]> converted_pixels(new uint8_t[data_length]);
1786     if (data->ImageDataInCanvasColorSettings(
1787             ColorParams().ColorSpace(), PixelFormat(), converted_pixels.get(),
1788             kRGBAColorType)) {
1789       PutByteArray(converted_pixels.get(),
1790                    IntSize(data->width(), data->height()), source_rect,
1791                    IntPoint(dest_offset));
1792     }
1793   } else {
1794     PutByteArray(data->data()->Data(), IntSize(data->width(), data->height()),
1795                  source_rect, IntPoint(dest_offset));
1796   }
1797 
1798   if (!IsPaint2D()) {
1799     int scaled_time =
1800         getScaledElapsedTime(dest_rect.Width(), dest_rect.Height(), start_time);
1801     if (CanCreateCanvas2dResourceProvider() && IsAccelerated()) {
1802       base::UmaHistogramCounts1000(
1803           "Blink.Canvas.PutImageDataScaledDuration.GPU", scaled_time);
1804     } else {
1805       base::UmaHistogramCounts1000(
1806           "Blink.Canvas.PutImageDataScaledDuration.CPU", scaled_time);
1807     }
1808   }
1809 
1810   DidDraw(dest_rect);
1811 }
1812 
PutByteArray(const unsigned char * source,const IntSize & source_size,const IntRect & source_rect,const IntPoint & dest_point)1813 void BaseRenderingContext2D::PutByteArray(const unsigned char* source,
1814                                           const IntSize& source_size,
1815                                           const IntRect& source_rect,
1816                                           const IntPoint& dest_point) {
1817   if (!IsCanvas2DBufferValid())
1818     return;
1819   uint8_t bytes_per_pixel = ColorParams().BytesPerPixel();
1820 
1821   DCHECK_GT(source_rect.Width(), 0);
1822   DCHECK_GT(source_rect.Height(), 0);
1823 
1824   int origin_x = source_rect.X();
1825   int dest_x = dest_point.X() + source_rect.X();
1826   DCHECK_GE(dest_x, 0);
1827   DCHECK_LT(dest_x, Width());
1828   DCHECK_GE(origin_x, 0);
1829   DCHECK_LT(origin_x, source_rect.MaxX());
1830 
1831   int origin_y = source_rect.Y();
1832   int dest_y = dest_point.Y() + source_rect.Y();
1833   DCHECK_GE(dest_y, 0);
1834   DCHECK_LT(dest_y, Height());
1835   DCHECK_GE(origin_y, 0);
1836   DCHECK_LT(origin_y, source_rect.MaxY());
1837 
1838   const size_t src_bytes_per_row = bytes_per_pixel * source_size.Width();
1839   const void* src_addr =
1840       source + origin_y * src_bytes_per_row + origin_x * bytes_per_pixel;
1841 
1842   SkAlphaType alpha_type;
1843   if (kOpaque == ColorParams().GetOpacityMode()) {
1844     // If the surface is opaque, tell it that we are writing opaque
1845     // pixels.  Writing non-opaque pixels to opaque is undefined in
1846     // Skia.  There is some discussion about whether it should be
1847     // defined in skbug.com/6157.  For now, we can get the desired
1848     // behavior (memcpy) by pretending the write is opaque.
1849     alpha_type = kOpaque_SkAlphaType;
1850   } else {
1851     alpha_type = kUnpremul_SkAlphaType;
1852   }
1853 
1854   SkImageInfo info;
1855   if (ColorParams().GetSkColorSpaceForSkSurfaces()) {
1856     info = SkImageInfo::Make(source_rect.Width(), source_rect.Height(),
1857                              ColorParams().GetSkColorType(), alpha_type,
1858                              ColorParams().GetSkColorSpaceForSkSurfaces());
1859     if (info.colorType() == kN32_SkColorType)
1860       info = info.makeColorType(kRGBA_8888_SkColorType);
1861   } else {
1862     info = SkImageInfo::Make(source_rect.Width(), source_rect.Height(),
1863                              kRGBA_8888_SkColorType, alpha_type);
1864   }
1865   WritePixels(info, src_addr, src_bytes_per_row, dest_x, dest_y);
1866 }
1867 
InflateStrokeRect(FloatRect & rect) const1868 void BaseRenderingContext2D::InflateStrokeRect(FloatRect& rect) const {
1869   // Fast approximation of the stroke's bounding rect.
1870   // This yields a slightly oversized rect but is very fast
1871   // compared to Path::strokeBoundingRect().
1872   static const double kRoot2 = sqrtf(2);
1873   double delta = GetState().LineWidth() / 2;
1874   if (GetState().GetLineJoin() == kMiterJoin)
1875     delta *= GetState().MiterLimit();
1876   else if (GetState().GetLineCap() == kSquareCap)
1877     delta *= kRoot2;
1878 
1879   rect.Inflate(clampTo<float>(delta));
1880 }
1881 
imageSmoothingEnabled() const1882 bool BaseRenderingContext2D::imageSmoothingEnabled() const {
1883   return GetState().ImageSmoothingEnabled();
1884 }
1885 
setImageSmoothingEnabled(bool enabled)1886 void BaseRenderingContext2D::setImageSmoothingEnabled(bool enabled) {
1887   if (enabled == GetState().ImageSmoothingEnabled())
1888     return;
1889 
1890   ModifiableState().SetImageSmoothingEnabled(enabled);
1891 }
1892 
imageSmoothingQuality() const1893 String BaseRenderingContext2D::imageSmoothingQuality() const {
1894   return GetState().ImageSmoothingQuality();
1895 }
1896 
setImageSmoothingQuality(const String & quality)1897 void BaseRenderingContext2D::setImageSmoothingQuality(const String& quality) {
1898   if (quality == GetState().ImageSmoothingQuality())
1899     return;
1900 
1901   ModifiableState().SetImageSmoothingQuality(quality);
1902 }
1903 
CheckOverdraw(const SkRect & rect,const PaintFlags * flags,CanvasRenderingContext2DState::ImageType image_type,DrawType draw_type)1904 void BaseRenderingContext2D::CheckOverdraw(
1905     const SkRect& rect,
1906     const PaintFlags* flags,
1907     CanvasRenderingContext2DState::ImageType image_type,
1908     DrawType draw_type) {
1909   cc::PaintCanvas* c = GetOrCreatePaintCanvas();
1910   if (!c)
1911     return;
1912 
1913   SkRect device_rect;
1914   if (draw_type == kUntransformedUnclippedFill) {
1915     device_rect = rect;
1916   } else {
1917     DCHECK_EQ(draw_type, kClipFill);
1918     if (GetState().HasComplexClip())
1919       return;
1920 
1921     SkIRect sk_i_bounds;
1922     if (!c->getDeviceClipBounds(&sk_i_bounds))
1923       return;
1924     device_rect = SkRect::Make(sk_i_bounds);
1925   }
1926 
1927   const SkImageInfo& image_info = c->imageInfo();
1928   if (!device_rect.contains(
1929           SkRect::MakeWH(image_info.width(), image_info.height())))
1930     return;
1931 
1932   bool is_source_over = true;
1933   unsigned alpha = 0xFF;
1934   if (flags) {
1935     if (flags->getLooper() || flags->getImageFilter() || flags->getMaskFilter())
1936       return;
1937 
1938     SkBlendMode mode = flags->getBlendMode();
1939     is_source_over = mode == SkBlendMode::kSrcOver;
1940     if (!is_source_over && mode != SkBlendMode::kSrc &&
1941         mode != SkBlendMode::kClear)
1942       return;  // The code below only knows how to handle Src, SrcOver, and
1943                // Clear
1944 
1945     alpha = flags->getAlpha();
1946 
1947     if (is_source_over &&
1948         image_type == CanvasRenderingContext2DState::kNoImage) {
1949       if (flags->HasShader()) {
1950         if (flags->ShaderIsOpaque() && alpha == 0xFF)
1951           WillOverwriteCanvas();
1952         return;
1953       }
1954     }
1955   }
1956 
1957   if (is_source_over) {
1958     // With source over, we need to certify that alpha == 0xFF for all pixels
1959     if (image_type == CanvasRenderingContext2DState::kNonOpaqueImage)
1960       return;
1961     if (alpha < 0xFF)
1962       return;
1963   }
1964 
1965   WillOverwriteCanvas();
1966 }
1967 
GetFontBaseline(const SimpleFontData & font_data) const1968 float BaseRenderingContext2D::GetFontBaseline(
1969     const SimpleFontData& font_data) const {
1970   return TextMetrics::GetFontBaseline(GetState().GetTextBaseline(), font_data);
1971 }
1972 
textAlign() const1973 String BaseRenderingContext2D::textAlign() const {
1974   return TextAlignName(GetState().GetTextAlign());
1975 }
1976 
setTextAlign(const String & s)1977 void BaseRenderingContext2D::setTextAlign(const String& s) {
1978   TextAlign align;
1979   if (!ParseTextAlign(s, align))
1980     return;
1981   if (GetState().GetTextAlign() == align)
1982     return;
1983   ModifiableState().SetTextAlign(align);
1984 }
1985 
textBaseline() const1986 String BaseRenderingContext2D::textBaseline() const {
1987   return TextBaselineName(GetState().GetTextBaseline());
1988 }
1989 
setTextBaseline(const String & s)1990 void BaseRenderingContext2D::setTextBaseline(const String& s) {
1991   TextBaseline baseline;
1992   if (!ParseTextBaseline(s, baseline))
1993     return;
1994   if (GetState().GetTextBaseline() == baseline)
1995     return;
1996   ModifiableState().SetTextBaseline(baseline);
1997 }
1998 
Trace(Visitor * visitor)1999 void BaseRenderingContext2D::Trace(Visitor* visitor) {
2000   visitor->Trace(state_stack_);
2001 }
2002 
UsageCounters()2003 BaseRenderingContext2D::UsageCounters::UsageCounters()
2004     : num_draw_calls{0, 0, 0, 0, 0, 0, 0},
2005       bounding_box_perimeter_draw_calls{0.0f, 0.0f, 0.0f, 0.0f,
2006                                         0.0f, 0.0f, 0.0f},
2007       bounding_box_area_draw_calls{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
2008       bounding_box_area_fill_type{0.0f, 0.0f, 0.0f, 0.0f},
2009       num_non_convex_fill_path_calls(0),
2010       non_convex_fill_path_area(0.0f),
2011       num_radial_gradients(0),
2012       num_linear_gradients(0),
2013       num_patterns(0),
2014       num_draw_with_complex_clips(0),
2015       num_blurred_shadows(0),
2016       bounding_box_area_times_shadow_blur_squared(0.0f),
2017       bounding_box_perimeter_times_shadow_blur_squared(0.0f),
2018       num_filters(0),
2019       num_get_image_data_calls(0),
2020       area_get_image_data_calls(0.0),
2021       num_put_image_data_calls(0),
2022       area_put_image_data_calls(0.0),
2023       num_clear_rect_calls(0),
2024       num_draw_focus_calls(0),
2025       num_frames_since_reset(0) {}
2026 
2027 }  // namespace blink
2028