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