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