[Web IDL] Specify default values for optional parameters of type 'float' / 'unrestric...
[WebKit.git] / Source / WebCore / html / canvas / CanvasRenderingContext2D.cpp
1 /*
2  * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved.
3  * Copyright (C) 2008, 2010 Nokia Corporation and/or its subsidiary(-ies)
4  * Copyright (C) 2007 Alp Toker <alp@atoker.com>
5  * Copyright (C) 2008 Eric Seidel <eric@webkit.org>
6  * Copyright (C) 2008 Dirk Schulze <krit@webkit.org>
7  * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved.
8  * Copyright (C) 2012 Intel Corporation. All rights reserved.
9  * Copyright (C) 2013, 2014 Adobe Systems Incorporated. All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
21  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
28  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include "config.h"
34 #include "CanvasRenderingContext2D.h"
35
36 #include "CSSFontSelector.h"
37 #include "CSSParser.h"
38 #include "CSSPropertyNames.h"
39 #include "CachedImage.h"
40 #include "CanvasGradient.h"
41 #include "CanvasPattern.h"
42 #include "DOMPath.h"
43 #include "DisplayListRecorder.h"
44 #include "DisplayListReplayer.h"
45 #include "ExceptionCodePlaceholder.h"
46 #include "FloatQuad.h"
47 #include "HTMLImageElement.h"
48 #include "HTMLVideoElement.h"
49 #include "ImageData.h"
50 #include "RenderElement.h"
51 #include "RenderImage.h"
52 #include "RenderLayer.h"
53 #include "RenderTheme.h"
54 #include "SecurityOrigin.h"
55 #include "StrokeStyleApplier.h"
56 #include "StyleProperties.h"
57 #include "StyleResolver.h"
58 #include "TextMetrics.h"
59 #include "TextRun.h"
60 #include "TextStream.h"
61
62 #include <wtf/CheckedArithmetic.h>
63 #include <wtf/MathExtras.h>
64 #include <wtf/NeverDestroyed.h>
65 #include <wtf/text/StringBuilder.h>
66
67 #if USE(CG)
68 #if !PLATFORM(IOS)
69 #include <ApplicationServices/ApplicationServices.h>
70 #endif // !PLATFORM(IOS)
71 #endif
72
73 #if PLATFORM(IOS)
74 #include "Settings.h"
75 #endif
76
77 #if USE(CG)
78 #define DefaultSmoothingQuality SmoothingQuality::Low
79 #else
80 #define DefaultSmoothingQuality SmoothingQuality::Medium
81 #endif
82
83 namespace WebCore {
84
85 using namespace HTMLNames;
86
87 static const int defaultFontSize = 10;
88 static const char* const defaultFontFamily = "sans-serif";
89 static const char* const defaultFont = "10px sans-serif";
90
91 struct DisplayListDrawingContext {
92     WTF_MAKE_FAST_ALLOCATED;
93 public:
94     GraphicsContext context;
95     DisplayList::Recorder recorder;
96     DisplayList::DisplayList displayList;
97     
98     DisplayListDrawingContext(const FloatRect& clip)
99         : recorder(context, displayList, clip, AffineTransform())
100     {
101     }
102 };
103
104 typedef HashMap<const CanvasRenderingContext2D*, std::unique_ptr<DisplayList::DisplayList>> ContextDisplayListHashMap;
105
106 static ContextDisplayListHashMap& contextDisplayListMap()
107 {
108     static NeverDestroyed<ContextDisplayListHashMap> sharedHashMap;
109     return sharedHashMap;
110 }
111
112 class CanvasStrokeStyleApplier : public StrokeStyleApplier {
113 public:
114     CanvasStrokeStyleApplier(CanvasRenderingContext2D* canvasContext)
115         : m_canvasContext(canvasContext)
116     {
117     }
118
119     void strokeStyle(GraphicsContext* c) override
120     {
121         c->setStrokeThickness(m_canvasContext->lineWidth());
122         c->setLineCap(m_canvasContext->getLineCap());
123         c->setLineJoin(m_canvasContext->getLineJoin());
124         c->setMiterLimit(m_canvasContext->miterLimit());
125         const Vector<float>& lineDash = m_canvasContext->getLineDash();
126         DashArray convertedLineDash(lineDash.size());
127         for (size_t i = 0; i < lineDash.size(); ++i)
128             convertedLineDash[i] = static_cast<DashArrayElement>(lineDash[i]);
129         c->setLineDash(convertedLineDash, m_canvasContext->lineDashOffset());
130     }
131
132 private:
133     CanvasRenderingContext2D* m_canvasContext;
134 };
135
136 CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement* canvas, bool usesCSSCompatibilityParseMode, bool usesDashboardCompatibilityMode)
137     : CanvasRenderingContext(canvas)
138     , m_stateStack(1)
139     , m_usesCSSCompatibilityParseMode(usesCSSCompatibilityParseMode)
140 #if ENABLE(DASHBOARD_SUPPORT)
141     , m_usesDashboardCompatibilityMode(usesDashboardCompatibilityMode)
142 #endif
143 {
144 #if !ENABLE(DASHBOARD_SUPPORT)
145     ASSERT_UNUSED(usesDashboardCompatibilityMode, !usesDashboardCompatibilityMode);
146 #endif
147 }
148
149 void CanvasRenderingContext2D::unwindStateStack()
150 {
151     // Ensure that the state stack in the ImageBuffer's context
152     // is cleared before destruction, to avoid assertions in the
153     // GraphicsContext dtor.
154     if (size_t stackSize = m_stateStack.size()) {
155         if (GraphicsContext* context = canvas()->existingDrawingContext()) {
156             while (--stackSize)
157                 context->restore();
158         }
159     }
160 }
161
162 CanvasRenderingContext2D::~CanvasRenderingContext2D()
163 {
164 #if !ASSERT_DISABLED
165     unwindStateStack();
166 #endif
167
168     if (UNLIKELY(tracksDisplayListReplay()))
169         contextDisplayListMap().remove(this);
170 }
171
172 bool CanvasRenderingContext2D::isAccelerated() const
173 {
174 #if USE(IOSURFACE_CANVAS_BACKING_STORE) || ENABLE(ACCELERATED_2D_CANVAS)
175     if (!canvas()->hasCreatedImageBuffer())
176         return false;
177     GraphicsContext* context = drawingContext();
178     return context && context->isAcceleratedContext();
179 #else
180     return false;
181 #endif
182 }
183
184 void CanvasRenderingContext2D::reset()
185 {
186     unwindStateStack();
187     m_stateStack.resize(1);
188     m_stateStack.first() = State();
189     m_path.clear();
190     m_unrealizedSaveCount = 0;
191     
192     m_recordingContext = nullptr;
193 }
194
195 CanvasRenderingContext2D::State::State()
196     : strokeStyle(Color::black)
197     , fillStyle(Color::black)
198     , lineWidth(1)
199     , lineCap(ButtCap)
200     , lineJoin(MiterJoin)
201     , miterLimit(10)
202     , shadowBlur(0)
203     , shadowColor(Color::transparent)
204     , globalAlpha(1)
205     , globalComposite(CompositeSourceOver)
206     , globalBlend(BlendModeNormal)
207     , hasInvertibleTransform(true)
208     , lineDashOffset(0)
209     , imageSmoothingEnabled(true)
210     , imageSmoothingQuality(DefaultSmoothingQuality)
211     , textAlign(StartTextAlign)
212     , textBaseline(AlphabeticTextBaseline)
213     , direction(Direction::Inherit)
214     , unparsedFont(defaultFont)
215 {
216 }
217
218 CanvasRenderingContext2D::State::State(const State& other)
219     : unparsedStrokeColor(other.unparsedStrokeColor)
220     , unparsedFillColor(other.unparsedFillColor)
221     , strokeStyle(other.strokeStyle)
222     , fillStyle(other.fillStyle)
223     , lineWidth(other.lineWidth)
224     , lineCap(other.lineCap)
225     , lineJoin(other.lineJoin)
226     , miterLimit(other.miterLimit)
227     , shadowOffset(other.shadowOffset)
228     , shadowBlur(other.shadowBlur)
229     , shadowColor(other.shadowColor)
230     , globalAlpha(other.globalAlpha)
231     , globalComposite(other.globalComposite)
232     , globalBlend(other.globalBlend)
233     , transform(other.transform)
234     , hasInvertibleTransform(other.hasInvertibleTransform)
235     , lineDashOffset(other.lineDashOffset)
236     , imageSmoothingEnabled(other.imageSmoothingEnabled)
237     , imageSmoothingQuality(other.imageSmoothingQuality)
238     , textAlign(other.textAlign)
239     , textBaseline(other.textBaseline)
240     , direction(other.direction)
241     , unparsedFont(other.unparsedFont)
242     , font(other.font)
243 {
244 }
245
246 CanvasRenderingContext2D::State& CanvasRenderingContext2D::State::operator=(const State& other)
247 {
248     if (this == &other)
249         return *this;
250
251     unparsedStrokeColor = other.unparsedStrokeColor;
252     unparsedFillColor = other.unparsedFillColor;
253     strokeStyle = other.strokeStyle;
254     fillStyle = other.fillStyle;
255     lineWidth = other.lineWidth;
256     lineCap = other.lineCap;
257     lineJoin = other.lineJoin;
258     miterLimit = other.miterLimit;
259     shadowOffset = other.shadowOffset;
260     shadowBlur = other.shadowBlur;
261     shadowColor = other.shadowColor;
262     globalAlpha = other.globalAlpha;
263     globalComposite = other.globalComposite;
264     globalBlend = other.globalBlend;
265     transform = other.transform;
266     hasInvertibleTransform = other.hasInvertibleTransform;
267     imageSmoothingEnabled = other.imageSmoothingEnabled;
268     imageSmoothingQuality = other.imageSmoothingQuality;
269     textAlign = other.textAlign;
270     textBaseline = other.textBaseline;
271     direction = other.direction;
272     unparsedFont = other.unparsedFont;
273     font = other.font;
274
275     return *this;
276 }
277
278 CanvasRenderingContext2D::FontProxy::~FontProxy()
279 {
280     if (realized())
281         m_font.fontSelector()->unregisterForInvalidationCallbacks(*this);
282 }
283
284 CanvasRenderingContext2D::FontProxy::FontProxy(const FontProxy& other)
285     : m_font(other.m_font)
286 {
287     if (realized())
288         m_font.fontSelector()->registerForInvalidationCallbacks(*this);
289 }
290
291 auto CanvasRenderingContext2D::FontProxy::operator=(const FontProxy& other) -> FontProxy&
292 {
293     if (realized())
294         m_font.fontSelector()->unregisterForInvalidationCallbacks(*this);
295
296     m_font = other.m_font;
297
298     if (realized())
299         m_font.fontSelector()->registerForInvalidationCallbacks(*this);
300
301     return *this;
302 }
303
304 inline void CanvasRenderingContext2D::FontProxy::update(FontSelector& selector)
305 {
306     ASSERT(&selector == m_font.fontSelector()); // This is an invariant. We should only ever be registered for callbacks on m_font.m_fonts.m_fontSelector.
307     if (realized())
308         m_font.fontSelector()->unregisterForInvalidationCallbacks(*this);
309     m_font.update(&selector);
310     if (realized())
311         m_font.fontSelector()->registerForInvalidationCallbacks(*this);
312     ASSERT(&selector == m_font.fontSelector());
313 }
314
315 void CanvasRenderingContext2D::FontProxy::fontsNeedUpdate(FontSelector& selector)
316 {
317     ASSERT_ARG(selector, &selector == m_font.fontSelector());
318     ASSERT(realized());
319
320     update(selector);
321 }
322
323 inline void CanvasRenderingContext2D::FontProxy::initialize(FontSelector& fontSelector, RenderStyle& newStyle)
324 {
325     // Beware! m_font.fontSelector() might not point to document.fontSelector()!
326     ASSERT(newStyle.fontCascade().fontSelector() == &fontSelector);
327     if (realized())
328         m_font.fontSelector()->unregisterForInvalidationCallbacks(*this);
329     m_font = newStyle.fontCascade();
330     m_font.update(&fontSelector);
331     ASSERT(&fontSelector == m_font.fontSelector());
332     m_font.fontSelector()->registerForInvalidationCallbacks(*this);
333 }
334
335 inline FontMetrics CanvasRenderingContext2D::FontProxy::fontMetrics() const
336 {
337     return m_font.fontMetrics();
338 }
339
340 inline const FontCascadeDescription& CanvasRenderingContext2D::FontProxy::fontDescription() const
341 {
342     return m_font.fontDescription();
343 }
344
345 inline float CanvasRenderingContext2D::FontProxy::width(const TextRun& textRun) const
346 {
347     return m_font.width(textRun);
348 }
349
350 inline void CanvasRenderingContext2D::FontProxy::drawBidiText(GraphicsContext& context, const TextRun& run, const FloatPoint& point, FontCascade::CustomFontNotReadyAction action) const
351 {
352     context.drawBidiText(m_font, run, point, action);
353 }
354
355 void CanvasRenderingContext2D::realizeSavesLoop()
356 {
357     ASSERT(m_unrealizedSaveCount);
358     ASSERT(m_stateStack.size() >= 1);
359     GraphicsContext* context = drawingContext();
360     do {
361         m_stateStack.append(state());
362         if (context)
363             context->save();
364     } while (--m_unrealizedSaveCount);
365 }
366
367 void CanvasRenderingContext2D::restore()
368 {
369     if (m_unrealizedSaveCount) {
370         --m_unrealizedSaveCount;
371         return;
372     }
373     ASSERT(m_stateStack.size() >= 1);
374     if (m_stateStack.size() <= 1)
375         return;
376     m_path.transform(state().transform);
377     m_stateStack.removeLast();
378     if (Optional<AffineTransform> inverse = state().transform.inverse())
379         m_path.transform(inverse.value());
380     GraphicsContext* c = drawingContext();
381     if (!c)
382         return;
383     c->restore();
384 }
385
386 void CanvasRenderingContext2D::setStrokeStyle(CanvasStyle style)
387 {
388     if (!style.isValid())
389         return;
390
391     if (state().strokeStyle.isValid() && state().strokeStyle.isEquivalentColor(style))
392         return;
393
394     if (style.isCurrentColor()) {
395         if (style.hasOverrideAlpha())
396             style = CanvasStyle(colorWithOverrideAlpha(currentColor(canvas()), style.overrideAlpha()));
397         else
398             style = CanvasStyle(currentColor(canvas()));
399     } else
400         checkOrigin(style.canvasPattern());
401
402     realizeSaves();
403     State& state = modifiableState();
404     state.strokeStyle = style;
405     GraphicsContext* c = drawingContext();
406     if (!c)
407         return;
408     state.strokeStyle.applyStrokeColor(c);
409     state.unparsedStrokeColor = String();
410 }
411
412 void CanvasRenderingContext2D::setFillStyle(CanvasStyle style)
413 {
414     if (!style.isValid())
415         return;
416
417     if (state().fillStyle.isValid() && state().fillStyle.isEquivalentColor(style))
418         return;
419
420     if (style.isCurrentColor()) {
421         if (style.hasOverrideAlpha())
422             style = CanvasStyle(colorWithOverrideAlpha(currentColor(canvas()), style.overrideAlpha()));
423         else
424             style = CanvasStyle(currentColor(canvas()));
425     } else
426         checkOrigin(style.canvasPattern());
427
428     realizeSaves();
429     State& state = modifiableState();
430     state.fillStyle = style;
431     GraphicsContext* c = drawingContext();
432     if (!c)
433         return;
434     state.fillStyle.applyFillColor(c);
435     state.unparsedFillColor = String();
436 }
437
438 float CanvasRenderingContext2D::lineWidth() const
439 {
440     return state().lineWidth;
441 }
442
443 void CanvasRenderingContext2D::setLineWidth(float width)
444 {
445     if (!(std::isfinite(width) && width > 0))
446         return;
447     if (state().lineWidth == width)
448         return;
449     realizeSaves();
450     modifiableState().lineWidth = width;
451     GraphicsContext* c = drawingContext();
452     if (!c)
453         return;
454     c->setStrokeThickness(width);
455 }
456
457 String CanvasRenderingContext2D::lineCap() const
458 {
459     return lineCapName(state().lineCap);
460 }
461
462 void CanvasRenderingContext2D::setLineCap(const String& s)
463 {
464     LineCap cap;
465     if (!parseLineCap(s, cap))
466         return;
467     if (state().lineCap == cap)
468         return;
469     realizeSaves();
470     modifiableState().lineCap = cap;
471     GraphicsContext* c = drawingContext();
472     if (!c)
473         return;
474     c->setLineCap(cap);
475 }
476
477 String CanvasRenderingContext2D::lineJoin() const
478 {
479     return lineJoinName(state().lineJoin);
480 }
481
482 void CanvasRenderingContext2D::setLineJoin(const String& s)
483 {
484     LineJoin join;
485     if (!parseLineJoin(s, join))
486         return;
487     if (state().lineJoin == join)
488         return;
489     realizeSaves();
490     modifiableState().lineJoin = join;
491     GraphicsContext* c = drawingContext();
492     if (!c)
493         return;
494     c->setLineJoin(join);
495 }
496
497 float CanvasRenderingContext2D::miterLimit() const
498 {
499     return state().miterLimit;
500 }
501
502 void CanvasRenderingContext2D::setMiterLimit(float limit)
503 {
504     if (!(std::isfinite(limit) && limit > 0))
505         return;
506     if (state().miterLimit == limit)
507         return;
508     realizeSaves();
509     modifiableState().miterLimit = limit;
510     GraphicsContext* c = drawingContext();
511     if (!c)
512         return;
513     c->setMiterLimit(limit);
514 }
515
516 float CanvasRenderingContext2D::shadowOffsetX() const
517 {
518     return state().shadowOffset.width();
519 }
520
521 void CanvasRenderingContext2D::setShadowOffsetX(float x)
522 {
523     if (!std::isfinite(x))
524         return;
525     if (state().shadowOffset.width() == x)
526         return;
527     realizeSaves();
528     modifiableState().shadowOffset.setWidth(x);
529     applyShadow();
530 }
531
532 float CanvasRenderingContext2D::shadowOffsetY() const
533 {
534     return state().shadowOffset.height();
535 }
536
537 void CanvasRenderingContext2D::setShadowOffsetY(float y)
538 {
539     if (!std::isfinite(y))
540         return;
541     if (state().shadowOffset.height() == y)
542         return;
543     realizeSaves();
544     modifiableState().shadowOffset.setHeight(y);
545     applyShadow();
546 }
547
548 float CanvasRenderingContext2D::shadowBlur() const
549 {
550     return state().shadowBlur;
551 }
552
553 void CanvasRenderingContext2D::setShadowBlur(float blur)
554 {
555     if (!(std::isfinite(blur) && blur >= 0))
556         return;
557     if (state().shadowBlur == blur)
558         return;
559     realizeSaves();
560     modifiableState().shadowBlur = blur;
561     applyShadow();
562 }
563
564 String CanvasRenderingContext2D::shadowColor() const
565 {
566     return Color(state().shadowColor).serialized();
567 }
568
569 void CanvasRenderingContext2D::setShadowColor(const String& color)
570 {
571     RGBA32 rgba;
572     if (!parseColorOrCurrentColor(rgba, color, canvas()))
573         return;
574     if (state().shadowColor == rgba)
575         return;
576     realizeSaves();
577     modifiableState().shadowColor = rgba;
578     applyShadow();
579 }
580
581 const Vector<float>& CanvasRenderingContext2D::getLineDash() const
582 {
583     return state().lineDash;
584 }
585
586 static bool lineDashSequenceIsValid(const Vector<float>& dash)
587 {
588     for (size_t i = 0; i < dash.size(); i++) {
589         if (!std::isfinite(dash[i]) || dash[i] < 0)
590             return false;
591     }
592     return true;
593 }
594
595 void CanvasRenderingContext2D::setLineDash(const Vector<float>& dash)
596 {
597     if (!lineDashSequenceIsValid(dash))
598         return;
599
600     realizeSaves();
601     modifiableState().lineDash = dash;
602     // Spec requires the concatenation of two copies the dash list when the
603     // number of elements is odd
604     if (dash.size() % 2)
605         modifiableState().lineDash.appendVector(dash);
606
607     applyLineDash();
608 }
609
610 void CanvasRenderingContext2D::setWebkitLineDash(const Vector<float>& dash)
611 {
612     if (!lineDashSequenceIsValid(dash))
613         return;
614
615     realizeSaves();
616     modifiableState().lineDash = dash;
617
618     applyLineDash();
619 }
620
621 float CanvasRenderingContext2D::lineDashOffset() const
622 {
623     return state().lineDashOffset;
624 }
625
626 void CanvasRenderingContext2D::setLineDashOffset(float offset)
627 {
628     if (!std::isfinite(offset) || state().lineDashOffset == offset)
629         return;
630
631     realizeSaves();
632     modifiableState().lineDashOffset = offset;
633     applyLineDash();
634 }
635
636 float CanvasRenderingContext2D::webkitLineDashOffset() const
637 {
638     return lineDashOffset();
639 }
640
641 void CanvasRenderingContext2D::setWebkitLineDashOffset(float offset)
642 {
643     setLineDashOffset(offset);
644 }
645
646 void CanvasRenderingContext2D::applyLineDash() const
647 {
648     GraphicsContext* c = drawingContext();
649     if (!c)
650         return;
651     DashArray convertedLineDash(state().lineDash.size());
652     for (size_t i = 0; i < state().lineDash.size(); ++i)
653         convertedLineDash[i] = static_cast<DashArrayElement>(state().lineDash[i]);
654     c->setLineDash(convertedLineDash, state().lineDashOffset);
655 }
656
657 float CanvasRenderingContext2D::globalAlpha() const
658 {
659     return state().globalAlpha;
660 }
661
662 void CanvasRenderingContext2D::setGlobalAlpha(float alpha)
663 {
664     if (!(alpha >= 0 && alpha <= 1))
665         return;
666     if (state().globalAlpha == alpha)
667         return;
668     realizeSaves();
669     modifiableState().globalAlpha = alpha;
670     GraphicsContext* c = drawingContext();
671     if (!c)
672         return;
673     c->setAlpha(alpha);
674 }
675
676 String CanvasRenderingContext2D::globalCompositeOperation() const
677 {
678     return compositeOperatorName(state().globalComposite, state().globalBlend);
679 }
680
681 void CanvasRenderingContext2D::setGlobalCompositeOperation(const String& operation)
682 {
683     CompositeOperator op = CompositeSourceOver;
684     BlendMode blendMode = BlendModeNormal;
685     if (!parseCompositeAndBlendOperator(operation, op, blendMode))
686         return;
687     if ((state().globalComposite == op) && (state().globalBlend == blendMode))
688         return;
689     realizeSaves();
690     modifiableState().globalComposite = op;
691     modifiableState().globalBlend = blendMode;
692     GraphicsContext* c = drawingContext();
693     if (!c)
694         return;
695     c->setCompositeOperation(op, blendMode);
696 }
697
698 void CanvasRenderingContext2D::scale(float sx, float sy)
699 {
700     GraphicsContext* c = drawingContext();
701     if (!c)
702         return;
703     if (!state().hasInvertibleTransform)
704         return;
705
706     if (!std::isfinite(sx) || !std::isfinite(sy))
707         return;
708
709     AffineTransform newTransform = state().transform;
710     newTransform.scaleNonUniform(sx, sy);
711     if (state().transform == newTransform)
712         return;
713
714     realizeSaves();
715
716     if (!sx || !sy) {
717         modifiableState().hasInvertibleTransform = false;
718         return;
719     }
720
721     modifiableState().transform = newTransform;
722     c->scale(FloatSize(sx, sy));
723     m_path.transform(AffineTransform().scaleNonUniform(1.0 / sx, 1.0 / sy));
724 }
725
726 void CanvasRenderingContext2D::rotate(float angleInRadians)
727 {
728     GraphicsContext* c = drawingContext();
729     if (!c)
730         return;
731     if (!state().hasInvertibleTransform)
732         return;
733
734     if (!std::isfinite(angleInRadians))
735         return;
736
737     AffineTransform newTransform = state().transform;
738     newTransform.rotate(angleInRadians / piDouble * 180.0);
739     if (state().transform == newTransform)
740         return;
741
742     realizeSaves();
743
744     modifiableState().transform = newTransform;
745     c->rotate(angleInRadians);
746     m_path.transform(AffineTransform().rotate(-angleInRadians / piDouble * 180.0));
747 }
748
749 void CanvasRenderingContext2D::translate(float tx, float ty)
750 {
751     GraphicsContext* c = drawingContext();
752     if (!c)
753         return;
754     if (!state().hasInvertibleTransform)
755         return;
756
757     if (!std::isfinite(tx) | !std::isfinite(ty))
758         return;
759
760     AffineTransform newTransform = state().transform;
761     newTransform.translate(tx, ty);
762     if (state().transform == newTransform)
763         return;
764
765     realizeSaves();
766
767     modifiableState().transform = newTransform;
768     c->translate(tx, ty);
769     m_path.transform(AffineTransform().translate(-tx, -ty));
770 }
771
772 void CanvasRenderingContext2D::transform(float m11, float m12, float m21, float m22, float dx, float dy)
773 {
774     GraphicsContext* c = drawingContext();
775     if (!c)
776         return;
777     if (!state().hasInvertibleTransform)
778         return;
779
780     if (!std::isfinite(m11) | !std::isfinite(m21) | !std::isfinite(dx) | !std::isfinite(m12) | !std::isfinite(m22) | !std::isfinite(dy))
781         return;
782
783     AffineTransform transform(m11, m12, m21, m22, dx, dy);
784     AffineTransform newTransform = state().transform * transform;
785     if (state().transform == newTransform)
786         return;
787
788     realizeSaves();
789
790     if (auto inverse = newTransform.inverse()) {
791         modifiableState().transform = newTransform;
792         c->concatCTM(transform);
793         m_path.transform(inverse.value());
794         return;
795     }
796     modifiableState().hasInvertibleTransform = false;
797 }
798
799 void CanvasRenderingContext2D::setTransform(float m11, float m12, float m21, float m22, float dx, float dy)
800 {
801     GraphicsContext* c = drawingContext();
802     if (!c)
803         return;
804
805     if (!std::isfinite(m11) | !std::isfinite(m21) | !std::isfinite(dx) | !std::isfinite(m12) | !std::isfinite(m22) | !std::isfinite(dy))
806         return;
807
808     AffineTransform ctm = state().transform;
809     if (!ctm.isInvertible())
810         return;
811
812     realizeSaves();
813     
814     c->setCTM(canvas()->baseTransform());
815     modifiableState().transform = AffineTransform();
816     m_path.transform(ctm);
817
818     modifiableState().hasInvertibleTransform = true;
819     transform(m11, m12, m21, m22, dx, dy);
820 }
821
822 void CanvasRenderingContext2D::setStrokeColor(const String& color, Optional<float> alpha)
823 {
824     if (alpha) {
825         setStrokeStyle(CanvasStyle::createFromStringWithOverrideAlpha(color, alpha.value()));
826         return;
827     }
828
829     if (color == state().unparsedStrokeColor)
830         return;
831
832     realizeSaves();
833     setStrokeStyle(CanvasStyle::createFromString(color, &canvas()->document()));
834     modifiableState().unparsedStrokeColor = color;
835 }
836
837 void CanvasRenderingContext2D::setStrokeColor(float grayLevel, float alpha)
838 {
839     if (state().strokeStyle.isValid() && state().strokeStyle.isEquivalentRGBA(grayLevel, grayLevel, grayLevel, alpha))
840         return;
841     setStrokeStyle(CanvasStyle(grayLevel, alpha));
842 }
843
844 void CanvasRenderingContext2D::setStrokeColor(float r, float g, float b, float a)
845 {
846     if (state().strokeStyle.isValid() && state().strokeStyle.isEquivalentRGBA(r, g, b, a))
847         return;
848     setStrokeStyle(CanvasStyle(r, g, b, a));
849 }
850
851 void CanvasRenderingContext2D::setStrokeColor(float c, float m, float y, float k, float a)
852 {
853     if (state().strokeStyle.isValid() && state().strokeStyle.isEquivalentCMYKA(c, m, y, k, a))
854         return;
855     setStrokeStyle(CanvasStyle(c, m, y, k, a));
856 }
857
858 void CanvasRenderingContext2D::setFillColor(const String& color, Optional<float> alpha)
859 {
860     if (alpha) {
861         setFillStyle(CanvasStyle::createFromStringWithOverrideAlpha(color, alpha.value()));
862         return;
863     }
864
865     if (color == state().unparsedFillColor)
866         return;
867
868     realizeSaves();
869     setFillStyle(CanvasStyle::createFromString(color, &canvas()->document()));
870     modifiableState().unparsedFillColor = color;
871 }
872
873 void CanvasRenderingContext2D::setFillColor(float grayLevel, float alpha)
874 {
875     if (state().fillStyle.isValid() && state().fillStyle.isEquivalentRGBA(grayLevel, grayLevel, grayLevel, alpha))
876         return;
877     setFillStyle(CanvasStyle(grayLevel, alpha));
878 }
879
880 void CanvasRenderingContext2D::setFillColor(float r, float g, float b, float a)
881 {
882     if (state().fillStyle.isValid() && state().fillStyle.isEquivalentRGBA(r, g, b, a))
883         return;
884     setFillStyle(CanvasStyle(r, g, b, a));
885 }
886
887 void CanvasRenderingContext2D::setFillColor(float c, float m, float y, float k, float a)
888 {
889     if (state().fillStyle.isValid() && state().fillStyle.isEquivalentCMYKA(c, m, y, k, a))
890         return;
891     setFillStyle(CanvasStyle(c, m, y, k, a));
892 }
893
894 void CanvasRenderingContext2D::beginPath()
895 {
896     m_path.clear();
897 }
898
899 static bool validateRectForCanvas(float& x, float& y, float& width, float& height)
900 {
901     if (!std::isfinite(x) | !std::isfinite(y) | !std::isfinite(width) | !std::isfinite(height))
902         return false;
903
904     if (!width && !height)
905         return false;
906
907     if (width < 0) {
908         width = -width;
909         x -= width;
910     }
911
912     if (height < 0) {
913         height = -height;
914         y -= height;
915     }
916
917     return true;
918 }
919
920 #if ENABLE(DASHBOARD_SUPPORT)
921 void CanvasRenderingContext2D::clearPathForDashboardBackwardCompatibilityMode()
922 {
923     if (m_usesDashboardCompatibilityMode)
924         m_path.clear();
925 }
926 #endif
927
928 static bool isFullCanvasCompositeMode(CompositeOperator op)
929 {
930     // See 4.8.11.1.3 Compositing
931     // CompositeSourceAtop and CompositeDestinationOut are not listed here as the platforms already
932     // implement the specification's behavior.
933     return op == CompositeSourceIn || op == CompositeSourceOut || op == CompositeDestinationIn || op == CompositeDestinationAtop;
934 }
935
936 static bool parseWinding(const String& windingRuleString, WindRule& windRule)
937 {
938     if (windingRuleString == "nonzero")
939         windRule = RULE_NONZERO;
940     else if (windingRuleString == "evenodd")
941         windRule = RULE_EVENODD;
942     else
943         return false;
944     
945     return true;
946 }
947
948 void CanvasRenderingContext2D::fill(const String& windingRuleString)
949 {
950     fillInternal(m_path, windingRuleString);
951
952 #if ENABLE(DASHBOARD_SUPPORT)
953     clearPathForDashboardBackwardCompatibilityMode();
954 #endif
955 }
956
957 void CanvasRenderingContext2D::stroke()
958 {
959     strokeInternal(m_path);
960
961 #if ENABLE(DASHBOARD_SUPPORT)
962     clearPathForDashboardBackwardCompatibilityMode();
963 #endif
964 }
965
966 void CanvasRenderingContext2D::clip(const String& windingRuleString)
967 {
968     clipInternal(m_path, windingRuleString);
969
970 #if ENABLE(DASHBOARD_SUPPORT)
971     clearPathForDashboardBackwardCompatibilityMode();
972 #endif
973 }
974
975 void CanvasRenderingContext2D::fill(DOMPath& path, const String& windingRuleString)
976 {
977     fillInternal(path.path(), windingRuleString);
978 }
979
980 void CanvasRenderingContext2D::stroke(DOMPath& path)
981 {
982     strokeInternal(path.path());
983 }
984
985 void CanvasRenderingContext2D::clip(DOMPath& path, const String& windingRuleString)
986 {
987     clipInternal(path.path(), windingRuleString);
988 }
989
990 void CanvasRenderingContext2D::fillInternal(const Path& path, const String& windingRuleString)
991 {
992     GraphicsContext* c = drawingContext();
993     if (!c)
994         return;
995     if (!state().hasInvertibleTransform)
996         return;
997
998     // If gradient size is zero, then paint nothing.
999     Gradient* gradient = c->fillGradient();
1000     if (gradient && gradient->isZeroSize())
1001         return;
1002
1003     if (!path.isEmpty()) {
1004         WindRule windRule = c->fillRule();
1005         WindRule newWindRule = RULE_NONZERO;
1006         if (!parseWinding(windingRuleString, newWindRule))
1007             return;
1008         c->setFillRule(newWindRule);
1009
1010         if (isFullCanvasCompositeMode(state().globalComposite)) {
1011             beginCompositeLayer();
1012             c->fillPath(path);
1013             endCompositeLayer();
1014             didDrawEntireCanvas();
1015         } else if (state().globalComposite == CompositeCopy) {
1016             clearCanvas();
1017             c->fillPath(path);
1018             didDrawEntireCanvas();
1019         } else {
1020             c->fillPath(path);
1021             didDraw(path.fastBoundingRect());
1022         }
1023         
1024         c->setFillRule(windRule);
1025     }
1026 }
1027
1028 void CanvasRenderingContext2D::strokeInternal(const Path& path)
1029 {
1030     GraphicsContext* c = drawingContext();
1031     if (!c)
1032         return;
1033     if (!state().hasInvertibleTransform)
1034         return;
1035
1036     // If gradient size is zero, then paint nothing.
1037     Gradient* gradient = c->strokeGradient();
1038     if (gradient && gradient->isZeroSize())
1039         return;
1040
1041     if (!path.isEmpty()) {
1042         if (isFullCanvasCompositeMode(state().globalComposite)) {
1043             beginCompositeLayer();
1044             c->strokePath(path);
1045             endCompositeLayer();
1046             didDrawEntireCanvas();
1047         } else if (state().globalComposite == CompositeCopy) {
1048             clearCanvas();
1049             c->strokePath(path);
1050             didDrawEntireCanvas();
1051         } else {
1052             FloatRect dirtyRect = path.fastBoundingRect();
1053             inflateStrokeRect(dirtyRect);
1054             c->strokePath(path);
1055             didDraw(dirtyRect);
1056         }
1057     }
1058 }
1059
1060 void CanvasRenderingContext2D::clipInternal(const Path& path, const String& windingRuleString)
1061 {
1062     GraphicsContext* c = drawingContext();
1063     if (!c)
1064         return;
1065     if (!state().hasInvertibleTransform)
1066         return;
1067
1068     WindRule newWindRule = RULE_NONZERO;
1069     if (!parseWinding(windingRuleString, newWindRule))
1070         return;
1071
1072     realizeSaves();
1073     c->canvasClip(path, newWindRule);
1074 }
1075
1076 inline void CanvasRenderingContext2D::beginCompositeLayer()
1077 {
1078 #if !USE(CAIRO)
1079     drawingContext()->beginTransparencyLayer(1);
1080 #endif
1081 }
1082
1083 inline void CanvasRenderingContext2D::endCompositeLayer()
1084 {
1085 #if !USE(CAIRO)
1086     drawingContext()->endTransparencyLayer();    
1087 #endif
1088 }
1089
1090 bool CanvasRenderingContext2D::isPointInPath(const float x, const float y, const String& windingRuleString)
1091 {
1092     return isPointInPathInternal(m_path, x, y, windingRuleString);
1093 }
1094
1095 bool CanvasRenderingContext2D::isPointInStroke(const float x, const float y)
1096 {
1097     return isPointInStrokeInternal(m_path, x, y);
1098 }
1099
1100 bool CanvasRenderingContext2D::isPointInPath(DOMPath& path, const float x, const float y, const String& windingRuleString)
1101 {
1102     return isPointInPathInternal(path.path(), x, y, windingRuleString);
1103 }
1104
1105 bool CanvasRenderingContext2D::isPointInStroke(DOMPath& path, const float x, const float y)
1106 {
1107     return isPointInStrokeInternal(path.path(), x, y);
1108 }
1109
1110 bool CanvasRenderingContext2D::isPointInPathInternal(const Path& path, float x, float y, const String& windingRuleString)
1111 {
1112     GraphicsContext* c = drawingContext();
1113     if (!c)
1114         return false;
1115     if (!state().hasInvertibleTransform)
1116         return false;
1117
1118     FloatPoint transformedPoint = state().transform.inverse().valueOr(AffineTransform()).mapPoint(FloatPoint(x, y));
1119
1120     if (!std::isfinite(transformedPoint.x()) || !std::isfinite(transformedPoint.y()))
1121         return false;
1122
1123     WindRule windRule = RULE_NONZERO;
1124     if (!parseWinding(windingRuleString, windRule))
1125         return false;
1126     
1127     return path.contains(transformedPoint, windRule);
1128 }
1129
1130 bool CanvasRenderingContext2D::isPointInStrokeInternal(const Path& path, float x, float y)
1131 {
1132     GraphicsContext* c = drawingContext();
1133     if (!c)
1134         return false;
1135     if (!state().hasInvertibleTransform)
1136         return false;
1137
1138     FloatPoint transformedPoint = state().transform.inverse().valueOr(AffineTransform()).mapPoint(FloatPoint(x, y));
1139     if (!std::isfinite(transformedPoint.x()) || !std::isfinite(transformedPoint.y()))
1140         return false;
1141
1142     CanvasStrokeStyleApplier applier(this);
1143     return path.strokeContains(&applier, transformedPoint);
1144 }
1145
1146 void CanvasRenderingContext2D::clearRect(float x, float y, float width, float height)
1147 {
1148     if (!validateRectForCanvas(x, y, width, height))
1149         return;
1150     GraphicsContext* context = drawingContext();
1151     if (!context)
1152         return;
1153     if (!state().hasInvertibleTransform)
1154         return;
1155     FloatRect rect(x, y, width, height);
1156
1157     bool saved = false;
1158     if (shouldDrawShadows()) {
1159         context->save();
1160         saved = true;
1161         context->setLegacyShadow(FloatSize(), 0, Color::transparent);
1162     }
1163     if (state().globalAlpha != 1) {
1164         if (!saved) {
1165             context->save();
1166             saved = true;
1167         }
1168         context->setAlpha(1);
1169     }
1170     if (state().globalComposite != CompositeSourceOver) {
1171         if (!saved) {
1172             context->save();
1173             saved = true;
1174         }
1175         context->setCompositeOperation(CompositeSourceOver);
1176     }
1177     context->clearRect(rect);
1178     if (saved)
1179         context->restore();
1180     didDraw(rect);
1181 }
1182
1183 void CanvasRenderingContext2D::fillRect(float x, float y, float width, float height)
1184 {
1185     if (!validateRectForCanvas(x, y, width, height))
1186         return;
1187
1188     GraphicsContext* c = drawingContext();
1189     if (!c)
1190         return;
1191     if (!state().hasInvertibleTransform)
1192         return;
1193
1194     // from the HTML5 Canvas spec:
1195     // If x0 = x1 and y0 = y1, then the linear gradient must paint nothing
1196     // If x0 = x1 and y0 = y1 and r0 = r1, then the radial gradient must paint nothing
1197     Gradient* gradient = c->fillGradient();
1198     if (gradient && gradient->isZeroSize())
1199         return;
1200
1201     FloatRect rect(x, y, width, height);
1202
1203     if (rectContainsCanvas(rect)) {
1204         c->fillRect(rect);
1205         didDrawEntireCanvas();
1206     } else if (isFullCanvasCompositeMode(state().globalComposite)) {
1207         beginCompositeLayer();
1208         c->fillRect(rect);
1209         endCompositeLayer();
1210         didDrawEntireCanvas();
1211     } else if (state().globalComposite == CompositeCopy) {
1212         clearCanvas();
1213         c->fillRect(rect);
1214         didDrawEntireCanvas();
1215     } else {
1216         c->fillRect(rect);
1217         didDraw(rect);
1218     }
1219 }
1220
1221 void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height)
1222 {
1223     if (!validateRectForCanvas(x, y, width, height))
1224         return;
1225
1226     GraphicsContext* c = drawingContext();
1227     if (!c)
1228         return;
1229     if (!state().hasInvertibleTransform)
1230         return;
1231     if (!(state().lineWidth >= 0))
1232         return;
1233
1234     // If gradient size is zero, then paint nothing.
1235     Gradient* gradient = c->strokeGradient();
1236     if (gradient && gradient->isZeroSize())
1237         return;
1238
1239     FloatRect rect(x, y, width, height);
1240     if (isFullCanvasCompositeMode(state().globalComposite)) {
1241         beginCompositeLayer();
1242         c->strokeRect(rect, state().lineWidth);
1243         endCompositeLayer();
1244         didDrawEntireCanvas();
1245     } else if (state().globalComposite == CompositeCopy) {
1246         clearCanvas();
1247         c->strokeRect(rect, state().lineWidth);
1248         didDrawEntireCanvas();
1249     } else {
1250         FloatRect boundingRect = rect;
1251         boundingRect.inflate(state().lineWidth / 2);
1252         c->strokeRect(rect, state().lineWidth);
1253         didDraw(boundingRect);
1254     }
1255 }
1256
1257 void CanvasRenderingContext2D::setShadow(float width, float height, float blur)
1258 {
1259     setShadow(FloatSize(width, height), blur, Color::transparent);
1260 }
1261
1262 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color, Optional<float> alpha)
1263 {
1264     RGBA32 rgba;
1265     if (!parseColorOrCurrentColor(rgba, color, canvas()))
1266         return;
1267     setShadow(FloatSize(width, height), blur, colorWithOverrideAlpha(rgba, alpha));
1268 }
1269
1270 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel, float alpha)
1271 {
1272     setShadow(FloatSize(width, height), blur, makeRGBA32FromFloats(grayLevel, grayLevel, grayLevel, alpha));
1273 }
1274
1275 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float r, float g, float b, float a)
1276 {
1277     setShadow(FloatSize(width, height), blur, makeRGBA32FromFloats(r, g, b, a));
1278 }
1279
1280 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float c, float m, float y, float k, float a)
1281 {
1282     setShadow(FloatSize(width, height), blur, makeRGBAFromCMYKA(c, m, y, k, a));
1283 }
1284
1285 void CanvasRenderingContext2D::clearShadow()
1286 {
1287     setShadow(FloatSize(), 0, Color::transparent);
1288 }
1289
1290 void CanvasRenderingContext2D::setShadow(const FloatSize& offset, float blur, RGBA32 color)
1291 {
1292     if (state().shadowOffset == offset && state().shadowBlur == blur && state().shadowColor == color)
1293         return;
1294     bool wasDrawingShadows = shouldDrawShadows();
1295     realizeSaves();
1296     modifiableState().shadowOffset = offset;
1297     modifiableState().shadowBlur = blur;
1298     modifiableState().shadowColor = color;
1299     if (!wasDrawingShadows && !shouldDrawShadows())
1300         return;
1301     applyShadow();
1302 }
1303
1304 void CanvasRenderingContext2D::applyShadow()
1305 {
1306     GraphicsContext* c = drawingContext();
1307     if (!c)
1308         return;
1309
1310     if (shouldDrawShadows()) {
1311         float width = state().shadowOffset.width();
1312         float height = state().shadowOffset.height();
1313         c->setLegacyShadow(FloatSize(width, -height), state().shadowBlur, state().shadowColor);
1314     } else
1315         c->setLegacyShadow(FloatSize(), 0, Color::transparent);
1316 }
1317
1318 bool CanvasRenderingContext2D::shouldDrawShadows() const
1319 {
1320     return alphaChannel(state().shadowColor) && (state().shadowBlur || !state().shadowOffset.isZero());
1321 }
1322
1323 enum ImageSizeType {
1324     ImageSizeAfterDevicePixelRatio,
1325     ImageSizeBeforeDevicePixelRatio
1326 };
1327
1328 static LayoutSize size(HTMLImageElement& imageElement, ImageSizeType sizeType)
1329 {
1330     LayoutSize size;
1331     if (CachedImage* cachedImage = imageElement.cachedImage()) {
1332         size = cachedImage->imageSizeForRenderer(imageElement.renderer(), 1.0f); // FIXME: Not sure about this.
1333
1334         if (sizeType == ImageSizeAfterDevicePixelRatio && is<RenderImage>(imageElement.renderer()) && cachedImage->image() && !cachedImage->image()->hasRelativeWidth())
1335             size.scale(downcast<RenderImage>(*imageElement.renderer()).imageDevicePixelRatio());
1336     }
1337     return size;
1338 }
1339
1340 #if ENABLE(VIDEO)
1341 static FloatSize size(HTMLVideoElement& video)
1342 {
1343     if (MediaPlayer* player = video.player())
1344         return player->naturalSize();
1345     return FloatSize();
1346 }
1347 #endif
1348
1349 static inline FloatRect normalizeRect(const FloatRect& rect)
1350 {
1351     return FloatRect(std::min(rect.x(), rect.maxX()),
1352         std::min(rect.y(), rect.maxY()),
1353         std::max(rect.width(), -rect.width()),
1354         std::max(rect.height(), -rect.height()));
1355 }
1356
1357 void CanvasRenderingContext2D::drawImage(HTMLImageElement& imageElement, float x, float y, ExceptionCode& ec)
1358 {
1359     LayoutSize destRectSize = size(imageElement, ImageSizeAfterDevicePixelRatio);
1360     drawImage(imageElement, x, y, destRectSize.width(), destRectSize.height(), ec);
1361 }
1362
1363 void CanvasRenderingContext2D::drawImage(HTMLImageElement& imageElement,
1364     float x, float y, float width, float height, ExceptionCode& ec)
1365 {
1366     LayoutSize sourceRectSize = size(imageElement, ImageSizeBeforeDevicePixelRatio);
1367     drawImage(imageElement, FloatRect(0, 0, sourceRectSize.width(), sourceRectSize.height()), FloatRect(x, y, width, height), ec);
1368 }
1369
1370 void CanvasRenderingContext2D::drawImage(HTMLImageElement& imageElement,
1371     float sx, float sy, float sw, float sh,
1372     float dx, float dy, float dw, float dh, ExceptionCode& ec)
1373 {
1374     drawImage(imageElement, FloatRect(sx, sy, sw, sh), FloatRect(dx, dy, dw, dh), ec);
1375 }
1376
1377 void CanvasRenderingContext2D::drawImage(HTMLImageElement& imageElement, const FloatRect& srcRect, const FloatRect& dstRect, ExceptionCode& ec)
1378 {
1379     drawImage(imageElement, srcRect, dstRect, state().globalComposite, state().globalBlend, ec);
1380 }
1381
1382 void CanvasRenderingContext2D::drawImage(HTMLImageElement& imageElement, const FloatRect& srcRect, const FloatRect& dstRect, const CompositeOperator& op, const BlendMode& blendMode, ExceptionCode& ec)
1383 {
1384     ec = 0;
1385
1386     if (!std::isfinite(dstRect.x()) || !std::isfinite(dstRect.y()) || !std::isfinite(dstRect.width()) || !std::isfinite(dstRect.height())
1387         || !std::isfinite(srcRect.x()) || !std::isfinite(srcRect.y()) || !std::isfinite(srcRect.width()) || !std::isfinite(srcRect.height()))
1388         return;
1389
1390     if (!dstRect.width() || !dstRect.height())
1391         return;
1392
1393     if (!imageElement.complete())
1394         return;
1395
1396     FloatRect normalizedSrcRect = normalizeRect(srcRect);
1397     FloatRect normalizedDstRect = normalizeRect(dstRect);
1398
1399     FloatRect imageRect = FloatRect(FloatPoint(), size(imageElement, ImageSizeBeforeDevicePixelRatio));
1400     if (!srcRect.width() || !srcRect.height()) {
1401         ec = INDEX_SIZE_ERR;
1402         return;
1403     }
1404     if (!imageRect.contains(normalizedSrcRect))
1405         return;
1406
1407     GraphicsContext* c = drawingContext();
1408     if (!c)
1409         return;
1410     if (!state().hasInvertibleTransform)
1411         return;
1412
1413     CachedImage* cachedImage = imageElement.cachedImage();
1414     if (!cachedImage)
1415         return;
1416
1417     Image* image = cachedImage->imageForRenderer(imageElement.renderer());
1418     if (!image)
1419         return;
1420     
1421     ImageObserver* observer = image->imageObserver();
1422
1423     if (image->isSVGImage()) {
1424         image->setImageObserver(nullptr);
1425         image->setContainerSize(normalizedSrcRect.size());
1426     }
1427
1428     if (rectContainsCanvas(normalizedDstRect)) {
1429         c->drawImage(*image, normalizedDstRect, normalizedSrcRect, ImagePaintingOptions(op, blendMode));
1430         didDrawEntireCanvas();
1431     } else if (isFullCanvasCompositeMode(op)) {
1432         fullCanvasCompositedDrawImage(*image, normalizedDstRect, normalizedSrcRect, op);
1433         didDrawEntireCanvas();
1434     } else if (op == CompositeCopy) {
1435         clearCanvas();
1436         c->drawImage(*image, normalizedDstRect, normalizedSrcRect, ImagePaintingOptions(op, blendMode));
1437         didDrawEntireCanvas();
1438     } else {
1439         c->drawImage(*image, normalizedDstRect, normalizedSrcRect, ImagePaintingOptions(op, blendMode));
1440         didDraw(normalizedDstRect);
1441     }
1442     
1443     if (image->isSVGImage())
1444         image->setImageObserver(observer);
1445
1446     checkOrigin(&imageElement);
1447 }
1448
1449 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement& sourceCanvas, float x, float y, ExceptionCode& ec)
1450 {
1451     drawImage(sourceCanvas, 0, 0, sourceCanvas.width(), sourceCanvas.height(), x, y, sourceCanvas.width(), sourceCanvas.height(), ec);
1452 }
1453
1454 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement& sourceCanvas,
1455     float x, float y, float width, float height, ExceptionCode& ec)
1456 {
1457     drawImage(sourceCanvas, FloatRect(0, 0, sourceCanvas.width(), sourceCanvas.height()), FloatRect(x, y, width, height), ec);
1458 }
1459
1460 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement& sourceCanvas,
1461     float sx, float sy, float sw, float sh,
1462     float dx, float dy, float dw, float dh, ExceptionCode& ec)
1463 {
1464     drawImage(sourceCanvas, FloatRect(sx, sy, sw, sh), FloatRect(dx, dy, dw, dh), ec);
1465 }
1466
1467 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement& sourceCanvas, const FloatRect& srcRect,
1468     const FloatRect& dstRect, ExceptionCode& ec)
1469 {
1470     FloatRect srcCanvasRect = FloatRect(FloatPoint(), sourceCanvas.size());
1471
1472     if (!srcCanvasRect.width() || !srcCanvasRect.height()) {
1473         ec = INVALID_STATE_ERR;
1474         return;
1475     }
1476
1477     if (!srcRect.width() || !srcRect.height()) {
1478         ec = INDEX_SIZE_ERR;
1479         return;
1480     }
1481
1482     ec = 0;
1483
1484     if (!srcCanvasRect.contains(normalizeRect(srcRect)) || !dstRect.width() || !dstRect.height())
1485         return;
1486
1487     GraphicsContext* c = drawingContext();
1488     if (!c)
1489         return;
1490     if (!state().hasInvertibleTransform)
1491         return;
1492
1493     // FIXME: Do this through platform-independent GraphicsContext API.
1494     ImageBuffer* buffer = sourceCanvas.buffer();
1495     if (!buffer)
1496         return;
1497
1498     checkOrigin(&sourceCanvas);
1499
1500 #if ENABLE(ACCELERATED_2D_CANVAS)
1501     // If we're drawing from one accelerated canvas 2d to another, avoid calling sourceCanvas.makeRenderingResultsAvailable()
1502     // as that will do a readback to software.
1503     CanvasRenderingContext* sourceContext = sourceCanvas.renderingContext();
1504     // FIXME: Implement an accelerated path for drawing from a WebGL canvas to a 2d canvas when possible.
1505     if (!isAccelerated() || !sourceContext || !sourceContext->isAccelerated() || !sourceContext->is2d())
1506         sourceCanvas.makeRenderingResultsAvailable();
1507 #else
1508     sourceCanvas.makeRenderingResultsAvailable();
1509 #endif
1510
1511     if (rectContainsCanvas(dstRect)) {
1512         c->drawImageBuffer(*buffer, dstRect, srcRect, ImagePaintingOptions(state().globalComposite, state().globalBlend));
1513         didDrawEntireCanvas();
1514     } else if (isFullCanvasCompositeMode(state().globalComposite)) {
1515         fullCanvasCompositedDrawImage(*buffer, dstRect, srcRect, state().globalComposite);
1516         didDrawEntireCanvas();
1517     } else if (state().globalComposite == CompositeCopy) {
1518         clearCanvas();
1519         c->drawImageBuffer(*buffer, dstRect, srcRect, ImagePaintingOptions(state().globalComposite, state().globalBlend));
1520         didDrawEntireCanvas();
1521     } else {
1522         c->drawImageBuffer(*buffer, dstRect, srcRect, ImagePaintingOptions(state().globalComposite, state().globalBlend));
1523         didDraw(dstRect);
1524     }
1525 }
1526
1527 #if ENABLE(VIDEO)
1528 void CanvasRenderingContext2D::drawImage(HTMLVideoElement& video, float x, float y, ExceptionCode& ec)
1529 {
1530     FloatSize videoSize = size(video);
1531     drawImage(video, x, y, videoSize.width(), videoSize.height(), ec);
1532 }
1533
1534 void CanvasRenderingContext2D::drawImage(HTMLVideoElement& video,
1535                                          float x, float y, float width, float height, ExceptionCode& ec)
1536 {
1537     FloatSize videoSize = size(video);
1538     drawImage(video, FloatRect(0, 0, videoSize.width(), videoSize.height()), FloatRect(x, y, width, height), ec);
1539 }
1540
1541 void CanvasRenderingContext2D::drawImage(HTMLVideoElement& video,
1542     float sx, float sy, float sw, float sh,
1543     float dx, float dy, float dw, float dh, ExceptionCode& ec)
1544 {
1545     drawImage(video, FloatRect(sx, sy, sw, sh), FloatRect(dx, dy, dw, dh), ec);
1546 }
1547
1548 void CanvasRenderingContext2D::drawImage(HTMLVideoElement& video, const FloatRect& srcRect, const FloatRect& dstRect,
1549                                          ExceptionCode& ec)
1550 {
1551     ec = 0;
1552
1553     if (video.readyState() == HTMLMediaElement::HAVE_NOTHING || video.readyState() == HTMLMediaElement::HAVE_METADATA)
1554         return;
1555
1556     FloatRect videoRect = FloatRect(FloatPoint(), size(video));
1557     if (!srcRect.width() || !srcRect.height()) {
1558         ec = INDEX_SIZE_ERR;
1559         return;
1560     }
1561
1562     if (!videoRect.contains(normalizeRect(srcRect)) || !dstRect.width() || !dstRect.height())
1563         return;
1564
1565     GraphicsContext* c = drawingContext();
1566     if (!c)
1567         return;
1568     if (!state().hasInvertibleTransform)
1569         return;
1570
1571     checkOrigin(&video);
1572
1573 #if USE(CG) || (ENABLE(ACCELERATED_2D_CANVAS) && USE(GSTREAMER_GL) && USE(CAIRO))
1574     if (NativeImagePtr image = video.nativeImageForCurrentTime()) {
1575         c->drawNativeImage(image, FloatSize(video.videoWidth(), video.videoHeight()), dstRect, srcRect);
1576         if (rectContainsCanvas(dstRect))
1577             didDrawEntireCanvas();
1578         else
1579             didDraw(dstRect);
1580
1581         return;
1582     }
1583 #endif
1584
1585     GraphicsContextStateSaver stateSaver(*c);
1586     c->clip(dstRect);
1587     c->translate(dstRect.x(), dstRect.y());
1588     c->scale(FloatSize(dstRect.width() / srcRect.width(), dstRect.height() / srcRect.height()));
1589     c->translate(-srcRect.x(), -srcRect.y());
1590     video.paintCurrentFrameInContext(*c, FloatRect(FloatPoint(), size(video)));
1591     stateSaver.restore();
1592     didDraw(dstRect);
1593 }
1594 #endif
1595
1596 void CanvasRenderingContext2D::drawImageFromRect(HTMLImageElement& imageElement,
1597     float sx, float sy, float sw, float sh,
1598     float dx, float dy, float dw, float dh,
1599     const String& compositeOperation)
1600 {
1601     CompositeOperator op;
1602     BlendMode blendOp = BlendModeNormal;
1603     if (!parseCompositeAndBlendOperator(compositeOperation, op, blendOp) || blendOp != BlendModeNormal)
1604         op = CompositeSourceOver;
1605
1606     drawImage(imageElement, FloatRect(sx, sy, sw, sh), FloatRect(dx, dy, dw, dh), op, BlendModeNormal, IGNORE_EXCEPTION);
1607 }
1608
1609 void CanvasRenderingContext2D::setAlpha(float alpha)
1610 {
1611     setGlobalAlpha(alpha);
1612 }
1613
1614 void CanvasRenderingContext2D::setCompositeOperation(const String& operation)
1615 {
1616     setGlobalCompositeOperation(operation);
1617 }
1618
1619 void CanvasRenderingContext2D::clearCanvas()
1620 {
1621     FloatRect canvasRect(0, 0, canvas()->width(), canvas()->height());
1622     GraphicsContext* c = drawingContext();
1623     if (!c)
1624         return;
1625
1626     c->save();
1627     c->setCTM(canvas()->baseTransform());
1628     c->clearRect(canvasRect);
1629     c->restore();
1630 }
1631
1632 Path CanvasRenderingContext2D::transformAreaToDevice(const Path& path) const
1633 {
1634     Path transformed(path);
1635     transformed.transform(state().transform);
1636     transformed.transform(canvas()->baseTransform());
1637     return transformed;
1638 }
1639
1640 Path CanvasRenderingContext2D::transformAreaToDevice(const FloatRect& rect) const
1641 {
1642     Path path;
1643     path.addRect(rect);
1644     return transformAreaToDevice(path);
1645 }
1646
1647 bool CanvasRenderingContext2D::rectContainsCanvas(const FloatRect& rect) const
1648 {
1649     FloatQuad quad(rect);
1650     FloatQuad canvasQuad(FloatRect(0, 0, canvas()->width(), canvas()->height()));
1651     return state().transform.mapQuad(quad).containsQuad(canvasQuad);
1652 }
1653
1654 template<class T> IntRect CanvasRenderingContext2D::calculateCompositingBufferRect(const T& area, IntSize* croppedOffset)
1655 {
1656     IntRect canvasRect(0, 0, canvas()->width(), canvas()->height());
1657     canvasRect = canvas()->baseTransform().mapRect(canvasRect);
1658     Path path = transformAreaToDevice(area);
1659     IntRect bufferRect = enclosingIntRect(path.fastBoundingRect());
1660     IntPoint originalLocation = bufferRect.location();
1661     bufferRect.intersect(canvasRect);
1662     if (croppedOffset)
1663         *croppedOffset = originalLocation - bufferRect.location();
1664     return bufferRect;
1665 }
1666
1667 std::unique_ptr<ImageBuffer> CanvasRenderingContext2D::createCompositingBuffer(const IntRect& bufferRect)
1668 {
1669     return ImageBuffer::create(bufferRect.size(), isAccelerated() ? Accelerated : Unaccelerated);
1670 }
1671
1672 void CanvasRenderingContext2D::compositeBuffer(ImageBuffer& buffer, const IntRect& bufferRect, CompositeOperator op)
1673 {
1674     IntRect canvasRect(0, 0, canvas()->width(), canvas()->height());
1675     canvasRect = canvas()->baseTransform().mapRect(canvasRect);
1676
1677     GraphicsContext* c = drawingContext();
1678     if (!c)
1679         return;
1680
1681     c->save();
1682     c->setCTM(AffineTransform());
1683     c->setCompositeOperation(op);
1684
1685     c->save();
1686     c->clipOut(bufferRect);
1687     c->clearRect(canvasRect);
1688     c->restore();
1689     c->drawImageBuffer(buffer, bufferRect.location(), state().globalComposite);
1690     c->restore();
1691 }
1692
1693 static void drawImageToContext(Image& image, GraphicsContext& context, const FloatRect& dest, const FloatRect& src, CompositeOperator op)
1694 {
1695     context.drawImage(image, dest, src, op);
1696 }
1697
1698 static void drawImageToContext(ImageBuffer& imageBuffer, GraphicsContext& context, const FloatRect& dest, const FloatRect& src, CompositeOperator op)
1699 {
1700     context.drawImageBuffer(imageBuffer, dest, src, op);
1701 }
1702
1703 template<class T> void CanvasRenderingContext2D::fullCanvasCompositedDrawImage(T& image, const FloatRect& dest, const FloatRect& src, CompositeOperator op)
1704 {
1705     ASSERT(isFullCanvasCompositeMode(op));
1706
1707     IntSize croppedOffset;
1708     IntRect bufferRect = calculateCompositingBufferRect(dest, &croppedOffset);
1709     if (bufferRect.isEmpty()) {
1710         clearCanvas();
1711         return;
1712     }
1713
1714     std::unique_ptr<ImageBuffer> buffer = createCompositingBuffer(bufferRect);
1715     if (!buffer)
1716         return;
1717
1718     GraphicsContext* c = drawingContext();
1719     if (!c)
1720         return;
1721
1722     FloatRect adjustedDest = dest;
1723     adjustedDest.setLocation(FloatPoint(0, 0));
1724     AffineTransform effectiveTransform = c->getCTM();
1725     IntRect transformedAdjustedRect = enclosingIntRect(effectiveTransform.mapRect(adjustedDest));
1726     buffer->context().translate(-transformedAdjustedRect.location().x(), -transformedAdjustedRect.location().y());
1727     buffer->context().translate(croppedOffset.width(), croppedOffset.height());
1728     buffer->context().concatCTM(effectiveTransform);
1729     drawImageToContext(image, buffer->context(), adjustedDest, src, CompositeSourceOver);
1730
1731     compositeBuffer(*buffer, bufferRect, op);
1732 }
1733
1734 void CanvasRenderingContext2D::prepareGradientForDashboard(CanvasGradient& gradient) const
1735 {
1736 #if ENABLE(DASHBOARD_SUPPORT)
1737     if (m_usesDashboardCompatibilityMode)
1738         gradient.setDashboardCompatibilityMode();
1739 #else
1740     UNUSED_PARAM(gradient);
1741 #endif
1742 }
1743
1744 RefPtr<CanvasGradient> CanvasRenderingContext2D::createLinearGradient(float x0, float y0, float x1, float y1, ExceptionCode& ec)
1745 {
1746     if (!std::isfinite(x0) || !std::isfinite(y0) || !std::isfinite(x1) || !std::isfinite(y1)) {
1747         ec = NOT_SUPPORTED_ERR;
1748         return nullptr;
1749     }
1750
1751     Ref<CanvasGradient> gradient = CanvasGradient::create(FloatPoint(x0, y0), FloatPoint(x1, y1));
1752     prepareGradientForDashboard(gradient.get());
1753     return WTFMove(gradient);
1754 }
1755
1756 RefPtr<CanvasGradient> CanvasRenderingContext2D::createRadialGradient(float x0, float y0, float r0, float x1, float y1, float r1, ExceptionCode& ec)
1757 {
1758     if (!std::isfinite(x0) || !std::isfinite(y0) || !std::isfinite(r0) || !std::isfinite(x1) || !std::isfinite(y1) || !std::isfinite(r1)) {
1759         ec = NOT_SUPPORTED_ERR;
1760         return nullptr;
1761     }
1762
1763     if (r0 < 0 || r1 < 0) {
1764         ec = INDEX_SIZE_ERR;
1765         return nullptr;
1766     }
1767
1768     Ref<CanvasGradient> gradient = CanvasGradient::create(FloatPoint(x0, y0), r0, FloatPoint(x1, y1), r1);
1769     prepareGradientForDashboard(gradient.get());
1770     return WTFMove(gradient);
1771 }
1772
1773 RefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLImageElement* imageElement,
1774     const String& repetitionType, ExceptionCode& ec)
1775 {
1776     if (!imageElement) {
1777         ec = TYPE_MISMATCH_ERR;
1778         return nullptr;
1779     }
1780     bool repeatX, repeatY;
1781     ec = 0;
1782     CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec);
1783     if (ec)
1784         return nullptr;
1785
1786     CachedImage* cachedImage = imageElement->cachedImage();
1787     // If the image loading hasn't started or the image is not complete, it is not fully decodable.
1788     if (!cachedImage || !imageElement->complete())
1789         return nullptr;
1790
1791     if (cachedImage->status() == CachedResource::LoadError) {
1792         ec = INVALID_STATE_ERR;
1793         return nullptr;
1794     }
1795
1796     if (!imageElement->cachedImage()->imageForRenderer(imageElement->renderer()))
1797         return CanvasPattern::create(Image::nullImage(), repeatX, repeatY, true);
1798
1799     bool originClean = cachedImage->isOriginClean(canvas()->securityOrigin());
1800
1801     // FIXME: SVG images with animations can switch between clean and dirty (leaking cross-origin
1802     // data). We should either:
1803     //   1) Take a fixed snapshot of an SVG image when creating a pattern and determine then whether
1804     //      the origin is clean.
1805     //   2) Dynamically verify the origin checks at draw time, and dirty the canvas accordingly.
1806     // To be on the safe side, taint the origin for all patterns containing SVG images for now.
1807     if (cachedImage->image()->isSVGImage())
1808         originClean = false;
1809
1810     return CanvasPattern::create(cachedImage->imageForRenderer(imageElement->renderer()), repeatX, repeatY, originClean);
1811 }
1812
1813 RefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLCanvasElement* canvas,
1814     const String& repetitionType, ExceptionCode& ec)
1815 {
1816     if (!canvas) {
1817         ec = TYPE_MISMATCH_ERR;
1818         return nullptr;
1819     }
1820     if (!canvas->width() || !canvas->height() || !canvas->buffer()) {
1821         ec = INVALID_STATE_ERR;
1822         return nullptr;
1823     }
1824
1825     bool repeatX, repeatY;
1826     ec = 0;
1827     CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec);
1828     if (ec)
1829         return nullptr;
1830     return CanvasPattern::create(canvas->copiedImage(), repeatX, repeatY, canvas->originClean());
1831 }
1832
1833 void CanvasRenderingContext2D::didDrawEntireCanvas()
1834 {
1835     didDraw(FloatRect(FloatPoint::zero(), canvas()->size()), CanvasDidDrawApplyClip);
1836 }
1837
1838 void CanvasRenderingContext2D::didDraw(const FloatRect& r, unsigned options)
1839 {
1840     GraphicsContext* c = drawingContext();
1841     if (!c)
1842         return;
1843     if (!state().hasInvertibleTransform)
1844         return;
1845
1846 #if ENABLE(ACCELERATED_2D_CANVAS)
1847     // If we are drawing to hardware and we have a composited layer, just call contentChanged().
1848     if (isAccelerated()) {
1849         RenderBox* renderBox = canvas()->renderBox();
1850         if (renderBox && renderBox->hasAcceleratedCompositing()) {
1851             renderBox->contentChanged(CanvasPixelsChanged);
1852             canvas()->clearCopiedImage();
1853             canvas()->notifyObserversCanvasChanged(r);
1854             return;
1855         }
1856     }
1857 #endif
1858
1859     FloatRect dirtyRect = r;
1860     if (options & CanvasDidDrawApplyTransform) {
1861         AffineTransform ctm = state().transform;
1862         dirtyRect = ctm.mapRect(r);
1863     }
1864
1865     if (options & CanvasDidDrawApplyShadow && alphaChannel(state().shadowColor)) {
1866         // The shadow gets applied after transformation
1867         FloatRect shadowRect(dirtyRect);
1868         shadowRect.move(state().shadowOffset);
1869         shadowRect.inflate(state().shadowBlur);
1870         dirtyRect.unite(shadowRect);
1871     }
1872
1873     if (options & CanvasDidDrawApplyClip) {
1874         // FIXME: apply the current clip to the rectangle. Unfortunately we can't get the clip
1875         // back out of the GraphicsContext, so to take clip into account for incremental painting,
1876         // we'd have to keep the clip path around.
1877     }
1878
1879     canvas()->didDraw(dirtyRect);
1880 }
1881
1882 void CanvasRenderingContext2D::setTracksDisplayListReplay(bool tracksDisplayListReplay)
1883 {
1884     if (tracksDisplayListReplay == m_tracksDisplayListReplay)
1885         return;
1886
1887     m_tracksDisplayListReplay = tracksDisplayListReplay;
1888     if (!m_tracksDisplayListReplay)
1889         contextDisplayListMap().remove(this);
1890 }
1891
1892 String CanvasRenderingContext2D::displayListAsText(DisplayList::AsTextFlags flags) const
1893 {
1894     if (m_recordingContext)
1895         return m_recordingContext->displayList.asText(flags);
1896     
1897     return String();
1898 }
1899
1900 String CanvasRenderingContext2D::replayDisplayListAsText(DisplayList::AsTextFlags flags) const
1901 {
1902     auto it = contextDisplayListMap().find(this);
1903     if (it != contextDisplayListMap().end()) {
1904         TextStream stream;
1905         stream << it->value->asText(flags);
1906         return stream.release();
1907     }
1908
1909     return String();
1910 }
1911
1912 void CanvasRenderingContext2D::paintRenderingResultsToCanvas()
1913 {
1914     if (UNLIKELY(m_usesDisplayListDrawing)) {
1915         if (!m_recordingContext)
1916             return;
1917         
1918         FloatRect clip(FloatPoint::zero(), canvas()->size());
1919         DisplayList::Replayer replayer(*canvas()->drawingContext(), m_recordingContext->displayList);
1920
1921         if (UNLIKELY(m_tracksDisplayListReplay)) {
1922             auto replayList = replayer.replay(clip, m_tracksDisplayListReplay);
1923             contextDisplayListMap().add(this, WTFMove(replayList));
1924         } else
1925             replayer.replay(clip);
1926
1927         m_recordingContext->displayList.clear();
1928     }
1929 }
1930
1931 GraphicsContext* CanvasRenderingContext2D::drawingContext() const
1932 {
1933     if (UNLIKELY(m_usesDisplayListDrawing)) {
1934         if (!m_recordingContext)
1935             m_recordingContext = std::make_unique<DisplayListDrawingContext>(FloatRect(FloatPoint::zero(), canvas()->size()));
1936
1937         return &m_recordingContext->context;
1938     }
1939
1940     return canvas()->drawingContext();
1941 }
1942
1943 static RefPtr<ImageData> createEmptyImageData(const IntSize& size)
1944 {
1945     if (RefPtr<ImageData> data = ImageData::create(size)) {
1946         data->data()->zeroFill();
1947         return data;
1948     }
1949
1950     return nullptr;
1951 }
1952
1953 RefPtr<ImageData> CanvasRenderingContext2D::createImageData(RefPtr<ImageData>&& imageData, ExceptionCode& ec) const
1954 {
1955     if (!imageData) {
1956         ec = NOT_SUPPORTED_ERR;
1957         return nullptr;
1958     }
1959
1960     return createEmptyImageData(imageData->size());
1961 }
1962
1963 RefPtr<ImageData> CanvasRenderingContext2D::createImageData(float sw, float sh, ExceptionCode& ec) const
1964 {
1965     ec = 0;
1966     if (!sw || !sh) {
1967         ec = INDEX_SIZE_ERR;
1968         return nullptr;
1969     }
1970     if (!std::isfinite(sw) || !std::isfinite(sh)) {
1971         ec = TypeError;
1972         return nullptr;
1973     }
1974
1975     FloatSize logicalSize(fabs(sw), fabs(sh));
1976     if (!logicalSize.isExpressibleAsIntSize())
1977         return nullptr;
1978
1979     IntSize size = expandedIntSize(logicalSize);
1980     if (size.width() < 1)
1981         size.setWidth(1);
1982     if (size.height() < 1)
1983         size.setHeight(1);
1984
1985     return createEmptyImageData(size);
1986 }
1987
1988 RefPtr<ImageData> CanvasRenderingContext2D::getImageData(float sx, float sy, float sw, float sh, ExceptionCode& ec) const
1989 {
1990     return getImageData(ImageBuffer::LogicalCoordinateSystem, sx, sy, sw, sh, ec);
1991 }
1992
1993 RefPtr<ImageData> CanvasRenderingContext2D::webkitGetImageDataHD(float sx, float sy, float sw, float sh, ExceptionCode& ec) const
1994 {
1995     return getImageData(ImageBuffer::BackingStoreCoordinateSystem, sx, sy, sw, sh, ec);
1996 }
1997
1998 RefPtr<ImageData> CanvasRenderingContext2D::getImageData(ImageBuffer::CoordinateSystem coordinateSystem, float sx, float sy, float sw, float sh, ExceptionCode& ec) const
1999 {
2000     if (!canvas()->originClean()) {
2001         static NeverDestroyed<String> consoleMessage(ASCIILiteral("Unable to get image data from canvas because the canvas has been tainted by cross-origin data."));
2002         canvas()->document().addConsoleMessage(MessageSource::Security, MessageLevel::Error, consoleMessage);
2003         ec = SECURITY_ERR;
2004         return nullptr;
2005     }
2006
2007     if (!sw || !sh) {
2008         ec = INDEX_SIZE_ERR;
2009         return nullptr;
2010     }
2011     if (!std::isfinite(sx) || !std::isfinite(sy) || !std::isfinite(sw) || !std::isfinite(sh)) {
2012         ec = NOT_SUPPORTED_ERR;
2013         return nullptr;
2014     }
2015
2016     if (sw < 0) {
2017         sx += sw;
2018         sw = -sw;
2019     }    
2020     if (sh < 0) {
2021         sy += sh;
2022         sh = -sh;
2023     }
2024
2025     FloatRect logicalRect(sx, sy, sw, sh);
2026     if (logicalRect.width() < 1)
2027         logicalRect.setWidth(1);
2028     if (logicalRect.height() < 1)
2029         logicalRect.setHeight(1);
2030     if (!logicalRect.isExpressibleAsIntRect())
2031         return nullptr;
2032
2033     IntRect imageDataRect = enclosingIntRect(logicalRect);
2034     ImageBuffer* buffer = canvas()->buffer();
2035     if (!buffer)
2036         return createEmptyImageData(imageDataRect.size());
2037
2038     RefPtr<Uint8ClampedArray> byteArray = buffer->getUnmultipliedImageData(imageDataRect, coordinateSystem);
2039     if (!byteArray)
2040         return nullptr;
2041
2042     return ImageData::create(imageDataRect.size(), byteArray.release());
2043 }
2044
2045 void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, ExceptionCode& ec)
2046 {
2047     if (!data) {
2048         ec = TYPE_MISMATCH_ERR;
2049         return;
2050     }
2051     putImageData(data, dx, dy, 0, 0, data->width(), data->height(), ec);
2052 }
2053
2054 void CanvasRenderingContext2D::webkitPutImageDataHD(ImageData* data, float dx, float dy, ExceptionCode& ec)
2055 {
2056     if (!data) {
2057         ec = TYPE_MISMATCH_ERR;
2058         return;
2059     }
2060     webkitPutImageDataHD(data, dx, dy, 0, 0, data->width(), data->height(), ec);
2061 }
2062
2063 void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, float dirtyX, float dirtyY,
2064                                             float dirtyWidth, float dirtyHeight, ExceptionCode& ec)
2065 {
2066     putImageData(data, ImageBuffer::LogicalCoordinateSystem, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight, ec);
2067 }
2068
2069 void CanvasRenderingContext2D::webkitPutImageDataHD(ImageData* data, float dx, float dy, float dirtyX, float dirtyY, float dirtyWidth, float dirtyHeight, ExceptionCode& ec)
2070 {
2071     putImageData(data, ImageBuffer::BackingStoreCoordinateSystem, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight, ec);
2072 }
2073
2074 void CanvasRenderingContext2D::drawFocusIfNeeded(Element* element)
2075 {
2076     drawFocusIfNeededInternal(m_path, element);
2077 }
2078
2079 void CanvasRenderingContext2D::drawFocusIfNeeded(DOMPath& path, Element* element)
2080 {
2081     drawFocusIfNeededInternal(path.path(), element);
2082 }
2083
2084 void CanvasRenderingContext2D::drawFocusIfNeededInternal(const Path& path, Element* element)
2085 {
2086     GraphicsContext* context = drawingContext();
2087
2088     if (!element || !element->focused() || !state().hasInvertibleTransform || path.isEmpty()
2089         || !element->isDescendantOf(canvas()) || !context)
2090         return;
2091
2092     context->drawFocusRing(path, 1, 1, RenderTheme::focusRingColor());
2093 }
2094
2095 void CanvasRenderingContext2D::putImageData(ImageData* data, ImageBuffer::CoordinateSystem coordinateSystem, float dx, float dy, float dirtyX, float dirtyY,
2096                                             float dirtyWidth, float dirtyHeight, ExceptionCode& ec)
2097 {
2098     if (!data) {
2099         ec = TYPE_MISMATCH_ERR;
2100         return;
2101     }
2102     if (!std::isfinite(dx) || !std::isfinite(dy) || !std::isfinite(dirtyX) || !std::isfinite(dirtyY) || !std::isfinite(dirtyWidth) || !std::isfinite(dirtyHeight)) {
2103         ec = NOT_SUPPORTED_ERR;
2104         return;
2105     }
2106
2107     ImageBuffer* buffer = canvas()->buffer();
2108     if (!buffer)
2109         return;
2110
2111     if (dirtyWidth < 0) {
2112         dirtyX += dirtyWidth;
2113         dirtyWidth = -dirtyWidth;
2114     }
2115
2116     if (dirtyHeight < 0) {
2117         dirtyY += dirtyHeight;
2118         dirtyHeight = -dirtyHeight;
2119     }
2120
2121     FloatRect clipRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
2122     clipRect.intersect(IntRect(0, 0, data->width(), data->height()));
2123     IntSize destOffset(static_cast<int>(dx), static_cast<int>(dy));
2124     IntRect destRect = enclosingIntRect(clipRect);
2125     destRect.move(destOffset);
2126     destRect.intersect(IntRect(IntPoint(), coordinateSystem == ImageBuffer::LogicalCoordinateSystem ? buffer->logicalSize() : buffer->internalSize()));
2127     if (destRect.isEmpty())
2128         return;
2129     IntRect sourceRect(destRect);
2130     sourceRect.move(-destOffset);
2131
2132     buffer->putByteArray(Unmultiplied, data->data(), IntSize(data->width(), data->height()), sourceRect, IntPoint(destOffset), coordinateSystem);
2133
2134     didDraw(destRect, CanvasDidDrawApplyNone); // ignore transform, shadow and clip
2135 }
2136
2137 String CanvasRenderingContext2D::font() const
2138 {
2139     if (!state().font.realized())
2140         return defaultFont;
2141
2142     StringBuilder serializedFont;
2143     const auto& fontDescription = state().font.fontDescription();
2144
2145     if (fontDescription.italic())
2146         serializedFont.appendLiteral("italic ");
2147     if (fontDescription.variantCaps() == FontVariantCaps::Small)
2148         serializedFont.appendLiteral("small-caps ");
2149
2150     serializedFont.appendNumber(fontDescription.computedPixelSize());
2151     serializedFont.appendLiteral("px");
2152
2153     for (unsigned i = 0; i < fontDescription.familyCount(); ++i) {
2154         if (i)
2155             serializedFont.append(',');
2156         // FIXME: We should append family directly to serializedFont rather than building a temporary string.
2157         String family = fontDescription.familyAt(i);
2158         if (family.startsWith("-webkit-"))
2159             family = family.substring(8);
2160         if (family.contains(' '))
2161             family = makeString('"', family, '"');
2162
2163         serializedFont.append(' ');
2164         serializedFont.append(family);
2165     }
2166
2167     return serializedFont.toString();
2168 }
2169
2170 void CanvasRenderingContext2D::setFont(const String& newFont)
2171 {
2172     if (newFont == state().unparsedFont && state().font.realized())
2173         return;
2174
2175     RefPtr<MutableStyleProperties> parsedStyle = MutableStyleProperties::create();
2176     CSSParser::parseValue(parsedStyle.get(), CSSPropertyFont, newFont, true, strictToCSSParserMode(!m_usesCSSCompatibilityParseMode), nullptr);
2177     if (parsedStyle->isEmpty())
2178         return;
2179
2180     String fontValue = parsedStyle->getPropertyValue(CSSPropertyFont);
2181
2182     // According to http://lists.w3.org/Archives/Public/public-html/2009Jul/0947.html,
2183     // the "inherit" and "initial" values must be ignored.
2184     if (fontValue == "inherit" || fontValue == "initial")
2185         return;
2186
2187     // The parse succeeded.
2188     String newFontSafeCopy(newFont); // Create a string copy since newFont can be deleted inside realizeSaves.
2189     realizeSaves();
2190     modifiableState().unparsedFont = newFontSafeCopy;
2191
2192     // Map the <canvas> font into the text style. If the font uses keywords like larger/smaller, these will work
2193     // relative to the canvas.
2194     auto newStyle = RenderStyle::createPtr();
2195
2196     Document& document = canvas()->document();
2197     document.updateStyleIfNeeded();
2198
2199     if (RenderStyle* computedStyle = canvas()->computedStyle())
2200         newStyle->setFontDescription(computedStyle->fontDescription());
2201     else {
2202         FontCascadeDescription defaultFontDescription;
2203         defaultFontDescription.setOneFamily(defaultFontFamily);
2204         defaultFontDescription.setSpecifiedSize(defaultFontSize);
2205         defaultFontDescription.setComputedSize(defaultFontSize);
2206
2207         newStyle->setFontDescription(defaultFontDescription);
2208     }
2209
2210     newStyle->fontCascade().update(&document.fontSelector());
2211
2212     // Now map the font property longhands into the style.
2213     StyleResolver& styleResolver = canvas()->styleResolver();
2214     styleResolver.applyPropertyToStyle(CSSPropertyFontFamily, parsedStyle->getPropertyCSSValue(CSSPropertyFontFamily).get(), WTFMove(newStyle));
2215     styleResolver.applyPropertyToCurrentStyle(CSSPropertyFontStyle, parsedStyle->getPropertyCSSValue(CSSPropertyFontStyle).get());
2216     styleResolver.applyPropertyToCurrentStyle(CSSPropertyFontVariantCaps, parsedStyle->getPropertyCSSValue(CSSPropertyFontVariantCaps).get());
2217     styleResolver.applyPropertyToCurrentStyle(CSSPropertyFontWeight, parsedStyle->getPropertyCSSValue(CSSPropertyFontWeight).get());
2218
2219     // As described in BUG66291, setting font-size and line-height on a font may entail a CSSPrimitiveValue::computeLengthDouble call,
2220     // which assumes the fontMetrics are available for the affected font, otherwise a crash occurs (see http://trac.webkit.org/changeset/96122).
2221     // The updateFont() calls below update the fontMetrics and ensure the proper setting of font-size and line-height.
2222     styleResolver.updateFont();
2223     styleResolver.applyPropertyToCurrentStyle(CSSPropertyFontSize, parsedStyle->getPropertyCSSValue(CSSPropertyFontSize).get());
2224     styleResolver.updateFont();
2225     styleResolver.applyPropertyToCurrentStyle(CSSPropertyLineHeight, parsedStyle->getPropertyCSSValue(CSSPropertyLineHeight).get());
2226
2227     modifiableState().font.initialize(document.fontSelector(), *styleResolver.style());
2228 }
2229
2230 String CanvasRenderingContext2D::textAlign() const
2231 {
2232     return textAlignName(state().textAlign);
2233 }
2234
2235 void CanvasRenderingContext2D::setTextAlign(const String& s)
2236 {
2237     TextAlign align;
2238     if (!parseTextAlign(s, align))
2239         return;
2240     if (state().textAlign == align)
2241         return;
2242     realizeSaves();
2243     modifiableState().textAlign = align;
2244 }
2245
2246 String CanvasRenderingContext2D::textBaseline() const
2247 {
2248     return textBaselineName(state().textBaseline);
2249 }
2250
2251 void CanvasRenderingContext2D::setTextBaseline(const String& s)
2252 {
2253     TextBaseline baseline;
2254     if (!parseTextBaseline(s, baseline))
2255         return;
2256     if (state().textBaseline == baseline)
2257         return;
2258     realizeSaves();
2259     modifiableState().textBaseline = baseline;
2260 }
2261
2262 inline TextDirection CanvasRenderingContext2D::toTextDirection(Direction direction, RenderStyle** computedStyle) const
2263 {
2264     RenderStyle* style = (computedStyle || direction == Direction::Inherit) ? canvas()->computedStyle() : nullptr;
2265     if (computedStyle)
2266         *computedStyle = style;
2267     switch (direction) {
2268     case Direction::Inherit:
2269         return style ? style->direction() : LTR;
2270     case Direction::RTL:
2271         return RTL;
2272     case Direction::LTR:
2273         return LTR;
2274     }
2275     ASSERT_NOT_REACHED();
2276     return LTR;
2277 }
2278
2279 String CanvasRenderingContext2D::direction() const
2280 {
2281     if (state().direction == Direction::Inherit)
2282         canvas()->document().updateStyleIfNeeded();
2283     return toTextDirection(state().direction) == RTL ? ASCIILiteral("rtl") : ASCIILiteral("ltr");
2284 }
2285
2286 void CanvasRenderingContext2D::setDirection(const String& directionString)
2287 {
2288     Direction direction;
2289     if (directionString == "inherit")
2290         direction = Direction::Inherit;
2291     else if (directionString == "rtl")
2292         direction = Direction::RTL;
2293     else if (directionString == "ltr")
2294         direction = Direction::LTR;
2295     else
2296         return;
2297
2298     if (state().direction == direction)
2299         return;
2300
2301     realizeSaves();
2302     modifiableState().direction = direction;
2303 }
2304
2305 void CanvasRenderingContext2D::fillText(const String& text, float x, float y, Optional<float> maxWidth)
2306 {
2307     drawTextInternal(text, x, y, true, maxWidth);
2308 }
2309
2310 void CanvasRenderingContext2D::strokeText(const String& text, float x, float y, Optional<float> maxWidth)
2311 {
2312     drawTextInternal(text, x, y, false, maxWidth);
2313 }
2314
2315 static inline bool isSpaceThatNeedsReplacing(UChar c)
2316 {
2317     // According to specification all space characters should be replaced with 0x0020 space character.
2318     // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#text-preparation-algorithm
2319     // The space characters according to specification are : U+0020, U+0009, U+000A, U+000C, and U+000D.
2320     // http://www.whatwg.org/specs/web-apps/current-work/multipage/common-microsyntaxes.html#space-character
2321     // This function returns true for 0x000B also, so that this is backward compatible.
2322     // Otherwise, the test LayoutTests/canvas/philip/tests/2d.text.draw.space.collapse.space.html will fail
2323     return c == 0x0009 || c == 0x000A || c == 0x000B || c == 0x000C || c == 0x000D;
2324 }
2325
2326 static void normalizeSpaces(String& text)
2327 {
2328     size_t i = text.find(isSpaceThatNeedsReplacing);
2329
2330     if (i == notFound)
2331         return;
2332
2333     unsigned textLength = text.length();
2334     Vector<UChar> charVector(textLength);
2335     StringView(text).getCharactersWithUpconvert(charVector.data());
2336
2337     charVector[i++] = ' ';
2338
2339     for (; i < textLength; ++i) {
2340         if (isSpaceThatNeedsReplacing(charVector[i]))
2341             charVector[i] = ' ';
2342     }
2343     text = String::adopt(charVector);
2344 }
2345
2346 Ref<TextMetrics> CanvasRenderingContext2D::measureText(const String& text)
2347 {
2348     Ref<TextMetrics> metrics = TextMetrics::create();
2349
2350     String normalizedText = text;
2351     normalizeSpaces(normalizedText);
2352
2353     metrics->setWidth(fontProxy().width(TextRun(normalizedText)));
2354
2355     return metrics;
2356 }
2357
2358 void CanvasRenderingContext2D::drawTextInternal(const String& text, float x, float y, bool fill, Optional<float> maxWidth)
2359 {
2360     const auto& fontProxy = this->fontProxy();
2361     const FontMetrics& fontMetrics = fontProxy.fontMetrics();
2362
2363     GraphicsContext* c = drawingContext();
2364     if (!c)
2365         return;
2366     if (!state().hasInvertibleTransform)
2367         return;
2368     if (!std::isfinite(x) | !std::isfinite(y))
2369         return;
2370     if (maxWidth && (!std::isfinite(maxWidth.value()) || maxWidth.value() <= 0))
2371         return;
2372
2373     // If gradient size is zero, then paint nothing.
2374     Gradient* gradient = c->strokeGradient();
2375     if (!fill && gradient && gradient->isZeroSize())
2376         return;
2377
2378     gradient = c->fillGradient();
2379     if (fill && gradient && gradient->isZeroSize())
2380         return;
2381
2382     String normalizedText = text;
2383     normalizeSpaces(normalizedText);
2384
2385     // FIXME: Need to turn off font smoothing.
2386
2387     RenderStyle* computedStyle;
2388     TextDirection direction = toTextDirection(state().direction, &computedStyle);
2389     bool isRTL = direction == RTL;
2390     bool override = computedStyle ? isOverride(computedStyle->unicodeBidi()) : false;
2391
2392     TextRun textRun(normalizedText, 0, 0, AllowTrailingExpansion, direction, override, true);
2393     // Draw the item text at the correct point.
2394     FloatPoint location(x, y);
2395     switch (state().textBaseline) {
2396     case TopTextBaseline:
2397     case HangingTextBaseline:
2398         location.setY(y + fontMetrics.ascent());
2399         break;
2400     case BottomTextBaseline:
2401     case IdeographicTextBaseline:
2402         location.setY(y - fontMetrics.descent());
2403         break;
2404     case MiddleTextBaseline:
2405         location.setY(y - fontMetrics.descent() + fontMetrics.height() / 2);
2406         break;
2407     case AlphabeticTextBaseline:
2408     default:
2409          // Do nothing.
2410         break;
2411     }
2412
2413     float fontWidth = fontProxy.width(TextRun(normalizedText, 0, 0, AllowTrailingExpansion, direction, override));
2414
2415     bool useMaxWidth = maxWidth && maxWidth.value() < fontWidth;
2416     float width = useMaxWidth ? maxWidth.value() : fontWidth;
2417
2418     TextAlign align = state().textAlign;
2419     if (align == StartTextAlign)
2420         align = isRTL ? RightTextAlign : LeftTextAlign;
2421     else if (align == EndTextAlign)
2422         align = isRTL ? LeftTextAlign : RightTextAlign;
2423
2424     switch (align) {
2425     case CenterTextAlign:
2426         location.setX(location.x() - width / 2);
2427         break;
2428     case RightTextAlign:
2429         location.setX(location.x() - width);
2430         break;
2431     default:
2432         break;
2433     }
2434
2435     // The slop built in to this mask rect matches the heuristic used in FontCGWin.cpp for GDI text.
2436     FloatRect textRect = FloatRect(location.x() - fontMetrics.height() / 2, location.y() - fontMetrics.ascent() - fontMetrics.lineGap(),
2437         width + fontMetrics.height(), fontMetrics.lineSpacing());
2438     if (!fill)
2439         inflateStrokeRect(textRect);
2440
2441 #if USE(CG)
2442     const CanvasStyle& drawStyle = fill ? state().fillStyle : state().strokeStyle;
2443     if (drawStyle.canvasGradient() || drawStyle.canvasPattern()) {
2444
2445         IntRect maskRect = enclosingIntRect(textRect);
2446
2447         // If we have a shadow, we need to draw it before the mask operation.
2448         // Follow a procedure similar to paintTextWithShadows in TextPainter.
2449
2450         if (shouldDrawShadows()) {
2451             GraphicsContextStateSaver stateSaver(*c);
2452
2453             FloatSize offset = FloatSize(0, 2 * maskRect.height());
2454
2455             FloatSize shadowOffset;
2456             float shadowRadius;
2457             Color shadowColor;
2458             c->getShadow(shadowOffset, shadowRadius, shadowColor);
2459
2460             FloatRect shadowRect(maskRect);
2461             shadowRect.inflate(shadowRadius * 1.4);
2462             shadowRect.move(shadowOffset * -1);
2463             c->clip(shadowRect);
2464
2465             shadowOffset += offset;
2466
2467             c->setLegacyShadow(shadowOffset, shadowRadius, shadowColor);
2468
2469             if (fill)
2470                 c->setFillColor(Color::black);
2471             else
2472                 c->setStrokeColor(Color::black);
2473
2474             fontProxy.drawBidiText(*c, textRun, location + offset, FontCascade::UseFallbackIfFontNotReady);
2475         }
2476
2477         std::unique_ptr<ImageBuffer> maskImage = c->createCompatibleBuffer(maskRect.size());
2478         if (!maskImage)
2479             return;
2480
2481         GraphicsContext& maskImageContext = maskImage->context();
2482
2483         if (fill)
2484             maskImageContext.setFillColor(Color::black);
2485         else {
2486             maskImageContext.setStrokeColor(Color::black);
2487             maskImageContext.setStrokeThickness(c->strokeThickness());
2488         }
2489
2490         maskImageContext.setTextDrawingMode(fill ? TextModeFill : TextModeStroke);
2491
2492         if (useMaxWidth) {
2493             maskImageContext.translate(location.x() - maskRect.x(), location.y() - maskRect.y());
2494             // We draw when fontWidth is 0 so compositing operations (eg, a "copy" op) still work.
2495             maskImageContext.scale(FloatSize((fontWidth > 0 ? (width / fontWidth) : 0), 1));
2496             fontProxy.drawBidiText(maskImageContext, textRun, FloatPoint(0, 0), FontCascade::UseFallbackIfFontNotReady);
2497         } else {
2498             maskImageContext.translate(-maskRect.x(), -maskRect.y());
2499             fontProxy.drawBidiText(maskImageContext, textRun, location, FontCascade::UseFallbackIfFontNotReady);
2500         }
2501
2502         GraphicsContextStateSaver stateSaver(*c);
2503         c->clipToImageBuffer(*maskImage, maskRect);
2504         drawStyle.applyFillColor(c);
2505         c->fillRect(maskRect);
2506         return;
2507     }
2508 #endif
2509
2510     c->setTextDrawingMode(fill ? TextModeFill : TextModeStroke);
2511
2512     GraphicsContextStateSaver stateSaver(*c);
2513     if (useMaxWidth) {
2514         c->translate(location.x(), location.y());
2515         // We draw when fontWidth is 0 so compositing operations (eg, a "copy" op) still work.
2516         c->scale(FloatSize((fontWidth > 0 ? (width / fontWidth) : 0), 1));
2517         location = FloatPoint();
2518     }
2519
2520     if (isFullCanvasCompositeMode(state().globalComposite)) {
2521         beginCompositeLayer();
2522         fontProxy.drawBidiText(*c, textRun, location, FontCascade::UseFallbackIfFontNotReady);
2523         endCompositeLayer();
2524         didDrawEntireCanvas();
2525     } else if (state().globalComposite == CompositeCopy) {
2526         clearCanvas();
2527         fontProxy.drawBidiText(*c, textRun, location, FontCascade::UseFallbackIfFontNotReady);
2528         didDrawEntireCanvas();
2529     } else {
2530         fontProxy.drawBidiText(*c, textRun, location, FontCascade::UseFallbackIfFontNotReady);
2531         didDraw(textRect);
2532     }
2533 }
2534
2535 void CanvasRenderingContext2D::inflateStrokeRect(FloatRect& rect) const
2536 {
2537     // Fast approximation of the stroke's bounding rect.
2538     // This yields a slightly oversized rect but is very fast
2539     // compared to Path::strokeBoundingRect().
2540     static const float root2 = sqrtf(2);
2541     float delta = state().lineWidth / 2;
2542     if (state().lineJoin == MiterJoin)
2543         delta *= state().miterLimit;
2544     else if (state().lineCap == SquareCap)
2545         delta *= root2;
2546
2547     rect.inflate(delta);
2548 }
2549
2550 auto CanvasRenderingContext2D::fontProxy() -> const FontProxy&
2551 {
2552     canvas()->document().updateStyleIfNeeded();
2553
2554     if (!state().font.realized())
2555         setFont(state().unparsedFont);
2556     return state().font;
2557 }
2558
2559 #if ENABLE(ACCELERATED_2D_CANVAS)
2560 PlatformLayer* CanvasRenderingContext2D::platformLayer() const
2561 {
2562     return canvas()->buffer() ? canvas()->buffer()->platformLayer() : 0;
2563 }
2564 #endif
2565
2566 static InterpolationQuality smoothingToInterpolationQuality(CanvasRenderingContext2D::SmoothingQuality quality)
2567 {
2568     switch (quality) {
2569     case CanvasRenderingContext2D::SmoothingQuality::Low:
2570         return InterpolationLow;
2571     case CanvasRenderingContext2D::SmoothingQuality::Medium:
2572         return InterpolationMedium;
2573     case CanvasRenderingContext2D::SmoothingQuality::High:
2574         return InterpolationHigh;
2575     }
2576
2577     ASSERT_NOT_REACHED();
2578     return InterpolationLow;
2579 };
2580
2581 String CanvasRenderingContext2D::imageSmoothingQuality() const
2582 {
2583     switch (state().imageSmoothingQuality) {
2584     case SmoothingQuality::Low:
2585         return ASCIILiteral("low");
2586     case SmoothingQuality::Medium:
2587         return ASCIILiteral("medium");
2588     case SmoothingQuality::High:
2589         return ASCIILiteral("high");
2590     }
2591
2592     ASSERT_NOT_REACHED();
2593     return ASCIILiteral("low");
2594 }
2595
2596 void CanvasRenderingContext2D::setImageSmoothingQuality(const String& smoothingQualityString)
2597 {
2598     SmoothingQuality quality;
2599     if (smoothingQualityString == "low")
2600         quality = SmoothingQuality::Low;
2601     else if (smoothingQualityString == "medium")
2602         quality = SmoothingQuality::Medium;
2603     else if (smoothingQualityString == "high")
2604         quality = SmoothingQuality::High;
2605     else {
2606         ASSERT_NOT_REACHED();
2607         return;
2608     }
2609
2610     if (quality == state().imageSmoothingQuality)
2611         return;
2612
2613     realizeSaves();
2614     modifiableState().imageSmoothingQuality = quality;
2615
2616     if (!state().imageSmoothingEnabled)
2617         return;
2618
2619     if (auto* context = drawingContext())
2620         context->setImageInterpolationQuality(smoothingToInterpolationQuality(quality));
2621 }
2622
2623 bool CanvasRenderingContext2D::imageSmoothingEnabled() const
2624 {
2625     return state().imageSmoothingEnabled;
2626 }
2627
2628 void CanvasRenderingContext2D::setImageSmoothingEnabled(bool enabled)
2629 {
2630     if (enabled == state().imageSmoothingEnabled)
2631         return;
2632
2633     realizeSaves();
2634     modifiableState().imageSmoothingEnabled = enabled;
2635     GraphicsContext* c = drawingContext();
2636     if (c)
2637         c->setImageInterpolationQuality(enabled ? smoothingToInterpolationQuality(state().imageSmoothingQuality) : InterpolationNone);
2638 }
2639
2640 } // namespace WebCore