1 /*
2  * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 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  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
22  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
26  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include "config.h"
32 #include "CanvasRenderingContext2D.h"
33 
34 #include "AffineTransform.h"
35 #include "CSSMutableStyleDeclaration.h"
36 #include "CSSParser.h"
37 #include "CSSPropertyNames.h"
38 #include "CSSStyleSelector.h"
39 #include "CachedImage.h"
40 #include "CanvasGradient.h"
41 #include "CanvasPattern.h"
42 #include "CanvasStyle.h"
43 #include "ExceptionCode.h"
44 #include "FloatConversion.h"
45 #include "GraphicsContext.h"
46 #include "HTMLCanvasElement.h"
47 #include "HTMLImageElement.h"
48 #include "HTMLMediaElement.h"
49 #include "HTMLNames.h"
50 #include "HTMLVideoElement.h"
51 #include "ImageBuffer.h"
52 #include "ImageData.h"
53 #include "KURL.h"
54 #include "Page.h"
55 #include "RenderHTMLCanvas.h"
56 #include "SecurityOrigin.h"
57 #include "Settings.h"
58 #include "StrokeStyleApplier.h"
59 #include "TextMetrics.h"
60 #include "TextRun.h"
61 
62 #if ENABLE(ACCELERATED_2D_CANVAS)
63 #include "Chrome.h"
64 #include "ChromeClient.h"
65 #include "DrawingBuffer.h"
66 #include "FrameView.h"
67 #include "GraphicsContext3D.h"
68 #include "SharedGraphicsContext3D.h"
69 #if USE(ACCELERATED_COMPOSITING)
70 #include "RenderLayer.h"
71 #endif
72 #endif
73 
74 #include <wtf/ByteArray.h>
75 #include <wtf/MathExtras.h>
76 #include <wtf/OwnPtr.h>
77 #include <wtf/UnusedParam.h>
78 
79 #if USE(CG)
80 #include <ApplicationServices/ApplicationServices.h>
81 #endif
82 
83 using namespace std;
84 
85 namespace WebCore {
86 
87 using namespace HTMLNames;
88 
89 static const char* const defaultFont = "10px sans-serif";
90 
91 
92 class CanvasStrokeStyleApplier : public StrokeStyleApplier {
93 public:
CanvasStrokeStyleApplier(CanvasRenderingContext2D * canvasContext)94     CanvasStrokeStyleApplier(CanvasRenderingContext2D* canvasContext)
95         : m_canvasContext(canvasContext)
96     {
97     }
98 
strokeStyle(GraphicsContext * c)99     virtual void strokeStyle(GraphicsContext* c)
100     {
101         c->setStrokeThickness(m_canvasContext->lineWidth());
102         c->setLineCap(m_canvasContext->getLineCap());
103         c->setLineJoin(m_canvasContext->getLineJoin());
104         c->setMiterLimit(m_canvasContext->miterLimit());
105     }
106 
107 private:
108     CanvasRenderingContext2D* m_canvasContext;
109 };
110 
CanvasRenderingContext2D(HTMLCanvasElement * canvas,bool usesCSSCompatibilityParseMode,bool usesDashboardCompatibilityMode)111 CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement* canvas, bool usesCSSCompatibilityParseMode, bool usesDashboardCompatibilityMode)
112     : CanvasRenderingContext(canvas)
113     , m_stateStack(1)
114     , m_usesCSSCompatibilityParseMode(usesCSSCompatibilityParseMode)
115 #if ENABLE(DASHBOARD_SUPPORT)
116     , m_usesDashboardCompatibilityMode(usesDashboardCompatibilityMode)
117 #endif
118 #if ENABLE(ACCELERATED_2D_CANVAS)
119     , m_context3D(0)
120 #endif
121 {
122 #if !ENABLE(DASHBOARD_SUPPORT)
123     ASSERT_UNUSED(usesDashboardCompatibilityMode, !usesDashboardCompatibilityMode);
124 #endif
125 
126     // Make sure that even if the drawingContext() has a different default
127     // thickness, it is in sync with the canvas thickness.
128     setLineWidth(lineWidth());
129 
130 #if ENABLE(ACCELERATED_2D_CANVAS)
131     Page* p = canvas->document()->page();
132     if (!p)
133         return;
134     if (!(p->settings()->accelerated2dCanvasEnabled() || p->settings()->legacyAccelerated2dCanvasEnabled()))
135         return;
136     if (GraphicsContext* c = drawingContext()) {
137         m_context3D = p->sharedGraphicsContext3D();
138         if (m_context3D) {
139             m_drawingBuffer = m_context3D->graphicsContext3D()->createDrawingBuffer(IntSize(canvas->width(), canvas->height()));
140             if (!m_drawingBuffer) {
141                 c->setSharedGraphicsContext3D(0, 0, IntSize());
142                 m_context3D.clear();
143             } else
144                 c->setSharedGraphicsContext3D(m_context3D.get(), m_drawingBuffer.get(), IntSize(canvas->width(), canvas->height()));
145         }
146     }
147 #endif
148 }
149 
~CanvasRenderingContext2D()150 CanvasRenderingContext2D::~CanvasRenderingContext2D()
151 {
152 #if !ASSERT_DISABLED
153     // Ensure that the state stack in the ImageBuffer's context
154     // is cleared before destruction, to avoid assertions in the
155     // GraphicsContext dtor.
156     if (size_t stackSize = m_stateStack.size()) {
157         if (GraphicsContext* context = canvas()->existingDrawingContext()) {
158             while (--stackSize)
159                 context->restore();
160         }
161     }
162 #endif
163 
164 #if ENABLE(ACCELERATED_2D_CANVAS)
165     if (GraphicsContext* context = drawingContext())
166         context->setSharedGraphicsContext3D(0, 0, IntSize());
167 #endif
168 }
169 
isAccelerated() const170 bool CanvasRenderingContext2D::isAccelerated() const
171 {
172 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
173     ImageBuffer* buffer = canvas()->buffer();
174     return buffer ? buffer->isAccelerated() : false;
175 #elif ENABLE(ACCELERATED_2D_CANVAS)
176     return m_context3D;
177 #else
178     return false;
179 #endif
180 }
181 
paintsIntoCanvasBuffer() const182 bool CanvasRenderingContext2D::paintsIntoCanvasBuffer() const
183 {
184 #if ENABLE(ACCELERATED_2D_CANVAS)
185     if (m_context3D)
186         return m_context3D->paintsIntoCanvasBuffer();
187 #endif
188     return true;
189 }
190 
191 
reset()192 void CanvasRenderingContext2D::reset()
193 {
194     m_stateStack.resize(1);
195     m_stateStack.first() = State();
196     m_path.clear();
197 #if ENABLE(ACCELERATED_2D_CANVAS)
198     if (GraphicsContext* c = drawingContext()) {
199         if (m_context3D && m_drawingBuffer) {
200             if (m_drawingBuffer->reset(IntSize(canvas()->width(), canvas()->height()))) {
201                 c->setSharedGraphicsContext3D(m_context3D.get(), m_drawingBuffer.get(), IntSize(canvas()->width(), canvas()->height()));
202             } else {
203                 c->setSharedGraphicsContext3D(0, 0, IntSize());
204                 m_drawingBuffer.clear();
205                 m_context3D.clear();
206             }
207 #if USE(ACCELERATED_COMPOSITING)
208             RenderBox* renderBox = canvas()->renderBox();
209             if (renderBox && renderBox->hasLayer() && renderBox->layer()->hasAcceleratedCompositing())
210                 renderBox->layer()->contentChanged(RenderLayer::CanvasChanged);
211 #endif
212         }
213     }
214 #endif
215 }
216 
State()217 CanvasRenderingContext2D::State::State()
218     : m_strokeStyle(CanvasStyle::createFromRGBA(Color::black))
219     , m_fillStyle(CanvasStyle::createFromRGBA(Color::black))
220     , m_lineWidth(1)
221     , m_lineCap(ButtCap)
222     , m_lineJoin(MiterJoin)
223     , m_miterLimit(10)
224     , m_shadowBlur(0)
225     , m_shadowColor(Color::transparent)
226     , m_globalAlpha(1)
227     , m_globalComposite(CompositeSourceOver)
228     , m_invertibleCTM(true)
229     , m_textAlign(StartTextAlign)
230     , m_textBaseline(AlphabeticTextBaseline)
231     , m_unparsedFont(defaultFont)
232     , m_realizedFont(false)
233 {
234 }
235 
State(const State & other)236 CanvasRenderingContext2D::State::State(const State& other)
237     : FontSelectorClient()
238 {
239     m_unparsedStrokeColor = other.m_unparsedStrokeColor;
240     m_unparsedFillColor = other.m_unparsedFillColor;
241     m_strokeStyle = other.m_strokeStyle;
242     m_fillStyle = other.m_fillStyle;
243     m_lineWidth = other.m_lineWidth;
244     m_lineCap = other.m_lineCap;
245     m_lineJoin = other.m_lineJoin;
246     m_miterLimit = other.m_miterLimit;
247     m_shadowOffset = other.m_shadowOffset;
248     m_shadowBlur = other.m_shadowBlur;
249     m_shadowColor = other.m_shadowColor;
250     m_globalAlpha = other.m_globalAlpha;
251     m_globalComposite = other.m_globalComposite;
252     m_transform = other.m_transform;
253     m_invertibleCTM = other.m_invertibleCTM;
254     m_textAlign = other.m_textAlign;
255     m_textBaseline = other.m_textBaseline;
256     m_unparsedFont = other.m_unparsedFont;
257     m_font = other.m_font;
258     m_realizedFont = other.m_realizedFont;
259 
260     if (m_realizedFont)
261         m_font.fontSelector()->registerForInvalidationCallbacks(this);
262 }
263 
operator =(const State & other)264 CanvasRenderingContext2D::State& CanvasRenderingContext2D::State::operator=(const State& other)
265 {
266     if (this == &other)
267         return *this;
268 
269     if (m_realizedFont)
270         m_font.fontSelector()->unregisterForInvalidationCallbacks(this);
271 
272     m_unparsedStrokeColor = other.m_unparsedStrokeColor;
273     m_unparsedFillColor = other.m_unparsedFillColor;
274     m_strokeStyle = other.m_strokeStyle;
275     m_fillStyle = other.m_fillStyle;
276     m_lineWidth = other.m_lineWidth;
277     m_lineCap = other.m_lineCap;
278     m_lineJoin = other.m_lineJoin;
279     m_miterLimit = other.m_miterLimit;
280     m_shadowOffset = other.m_shadowOffset;
281     m_shadowBlur = other.m_shadowBlur;
282     m_shadowColor = other.m_shadowColor;
283     m_globalAlpha = other.m_globalAlpha;
284     m_globalComposite = other.m_globalComposite;
285     m_transform = other.m_transform;
286     m_invertibleCTM = other.m_invertibleCTM;
287     m_textAlign = other.m_textAlign;
288     m_textBaseline = other.m_textBaseline;
289     m_unparsedFont = other.m_unparsedFont;
290     m_font = other.m_font;
291     m_realizedFont = other.m_realizedFont;
292 
293     if (m_realizedFont)
294         m_font.fontSelector()->registerForInvalidationCallbacks(this);
295 
296     return *this;
297 }
298 
~State()299 CanvasRenderingContext2D::State::~State()
300 {
301     if (m_realizedFont)
302         m_font.fontSelector()->unregisterForInvalidationCallbacks(this);
303 }
304 
fontsNeedUpdate(FontSelector * fontSelector)305 void CanvasRenderingContext2D::State::fontsNeedUpdate(FontSelector* fontSelector)
306 {
307     ASSERT_ARG(fontSelector, fontSelector == m_font.fontSelector());
308     ASSERT(m_realizedFont);
309 
310     m_font.update(fontSelector);
311 }
312 
save()313 void CanvasRenderingContext2D::save()
314 {
315     ASSERT(m_stateStack.size() >= 1);
316     m_stateStack.append(state());
317     GraphicsContext* c = drawingContext();
318     if (!c)
319         return;
320     c->save();
321 }
322 
restore()323 void CanvasRenderingContext2D::restore()
324 {
325     ASSERT(m_stateStack.size() >= 1);
326     if (m_stateStack.size() <= 1)
327         return;
328     m_path.transform(state().m_transform);
329     m_stateStack.removeLast();
330     m_path.transform(state().m_transform.inverse());
331     GraphicsContext* c = drawingContext();
332     if (!c)
333         return;
334     c->restore();
335 }
336 
setAllAttributesToDefault()337 void CanvasRenderingContext2D::setAllAttributesToDefault()
338 {
339     state().m_globalAlpha = 1;
340     state().m_shadowOffset = FloatSize();
341     state().m_shadowBlur = 0;
342     state().m_shadowColor = Color::transparent;
343     state().m_globalComposite = CompositeSourceOver;
344 
345     GraphicsContext* context = drawingContext();
346     if (!context)
347         return;
348 
349     context->setLegacyShadow(FloatSize(), 0, Color::transparent, ColorSpaceDeviceRGB);
350     context->setAlpha(1);
351     context->setCompositeOperation(CompositeSourceOver);
352 }
353 
strokeStyle() const354 CanvasStyle* CanvasRenderingContext2D::strokeStyle() const
355 {
356     return state().m_strokeStyle.get();
357 }
358 
setStrokeStyle(PassRefPtr<CanvasStyle> style)359 void CanvasRenderingContext2D::setStrokeStyle(PassRefPtr<CanvasStyle> style)
360 {
361     if (!style)
362         return;
363 
364     if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentColor(*style))
365         return;
366 
367     if (style->isCurrentColor()) {
368         if (style->hasOverrideAlpha())
369             style = CanvasStyle::createFromRGBA(colorWithOverrideAlpha(currentColor(canvas()), style->overrideAlpha()));
370         else
371             style = CanvasStyle::createFromRGBA(currentColor(canvas()));
372     } else
373         checkOrigin(style->canvasPattern());
374 
375     state().m_strokeStyle = style;
376     GraphicsContext* c = drawingContext();
377     if (!c)
378         return;
379     state().m_strokeStyle->applyStrokeColor(c);
380     state().m_unparsedStrokeColor = String();
381 }
382 
fillStyle() const383 CanvasStyle* CanvasRenderingContext2D::fillStyle() const
384 {
385     return state().m_fillStyle.get();
386 }
387 
setFillStyle(PassRefPtr<CanvasStyle> style)388 void CanvasRenderingContext2D::setFillStyle(PassRefPtr<CanvasStyle> style)
389 {
390     if (!style)
391         return;
392 
393     if (state().m_fillStyle && state().m_fillStyle->isEquivalentColor(*style))
394         return;
395 
396     if (style->isCurrentColor()) {
397         if (style->hasOverrideAlpha())
398             style = CanvasStyle::createFromRGBA(colorWithOverrideAlpha(currentColor(canvas()), style->overrideAlpha()));
399         else
400             style = CanvasStyle::createFromRGBA(currentColor(canvas()));
401     } else
402         checkOrigin(style->canvasPattern());
403 
404     state().m_fillStyle = style;
405     GraphicsContext* c = drawingContext();
406     if (!c)
407         return;
408     state().m_fillStyle->applyFillColor(c);
409     state().m_unparsedFillColor = String();
410 }
411 
lineWidth() const412 float CanvasRenderingContext2D::lineWidth() const
413 {
414     return state().m_lineWidth;
415 }
416 
setLineWidth(float width)417 void CanvasRenderingContext2D::setLineWidth(float width)
418 {
419     if (!(isfinite(width) && width > 0))
420         return;
421     state().m_lineWidth = width;
422     GraphicsContext* c = drawingContext();
423     if (!c)
424         return;
425     c->setStrokeThickness(width);
426 }
427 
lineCap() const428 String CanvasRenderingContext2D::lineCap() const
429 {
430     return lineCapName(state().m_lineCap);
431 }
432 
setLineCap(const String & s)433 void CanvasRenderingContext2D::setLineCap(const String& s)
434 {
435     LineCap cap;
436     if (!parseLineCap(s, cap))
437         return;
438     state().m_lineCap = cap;
439     GraphicsContext* c = drawingContext();
440     if (!c)
441         return;
442     c->setLineCap(cap);
443 }
444 
lineJoin() const445 String CanvasRenderingContext2D::lineJoin() const
446 {
447     return lineJoinName(state().m_lineJoin);
448 }
449 
setLineJoin(const String & s)450 void CanvasRenderingContext2D::setLineJoin(const String& s)
451 {
452     LineJoin join;
453     if (!parseLineJoin(s, join))
454         return;
455     state().m_lineJoin = join;
456     GraphicsContext* c = drawingContext();
457     if (!c)
458         return;
459     c->setLineJoin(join);
460 }
461 
miterLimit() const462 float CanvasRenderingContext2D::miterLimit() const
463 {
464     return state().m_miterLimit;
465 }
466 
setMiterLimit(float limit)467 void CanvasRenderingContext2D::setMiterLimit(float limit)
468 {
469     if (!(isfinite(limit) && limit > 0))
470         return;
471     state().m_miterLimit = limit;
472     GraphicsContext* c = drawingContext();
473     if (!c)
474         return;
475     c->setMiterLimit(limit);
476 }
477 
shadowOffsetX() const478 float CanvasRenderingContext2D::shadowOffsetX() const
479 {
480     return state().m_shadowOffset.width();
481 }
482 
setShadowOffsetX(float x)483 void CanvasRenderingContext2D::setShadowOffsetX(float x)
484 {
485     if (!isfinite(x))
486         return;
487     state().m_shadowOffset.setWidth(x);
488     applyShadow();
489 }
490 
shadowOffsetY() const491 float CanvasRenderingContext2D::shadowOffsetY() const
492 {
493     return state().m_shadowOffset.height();
494 }
495 
setShadowOffsetY(float y)496 void CanvasRenderingContext2D::setShadowOffsetY(float y)
497 {
498     if (!isfinite(y))
499         return;
500     state().m_shadowOffset.setHeight(y);
501     applyShadow();
502 }
503 
shadowBlur() const504 float CanvasRenderingContext2D::shadowBlur() const
505 {
506     return state().m_shadowBlur;
507 }
508 
setShadowBlur(float blur)509 void CanvasRenderingContext2D::setShadowBlur(float blur)
510 {
511     if (!(isfinite(blur) && blur >= 0))
512         return;
513     state().m_shadowBlur = blur;
514     applyShadow();
515 }
516 
shadowColor() const517 String CanvasRenderingContext2D::shadowColor() const
518 {
519     return Color(state().m_shadowColor).serialized();
520 }
521 
setShadowColor(const String & color)522 void CanvasRenderingContext2D::setShadowColor(const String& color)
523 {
524     if (!parseColorOrCurrentColor(state().m_shadowColor, color, canvas()))
525         return;
526 
527     applyShadow();
528 }
529 
globalAlpha() const530 float CanvasRenderingContext2D::globalAlpha() const
531 {
532     return state().m_globalAlpha;
533 }
534 
setGlobalAlpha(float alpha)535 void CanvasRenderingContext2D::setGlobalAlpha(float alpha)
536 {
537     if (!(alpha >= 0 && alpha <= 1))
538         return;
539     state().m_globalAlpha = alpha;
540     GraphicsContext* c = drawingContext();
541     if (!c)
542         return;
543     c->setAlpha(alpha);
544 }
545 
globalCompositeOperation() const546 String CanvasRenderingContext2D::globalCompositeOperation() const
547 {
548     return compositeOperatorName(state().m_globalComposite);
549 }
550 
setGlobalCompositeOperation(const String & operation)551 void CanvasRenderingContext2D::setGlobalCompositeOperation(const String& operation)
552 {
553     CompositeOperator op;
554     if (!parseCompositeOperator(operation, op))
555         return;
556     state().m_globalComposite = op;
557     GraphicsContext* c = drawingContext();
558     if (!c)
559         return;
560     c->setCompositeOperation(op);
561 #if ENABLE(ACCELERATED_2D_CANVAS)
562     if (isAccelerated() && !m_context3D->supportsCompositeOp(op)) {
563         c->setSharedGraphicsContext3D(0, 0, IntSize());
564         m_drawingBuffer.clear();
565         m_context3D.clear();
566         // Mark as needing a style recalc so our compositing layer can be removed.
567         canvas()->setNeedsStyleRecalc(SyntheticStyleChange);
568     }
569 #endif
570 }
571 
scale(float sx,float sy)572 void CanvasRenderingContext2D::scale(float sx, float sy)
573 {
574     GraphicsContext* c = drawingContext();
575     if (!c)
576         return;
577     if (!state().m_invertibleCTM)
578         return;
579 
580     if (!isfinite(sx) | !isfinite(sy))
581         return;
582 
583     AffineTransform newTransform = state().m_transform;
584     newTransform.scaleNonUniform(sx, sy);
585     if (!newTransform.isInvertible()) {
586         state().m_invertibleCTM = false;
587         return;
588     }
589 
590     state().m_transform = newTransform;
591     c->scale(FloatSize(sx, sy));
592     m_path.transform(AffineTransform().scaleNonUniform(1.0 / sx, 1.0 / sy));
593 }
594 
rotate(float angleInRadians)595 void CanvasRenderingContext2D::rotate(float angleInRadians)
596 {
597     GraphicsContext* c = drawingContext();
598     if (!c)
599         return;
600     if (!state().m_invertibleCTM)
601         return;
602 
603     if (!isfinite(angleInRadians))
604         return;
605 
606     AffineTransform newTransform = state().m_transform;
607     newTransform.rotate(angleInRadians / piDouble * 180.0);
608     if (!newTransform.isInvertible()) {
609         state().m_invertibleCTM = false;
610         return;
611     }
612 
613     state().m_transform = newTransform;
614     c->rotate(angleInRadians);
615     m_path.transform(AffineTransform().rotate(-angleInRadians / piDouble * 180.0));
616 }
617 
translate(float tx,float ty)618 void CanvasRenderingContext2D::translate(float tx, float ty)
619 {
620     GraphicsContext* c = drawingContext();
621     if (!c)
622         return;
623     if (!state().m_invertibleCTM)
624         return;
625 
626     if (!isfinite(tx) | !isfinite(ty))
627         return;
628 
629     AffineTransform newTransform = state().m_transform;
630     newTransform.translate(tx, ty);
631     if (!newTransform.isInvertible()) {
632         state().m_invertibleCTM = false;
633         return;
634     }
635 
636     state().m_transform = newTransform;
637     c->translate(tx, ty);
638     m_path.transform(AffineTransform().translate(-tx, -ty));
639 }
640 
transform(float m11,float m12,float m21,float m22,float dx,float dy)641 void CanvasRenderingContext2D::transform(float m11, float m12, float m21, float m22, float dx, float dy)
642 {
643     GraphicsContext* c = drawingContext();
644     if (!c)
645         return;
646     if (!state().m_invertibleCTM)
647         return;
648 
649     if (!isfinite(m11) | !isfinite(m21) | !isfinite(dx) | !isfinite(m12) | !isfinite(m22) | !isfinite(dy))
650         return;
651 
652     AffineTransform transform(m11, m12, m21, m22, dx, dy);
653     AffineTransform newTransform = state().m_transform * transform;
654     if (!newTransform.isInvertible()) {
655         state().m_invertibleCTM = false;
656         return;
657     }
658 
659     state().m_transform = newTransform;
660     c->concatCTM(transform);
661     m_path.transform(transform.inverse());
662 }
663 
setTransform(float m11,float m12,float m21,float m22,float dx,float dy)664 void CanvasRenderingContext2D::setTransform(float m11, float m12, float m21, float m22, float dx, float dy)
665 {
666     GraphicsContext* c = drawingContext();
667     if (!c)
668         return;
669 
670     if (!isfinite(m11) | !isfinite(m21) | !isfinite(dx) | !isfinite(m12) | !isfinite(m22) | !isfinite(dy))
671         return;
672 
673     AffineTransform ctm = state().m_transform;
674     if (!ctm.isInvertible())
675         return;
676     c->concatCTM(c->getCTM().inverse());
677     c->concatCTM(canvas()->baseTransform());
678     state().m_transform = ctm.inverse() * state().m_transform;
679     m_path.transform(ctm);
680 
681     state().m_invertibleCTM = true;
682     transform(m11, m12, m21, m22, dx, dy);
683 }
684 
setStrokeColor(const String & color)685 void CanvasRenderingContext2D::setStrokeColor(const String& color)
686 {
687     if (color == state().m_unparsedStrokeColor)
688         return;
689     setStrokeStyle(CanvasStyle::createFromString(color, canvas()->document()));
690     state().m_unparsedStrokeColor = color;
691 }
692 
setStrokeColor(float grayLevel)693 void CanvasRenderingContext2D::setStrokeColor(float grayLevel)
694 {
695     if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentRGBA(grayLevel, grayLevel, grayLevel, 1.0f))
696         return;
697     setStrokeStyle(CanvasStyle::createFromGrayLevelWithAlpha(grayLevel, 1.0f));
698 }
699 
setStrokeColor(const String & color,float alpha)700 void CanvasRenderingContext2D::setStrokeColor(const String& color, float alpha)
701 {
702     setStrokeStyle(CanvasStyle::createFromStringWithOverrideAlpha(color, alpha));
703 }
704 
setStrokeColor(float grayLevel,float alpha)705 void CanvasRenderingContext2D::setStrokeColor(float grayLevel, float alpha)
706 {
707     if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentRGBA(grayLevel, grayLevel, grayLevel, alpha))
708         return;
709     setStrokeStyle(CanvasStyle::createFromGrayLevelWithAlpha(grayLevel, alpha));
710 }
711 
setStrokeColor(float r,float g,float b,float a)712 void CanvasRenderingContext2D::setStrokeColor(float r, float g, float b, float a)
713 {
714     if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentRGBA(r, g, b, a))
715         return;
716     setStrokeStyle(CanvasStyle::createFromRGBAChannels(r, g, b, a));
717 }
718 
setStrokeColor(float c,float m,float y,float k,float a)719 void CanvasRenderingContext2D::setStrokeColor(float c, float m, float y, float k, float a)
720 {
721     if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentCMYKA(c, m, y, k, a))
722         return;
723     setStrokeStyle(CanvasStyle::createFromCMYKAChannels(c, m, y, k, a));
724 }
725 
setFillColor(const String & color)726 void CanvasRenderingContext2D::setFillColor(const String& color)
727 {
728     if (color == state().m_unparsedFillColor)
729         return;
730     setFillStyle(CanvasStyle::createFromString(color, canvas()->document()));
731     state().m_unparsedFillColor = color;
732 }
733 
setFillColor(float grayLevel)734 void CanvasRenderingContext2D::setFillColor(float grayLevel)
735 {
736     if (state().m_fillStyle && state().m_fillStyle->isEquivalentRGBA(grayLevel, grayLevel, grayLevel, 1.0f))
737         return;
738     setFillStyle(CanvasStyle::createFromGrayLevelWithAlpha(grayLevel, 1.0f));
739 }
740 
setFillColor(const String & color,float alpha)741 void CanvasRenderingContext2D::setFillColor(const String& color, float alpha)
742 {
743     setFillStyle(CanvasStyle::createFromStringWithOverrideAlpha(color, alpha));
744 }
745 
setFillColor(float grayLevel,float alpha)746 void CanvasRenderingContext2D::setFillColor(float grayLevel, float alpha)
747 {
748     if (state().m_fillStyle && state().m_fillStyle->isEquivalentRGBA(grayLevel, grayLevel, grayLevel, alpha))
749         return;
750     setFillStyle(CanvasStyle::createFromGrayLevelWithAlpha(grayLevel, alpha));
751 }
752 
setFillColor(float r,float g,float b,float a)753 void CanvasRenderingContext2D::setFillColor(float r, float g, float b, float a)
754 {
755     if (state().m_fillStyle && state().m_fillStyle->isEquivalentRGBA(r, g, b, a))
756         return;
757     setFillStyle(CanvasStyle::createFromRGBAChannels(r, g, b, a));
758 }
759 
setFillColor(float c,float m,float y,float k,float a)760 void CanvasRenderingContext2D::setFillColor(float c, float m, float y, float k, float a)
761 {
762     if (state().m_fillStyle && state().m_fillStyle->isEquivalentCMYKA(c, m, y, k, a))
763         return;
764     setFillStyle(CanvasStyle::createFromCMYKAChannels(c, m, y, k, a));
765 }
766 
beginPath()767 void CanvasRenderingContext2D::beginPath()
768 {
769     m_path.clear();
770 }
771 
closePath()772 void CanvasRenderingContext2D::closePath()
773 {
774     if (m_path.isEmpty())
775         return;
776 
777     FloatRect boundRect = m_path.boundingRect();
778     if (boundRect.width() || boundRect.height())
779         m_path.closeSubpath();
780 }
781 
moveTo(float x,float y)782 void CanvasRenderingContext2D::moveTo(float x, float y)
783 {
784     if (!isfinite(x) | !isfinite(y))
785         return;
786     if (!state().m_invertibleCTM)
787         return;
788     m_path.moveTo(FloatPoint(x, y));
789 }
790 
lineTo(float x,float y)791 void CanvasRenderingContext2D::lineTo(float x, float y)
792 {
793     if (!isfinite(x) | !isfinite(y))
794         return;
795     if (!state().m_invertibleCTM)
796         return;
797 
798     FloatPoint p1 = FloatPoint(x, y);
799     if (!m_path.hasCurrentPoint())
800         m_path.moveTo(p1);
801     else if (p1 != m_path.currentPoint())
802         m_path.addLineTo(FloatPoint(x, y));
803 }
804 
quadraticCurveTo(float cpx,float cpy,float x,float y)805 void CanvasRenderingContext2D::quadraticCurveTo(float cpx, float cpy, float x, float y)
806 {
807     if (!isfinite(cpx) | !isfinite(cpy) | !isfinite(x) | !isfinite(y))
808         return;
809     if (!state().m_invertibleCTM)
810         return;
811     if (!m_path.hasCurrentPoint())
812         m_path.moveTo(FloatPoint(cpx, cpy));
813 
814     FloatPoint p1 = FloatPoint(x, y);
815     if (p1 != m_path.currentPoint())
816         m_path.addQuadCurveTo(FloatPoint(cpx, cpy), p1);
817 }
818 
bezierCurveTo(float cp1x,float cp1y,float cp2x,float cp2y,float x,float y)819 void CanvasRenderingContext2D::bezierCurveTo(float cp1x, float cp1y, float cp2x, float cp2y, float x, float y)
820 {
821     if (!isfinite(cp1x) | !isfinite(cp1y) | !isfinite(cp2x) | !isfinite(cp2y) | !isfinite(x) | !isfinite(y))
822         return;
823     if (!state().m_invertibleCTM)
824         return;
825     if (!m_path.hasCurrentPoint())
826         m_path.moveTo(FloatPoint(cp1x, cp1y));
827 
828     FloatPoint p1 = FloatPoint(x, y);
829     if (p1 != m_path.currentPoint())
830         m_path.addBezierCurveTo(FloatPoint(cp1x, cp1y), FloatPoint(cp2x, cp2y), p1);
831 }
832 
arcTo(float x1,float y1,float x2,float y2,float r,ExceptionCode & ec)833 void CanvasRenderingContext2D::arcTo(float x1, float y1, float x2, float y2, float r, ExceptionCode& ec)
834 {
835     ec = 0;
836     if (!isfinite(x1) | !isfinite(y1) | !isfinite(x2) | !isfinite(y2) | !isfinite(r))
837         return;
838 
839     if (r < 0) {
840         ec = INDEX_SIZE_ERR;
841         return;
842     }
843 
844     if (!state().m_invertibleCTM)
845         return;
846 
847     FloatPoint p1 = FloatPoint(x1, y1);
848     FloatPoint p2 = FloatPoint(x2, y2);
849 
850     if (!m_path.hasCurrentPoint())
851         m_path.moveTo(p1);
852     else if (p1 == m_path.currentPoint() || p1 == p2 || !r)
853         lineTo(x1, y1);
854     else
855         m_path.addArcTo(p1, p2, r);
856 }
857 
arc(float x,float y,float r,float sa,float ea,bool anticlockwise,ExceptionCode & ec)858 void CanvasRenderingContext2D::arc(float x, float y, float r, float sa, float ea, bool anticlockwise, ExceptionCode& ec)
859 {
860     ec = 0;
861     if (!isfinite(x) | !isfinite(y) | !isfinite(r) | !isfinite(sa) | !isfinite(ea))
862         return;
863 
864     if (r < 0) {
865         ec = INDEX_SIZE_ERR;
866         return;
867     }
868 
869     if (sa == ea)
870         return;
871 
872     if (!state().m_invertibleCTM)
873         return;
874 
875     // If 'sa' and 'ea' differ by more than 2Pi, just add a circle starting/ending at 'sa'
876     if (anticlockwise && sa - ea >= 2 * piFloat) {
877         m_path.addArc(FloatPoint(x, y), r, sa, sa - 2 * piFloat, anticlockwise);
878         return;
879     }
880     if (!anticlockwise && ea - sa >= 2 * piFloat) {
881         m_path.addArc(FloatPoint(x, y), r, sa, sa + 2 * piFloat, anticlockwise);
882         return;
883     }
884 
885     m_path.addArc(FloatPoint(x, y), r, sa, ea, anticlockwise);
886 }
887 
validateRectForCanvas(float & x,float & y,float & width,float & height)888 static bool validateRectForCanvas(float& x, float& y, float& width, float& height)
889 {
890     if (!isfinite(x) | !isfinite(y) | !isfinite(width) | !isfinite(height))
891         return false;
892 
893     if (!width && !height)
894         return false;
895 
896     if (width < 0) {
897         width = -width;
898         x -= width;
899     }
900 
901     if (height < 0) {
902         height = -height;
903         y -= height;
904     }
905 
906     return true;
907 }
908 
rect(float x,float y,float width,float height)909 void CanvasRenderingContext2D::rect(float x, float y, float width, float height)
910 {
911     if (!state().m_invertibleCTM)
912         return;
913 
914     if (!isfinite(x) || !isfinite(y) || !isfinite(width) || !isfinite(height))
915         return;
916 
917     if (!width && !height) {
918         m_path.moveTo(FloatPoint(x, y));
919         return;
920     }
921 
922     m_path.addRect(FloatRect(x, y, width, height));
923 }
924 
925 #if ENABLE(DASHBOARD_SUPPORT)
clearPathForDashboardBackwardCompatibilityMode()926 void CanvasRenderingContext2D::clearPathForDashboardBackwardCompatibilityMode()
927 {
928     if (m_usesDashboardCompatibilityMode)
929         m_path.clear();
930 }
931 #endif
932 
fill()933 void CanvasRenderingContext2D::fill()
934 {
935     GraphicsContext* c = drawingContext();
936     if (!c)
937         return;
938     if (!state().m_invertibleCTM)
939         return;
940 
941     if (!m_path.isEmpty()) {
942         c->fillPath(m_path);
943         didDraw(m_path.boundingRect());
944     }
945 
946 #if ENABLE(DASHBOARD_SUPPORT)
947     clearPathForDashboardBackwardCompatibilityMode();
948 #endif
949 }
950 
stroke()951 void CanvasRenderingContext2D::stroke()
952 {
953     GraphicsContext* c = drawingContext();
954     if (!c)
955         return;
956     if (!state().m_invertibleCTM)
957         return;
958 
959     if (!m_path.isEmpty()) {
960 #if PLATFORM(QT)
961         // Fast approximation of the stroke's bounding rect.
962         // This yields a slightly oversized rect but is very fast
963         // compared to Path::strokeBoundingRect().
964         FloatRect boundingRect = m_path.platformPath().controlPointRect();
965         boundingRect.inflate(state().m_miterLimit + state().m_lineWidth);
966 #else
967         CanvasStrokeStyleApplier strokeApplier(this);
968         FloatRect boundingRect = m_path.strokeBoundingRect(&strokeApplier);
969 #endif
970         c->strokePath(m_path);
971         didDraw(boundingRect);
972     }
973 
974 #if ENABLE(DASHBOARD_SUPPORT)
975     clearPathForDashboardBackwardCompatibilityMode();
976 #endif
977 }
978 
clip()979 void CanvasRenderingContext2D::clip()
980 {
981     GraphicsContext* c = drawingContext();
982     if (!c)
983         return;
984     if (!state().m_invertibleCTM)
985         return;
986     c->canvasClip(m_path);
987 #if ENABLE(DASHBOARD_SUPPORT)
988     clearPathForDashboardBackwardCompatibilityMode();
989 #endif
990 }
991 
isPointInPath(const float x,const float y)992 bool CanvasRenderingContext2D::isPointInPath(const float x, const float y)
993 {
994     GraphicsContext* c = drawingContext();
995     if (!c)
996         return false;
997     if (!state().m_invertibleCTM)
998         return false;
999 
1000     FloatPoint point(x, y);
1001     AffineTransform ctm = state().m_transform;
1002     FloatPoint transformedPoint = ctm.inverse().mapPoint(point);
1003     if (!isfinite(transformedPoint.x()) || !isfinite(transformedPoint.y()))
1004         return false;
1005     return m_path.contains(transformedPoint);
1006 }
1007 
clearRect(float x,float y,float width,float height)1008 void CanvasRenderingContext2D::clearRect(float x, float y, float width, float height)
1009 {
1010     if (!validateRectForCanvas(x, y, width, height))
1011         return;
1012     GraphicsContext* context = drawingContext();
1013     if (!context)
1014         return;
1015     if (!state().m_invertibleCTM)
1016         return;
1017     FloatRect rect(x, y, width, height);
1018 
1019     save();
1020     setAllAttributesToDefault();
1021     context->clearRect(rect);
1022     didDraw(rect);
1023     restore();
1024 }
1025 
fillRect(float x,float y,float width,float height)1026 void CanvasRenderingContext2D::fillRect(float x, float y, float width, float height)
1027 {
1028     if (!validateRectForCanvas(x, y, width, height))
1029         return;
1030 
1031     GraphicsContext* c = drawingContext();
1032     if (!c)
1033         return;
1034     if (!state().m_invertibleCTM)
1035         return;
1036 
1037     // from the HTML5 Canvas spec:
1038     // If x0 = x1 and y0 = y1, then the linear gradient must paint nothing
1039     // If x0 = x1 and y0 = y1 and r0 = r1, then the radial gradient must paint nothing
1040     Gradient* gradient = c->fillGradient();
1041     if (gradient && gradient->isZeroSize())
1042         return;
1043 
1044     FloatRect rect(x, y, width, height);
1045 
1046     c->fillRect(rect);
1047     didDraw(rect);
1048 }
1049 
setLineDash(DashArray lineDash,float dashOffset)1050 void CanvasRenderingContext2D::setLineDash(DashArray lineDash, float dashOffset)
1051 {
1052     GraphicsContext* c = drawingContext();
1053     c->setLineDash(lineDash, dashOffset);
1054 }
1055 
strokeRect(float x,float y,float width,float height)1056 void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height)
1057 {
1058     if (!validateRectForCanvas(x, y, width, height))
1059         return;
1060     strokeRect(x, y, width, height, state().m_lineWidth);
1061 }
1062 
strokeRect(float x,float y,float width,float height,float lineWidth)1063 void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height, float lineWidth)
1064 {
1065     if (!validateRectForCanvas(x, y, width, height))
1066         return;
1067 
1068     if (!(lineWidth >= 0))
1069         return;
1070 
1071     GraphicsContext* c = drawingContext();
1072     if (!c)
1073         return;
1074     if (!state().m_invertibleCTM)
1075         return;
1076 
1077     FloatRect rect(x, y, width, height);
1078 
1079     FloatRect boundingRect = rect;
1080     boundingRect.inflate(lineWidth / 2);
1081 
1082     c->strokeRect(rect, lineWidth);
1083     didDraw(boundingRect);
1084 }
1085 
1086 #if USE(CG)
adjustedShadowSize(CGFloat width,CGFloat height)1087 static inline CGSize adjustedShadowSize(CGFloat width, CGFloat height)
1088 {
1089     // Work around <rdar://problem/5539388> by ensuring that shadow offsets will get truncated
1090     // to the desired integer.
1091     static const CGFloat extraShadowOffset = narrowPrecisionToCGFloat(1.0 / 128);
1092     if (width > 0)
1093         width += extraShadowOffset;
1094     else if (width < 0)
1095         width -= extraShadowOffset;
1096 
1097     if (height > 0)
1098         height += extraShadowOffset;
1099     else if (height < 0)
1100         height -= extraShadowOffset;
1101 
1102     return CGSizeMake(width, height);
1103 }
1104 #endif
1105 
setShadow(float width,float height,float blur)1106 void CanvasRenderingContext2D::setShadow(float width, float height, float blur)
1107 {
1108     state().m_shadowOffset = FloatSize(width, height);
1109     state().m_shadowBlur = blur;
1110     state().m_shadowColor = Color::transparent;
1111     applyShadow();
1112 }
1113 
setShadow(float width,float height,float blur,const String & color)1114 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color)
1115 {
1116     if (!parseColorOrCurrentColor(state().m_shadowColor, color, canvas()))
1117         return;
1118 
1119     state().m_shadowOffset = FloatSize(width, height);
1120     state().m_shadowBlur = blur;
1121     applyShadow();
1122 }
1123 
setShadow(float width,float height,float blur,float grayLevel)1124 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel)
1125 {
1126     state().m_shadowOffset = FloatSize(width, height);
1127     state().m_shadowBlur = blur;
1128     state().m_shadowColor = makeRGBA32FromFloats(grayLevel, grayLevel, grayLevel, 1.0f);
1129 
1130     GraphicsContext* c = drawingContext();
1131     if (!c)
1132         return;
1133 
1134     c->setLegacyShadow(FloatSize(width, -height), state().m_shadowBlur, state().m_shadowColor, ColorSpaceDeviceRGB);
1135 }
1136 
setShadow(float width,float height,float blur,const String & color,float alpha)1137 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color, float alpha)
1138 {
1139     RGBA32 rgba;
1140 
1141     if (!parseColorOrCurrentColor(rgba, color, canvas()))
1142         return;
1143 
1144     state().m_shadowColor = colorWithOverrideAlpha(rgba, alpha);
1145     state().m_shadowOffset = FloatSize(width, height);
1146     state().m_shadowBlur = blur;
1147 
1148     GraphicsContext* c = drawingContext();
1149     if (!c)
1150         return;
1151 
1152     c->setLegacyShadow(FloatSize(width, -height), state().m_shadowBlur, state().m_shadowColor, ColorSpaceDeviceRGB);
1153 }
1154 
setShadow(float width,float height,float blur,float grayLevel,float alpha)1155 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel, float alpha)
1156 {
1157     state().m_shadowOffset = FloatSize(width, height);
1158     state().m_shadowBlur = blur;
1159     state().m_shadowColor = makeRGBA32FromFloats(grayLevel, grayLevel, grayLevel, alpha);
1160 
1161     GraphicsContext* c = drawingContext();
1162     if (!c)
1163         return;
1164 
1165     c->setLegacyShadow(FloatSize(width, -height), state().m_shadowBlur, state().m_shadowColor, ColorSpaceDeviceRGB);
1166 }
1167 
setShadow(float width,float height,float blur,float r,float g,float b,float a)1168 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float r, float g, float b, float a)
1169 {
1170     state().m_shadowOffset = FloatSize(width, height);
1171     state().m_shadowBlur = blur;
1172     state().m_shadowColor = makeRGBA32FromFloats(r, g, b, a);
1173 
1174     GraphicsContext* c = drawingContext();
1175     if (!c)
1176         return;
1177 
1178     c->setLegacyShadow(FloatSize(width, -height), state().m_shadowBlur, state().m_shadowColor, ColorSpaceDeviceRGB);
1179 }
1180 
setShadow(float width,float height,float blur,float c,float m,float y,float k,float a)1181 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float c, float m, float y, float k, float a)
1182 {
1183     state().m_shadowOffset = FloatSize(width, height);
1184     state().m_shadowBlur = blur;
1185     state().m_shadowColor = makeRGBAFromCMYKA(c, m, y, k, a);
1186 
1187     GraphicsContext* dc = drawingContext();
1188     if (!dc)
1189         return;
1190 #if USE(CG)
1191     const CGFloat components[5] = { c, m, y, k, a };
1192     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceCMYK();
1193     CGColorRef shadowColor = CGColorCreate(colorSpace, components);
1194     CGColorSpaceRelease(colorSpace);
1195     CGContextSetShadowWithColor(dc->platformContext(), adjustedShadowSize(width, -height), blur, shadowColor);
1196     CGColorRelease(shadowColor);
1197 #else
1198     dc->setLegacyShadow(FloatSize(width, -height), blur, state().m_shadowColor, ColorSpaceDeviceRGB);
1199 #endif
1200 }
1201 
clearShadow()1202 void CanvasRenderingContext2D::clearShadow()
1203 {
1204     state().m_shadowOffset = FloatSize();
1205     state().m_shadowBlur = 0;
1206     state().m_shadowColor = Color::transparent;
1207     applyShadow();
1208 }
1209 
applyShadow()1210 void CanvasRenderingContext2D::applyShadow()
1211 {
1212     GraphicsContext* c = drawingContext();
1213     if (!c)
1214         return;
1215 
1216     float width = state().m_shadowOffset.width();
1217     float height = state().m_shadowOffset.height();
1218     c->setLegacyShadow(FloatSize(width, -height), state().m_shadowBlur, state().m_shadowColor, ColorSpaceDeviceRGB);
1219 }
1220 
size(HTMLImageElement * image)1221 static IntSize size(HTMLImageElement* image)
1222 {
1223     if (CachedImage* cachedImage = image->cachedImage())
1224         return cachedImage->imageSize(1.0f); // FIXME: Not sure about this.
1225     return IntSize();
1226 }
1227 
1228 #if ENABLE(VIDEO)
size(HTMLVideoElement * video)1229 static IntSize size(HTMLVideoElement* video)
1230 {
1231     if (MediaPlayer* player = video->player())
1232         return player->naturalSize();
1233     return IntSize();
1234 }
1235 #endif
1236 
normalizeRect(const FloatRect & rect)1237 static inline FloatRect normalizeRect(const FloatRect& rect)
1238 {
1239     return FloatRect(min(rect.x(), rect.maxX()),
1240         min(rect.y(), rect.maxY()),
1241         max(rect.width(), -rect.width()),
1242         max(rect.height(), -rect.height()));
1243 }
1244 
drawImage(HTMLImageElement * image,float x,float y,ExceptionCode & ec)1245 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, float x, float y, ExceptionCode& ec)
1246 {
1247     if (!image) {
1248         ec = TYPE_MISMATCH_ERR;
1249         return;
1250     }
1251     IntSize s = size(image);
1252     drawImage(image, x, y, s.width(), s.height(), ec);
1253 }
1254 
drawImage(HTMLImageElement * image,float x,float y,float width,float height,ExceptionCode & ec)1255 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image,
1256     float x, float y, float width, float height, ExceptionCode& ec)
1257 {
1258     if (!image) {
1259         ec = TYPE_MISMATCH_ERR;
1260         return;
1261     }
1262     IntSize s = size(image);
1263     drawImage(image, FloatRect(0, 0, s.width(), s.height()), FloatRect(x, y, width, height), ec);
1264 }
1265 
drawImage(HTMLImageElement * image,float sx,float sy,float sw,float sh,float dx,float dy,float dw,float dh,ExceptionCode & ec)1266 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image,
1267     float sx, float sy, float sw, float sh,
1268     float dx, float dy, float dw, float dh, ExceptionCode& ec)
1269 {
1270     if (!image) {
1271         ec = TYPE_MISMATCH_ERR;
1272         return;
1273     }
1274     drawImage(image, FloatRect(sx, sy, sw, sh), FloatRect(dx, dy, dw, dh), ec);
1275 }
1276 
drawImage(HTMLImageElement * image,const FloatRect & srcRect,const FloatRect & dstRect,ExceptionCode & ec)1277 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, const FloatRect& srcRect, const FloatRect& dstRect, ExceptionCode& ec)
1278 {
1279     drawImage(image, srcRect, dstRect, state().m_globalComposite, ec);
1280 }
1281 
drawImage(HTMLImageElement * image,const FloatRect & srcRect,const FloatRect & dstRect,const CompositeOperator & op,ExceptionCode & ec)1282 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, const FloatRect& srcRect, const FloatRect& dstRect, const CompositeOperator& op, ExceptionCode& ec)
1283 {
1284     if (!image) {
1285         ec = TYPE_MISMATCH_ERR;
1286         return;
1287     }
1288 
1289     ec = 0;
1290 
1291     if (!isfinite(dstRect.x()) || !isfinite(dstRect.y()) || !isfinite(dstRect.width()) || !isfinite(dstRect.height())
1292         || !isfinite(srcRect.x()) || !isfinite(srcRect.y()) || !isfinite(srcRect.width()) || !isfinite(srcRect.height()))
1293         return;
1294 
1295     if (!dstRect.width() || !dstRect.height())
1296         return;
1297 
1298     if (!image->complete())
1299         return;
1300 
1301     FloatRect normalizedSrcRect = normalizeRect(srcRect);
1302     FloatRect normalizedDstRect = normalizeRect(dstRect);
1303 
1304     FloatRect imageRect = FloatRect(FloatPoint(), size(image));
1305     if (!imageRect.contains(normalizedSrcRect) || !srcRect.width() || !srcRect.height()) {
1306         ec = INDEX_SIZE_ERR;
1307         return;
1308     }
1309 
1310     GraphicsContext* c = drawingContext();
1311     if (!c)
1312         return;
1313     if (!state().m_invertibleCTM)
1314         return;
1315 
1316     CachedImage* cachedImage = image->cachedImage();
1317     if (!cachedImage)
1318         return;
1319 
1320     checkOrigin(image);
1321 
1322     FloatRect sourceRect = c->roundToDevicePixels(normalizedSrcRect);
1323     FloatRect destRect = c->roundToDevicePixels(normalizedDstRect);
1324     c->drawImage(cachedImage->image(), ColorSpaceDeviceRGB, destRect, sourceRect, op);
1325     didDraw(destRect);
1326 }
1327 
drawImage(HTMLCanvasElement * canvas,float x,float y,ExceptionCode & ec)1328 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas, float x, float y, ExceptionCode& ec)
1329 {
1330     if (!canvas) {
1331         ec = TYPE_MISMATCH_ERR;
1332         return;
1333     }
1334     drawImage(canvas, x, y, canvas->width(), canvas->height(), ec);
1335 }
1336 
drawImage(HTMLCanvasElement * canvas,float x,float y,float width,float height,ExceptionCode & ec)1337 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas,
1338     float x, float y, float width, float height, ExceptionCode& ec)
1339 {
1340     if (!canvas) {
1341         ec = TYPE_MISMATCH_ERR;
1342         return;
1343     }
1344     drawImage(canvas, FloatRect(0, 0, canvas->width(), canvas->height()), FloatRect(x, y, width, height), ec);
1345 }
1346 
drawImage(HTMLCanvasElement * canvas,float sx,float sy,float sw,float sh,float dx,float dy,float dw,float dh,ExceptionCode & ec)1347 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas,
1348     float sx, float sy, float sw, float sh,
1349     float dx, float dy, float dw, float dh, ExceptionCode& ec)
1350 {
1351     drawImage(canvas, FloatRect(sx, sy, sw, sh), FloatRect(dx, dy, dw, dh), ec);
1352 }
1353 
drawImage(HTMLCanvasElement * sourceCanvas,const FloatRect & srcRect,const FloatRect & dstRect,ExceptionCode & ec)1354 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* sourceCanvas, const FloatRect& srcRect,
1355     const FloatRect& dstRect, ExceptionCode& ec)
1356 {
1357     if (!sourceCanvas) {
1358         ec = TYPE_MISMATCH_ERR;
1359         return;
1360     }
1361 
1362     FloatRect srcCanvasRect = FloatRect(FloatPoint(), sourceCanvas->size());
1363 
1364     if (!srcCanvasRect.width() || !srcCanvasRect.height()) {
1365         ec = INVALID_STATE_ERR;
1366         return;
1367     }
1368 
1369     if (!srcCanvasRect.contains(normalizeRect(srcRect)) || !srcRect.width() || !srcRect.height()) {
1370         ec = INDEX_SIZE_ERR;
1371         return;
1372     }
1373 
1374     ec = 0;
1375 
1376     if (!dstRect.width() || !dstRect.height())
1377         return;
1378 
1379     GraphicsContext* c = drawingContext();
1380     if (!c)
1381         return;
1382     if (!state().m_invertibleCTM)
1383         return;
1384 
1385     FloatRect sourceRect = c->roundToDevicePixels(srcRect);
1386     FloatRect destRect = c->roundToDevicePixels(dstRect);
1387 
1388     // FIXME: Do this through platform-independent GraphicsContext API.
1389     ImageBuffer* buffer = sourceCanvas->buffer();
1390     if (!buffer)
1391         return;
1392 
1393     checkOrigin(sourceCanvas);
1394 
1395 #if ENABLE(ACCELERATED_2D_CANVAS)
1396     // If we're drawing from one accelerated canvas 2d to another, avoid calling sourceCanvas->makeRenderingResultsAvailable()
1397     // as that will do a readback to software.
1398     CanvasRenderingContext* sourceContext = sourceCanvas->renderingContext();
1399     // FIXME: Implement an accelerated path for drawing from a WebGL canvas to a 2d canvas when possible.
1400     if (!isAccelerated() || !sourceContext || !sourceContext->isAccelerated() || !sourceContext->is2d())
1401         sourceCanvas->makeRenderingResultsAvailable();
1402 #else
1403     sourceCanvas->makeRenderingResultsAvailable();
1404 #endif
1405 
1406     c->drawImageBuffer(buffer, ColorSpaceDeviceRGB, destRect, sourceRect, state().m_globalComposite);
1407     didDraw(destRect);
1408 }
1409 
1410 #if ENABLE(VIDEO)
drawImage(HTMLVideoElement * video,float x,float y,ExceptionCode & ec)1411 void CanvasRenderingContext2D::drawImage(HTMLVideoElement* video, float x, float y, ExceptionCode& ec)
1412 {
1413     if (!video) {
1414         ec = TYPE_MISMATCH_ERR;
1415         return;
1416     }
1417     IntSize s = size(video);
1418     drawImage(video, x, y, s.width(), s.height(), ec);
1419 }
1420 
drawImage(HTMLVideoElement * video,float x,float y,float width,float height,ExceptionCode & ec)1421 void CanvasRenderingContext2D::drawImage(HTMLVideoElement* video,
1422                                          float x, float y, float width, float height, ExceptionCode& ec)
1423 {
1424     if (!video) {
1425         ec = TYPE_MISMATCH_ERR;
1426         return;
1427     }
1428     IntSize s = size(video);
1429     drawImage(video, FloatRect(0, 0, s.width(), s.height()), FloatRect(x, y, width, height), ec);
1430 }
1431 
drawImage(HTMLVideoElement * video,float sx,float sy,float sw,float sh,float dx,float dy,float dw,float dh,ExceptionCode & ec)1432 void CanvasRenderingContext2D::drawImage(HTMLVideoElement* video,
1433     float sx, float sy, float sw, float sh,
1434     float dx, float dy, float dw, float dh, ExceptionCode& ec)
1435 {
1436     drawImage(video, FloatRect(sx, sy, sw, sh), FloatRect(dx, dy, dw, dh), ec);
1437 }
1438 
drawImage(HTMLVideoElement * video,const FloatRect & srcRect,const FloatRect & dstRect,ExceptionCode & ec)1439 void CanvasRenderingContext2D::drawImage(HTMLVideoElement* video, const FloatRect& srcRect, const FloatRect& dstRect,
1440                                          ExceptionCode& ec)
1441 {
1442     if (!video) {
1443         ec = TYPE_MISMATCH_ERR;
1444         return;
1445     }
1446 
1447     ec = 0;
1448 
1449     if (video->readyState() == HTMLMediaElement::HAVE_NOTHING || video->readyState() == HTMLMediaElement::HAVE_METADATA)
1450         return;
1451 
1452     FloatRect videoRect = FloatRect(FloatPoint(), size(video));
1453     if (!videoRect.contains(normalizeRect(srcRect)) || !srcRect.width() || !srcRect.height()) {
1454         ec = INDEX_SIZE_ERR;
1455         return;
1456     }
1457 
1458     if (!dstRect.width() || !dstRect.height())
1459         return;
1460 
1461     GraphicsContext* c = drawingContext();
1462     if (!c)
1463         return;
1464     if (!state().m_invertibleCTM)
1465         return;
1466 
1467     checkOrigin(video);
1468 
1469     FloatRect sourceRect = c->roundToDevicePixels(srcRect);
1470     FloatRect destRect = c->roundToDevicePixels(dstRect);
1471 
1472     GraphicsContextStateSaver stateSaver(*c);
1473     c->clip(destRect);
1474     c->translate(destRect.x(), destRect.y());
1475     c->scale(FloatSize(destRect.width() / sourceRect.width(), destRect.height() / sourceRect.height()));
1476     c->translate(-sourceRect.x(), -sourceRect.y());
1477     video->paintCurrentFrameInContext(c, IntRect(IntPoint(), size(video)));
1478     stateSaver.restore();
1479     didDraw(destRect);
1480 }
1481 #endif
1482 
drawImageFromRect(HTMLImageElement * image,float sx,float sy,float sw,float sh,float dx,float dy,float dw,float dh,const String & compositeOperation)1483 void CanvasRenderingContext2D::drawImageFromRect(HTMLImageElement* image,
1484     float sx, float sy, float sw, float sh,
1485     float dx, float dy, float dw, float dh,
1486     const String& compositeOperation)
1487 {
1488     CompositeOperator op;
1489     if (!parseCompositeOperator(compositeOperation, op))
1490         op = CompositeSourceOver;
1491 
1492     ExceptionCode ec;
1493     drawImage(image, FloatRect(sx, sy, sw, sh), FloatRect(dx, dy, dw, dh), op, ec);
1494 }
1495 
setAlpha(float alpha)1496 void CanvasRenderingContext2D::setAlpha(float alpha)
1497 {
1498     setGlobalAlpha(alpha);
1499 }
1500 
setCompositeOperation(const String & operation)1501 void CanvasRenderingContext2D::setCompositeOperation(const String& operation)
1502 {
1503     setGlobalCompositeOperation(operation);
1504 }
1505 
prepareGradientForDashboard(CanvasGradient * gradient) const1506 void CanvasRenderingContext2D::prepareGradientForDashboard(CanvasGradient* gradient) const
1507 {
1508 #if ENABLE(DASHBOARD_SUPPORT)
1509     if (m_usesDashboardCompatibilityMode)
1510         gradient->setDashboardCompatibilityMode();
1511 #else
1512     UNUSED_PARAM(gradient);
1513 #endif
1514 }
1515 
createLinearGradient(float x0,float y0,float x1,float y1,ExceptionCode & ec)1516 PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createLinearGradient(float x0, float y0, float x1, float y1, ExceptionCode& ec)
1517 {
1518     if (!isfinite(x0) || !isfinite(y0) || !isfinite(x1) || !isfinite(y1)) {
1519         ec = NOT_SUPPORTED_ERR;
1520         return 0;
1521     }
1522 
1523     RefPtr<CanvasGradient> gradient = CanvasGradient::create(FloatPoint(x0, y0), FloatPoint(x1, y1));
1524     prepareGradientForDashboard(gradient.get());
1525     return gradient.release();
1526 }
1527 
createRadialGradient(float x0,float y0,float r0,float x1,float y1,float r1,ExceptionCode & ec)1528 PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createRadialGradient(float x0, float y0, float r0, float x1, float y1, float r1, ExceptionCode& ec)
1529 {
1530     if (!isfinite(x0) || !isfinite(y0) || !isfinite(r0) || !isfinite(x1) || !isfinite(y1) || !isfinite(r1)) {
1531         ec = NOT_SUPPORTED_ERR;
1532         return 0;
1533     }
1534 
1535     if (r0 < 0 || r1 < 0) {
1536         ec = INDEX_SIZE_ERR;
1537         return 0;
1538     }
1539 
1540     RefPtr<CanvasGradient> gradient = CanvasGradient::create(FloatPoint(x0, y0), r0, FloatPoint(x1, y1), r1);
1541     prepareGradientForDashboard(gradient.get());
1542     return gradient.release();
1543 }
1544 
createPattern(HTMLImageElement * image,const String & repetitionType,ExceptionCode & ec)1545 PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLImageElement* image,
1546     const String& repetitionType, ExceptionCode& ec)
1547 {
1548     if (!image) {
1549         ec = TYPE_MISMATCH_ERR;
1550         return 0;
1551     }
1552     bool repeatX, repeatY;
1553     ec = 0;
1554     CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec);
1555     if (ec)
1556         return 0;
1557 
1558     if (!image->complete())
1559         return 0;
1560 
1561     CachedImage* cachedImage = image->cachedImage();
1562     if (!cachedImage || !image->cachedImage()->image())
1563         return CanvasPattern::create(Image::nullImage(), repeatX, repeatY, true);
1564 
1565     bool originClean = !canvas()->securityOrigin().taintsCanvas(KURL(KURL(), cachedImage->response().url())) && cachedImage->image()->hasSingleSecurityOrigin();
1566     return CanvasPattern::create(cachedImage->image(), repeatX, repeatY, originClean);
1567 }
1568 
createPattern(HTMLCanvasElement * canvas,const String & repetitionType,ExceptionCode & ec)1569 PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLCanvasElement* canvas,
1570     const String& repetitionType, ExceptionCode& ec)
1571 {
1572     if (!canvas) {
1573         ec = TYPE_MISMATCH_ERR;
1574         return 0;
1575     }
1576     if (!canvas->width() || !canvas->height()) {
1577         ec = INVALID_STATE_ERR;
1578         return 0;
1579     }
1580 
1581     bool repeatX, repeatY;
1582     ec = 0;
1583     CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec);
1584     if (ec)
1585         return 0;
1586     return CanvasPattern::create(canvas->copiedImage(), repeatX, repeatY, canvas->originClean());
1587 }
1588 
didDraw(const FloatRect & r,unsigned options)1589 void CanvasRenderingContext2D::didDraw(const FloatRect& r, unsigned options)
1590 {
1591     GraphicsContext* c = drawingContext();
1592     if (!c)
1593         return;
1594     if (!state().m_invertibleCTM)
1595         return;
1596 
1597     FloatRect dirtyRect = r;
1598     if (options & CanvasDidDrawApplyTransform) {
1599         AffineTransform ctm = state().m_transform;
1600         dirtyRect = ctm.mapRect(r);
1601     }
1602 
1603     if (options & CanvasDidDrawApplyShadow && alphaChannel(state().m_shadowColor)) {
1604         // The shadow gets applied after transformation
1605         FloatRect shadowRect(dirtyRect);
1606         shadowRect.move(state().m_shadowOffset);
1607         shadowRect.inflate(state().m_shadowBlur);
1608         dirtyRect.unite(shadowRect);
1609     }
1610 
1611     if (options & CanvasDidDrawApplyClip) {
1612         // FIXME: apply the current clip to the rectangle. Unfortunately we can't get the clip
1613         // back out of the GraphicsContext, so to take clip into account for incremental painting,
1614         // we'd have to keep the clip path around.
1615     }
1616 
1617 #if ENABLE(ACCELERATED_2D_CANVAS)
1618     if (isAccelerated())
1619         drawingContext()->markDirtyRect(enclosingIntRect(dirtyRect));
1620 #endif
1621 #if ENABLE(ACCELERATED_2D_CANVAS) && USE(ACCELERATED_COMPOSITING)
1622     // If we are drawing to hardware and we have a composited layer, just call contentChanged().
1623     RenderBox* renderBox = canvas()->renderBox();
1624     if (isAccelerated() && renderBox && renderBox->hasLayer() && renderBox->layer()->hasAcceleratedCompositing())
1625         renderBox->layer()->contentChanged(RenderLayer::CanvasChanged);
1626     else
1627 #endif
1628         canvas()->didDraw(dirtyRect);
1629 }
1630 
drawingContext() const1631 GraphicsContext* CanvasRenderingContext2D::drawingContext() const
1632 {
1633     return canvas()->drawingContext();
1634 }
1635 
createEmptyImageData(const IntSize & size)1636 static PassRefPtr<ImageData> createEmptyImageData(const IntSize& size)
1637 {
1638     RefPtr<ImageData> data = ImageData::create(size);
1639     memset(data->data()->data()->data(), 0, data->data()->data()->length());
1640     return data.release();
1641 }
1642 
createImageData(PassRefPtr<ImageData> imageData,ExceptionCode & ec) const1643 PassRefPtr<ImageData> CanvasRenderingContext2D::createImageData(PassRefPtr<ImageData> imageData, ExceptionCode& ec) const
1644 {
1645     if (!imageData) {
1646         ec = NOT_SUPPORTED_ERR;
1647         return 0;
1648     }
1649 
1650     return createEmptyImageData(imageData->size());
1651 }
1652 
createImageData(float sw,float sh,ExceptionCode & ec) const1653 PassRefPtr<ImageData> CanvasRenderingContext2D::createImageData(float sw, float sh, ExceptionCode& ec) const
1654 {
1655     ec = 0;
1656     if (!sw || !sh) {
1657         ec = INDEX_SIZE_ERR;
1658         return 0;
1659     }
1660     if (!isfinite(sw) || !isfinite(sh)) {
1661         ec = NOT_SUPPORTED_ERR;
1662         return 0;
1663     }
1664 
1665     FloatSize unscaledSize(fabs(sw), fabs(sh));
1666     IntSize scaledSize = canvas()->convertLogicalToDevice(unscaledSize);
1667     if (scaledSize.width() < 1)
1668         scaledSize.setWidth(1);
1669     if (scaledSize.height() < 1)
1670         scaledSize.setHeight(1);
1671 
1672     float area = 4.0f * scaledSize.width() * scaledSize.height();
1673     if (area > static_cast<float>(std::numeric_limits<int>::max()))
1674         return 0;
1675 
1676     return createEmptyImageData(scaledSize);
1677 }
1678 
getImageData(float sx,float sy,float sw,float sh,ExceptionCode & ec) const1679 PassRefPtr<ImageData> CanvasRenderingContext2D::getImageData(float sx, float sy, float sw, float sh, ExceptionCode& ec) const
1680 {
1681     if (!canvas()->originClean()) {
1682         ec = SECURITY_ERR;
1683         return 0;
1684     }
1685     if (!sw || !sh) {
1686         ec = INDEX_SIZE_ERR;
1687         return 0;
1688     }
1689     if (!isfinite(sx) || !isfinite(sy) || !isfinite(sw) || !isfinite(sh)) {
1690         ec = NOT_SUPPORTED_ERR;
1691         return 0;
1692     }
1693 
1694     if (sw < 0) {
1695         sx += sw;
1696         sw = -sw;
1697     }
1698     if (sh < 0) {
1699         sy += sh;
1700         sh = -sh;
1701     }
1702 
1703     FloatRect unscaledRect(sx, sy, sw, sh);
1704     IntRect scaledRect = canvas()->convertLogicalToDevice(unscaledRect);
1705     if (scaledRect.width() < 1)
1706         scaledRect.setWidth(1);
1707     if (scaledRect.height() < 1)
1708         scaledRect.setHeight(1);
1709     ImageBuffer* buffer = canvas()->buffer();
1710     if (!buffer)
1711         return createEmptyImageData(scaledRect.size());
1712 
1713     RefPtr<ByteArray> byteArray = buffer->getUnmultipliedImageData(scaledRect);
1714     if (!byteArray)
1715         return 0;
1716 
1717     return ImageData::create(scaledRect.size(), byteArray.release());
1718 }
1719 
putImageData(ImageData * data,float dx,float dy,ExceptionCode & ec)1720 void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, ExceptionCode& ec)
1721 {
1722     if (!data) {
1723         ec = TYPE_MISMATCH_ERR;
1724         return;
1725     }
1726     putImageData(data, dx, dy, 0, 0, data->width(), data->height(), ec);
1727 }
1728 
putImageData(ImageData * data,float dx,float dy,float dirtyX,float dirtyY,float dirtyWidth,float dirtyHeight,ExceptionCode & ec)1729 void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, float dirtyX, float dirtyY,
1730                                             float dirtyWidth, float dirtyHeight, ExceptionCode& ec)
1731 {
1732     if (!data) {
1733         ec = TYPE_MISMATCH_ERR;
1734         return;
1735     }
1736     if (!isfinite(dx) || !isfinite(dy) || !isfinite(dirtyX) || !isfinite(dirtyY) || !isfinite(dirtyWidth) || !isfinite(dirtyHeight)) {
1737         ec = NOT_SUPPORTED_ERR;
1738         return;
1739     }
1740 
1741     ImageBuffer* buffer = canvas()->buffer();
1742     if (!buffer)
1743         return;
1744 
1745     if (dirtyWidth < 0) {
1746         dirtyX += dirtyWidth;
1747         dirtyWidth = -dirtyWidth;
1748     }
1749 
1750     if (dirtyHeight < 0) {
1751         dirtyY += dirtyHeight;
1752         dirtyHeight = -dirtyHeight;
1753     }
1754 
1755     FloatRect clipRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
1756     clipRect.intersect(IntRect(0, 0, data->width(), data->height()));
1757     IntSize destOffset(static_cast<int>(dx), static_cast<int>(dy));
1758     IntRect destRect = enclosingIntRect(clipRect);
1759     destRect.move(destOffset);
1760     destRect.intersect(IntRect(IntPoint(), buffer->size()));
1761     if (destRect.isEmpty())
1762         return;
1763     IntRect sourceRect(destRect);
1764     sourceRect.move(-destOffset);
1765 
1766     buffer->putUnmultipliedImageData(data->data()->data(), IntSize(data->width(), data->height()), sourceRect, IntPoint(destOffset));
1767     didDraw(destRect, CanvasDidDrawApplyNone); // ignore transform, shadow and clip
1768 }
1769 
font() const1770 String CanvasRenderingContext2D::font() const
1771 {
1772     return state().m_unparsedFont;
1773 }
1774 
setFont(const String & newFont)1775 void CanvasRenderingContext2D::setFont(const String& newFont)
1776 {
1777     RefPtr<CSSMutableStyleDeclaration> tempDecl = CSSMutableStyleDeclaration::create();
1778     CSSParser parser(!m_usesCSSCompatibilityParseMode);
1779 
1780     String declarationText("font: ");
1781     declarationText += newFont;
1782     parser.parseDeclaration(tempDecl.get(), declarationText);
1783     if (!tempDecl->length())
1784         return;
1785 
1786     // The parse succeeded.
1787     state().m_unparsedFont = newFont;
1788 
1789     // Map the <canvas> font into the text style. If the font uses keywords like larger/smaller, these will work
1790     // relative to the canvas.
1791     RefPtr<RenderStyle> newStyle = RenderStyle::create();
1792     if (RenderStyle* computedStyle = canvas()->computedStyle())
1793         newStyle->setFontDescription(computedStyle->fontDescription());
1794     newStyle->font().update(newStyle->font().fontSelector());
1795 
1796     // Now map the font property into the style.
1797     CSSStyleSelector* styleSelector = canvas()->styleSelector();
1798     styleSelector->applyPropertyToStyle(CSSPropertyFont, tempDecl->getPropertyCSSValue(CSSPropertyFont).get(), newStyle.get());
1799 
1800     state().m_font = newStyle->font();
1801     state().m_font.update(styleSelector->fontSelector());
1802     state().m_realizedFont = true;
1803     styleSelector->fontSelector()->registerForInvalidationCallbacks(&state());
1804 }
1805 
textAlign() const1806 String CanvasRenderingContext2D::textAlign() const
1807 {
1808     return textAlignName(state().m_textAlign);
1809 }
1810 
setTextAlign(const String & s)1811 void CanvasRenderingContext2D::setTextAlign(const String& s)
1812 {
1813     TextAlign align;
1814     if (!parseTextAlign(s, align))
1815         return;
1816     state().m_textAlign = align;
1817 }
1818 
textBaseline() const1819 String CanvasRenderingContext2D::textBaseline() const
1820 {
1821     return textBaselineName(state().m_textBaseline);
1822 }
1823 
setTextBaseline(const String & s)1824 void CanvasRenderingContext2D::setTextBaseline(const String& s)
1825 {
1826     TextBaseline baseline;
1827     if (!parseTextBaseline(s, baseline))
1828         return;
1829     state().m_textBaseline = baseline;
1830 }
1831 
fillText(const String & text,float x,float y)1832 void CanvasRenderingContext2D::fillText(const String& text, float x, float y)
1833 {
1834     drawTextInternal(text, x, y, true);
1835 }
1836 
fillText(const String & text,float x,float y,float maxWidth)1837 void CanvasRenderingContext2D::fillText(const String& text, float x, float y, float maxWidth)
1838 {
1839     drawTextInternal(text, x, y, true, maxWidth, true);
1840 }
1841 
strokeText(const String & text,float x,float y)1842 void CanvasRenderingContext2D::strokeText(const String& text, float x, float y)
1843 {
1844     drawTextInternal(text, x, y, false);
1845 }
1846 
strokeText(const String & text,float x,float y,float maxWidth)1847 void CanvasRenderingContext2D::strokeText(const String& text, float x, float y, float maxWidth)
1848 {
1849     drawTextInternal(text, x, y, false, maxWidth, true);
1850 }
1851 
measureText(const String & text)1852 PassRefPtr<TextMetrics> CanvasRenderingContext2D::measureText(const String& text)
1853 {
1854     RefPtr<TextMetrics> metrics = TextMetrics::create();
1855 
1856 #if PLATFORM(QT)
1857     // We always use complex text shaping since it can't be turned off for QPainterPath::addText().
1858     Font::CodePath oldCodePath = Font::codePath();
1859     Font::setCodePath(Font::Complex);
1860 #endif
1861 
1862     metrics->setWidth(accessFont().width(TextRun(text.characters(), text.length())));
1863 
1864 #if PLATFORM(QT)
1865     Font::setCodePath(oldCodePath);
1866 #endif
1867 
1868     return metrics.release();
1869 }
1870 
drawTextInternal(const String & text,float x,float y,bool fill,float,bool)1871 void CanvasRenderingContext2D::drawTextInternal(const String& text, float x, float y, bool fill, float /*maxWidth*/, bool /*useMaxWidth*/)
1872 {
1873     GraphicsContext* c = drawingContext();
1874     if (!c)
1875         return;
1876     if (!state().m_invertibleCTM)
1877         return;
1878     if (!isfinite(x) | !isfinite(y))
1879         return;
1880 
1881     const Font& font = accessFont();
1882     const FontMetrics& fontMetrics = font.fontMetrics();
1883 
1884     // FIXME: Handle maxWidth.
1885     // FIXME: Need to turn off font smoothing.
1886 
1887     RenderStyle* computedStyle = canvas()->computedStyle();
1888     TextDirection direction = computedStyle ? computedStyle->direction() : LTR;
1889     bool isRTL = direction == RTL;
1890     bool override = computedStyle ? computedStyle->unicodeBidi() == Override : false;
1891 
1892     unsigned length = text.length();
1893     const UChar* string = text.characters();
1894     TextRun textRun(string, length, false, 0, 0, TextRun::AllowTrailingExpansion, direction, override);
1895 
1896     // Draw the item text at the correct point.
1897     FloatPoint location(x, y);
1898     switch (state().m_textBaseline) {
1899     case TopTextBaseline:
1900     case HangingTextBaseline:
1901         location.setY(y + fontMetrics.ascent());
1902         break;
1903     case BottomTextBaseline:
1904     case IdeographicTextBaseline:
1905         location.setY(y - fontMetrics.descent());
1906         break;
1907     case MiddleTextBaseline:
1908         location.setY(y - fontMetrics.descent() + fontMetrics.height() / 2);
1909         break;
1910     case AlphabeticTextBaseline:
1911     default:
1912          // Do nothing.
1913         break;
1914     }
1915 
1916     float width = font.width(TextRun(text, false, 0, 0, TextRun::AllowTrailingExpansion, direction, override));
1917 
1918     TextAlign align = state().m_textAlign;
1919     if (align == StartTextAlign)
1920         align = isRTL ? RightTextAlign : LeftTextAlign;
1921     else if (align == EndTextAlign)
1922         align = isRTL ? LeftTextAlign : RightTextAlign;
1923 
1924     switch (align) {
1925     case CenterTextAlign:
1926         location.setX(location.x() - width / 2);
1927         break;
1928     case RightTextAlign:
1929         location.setX(location.x() - width);
1930         break;
1931     default:
1932         break;
1933     }
1934 
1935     // The slop built in to this mask rect matches the heuristic used in FontCGWin.cpp for GDI text.
1936     FloatRect textRect = FloatRect(location.x() - fontMetrics.height() / 2, location.y() - fontMetrics.ascent() - fontMetrics.lineGap(),
1937                                    width + fontMetrics.height(), fontMetrics.lineSpacing());
1938     if (!fill)
1939         textRect.inflate(c->strokeThickness() / 2);
1940 
1941 #if USE(CG)
1942     CanvasStyle* drawStyle = fill ? state().m_fillStyle.get() : state().m_strokeStyle.get();
1943     if (drawStyle->canvasGradient() || drawStyle->canvasPattern()) {
1944         // FIXME: The rect is not big enough for miters on stroked text.
1945         IntRect maskRect = enclosingIntRect(textRect);
1946 
1947 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
1948         OwnPtr<ImageBuffer> maskImage = ImageBuffer::create(maskRect.size(), ColorSpaceDeviceRGB, Accelerated);
1949 #else
1950         OwnPtr<ImageBuffer> maskImage = ImageBuffer::create(maskRect.size());
1951 #endif
1952 
1953         GraphicsContext* maskImageContext = maskImage->context();
1954 
1955         if (fill)
1956             maskImageContext->setFillColor(Color::black, ColorSpaceDeviceRGB);
1957         else {
1958             maskImageContext->setStrokeColor(Color::black, ColorSpaceDeviceRGB);
1959             maskImageContext->setStrokeThickness(c->strokeThickness());
1960         }
1961 
1962         maskImageContext->setTextDrawingMode(fill ? TextModeFill : TextModeStroke);
1963         maskImageContext->translate(-maskRect.x(), -maskRect.y());
1964 
1965         maskImageContext->drawBidiText(font, textRun, location);
1966 
1967         GraphicsContextStateSaver stateSaver(*c);
1968         c->clipToImageBuffer(maskImage.get(), maskRect);
1969         drawStyle->applyFillColor(c);
1970         c->fillRect(maskRect);
1971         return;
1972     }
1973 #endif
1974 
1975     c->setTextDrawingMode(fill ? TextModeFill : TextModeStroke);
1976 
1977 #if PLATFORM(QT)
1978     // We always use complex text shaping since it can't be turned off for QPainterPath::addText().
1979     Font::CodePath oldCodePath = Font::codePath();
1980     Font::setCodePath(Font::Complex);
1981 #endif
1982 
1983     c->drawBidiText(font, textRun, location);
1984 
1985     if (fill)
1986         didDraw(textRect);
1987     else {
1988         // When stroking text, pointy miters can extend outside of textRect, so we
1989         // punt and dirty the whole canvas.
1990         didDraw(FloatRect(0, 0, canvas()->width(), canvas()->height()));
1991     }
1992 
1993 #if PLATFORM(QT)
1994     Font::setCodePath(oldCodePath);
1995 #endif
1996 }
1997 
accessFont()1998 const Font& CanvasRenderingContext2D::accessFont()
1999 {
2000     canvas()->document()->updateStyleIfNeeded();
2001 
2002     if (!state().m_realizedFont)
2003         setFont(state().m_unparsedFont);
2004     return state().m_font;
2005 }
2006 
paintRenderingResultsToCanvas()2007 void CanvasRenderingContext2D::paintRenderingResultsToCanvas()
2008 {
2009 #if ENABLE(ACCELERATED_2D_CANVAS)
2010     if (GraphicsContext* c = drawingContext())
2011         c->syncSoftwareCanvas();
2012 #endif
2013 }
2014 
2015 #if ENABLE(ACCELERATED_2D_CANVAS) && USE(ACCELERATED_COMPOSITING)
platformLayer() const2016 PlatformLayer* CanvasRenderingContext2D::platformLayer() const
2017 {
2018     return m_drawingBuffer ? m_drawingBuffer->platformLayer() : 0;
2019 }
2020 #endif
2021 
2022 } // namespace WebCore
2023