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

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

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

Powered by Google App Engine
This is Rietveld 408576698