• 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 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  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
21  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
25  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #include "config.h"
31 #include "CanvasRenderingContext2D.h"
32 
33 #include "AffineTransform.h"
34 #include "CSSParser.h"
35 #include "CachedImage.h"
36 #include "CanvasGradient.h"
37 #include "CanvasPattern.h"
38 #include "CanvasStyle.h"
39 #include "CSSMutableStyleDeclaration.h"
40 #include "CSSPropertyNames.h"
41 #include "CSSStyleSelector.h"
42 #include "Document.h"
43 #include "ExceptionCode.h"
44 #include "FloatConversion.h"
45 #include "GraphicsContext.h"
46 #include "HTMLCanvasElement.h"
47 #include "HTMLImageElement.h"
48 #include "HTMLNames.h"
49 #include "ImageBuffer.h"
50 #include "ImageData.h"
51 #include "KURL.h"
52 #include "Page.h"
53 #include "RenderHTMLCanvas.h"
54 #include "SecurityOrigin.h"
55 #include "Settings.h"
56 #include "StrokeStyleApplier.h"
57 #include "TextMetrics.h"
58 #include "HTMLVideoElement.h"
59 #include <stdio.h>
60 #include <wtf/ByteArray.h>
61 #include <wtf/MathExtras.h>
62 #include <wtf/OwnPtr.h>
63 #include <wtf/UnusedParam.h>
64 
65 using namespace std;
66 
67 namespace WebCore {
68 
69 using namespace HTMLNames;
70 
71 static const char* const defaultFont = "10px sans-serif";
72 
73 
74 class CanvasStrokeStyleApplier : public StrokeStyleApplier {
75 public:
CanvasStrokeStyleApplier(CanvasRenderingContext2D * canvasContext)76     CanvasStrokeStyleApplier(CanvasRenderingContext2D* canvasContext)
77         : m_canvasContext(canvasContext)
78     {
79     }
80 
strokeStyle(GraphicsContext * c)81     virtual void strokeStyle(GraphicsContext* c)
82     {
83         c->setStrokeThickness(m_canvasContext->lineWidth());
84         c->setLineCap(m_canvasContext->getLineCap());
85         c->setLineJoin(m_canvasContext->getLineJoin());
86         c->setMiterLimit(m_canvasContext->miterLimit());
87     }
88 
89 private:
90     CanvasRenderingContext2D* m_canvasContext;
91 };
92 
93 
94 
CanvasRenderingContext2D(HTMLCanvasElement * canvas)95 CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement* canvas)
96     : CanvasRenderingContext(canvas)
97     , m_stateStack(1)
98 {
99     // Make sure that even if the drawingContext() has a different default
100     // thickness, it is in sync with the canvas thickness.
101     setLineWidth(lineWidth());
102 }
103 
~CanvasRenderingContext2D()104 CanvasRenderingContext2D::~CanvasRenderingContext2D()
105 {
106 }
107 
reset()108 void CanvasRenderingContext2D::reset()
109 {
110     m_stateStack.resize(1);
111     m_stateStack.first() = State();
112 }
113 
State()114 CanvasRenderingContext2D::State::State()
115     : m_strokeStyle(CanvasStyle::create("black"))
116     , m_fillStyle(CanvasStyle::create("black"))
117     , m_lineWidth(1)
118     , m_lineCap(ButtCap)
119     , m_lineJoin(MiterJoin)
120     , m_miterLimit(10)
121     , m_shadowBlur(0)
122     , m_shadowColor("black")
123     , m_globalAlpha(1)
124     , m_globalComposite(CompositeSourceOver)
125     , m_invertibleCTM(true)
126     , m_textAlign(StartTextAlign)
127     , m_textBaseline(AlphabeticTextBaseline)
128     , m_unparsedFont(defaultFont)
129     , m_realizedFont(false)
130 {
131 }
132 
save()133 void CanvasRenderingContext2D::save()
134 {
135     ASSERT(m_stateStack.size() >= 1);
136     m_stateStack.append(state());
137     GraphicsContext* c = drawingContext();
138     if (!c)
139         return;
140     c->save();
141 }
142 
restore()143 void CanvasRenderingContext2D::restore()
144 {
145     ASSERT(m_stateStack.size() >= 1);
146     if (m_stateStack.size() <= 1)
147         return;
148     m_path.transform(state().m_transform);
149     m_stateStack.removeLast();
150     m_path.transform(state().m_transform.inverse());
151     GraphicsContext* c = drawingContext();
152     if (!c)
153         return;
154     c->restore();
155 }
156 
strokeStyle() const157 CanvasStyle* CanvasRenderingContext2D::strokeStyle() const
158 {
159     return state().m_strokeStyle.get();
160 }
161 
setStrokeStyle(PassRefPtr<CanvasStyle> style)162 void CanvasRenderingContext2D::setStrokeStyle(PassRefPtr<CanvasStyle> style)
163 {
164     if (!style)
165         return;
166 
167     if (canvas()->originClean()) {
168         if (CanvasPattern* pattern = style->canvasPattern()) {
169             if (!pattern->originClean())
170                 canvas()->setOriginTainted();
171         }
172     }
173 
174     state().m_strokeStyle = style;
175     GraphicsContext* c = drawingContext();
176     if (!c)
177         return;
178     state().m_strokeStyle->applyStrokeColor(c);
179 }
180 
fillStyle() const181 CanvasStyle* CanvasRenderingContext2D::fillStyle() const
182 {
183     return state().m_fillStyle.get();
184 }
185 
setFillStyle(PassRefPtr<CanvasStyle> style)186 void CanvasRenderingContext2D::setFillStyle(PassRefPtr<CanvasStyle> style)
187 {
188     if (!style)
189         return;
190 
191     if (canvas()->originClean()) {
192         if (CanvasPattern* pattern = style->canvasPattern()) {
193             if (!pattern->originClean())
194                 canvas()->setOriginTainted();
195         }
196     }
197 
198     state().m_fillStyle = style;
199     GraphicsContext* c = drawingContext();
200     if (!c)
201         return;
202     state().m_fillStyle->applyFillColor(c);
203 }
204 
lineWidth() const205 float CanvasRenderingContext2D::lineWidth() const
206 {
207     return state().m_lineWidth;
208 }
209 
setLineWidth(float width)210 void CanvasRenderingContext2D::setLineWidth(float width)
211 {
212     if (!(width > 0))
213         return;
214     state().m_lineWidth = width;
215     GraphicsContext* c = drawingContext();
216     if (!c)
217         return;
218     c->setStrokeThickness(width);
219 }
220 
lineCap() const221 String CanvasRenderingContext2D::lineCap() const
222 {
223     return lineCapName(state().m_lineCap);
224 }
225 
setLineCap(const String & s)226 void CanvasRenderingContext2D::setLineCap(const String& s)
227 {
228     LineCap cap;
229     if (!parseLineCap(s, cap))
230         return;
231     state().m_lineCap = cap;
232     GraphicsContext* c = drawingContext();
233     if (!c)
234         return;
235     c->setLineCap(cap);
236 }
237 
lineJoin() const238 String CanvasRenderingContext2D::lineJoin() const
239 {
240     return lineJoinName(state().m_lineJoin);
241 }
242 
setLineJoin(const String & s)243 void CanvasRenderingContext2D::setLineJoin(const String& s)
244 {
245     LineJoin join;
246     if (!parseLineJoin(s, join))
247         return;
248     state().m_lineJoin = join;
249     GraphicsContext* c = drawingContext();
250     if (!c)
251         return;
252     c->setLineJoin(join);
253 }
254 
miterLimit() const255 float CanvasRenderingContext2D::miterLimit() const
256 {
257     return state().m_miterLimit;
258 }
259 
setMiterLimit(float limit)260 void CanvasRenderingContext2D::setMiterLimit(float limit)
261 {
262     if (!(limit > 0))
263         return;
264     state().m_miterLimit = limit;
265     GraphicsContext* c = drawingContext();
266     if (!c)
267         return;
268     c->setMiterLimit(limit);
269 }
270 
shadowOffsetX() const271 float CanvasRenderingContext2D::shadowOffsetX() const
272 {
273     return state().m_shadowOffset.width();
274 }
275 
setShadowOffsetX(float x)276 void CanvasRenderingContext2D::setShadowOffsetX(float x)
277 {
278     state().m_shadowOffset.setWidth(x);
279     applyShadow();
280 }
281 
shadowOffsetY() const282 float CanvasRenderingContext2D::shadowOffsetY() const
283 {
284     return state().m_shadowOffset.height();
285 }
286 
setShadowOffsetY(float y)287 void CanvasRenderingContext2D::setShadowOffsetY(float y)
288 {
289     state().m_shadowOffset.setHeight(y);
290     applyShadow();
291 }
292 
shadowBlur() const293 float CanvasRenderingContext2D::shadowBlur() const
294 {
295     return state().m_shadowBlur;
296 }
297 
setShadowBlur(float blur)298 void CanvasRenderingContext2D::setShadowBlur(float blur)
299 {
300     state().m_shadowBlur = blur;
301     applyShadow();
302 }
303 
shadowColor() const304 String CanvasRenderingContext2D::shadowColor() const
305 {
306     // FIXME: What should this return if you called setShadow with a non-string color?
307     return state().m_shadowColor;
308 }
309 
setShadowColor(const String & color)310 void CanvasRenderingContext2D::setShadowColor(const String& color)
311 {
312     state().m_shadowColor = color;
313     applyShadow();
314 }
315 
globalAlpha() const316 float CanvasRenderingContext2D::globalAlpha() const
317 {
318     return state().m_globalAlpha;
319 }
320 
setGlobalAlpha(float alpha)321 void CanvasRenderingContext2D::setGlobalAlpha(float alpha)
322 {
323     if (!(alpha >= 0 && alpha <= 1))
324         return;
325     state().m_globalAlpha = alpha;
326     GraphicsContext* c = drawingContext();
327     if (!c)
328         return;
329     c->setAlpha(alpha);
330 }
331 
globalCompositeOperation() const332 String CanvasRenderingContext2D::globalCompositeOperation() const
333 {
334     return compositeOperatorName(state().m_globalComposite);
335 }
336 
setGlobalCompositeOperation(const String & operation)337 void CanvasRenderingContext2D::setGlobalCompositeOperation(const String& operation)
338 {
339     CompositeOperator op;
340     if (!parseCompositeOperator(operation, op))
341         return;
342     state().m_globalComposite = op;
343     GraphicsContext* c = drawingContext();
344     if (!c)
345         return;
346     c->setCompositeOperation(op);
347 }
348 
scale(float sx,float sy)349 void CanvasRenderingContext2D::scale(float sx, float sy)
350 {
351     GraphicsContext* c = drawingContext();
352     if (!c)
353         return;
354     if (!state().m_invertibleCTM)
355         return;
356 
357     if (!isfinite(sx) | !isfinite(sy))
358         return;
359 
360     AffineTransform newTransform = state().m_transform;
361     newTransform.scaleNonUniform(sx, sy);
362     if (!newTransform.isInvertible()) {
363         state().m_invertibleCTM = false;
364         return;
365     }
366 
367     state().m_transform = newTransform;
368     c->scale(FloatSize(sx, sy));
369     m_path.transform(AffineTransform().scaleNonUniform(1.0 / sx, 1.0 / sy));
370 }
371 
rotate(float angleInRadians)372 void CanvasRenderingContext2D::rotate(float angleInRadians)
373 {
374     GraphicsContext* c = drawingContext();
375     if (!c)
376         return;
377     if (!state().m_invertibleCTM)
378         return;
379 
380     if (!isfinite(angleInRadians))
381         return;
382 
383     AffineTransform newTransform = state().m_transform;
384     newTransform.rotate(angleInRadians / piDouble * 180.0);
385     if (!newTransform.isInvertible()) {
386         state().m_invertibleCTM = false;
387         return;
388     }
389 
390     state().m_transform = newTransform;
391     c->rotate(angleInRadians);
392     m_path.transform(AffineTransform().rotate(-angleInRadians / piDouble * 180.0));
393 }
394 
translate(float tx,float ty)395 void CanvasRenderingContext2D::translate(float tx, float ty)
396 {
397     GraphicsContext* c = drawingContext();
398     if (!c)
399         return;
400     if (!state().m_invertibleCTM)
401         return;
402 
403     if (!isfinite(tx) | !isfinite(ty))
404         return;
405 
406     AffineTransform newTransform = state().m_transform;
407     newTransform.translate(tx, ty);
408     if (!newTransform.isInvertible()) {
409         state().m_invertibleCTM = false;
410         return;
411     }
412 
413     state().m_transform = newTransform;
414     c->translate(tx, ty);
415     m_path.transform(AffineTransform().translate(-tx, -ty));
416 }
417 
transform(float m11,float m12,float m21,float m22,float dx,float dy)418 void CanvasRenderingContext2D::transform(float m11, float m12, float m21, float m22, float dx, float dy)
419 {
420     GraphicsContext* c = drawingContext();
421     if (!c)
422         return;
423     if (!state().m_invertibleCTM)
424         return;
425 
426     if (!isfinite(m11) | !isfinite(m21) | !isfinite(dx) |
427         !isfinite(m12) | !isfinite(m22) | !isfinite(dy))
428         return;
429 
430     AffineTransform transform(m11, m12, m21, m22, dx, dy);
431     AffineTransform newTransform = transform * state().m_transform;
432     if (!newTransform.isInvertible()) {
433         state().m_invertibleCTM = false;
434         return;
435     }
436 
437     state().m_transform = newTransform;
438     c->concatCTM(transform);
439     m_path.transform(transform.inverse());
440 }
441 
setTransform(float m11,float m12,float m21,float m22,float dx,float dy)442 void CanvasRenderingContext2D::setTransform(float m11, float m12, float m21, float m22, float dx, float dy)
443 {
444     GraphicsContext* c = drawingContext();
445     if (!c)
446         return;
447 
448     if (!isfinite(m11) | !isfinite(m21) | !isfinite(dx) |
449         !isfinite(m12) | !isfinite(m22) | !isfinite(dy))
450         return;
451 
452     AffineTransform ctm = state().m_transform;
453     if (!ctm.isInvertible())
454         return;
455     c->concatCTM(c->getCTM().inverse());
456     c->concatCTM(canvas()->baseTransform());
457     state().m_transform.multiply(ctm.inverse());
458     m_path.transform(ctm);
459 
460     state().m_invertibleCTM = true;
461     transform(m11, m12, m21, m22, dx, dy);
462 }
463 
setStrokeColor(const String & color)464 void CanvasRenderingContext2D::setStrokeColor(const String& color)
465 {
466     setStrokeStyle(CanvasStyle::create(color));
467 }
468 
setStrokeColor(float grayLevel)469 void CanvasRenderingContext2D::setStrokeColor(float grayLevel)
470 {
471     setStrokeStyle(CanvasStyle::create(grayLevel, 1));
472 }
473 
setStrokeColor(const String & color,float alpha)474 void CanvasRenderingContext2D::setStrokeColor(const String& color, float alpha)
475 {
476     setStrokeStyle(CanvasStyle::create(color, alpha));
477 }
478 
setStrokeColor(float grayLevel,float alpha)479 void CanvasRenderingContext2D::setStrokeColor(float grayLevel, float alpha)
480 {
481     setStrokeStyle(CanvasStyle::create(grayLevel, alpha));
482 }
483 
setStrokeColor(float r,float g,float b,float a)484 void CanvasRenderingContext2D::setStrokeColor(float r, float g, float b, float a)
485 {
486     setStrokeStyle(CanvasStyle::create(r, g, b, a));
487 }
488 
setStrokeColor(float c,float m,float y,float k,float a)489 void CanvasRenderingContext2D::setStrokeColor(float c, float m, float y, float k, float a)
490 {
491     setStrokeStyle(CanvasStyle::create(c, m, y, k, a));
492 }
493 
setFillColor(const String & color)494 void CanvasRenderingContext2D::setFillColor(const String& color)
495 {
496     setFillStyle(CanvasStyle::create(color));
497 }
498 
setFillColor(float grayLevel)499 void CanvasRenderingContext2D::setFillColor(float grayLevel)
500 {
501     setFillStyle(CanvasStyle::create(grayLevel, 1));
502 }
503 
setFillColor(const String & color,float alpha)504 void CanvasRenderingContext2D::setFillColor(const String& color, float alpha)
505 {
506     setFillStyle(CanvasStyle::create(color, alpha));
507 }
508 
setFillColor(float grayLevel,float alpha)509 void CanvasRenderingContext2D::setFillColor(float grayLevel, float alpha)
510 {
511     setFillStyle(CanvasStyle::create(grayLevel, alpha));
512 }
513 
setFillColor(float r,float g,float b,float a)514 void CanvasRenderingContext2D::setFillColor(float r, float g, float b, float a)
515 {
516     setFillStyle(CanvasStyle::create(r, g, b, a));
517 }
518 
setFillColor(float c,float m,float y,float k,float a)519 void CanvasRenderingContext2D::setFillColor(float c, float m, float y, float k, float a)
520 {
521     setFillStyle(CanvasStyle::create(c, m, y, k, a));
522 }
523 
beginPath()524 void CanvasRenderingContext2D::beginPath()
525 {
526     m_path.clear();
527 }
528 
closePath()529 void CanvasRenderingContext2D::closePath()
530 {
531     m_path.closeSubpath();
532 }
533 
moveTo(float x,float y)534 void CanvasRenderingContext2D::moveTo(float x, float y)
535 {
536     if (!isfinite(x) | !isfinite(y))
537         return;
538     if (!state().m_invertibleCTM)
539         return;
540     m_path.moveTo(FloatPoint(x, y));
541 }
542 
lineTo(float x,float y)543 void CanvasRenderingContext2D::lineTo(float x, float y)
544 {
545     if (!isfinite(x) | !isfinite(y))
546         return;
547     if (!state().m_invertibleCTM)
548         return;
549     if (!m_path.hasCurrentPoint())
550         m_path.moveTo(FloatPoint(x, y));
551     else
552         m_path.addLineTo(FloatPoint(x, y));
553 }
554 
quadraticCurveTo(float cpx,float cpy,float x,float y)555 void CanvasRenderingContext2D::quadraticCurveTo(float cpx, float cpy, float x, float y)
556 {
557     if (!isfinite(cpx) | !isfinite(cpy) | !isfinite(x) | !isfinite(y))
558         return;
559     if (!state().m_invertibleCTM)
560         return;
561     if (!m_path.hasCurrentPoint())
562         m_path.moveTo(FloatPoint(x, y));
563     else
564         m_path.addQuadCurveTo(FloatPoint(cpx, cpy), FloatPoint(x, y));
565 }
566 
bezierCurveTo(float cp1x,float cp1y,float cp2x,float cp2y,float x,float y)567 void CanvasRenderingContext2D::bezierCurveTo(float cp1x, float cp1y, float cp2x, float cp2y, float x, float y)
568 {
569     if (!isfinite(cp1x) | !isfinite(cp1y) | !isfinite(cp2x) | !isfinite(cp2y) | !isfinite(x) | !isfinite(y))
570         return;
571     if (!state().m_invertibleCTM)
572         return;
573     if (!m_path.hasCurrentPoint())
574         m_path.moveTo(FloatPoint(x, y));
575     else
576         m_path.addBezierCurveTo(FloatPoint(cp1x, cp1y), FloatPoint(cp2x, cp2y), FloatPoint(x, y));
577 }
578 
arcTo(float x0,float y0,float x1,float y1,float r,ExceptionCode & ec)579 void CanvasRenderingContext2D::arcTo(float x0, float y0, float x1, float y1, float r, ExceptionCode& ec)
580 {
581     ec = 0;
582     if (!isfinite(x0) | !isfinite(y0) | !isfinite(x1) | !isfinite(y1) | !isfinite(r))
583         return;
584 
585     if (r < 0) {
586         ec = INDEX_SIZE_ERR;
587         return;
588     }
589     if (!state().m_invertibleCTM)
590         return;
591     m_path.addArcTo(FloatPoint(x0, y0), FloatPoint(x1, y1), r);
592 }
593 
arc(float x,float y,float r,float sa,float ea,bool anticlockwise,ExceptionCode & ec)594 void CanvasRenderingContext2D::arc(float x, float y, float r, float sa, float ea, bool anticlockwise, ExceptionCode& ec)
595 {
596     ec = 0;
597     if (!isfinite(x) | !isfinite(y) | !isfinite(r) | !isfinite(sa) | !isfinite(ea))
598         return;
599 
600     if (r < 0) {
601         ec = INDEX_SIZE_ERR;
602         return;
603     }
604     if (!state().m_invertibleCTM)
605         return;
606     m_path.addArc(FloatPoint(x, y), r, sa, ea, anticlockwise);
607 }
608 
validateRectForCanvas(float & x,float & y,float & width,float & height)609 static bool validateRectForCanvas(float& x, float& y, float& width, float& height)
610 {
611     if (!isfinite(x) | !isfinite(y) | !isfinite(width) | !isfinite(height))
612         return false;
613 
614     if (width < 0) {
615         width = -width;
616         x -= width;
617     }
618 
619     if (height < 0) {
620         height = -height;
621         y -= height;
622     }
623 
624     return true;
625 }
626 
rect(float x,float y,float width,float height)627 void CanvasRenderingContext2D::rect(float x, float y, float width, float height)
628 {
629     if (!validateRectForCanvas(x, y, width, height))
630         return;
631     if (!state().m_invertibleCTM)
632         return;
633     m_path.addRect(FloatRect(x, y, width, height));
634 }
635 
636 #if ENABLE(DASHBOARD_SUPPORT)
clearPathForDashboardBackwardCompatibilityMode()637 void CanvasRenderingContext2D::clearPathForDashboardBackwardCompatibilityMode()
638 {
639     if (Settings* settings = canvas()->document()->settings())
640         if (settings->usesDashboardBackwardCompatibilityMode())
641             m_path.clear();
642 }
643 #endif
644 
fill()645 void CanvasRenderingContext2D::fill()
646 {
647     GraphicsContext* c = drawingContext();
648     if (!c)
649         return;
650     if (!state().m_invertibleCTM)
651         return;
652 
653     if (!m_path.isEmpty()) {
654         c->beginPath();
655         c->addPath(m_path);
656         willDraw(m_path.boundingRect());
657         c->fillPath();
658     }
659 
660 #if ENABLE(DASHBOARD_SUPPORT)
661     clearPathForDashboardBackwardCompatibilityMode();
662 #endif
663 }
664 
stroke()665 void CanvasRenderingContext2D::stroke()
666 {
667     GraphicsContext* c = drawingContext();
668     if (!c)
669         return;
670     if (!state().m_invertibleCTM)
671         return;
672 
673     if (!m_path.isEmpty()) {
674         c->beginPath();
675         c->addPath(m_path);
676 
677         CanvasStrokeStyleApplier strokeApplier(this);
678         FloatRect boundingRect = m_path.strokeBoundingRect(&strokeApplier);
679         willDraw(boundingRect);
680 
681         c->strokePath();
682     }
683 
684 #if ENABLE(DASHBOARD_SUPPORT)
685     clearPathForDashboardBackwardCompatibilityMode();
686 #endif
687 }
688 
clip()689 void CanvasRenderingContext2D::clip()
690 {
691     GraphicsContext* c = drawingContext();
692     if (!c)
693         return;
694     if (!state().m_invertibleCTM)
695         return;
696     c->canvasClip(m_path);
697 #if ENABLE(DASHBOARD_SUPPORT)
698     clearPathForDashboardBackwardCompatibilityMode();
699 #endif
700 }
701 
isPointInPath(const float x,const float y)702 bool CanvasRenderingContext2D::isPointInPath(const float x, const float y)
703 {
704     GraphicsContext* c = drawingContext();
705     if (!c)
706         return false;
707     if (!state().m_invertibleCTM)
708         return false;
709 
710     FloatPoint point(x, y);
711     AffineTransform ctm = state().m_transform;
712     FloatPoint transformedPoint = ctm.inverse().mapPoint(point);
713     return m_path.contains(transformedPoint);
714 }
715 
clearRect(float x,float y,float width,float height)716 void CanvasRenderingContext2D::clearRect(float x, float y, float width, float height)
717 {
718     if (!validateRectForCanvas(x, y, width, height))
719         return;
720     GraphicsContext* c = drawingContext();
721     if (!c)
722         return;
723     if (!state().m_invertibleCTM)
724         return;
725     FloatRect rect(x, y, width, height);
726     willDraw(rect);
727     c->clearRect(rect);
728 }
729 
fillRect(float x,float y,float width,float height)730 void CanvasRenderingContext2D::fillRect(float x, float y, float width, float height)
731 {
732     if (!validateRectForCanvas(x, y, width, height))
733         return;
734 
735     GraphicsContext* c = drawingContext();
736     if (!c)
737         return;
738     if (!state().m_invertibleCTM)
739         return;
740 
741     FloatRect rect(x, y, width, height);
742     willDraw(rect);
743 
744     c->save();
745     c->fillRect(rect);
746     c->restore();
747 }
748 
strokeRect(float x,float y,float width,float height)749 void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height)
750 {
751     if (!validateRectForCanvas(x, y, width, height))
752         return;
753     strokeRect(x, y, width, height, state().m_lineWidth);
754 }
755 
strokeRect(float x,float y,float width,float height,float lineWidth)756 void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height, float lineWidth)
757 {
758     if (!validateRectForCanvas(x, y, width, height))
759         return;
760 
761     if (!(lineWidth >= 0))
762         return;
763 
764     GraphicsContext* c = drawingContext();
765     if (!c)
766         return;
767     if (!state().m_invertibleCTM)
768         return;
769 
770     FloatRect rect(x, y, width, height);
771 
772     FloatRect boundingRect = rect;
773     boundingRect.inflate(lineWidth / 2);
774     willDraw(boundingRect);
775 
776     c->strokeRect(rect, lineWidth);
777 }
778 
779 #if PLATFORM(CG)
adjustedShadowSize(CGFloat width,CGFloat height)780 static inline CGSize adjustedShadowSize(CGFloat width, CGFloat height)
781 {
782     // Work around <rdar://problem/5539388> by ensuring that shadow offsets will get truncated
783     // to the desired integer.
784     static const CGFloat extraShadowOffset = narrowPrecisionToCGFloat(1.0 / 128);
785     if (width > 0)
786         width += extraShadowOffset;
787     else if (width < 0)
788         width -= extraShadowOffset;
789 
790     if (height > 0)
791         height += extraShadowOffset;
792     else if (height < 0)
793         height -= extraShadowOffset;
794 
795     return CGSizeMake(width, height);
796 }
797 #endif
798 
setShadow(float width,float height,float blur)799 void CanvasRenderingContext2D::setShadow(float width, float height, float blur)
800 {
801     state().m_shadowOffset = FloatSize(width, height);
802     state().m_shadowBlur = blur;
803     state().m_shadowColor = "";
804     applyShadow();
805 }
806 
setShadow(float width,float height,float blur,const String & color)807 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color)
808 {
809     state().m_shadowOffset = FloatSize(width, height);
810     state().m_shadowBlur = blur;
811     state().m_shadowColor = color;
812     applyShadow();
813 }
814 
setShadow(float width,float height,float blur,float grayLevel)815 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel)
816 {
817     state().m_shadowOffset = FloatSize(width, height);
818     state().m_shadowBlur = blur;
819     state().m_shadowColor = "";
820 
821     GraphicsContext* c = drawingContext();
822     if (!c)
823         return;
824 
825     RGBA32 rgba = makeRGBA32FromFloats(grayLevel, grayLevel, grayLevel, 1.0f);
826     c->setShadow(IntSize(width, -height), state().m_shadowBlur, Color(rgba), DeviceColorSpace);
827 }
828 
setShadow(float width,float height,float blur,const String & color,float alpha)829 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color, float alpha)
830 {
831     state().m_shadowOffset = FloatSize(width, height);
832     state().m_shadowBlur = blur;
833     state().m_shadowColor = color;
834 
835     GraphicsContext* c = drawingContext();
836     if (!c)
837         return;
838 
839     RGBA32 rgba = 0; // default is transparent black
840     if (!state().m_shadowColor.isEmpty())
841         CSSParser::parseColor(rgba, state().m_shadowColor);
842     c->setShadow(IntSize(width, -height), state().m_shadowBlur, Color(colorWithOverrideAlpha(rgba, alpha)), DeviceColorSpace);
843 }
844 
setShadow(float width,float height,float blur,float grayLevel,float alpha)845 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel, float alpha)
846 {
847     state().m_shadowOffset = FloatSize(width, height);
848     state().m_shadowBlur = blur;
849     state().m_shadowColor = "";
850 
851     GraphicsContext* c = drawingContext();
852     if (!c)
853         return;
854 
855     RGBA32 rgba = makeRGBA32FromFloats(grayLevel, grayLevel, grayLevel, alpha);
856     c->setShadow(IntSize(width, -height), state().m_shadowBlur, Color(rgba), DeviceColorSpace);
857 }
858 
setShadow(float width,float height,float blur,float r,float g,float b,float a)859 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float r, float g, float b, float a)
860 {
861     state().m_shadowOffset = FloatSize(width, height);
862     state().m_shadowBlur = blur;
863     state().m_shadowColor = "";
864 
865     GraphicsContext* c = drawingContext();
866     if (!c)
867         return;
868 
869     RGBA32 rgba = makeRGBA32FromFloats(r, g, b, a); // default is transparent black
870     if (!state().m_shadowColor.isEmpty())
871         CSSParser::parseColor(rgba, state().m_shadowColor);
872     c->setShadow(IntSize(width, -height), state().m_shadowBlur, Color(rgba), DeviceColorSpace);
873 }
874 
setShadow(float width,float height,float blur,float c,float m,float y,float k,float a)875 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float c, float m, float y, float k, float a)
876 {
877     state().m_shadowOffset = FloatSize(width, height);
878     state().m_shadowBlur = blur;
879     state().m_shadowColor = "";
880 
881     GraphicsContext* dc = drawingContext();
882     if (!dc)
883         return;
884 #if PLATFORM(CG)
885     const CGFloat components[5] = { c, m, y, k, a };
886     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceCMYK();
887     CGColorRef shadowColor = CGColorCreate(colorSpace, components);
888     CGColorSpaceRelease(colorSpace);
889     CGContextSetShadowWithColor(dc->platformContext(), adjustedShadowSize(width, -height), blur, shadowColor);
890     CGColorRelease(shadowColor);
891 #else
892     dc->setShadow(IntSize(width, -height), blur, Color(c, m, y, k, a), DeviceColorSpace);
893 #endif
894 }
895 
clearShadow()896 void CanvasRenderingContext2D::clearShadow()
897 {
898     state().m_shadowOffset = FloatSize();
899     state().m_shadowBlur = 0;
900     state().m_shadowColor = "";
901     applyShadow();
902 }
903 
applyShadow()904 void CanvasRenderingContext2D::applyShadow()
905 {
906     GraphicsContext* c = drawingContext();
907     if (!c)
908         return;
909 
910     RGBA32 rgba = 0; // default is transparent black
911     if (!state().m_shadowColor.isEmpty())
912         CSSParser::parseColor(rgba, state().m_shadowColor);
913     float width = state().m_shadowOffset.width();
914     float height = state().m_shadowOffset.height();
915     c->setShadow(IntSize(width, -height), state().m_shadowBlur, Color(rgba), DeviceColorSpace);
916 }
917 
size(HTMLImageElement * image)918 static IntSize size(HTMLImageElement* image)
919 {
920     if (CachedImage* cachedImage = image->cachedImage())
921         return cachedImage->imageSize(1.0f); // FIXME: Not sure about this.
922     return IntSize();
923 }
924 
925 #if ENABLE(VIDEO)
size(HTMLVideoElement * video)926 static IntSize size(HTMLVideoElement* video)
927 {
928     if (MediaPlayer* player = video->player())
929         return player->naturalSize();
930     return IntSize();
931 }
932 #endif
933 
normalizeRect(const FloatRect & rect)934 static inline FloatRect normalizeRect(const FloatRect& rect)
935 {
936     return FloatRect(min(rect.x(), rect.right()),
937         min(rect.y(), rect.bottom()),
938         max(rect.width(), -rect.width()),
939         max(rect.height(), -rect.height()));
940 }
941 
checkOrigin(const KURL & url)942 void CanvasRenderingContext2D::checkOrigin(const KURL& url)
943 {
944     if (canvas()->document()->securityOrigin()->taintsCanvas(url))
945         canvas()->setOriginTainted();
946 }
947 
checkOrigin(const String & url)948 void CanvasRenderingContext2D::checkOrigin(const String& url)
949 {
950     checkOrigin(KURL(KURL(), url));
951 }
952 
drawImage(HTMLImageElement * image,float x,float y)953 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, float x, float y)
954 {
955     ASSERT(image);
956     IntSize s = size(image);
957     ExceptionCode ec;
958     drawImage(image, x, y, s.width(), s.height(), ec);
959 }
960 
drawImage(HTMLImageElement * image,float x,float y,float width,float height,ExceptionCode & ec)961 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image,
962     float x, float y, float width, float height, ExceptionCode& ec)
963 {
964     ASSERT(image);
965     IntSize s = size(image);
966     drawImage(image, FloatRect(0, 0, s.width(), s.height()), FloatRect(x, y, width, height), ec);
967 }
968 
drawImage(HTMLImageElement * image,const FloatRect & srcRect,const FloatRect & dstRect,ExceptionCode & ec)969 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, const FloatRect& srcRect, const FloatRect& dstRect,
970     ExceptionCode& ec)
971 {
972     ASSERT(image);
973 
974     ec = 0;
975 
976     FloatRect imageRect = FloatRect(FloatPoint(), size(image));
977     if (!imageRect.contains(normalizeRect(srcRect)) || srcRect.width() == 0 || srcRect.height() == 0) {
978         ec = INDEX_SIZE_ERR;
979         return;
980     }
981 
982     if (!dstRect.width() || !dstRect.height())
983         return;
984 
985     GraphicsContext* c = drawingContext();
986     if (!c)
987         return;
988     if (!state().m_invertibleCTM)
989         return;
990 
991     CachedImage* cachedImage = image->cachedImage();
992     if (!cachedImage)
993         return;
994 
995     if (canvas()->originClean())
996         checkOrigin(cachedImage->response().url());
997 
998     if (canvas()->originClean() && !cachedImage->image()->hasSingleSecurityOrigin())
999         canvas()->setOriginTainted();
1000 
1001     FloatRect sourceRect = c->roundToDevicePixels(srcRect);
1002     FloatRect destRect = c->roundToDevicePixels(dstRect);
1003     willDraw(destRect);
1004     c->drawImage(cachedImage->image(), DeviceColorSpace, destRect, sourceRect, state().m_globalComposite);
1005 }
1006 
drawImage(HTMLCanvasElement * canvas,float x,float y)1007 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas, float x, float y)
1008 {
1009     ASSERT(canvas);
1010     ExceptionCode ec;
1011     drawImage(canvas, x, y, canvas->width(), canvas->height(), ec);
1012 }
1013 
drawImage(HTMLCanvasElement * canvas,float x,float y,float width,float height,ExceptionCode & ec)1014 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas,
1015     float x, float y, float width, float height, ExceptionCode& ec)
1016 {
1017     ASSERT(canvas);
1018     drawImage(canvas, FloatRect(0, 0, canvas->width(), canvas->height()), FloatRect(x, y, width, height), ec);
1019 }
1020 
drawImage(HTMLCanvasElement * sourceCanvas,const FloatRect & srcRect,const FloatRect & dstRect,ExceptionCode & ec)1021 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* sourceCanvas, const FloatRect& srcRect,
1022     const FloatRect& dstRect, ExceptionCode& ec)
1023 {
1024     ASSERT(sourceCanvas);
1025 
1026     ec = 0;
1027 
1028     FloatRect srcCanvasRect = FloatRect(FloatPoint(), sourceCanvas->size());
1029     if (!srcCanvasRect.contains(normalizeRect(srcRect)) || srcRect.width() == 0 || srcRect.height() == 0) {
1030         ec = INDEX_SIZE_ERR;
1031         return;
1032     }
1033 
1034     if (!dstRect.width() || !dstRect.height())
1035         return;
1036 
1037     GraphicsContext* c = drawingContext();
1038     if (!c)
1039         return;
1040     if (!state().m_invertibleCTM)
1041         return;
1042 
1043     FloatRect sourceRect = c->roundToDevicePixels(srcRect);
1044     FloatRect destRect = c->roundToDevicePixels(dstRect);
1045 
1046     // FIXME: Do this through platform-independent GraphicsContext API.
1047     ImageBuffer* buffer = sourceCanvas->buffer();
1048     if (!buffer)
1049         return;
1050 
1051     if (!sourceCanvas->originClean())
1052         canvas()->setOriginTainted();
1053 
1054     c->drawImage(buffer->image(), DeviceColorSpace, destRect, sourceRect, state().m_globalComposite);
1055     willDraw(destRect); // This call comes after drawImage, since the buffer we draw into may be our own, and we need to make sure it is dirty.
1056                         // FIXME: Arguably willDraw should become didDraw and occur after drawing calls and not before them to avoid problems like this.
1057 }
1058 
1059 #if ENABLE(VIDEO)
drawImage(HTMLVideoElement * video,float x,float y)1060 void CanvasRenderingContext2D::drawImage(HTMLVideoElement* video, float x, float y)
1061 {
1062     ASSERT(video);
1063     IntSize s = size(video);
1064     ExceptionCode ec;
1065     drawImage(video, x, y, s.width(), s.height(), ec);
1066 }
1067 
drawImage(HTMLVideoElement * video,float x,float y,float width,float height,ExceptionCode & ec)1068 void CanvasRenderingContext2D::drawImage(HTMLVideoElement* video,
1069                                          float x, float y, float width, float height, ExceptionCode& ec)
1070 {
1071     ASSERT(video);
1072     IntSize s = size(video);
1073     drawImage(video, FloatRect(0, 0, s.width(), s.height()), FloatRect(x, y, width, height), ec);
1074 }
1075 
drawImage(HTMLVideoElement * video,const FloatRect & srcRect,const FloatRect & dstRect,ExceptionCode & ec)1076 void CanvasRenderingContext2D::drawImage(HTMLVideoElement* video, const FloatRect& srcRect, const FloatRect& dstRect,
1077                                          ExceptionCode& ec)
1078 {
1079     ASSERT(video);
1080 
1081     ec = 0;
1082     FloatRect videoRect = FloatRect(FloatPoint(), size(video));
1083     if (!videoRect.contains(normalizeRect(srcRect)) || srcRect.width() == 0 || srcRect.height() == 0) {
1084         ec = INDEX_SIZE_ERR;
1085         return;
1086     }
1087 
1088     if (!dstRect.width() || !dstRect.height())
1089         return;
1090 
1091     GraphicsContext* c = drawingContext();
1092     if (!c)
1093         return;
1094     if (!state().m_invertibleCTM)
1095         return;
1096 
1097     if (canvas()->originClean())
1098         checkOrigin(video->currentSrc());
1099 
1100     if (canvas()->originClean() && !video->hasSingleSecurityOrigin())
1101         canvas()->setOriginTainted();
1102 
1103     FloatRect sourceRect = c->roundToDevicePixels(srcRect);
1104     FloatRect destRect = c->roundToDevicePixels(dstRect);
1105     willDraw(destRect);
1106 
1107     c->save();
1108     c->clip(destRect);
1109     c->translate(destRect.x(), destRect.y());
1110     c->scale(FloatSize(destRect.width()/sourceRect.width(), destRect.height()/sourceRect.height()));
1111     c->translate(-sourceRect.x(), -sourceRect.y());
1112     video->paintCurrentFrameInContext(c, IntRect(IntPoint(), size(video)));
1113     c->restore();
1114 }
1115 #endif
1116 
1117 // FIXME: Why isn't this just another overload of drawImage? Why have a different name?
drawImageFromRect(HTMLImageElement * image,float sx,float sy,float sw,float sh,float dx,float dy,float dw,float dh,const String & compositeOperation)1118 void CanvasRenderingContext2D::drawImageFromRect(HTMLImageElement* image,
1119     float sx, float sy, float sw, float sh,
1120     float dx, float dy, float dw, float dh,
1121     const String& compositeOperation)
1122 {
1123     if (!image)
1124         return;
1125 
1126     CachedImage* cachedImage = image->cachedImage();
1127     if (!cachedImage)
1128         return;
1129 
1130     if (canvas()->originClean())
1131         checkOrigin(cachedImage->response().url());
1132 
1133     if (canvas()->originClean() && !cachedImage->image()->hasSingleSecurityOrigin())
1134         canvas()->setOriginTainted();
1135 
1136     GraphicsContext* c = drawingContext();
1137     if (!c)
1138         return;
1139     if (!state().m_invertibleCTM)
1140         return;
1141 
1142     CompositeOperator op;
1143     if (!parseCompositeOperator(compositeOperation, op))
1144         op = CompositeSourceOver;
1145 
1146     FloatRect destRect = FloatRect(dx, dy, dw, dh);
1147     willDraw(destRect);
1148     c->drawImage(cachedImage->image(), DeviceColorSpace, destRect, FloatRect(sx, sy, sw, sh), op);
1149 }
1150 
setAlpha(float alpha)1151 void CanvasRenderingContext2D::setAlpha(float alpha)
1152 {
1153     setGlobalAlpha(alpha);
1154 }
1155 
setCompositeOperation(const String & operation)1156 void CanvasRenderingContext2D::setCompositeOperation(const String& operation)
1157 {
1158     setGlobalCompositeOperation(operation);
1159 }
1160 
prepareGradientForDashboard(CanvasGradient * gradient) const1161 void CanvasRenderingContext2D::prepareGradientForDashboard(CanvasGradient* gradient) const
1162 {
1163 #if ENABLE(DASHBOARD_SUPPORT)
1164     if (Settings* settings = canvas()->document()->settings())
1165         if (settings->usesDashboardBackwardCompatibilityMode())
1166             gradient->setDashboardCompatibilityMode();
1167 #else
1168     UNUSED_PARAM(gradient);
1169 #endif
1170 }
1171 
createLinearGradient(float x0,float y0,float x1,float y1,ExceptionCode & ec)1172 PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createLinearGradient(float x0, float y0, float x1, float y1, ExceptionCode& ec)
1173 {
1174     if (!isfinite(x0) || !isfinite(y0) || !isfinite(x1) || !isfinite(y1)) {
1175         ec = NOT_SUPPORTED_ERR;
1176         return 0;
1177     }
1178 
1179     PassRefPtr<CanvasGradient> gradient = CanvasGradient::create(FloatPoint(x0, y0), FloatPoint(x1, y1));
1180     prepareGradientForDashboard(gradient.get());
1181     return gradient;
1182 }
1183 
createRadialGradient(float x0,float y0,float r0,float x1,float y1,float r1,ExceptionCode & ec)1184 PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createRadialGradient(float x0, float y0, float r0, float x1, float y1, float r1, ExceptionCode& ec)
1185 {
1186     if (!isfinite(x0) || !isfinite(y0) || !isfinite(r0) ||
1187         !isfinite(x1) || !isfinite(y1) || !isfinite(r1)) {
1188         ec = NOT_SUPPORTED_ERR;
1189         return 0;
1190     }
1191     PassRefPtr<CanvasGradient> gradient =  CanvasGradient::create(FloatPoint(x0, y0), r0, FloatPoint(x1, y1), r1);
1192     prepareGradientForDashboard(gradient.get());
1193     return gradient;
1194 }
1195 
createPattern(HTMLImageElement * image,const String & repetitionType,ExceptionCode & ec)1196 PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLImageElement* image,
1197     const String& repetitionType, ExceptionCode& ec)
1198 {
1199     bool repeatX, repeatY;
1200     ec = 0;
1201     CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec);
1202     if (ec)
1203         return 0;
1204 
1205     if (!image->complete()) {
1206         ec = INVALID_STATE_ERR;
1207         return 0;
1208     }
1209 
1210     CachedImage* cachedImage = image->cachedImage();
1211     if (!cachedImage || !image->cachedImage()->image())
1212         return CanvasPattern::create(Image::nullImage(), repeatX, repeatY, true);
1213 
1214     bool originClean = !canvas()->document()->securityOrigin()->taintsCanvas(KURL(KURL(), cachedImage->response().url())) && cachedImage->image()->hasSingleSecurityOrigin();
1215     return CanvasPattern::create(cachedImage->image(), repeatX, repeatY, originClean);
1216 }
1217 
createPattern(HTMLCanvasElement * canvas,const String & repetitionType,ExceptionCode & ec)1218 PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLCanvasElement* canvas,
1219     const String& repetitionType, ExceptionCode& ec)
1220 {
1221     if (!canvas->width() || !canvas->height()) {
1222         ec = INVALID_STATE_ERR;
1223         return 0;
1224     }
1225 
1226     bool repeatX, repeatY;
1227     ec = 0;
1228     CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec);
1229     if (ec)
1230         return 0;
1231     return CanvasPattern::create(canvas->buffer()->image(), repeatX, repeatY, canvas->originClean());
1232 }
1233 
willDraw(const FloatRect & r,unsigned options)1234 void CanvasRenderingContext2D::willDraw(const FloatRect& r, unsigned options)
1235 {
1236     GraphicsContext* c = drawingContext();
1237     if (!c)
1238         return;
1239     if (!state().m_invertibleCTM)
1240         return;
1241 
1242     FloatRect dirtyRect = r;
1243     if (options & CanvasWillDrawApplyTransform) {
1244         AffineTransform ctm = state().m_transform;
1245         dirtyRect = ctm.mapRect(r);
1246     }
1247 
1248     if (options & CanvasWillDrawApplyShadow) {
1249         // The shadow gets applied after transformation
1250         FloatRect shadowRect(dirtyRect);
1251         shadowRect.move(state().m_shadowOffset);
1252         shadowRect.inflate(state().m_shadowBlur);
1253         dirtyRect.unite(shadowRect);
1254     }
1255 
1256     if (options & CanvasWillDrawApplyClip) {
1257         // FIXME: apply the current clip to the rectangle. Unfortunately we can't get the clip
1258         // back out of the GraphicsContext, so to take clip into account for incremental painting,
1259         // we'd have to keep the clip path around.
1260     }
1261 
1262     canvas()->willDraw(dirtyRect);
1263 }
1264 
drawingContext() const1265 GraphicsContext* CanvasRenderingContext2D::drawingContext() const
1266 {
1267     return canvas()->drawingContext();
1268 }
1269 
createEmptyImageData(const IntSize & size)1270 static PassRefPtr<ImageData> createEmptyImageData(const IntSize& size)
1271 {
1272     RefPtr<ImageData> data = ImageData::create(size.width(), size.height());
1273     memset(data->data()->data()->data(), 0, data->data()->data()->length());
1274     return data.get();
1275 }
1276 
createImageData(float sw,float sh,ExceptionCode & ec) const1277 PassRefPtr<ImageData> CanvasRenderingContext2D::createImageData(float sw, float sh, ExceptionCode& ec) const
1278 {
1279     ec = 0;
1280     if (!isfinite(sw) || !isfinite(sh)) {
1281         ec = NOT_SUPPORTED_ERR;
1282         return 0;
1283     }
1284     FloatSize unscaledSize(sw, sh);
1285     IntSize scaledSize = canvas()->convertLogicalToDevice(unscaledSize);
1286     if (scaledSize.width() < 1)
1287         scaledSize.setWidth(1);
1288     if (scaledSize.height() < 1)
1289         scaledSize.setHeight(1);
1290 
1291     return createEmptyImageData(scaledSize);
1292 }
1293 
getImageData(float sx,float sy,float sw,float sh,ExceptionCode & ec) const1294 PassRefPtr<ImageData> CanvasRenderingContext2D::getImageData(float sx, float sy, float sw, float sh, ExceptionCode& ec) const
1295 {
1296     if (!canvas()->originClean()) {
1297         ec = SECURITY_ERR;
1298         return 0;
1299     }
1300 
1301     FloatRect unscaledRect(sx, sy, sw, sh);
1302     IntRect scaledRect = canvas()->convertLogicalToDevice(unscaledRect);
1303     if (scaledRect.width() < 1)
1304         scaledRect.setWidth(1);
1305     if (scaledRect.height() < 1)
1306         scaledRect.setHeight(1);
1307     ImageBuffer* buffer = canvas() ? canvas()->buffer() : 0;
1308     if (!buffer)
1309         return createEmptyImageData(scaledRect.size());
1310     return buffer->getUnmultipliedImageData(scaledRect);
1311 }
1312 
putImageData(ImageData * data,float dx,float dy,ExceptionCode & ec)1313 void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, ExceptionCode& ec)
1314 {
1315     if (!data) {
1316         ec = TYPE_MISMATCH_ERR;
1317         return;
1318     }
1319     putImageData(data, dx, dy, 0, 0, data->width(), data->height(), ec);
1320 }
1321 
putImageData(ImageData * data,float dx,float dy,float dirtyX,float dirtyY,float dirtyWidth,float dirtyHeight,ExceptionCode & ec)1322 void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, float dirtyX, float dirtyY,
1323                                             float dirtyWidth, float dirtyHeight, ExceptionCode& ec)
1324 {
1325     if (!data) {
1326         ec = TYPE_MISMATCH_ERR;
1327         return;
1328     }
1329     if (!isfinite(dx) || !isfinite(dy) || !isfinite(dirtyX) ||
1330         !isfinite(dirtyY) || !isfinite(dirtyWidth) || !isfinite(dirtyHeight)) {
1331         ec = INDEX_SIZE_ERR;
1332         return;
1333     }
1334 
1335     ImageBuffer* buffer = canvas()->buffer();
1336     if (!buffer)
1337         return;
1338 
1339     if (dirtyWidth < 0) {
1340         dirtyX += dirtyWidth;
1341         dirtyWidth = -dirtyWidth;
1342     }
1343 
1344     if (dirtyHeight < 0) {
1345         dirtyY += dirtyHeight;
1346         dirtyHeight = -dirtyHeight;
1347     }
1348 
1349     FloatRect clipRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
1350     clipRect.intersect(IntRect(0, 0, data->width(), data->height()));
1351     IntSize destOffset(static_cast<int>(dx), static_cast<int>(dy));
1352     IntRect sourceRect = enclosingIntRect(clipRect);
1353     sourceRect.move(destOffset);
1354     sourceRect.intersect(IntRect(IntPoint(), buffer->size()));
1355     if (sourceRect.isEmpty())
1356         return;
1357     willDraw(sourceRect, 0);  // ignore transform, shadow and clip
1358     sourceRect.move(-destOffset);
1359     IntPoint destPoint(destOffset.width(), destOffset.height());
1360 
1361     buffer->putUnmultipliedImageData(data, sourceRect, destPoint);
1362 }
1363 
font() const1364 String CanvasRenderingContext2D::font() const
1365 {
1366     return state().m_unparsedFont;
1367 }
1368 
setFont(const String & newFont)1369 void CanvasRenderingContext2D::setFont(const String& newFont)
1370 {
1371     RefPtr<CSSMutableStyleDeclaration> tempDecl = CSSMutableStyleDeclaration::create();
1372     CSSParser parser(!canvas()->document()->inCompatMode()); // Use the parse mode of the canvas' document when parsing CSS.
1373 
1374     String declarationText("font: ");
1375     declarationText += newFont;
1376     parser.parseDeclaration(tempDecl.get(), declarationText);
1377     if (!tempDecl->length())
1378         return;
1379 
1380     // The parse succeeded.
1381     state().m_unparsedFont = newFont;
1382 
1383     // Map the <canvas> font into the text style. If the font uses keywords like larger/smaller, these will work
1384     // relative to the canvas.
1385     RefPtr<RenderStyle> newStyle = RenderStyle::create();
1386     if (canvas()->computedStyle())
1387         newStyle->setFontDescription(canvas()->computedStyle()->fontDescription());
1388 
1389     // Now map the font property into the style.
1390     CSSStyleSelector* styleSelector = canvas()->document()->styleSelector();
1391     styleSelector->applyPropertyToStyle(CSSPropertyFont, tempDecl->getPropertyCSSValue(CSSPropertyFont).get(), newStyle.get());
1392 
1393     state().m_font = newStyle->font();
1394     state().m_font.update(styleSelector->fontSelector());
1395     state().m_realizedFont = true;
1396 }
1397 
textAlign() const1398 String CanvasRenderingContext2D::textAlign() const
1399 {
1400     return textAlignName(state().m_textAlign);
1401 }
1402 
setTextAlign(const String & s)1403 void CanvasRenderingContext2D::setTextAlign(const String& s)
1404 {
1405     TextAlign align;
1406     if (!parseTextAlign(s, align))
1407         return;
1408     state().m_textAlign = align;
1409 }
1410 
textBaseline() const1411 String CanvasRenderingContext2D::textBaseline() const
1412 {
1413     return textBaselineName(state().m_textBaseline);
1414 }
1415 
setTextBaseline(const String & s)1416 void CanvasRenderingContext2D::setTextBaseline(const String& s)
1417 {
1418     TextBaseline baseline;
1419     if (!parseTextBaseline(s, baseline))
1420         return;
1421     state().m_textBaseline = baseline;
1422 }
1423 
fillText(const String & text,float x,float y)1424 void CanvasRenderingContext2D::fillText(const String& text, float x, float y)
1425 {
1426     drawTextInternal(text, x, y, true);
1427 }
1428 
fillText(const String & text,float x,float y,float maxWidth)1429 void CanvasRenderingContext2D::fillText(const String& text, float x, float y, float maxWidth)
1430 {
1431     drawTextInternal(text, x, y, true, maxWidth, true);
1432 }
1433 
strokeText(const String & text,float x,float y)1434 void CanvasRenderingContext2D::strokeText(const String& text, float x, float y)
1435 {
1436     drawTextInternal(text, x, y, false);
1437 }
1438 
strokeText(const String & text,float x,float y,float maxWidth)1439 void CanvasRenderingContext2D::strokeText(const String& text, float x, float y, float maxWidth)
1440 {
1441     drawTextInternal(text, x, y, false, maxWidth, true);
1442 }
1443 
measureText(const String & text)1444 PassRefPtr<TextMetrics> CanvasRenderingContext2D::measureText(const String& text)
1445 {
1446     RefPtr<TextMetrics> metrics = TextMetrics::create();
1447     metrics->setWidth(accessFont().width(TextRun(text.characters(), text.length())));
1448     return metrics;
1449 }
1450 
drawTextInternal(const String & text,float x,float y,bool fill,float,bool)1451 void CanvasRenderingContext2D::drawTextInternal(const String& text, float x, float y, bool fill, float /*maxWidth*/, bool /*useMaxWidth*/)
1452 {
1453     GraphicsContext* c = drawingContext();
1454     if (!c)
1455         return;
1456     if (!state().m_invertibleCTM)
1457         return;
1458 
1459     const Font& font = accessFont();
1460 
1461     // FIXME: Handle maxWidth.
1462     // FIXME: Need to turn off font smoothing.
1463 
1464     bool rtl = canvas()->computedStyle() ? canvas()->computedStyle()->direction() == RTL : false;
1465     bool override = canvas()->computedStyle() ? canvas()->computedStyle()->unicodeBidi() == Override : false;
1466 
1467     unsigned length = text.length();
1468     const UChar* string = text.characters();
1469     TextRun textRun(string, length, 0, 0, 0, rtl, override, false, false);
1470 
1471     // Draw the item text at the correct point.
1472     FloatPoint location(x, y);
1473     switch (state().m_textBaseline) {
1474         case TopTextBaseline:
1475         case HangingTextBaseline:
1476             location.setY(y + font.ascent());
1477             break;
1478         case BottomTextBaseline:
1479         case IdeographicTextBaseline:
1480             location.setY(y - font.descent());
1481             break;
1482         case MiddleTextBaseline:
1483             location.setY(y - font.descent() + font.height() / 2);
1484             break;
1485         case AlphabeticTextBaseline:
1486         default:
1487              // Do nothing.
1488             break;
1489     }
1490 
1491     float width = font.width(TextRun(text, false, 0, 0, rtl, override));
1492 
1493     TextAlign align = state().m_textAlign;
1494     if (align == StartTextAlign)
1495          align = rtl ? RightTextAlign : LeftTextAlign;
1496     else if (align == EndTextAlign)
1497         align = rtl ? LeftTextAlign : RightTextAlign;
1498 
1499     switch (align) {
1500         case CenterTextAlign:
1501             location.setX(location.x() - width / 2);
1502             break;
1503         case RightTextAlign:
1504             location.setX(location.x() - width);
1505             break;
1506         default:
1507             break;
1508     }
1509 
1510     // The slop built in to this mask rect matches the heuristic used in FontCGWin.cpp for GDI text.
1511     FloatRect textRect = FloatRect(location.x() - font.height() / 2, location.y() - font.ascent() - font.lineGap(),
1512                                    width + font.height(), font.lineSpacing());
1513     if (!fill)
1514         textRect.inflate(c->strokeThickness() / 2);
1515 
1516     if (fill)
1517         canvas()->willDraw(textRect);
1518     else {
1519         // When stroking text, pointy miters can extend outside of textRect, so we
1520         // punt and dirty the whole canvas.
1521         canvas()->willDraw(FloatRect(0, 0, canvas()->width(), canvas()->height()));
1522     }
1523 
1524 #if PLATFORM(CG)
1525     CanvasStyle* drawStyle = fill ? state().m_fillStyle.get() : state().m_strokeStyle.get();
1526     if (drawStyle->canvasGradient() || drawStyle->canvasPattern()) {
1527         // FIXME: The rect is not big enough for miters on stroked text.
1528         IntRect maskRect = enclosingIntRect(textRect);
1529 
1530         OwnPtr<ImageBuffer> maskImage = ImageBuffer::create(maskRect.size());
1531 
1532         GraphicsContext* maskImageContext = maskImage->context();
1533 
1534         if (fill)
1535             maskImageContext->setFillColor(Color::black, DeviceColorSpace);
1536         else {
1537             maskImageContext->setStrokeColor(Color::black, DeviceColorSpace);
1538             maskImageContext->setStrokeThickness(c->strokeThickness());
1539         }
1540 
1541         maskImageContext->setTextDrawingMode(fill ? cTextFill : cTextStroke);
1542         maskImageContext->translate(-maskRect.x(), -maskRect.y());
1543 
1544         maskImageContext->drawBidiText(font, textRun, location);
1545 
1546         c->save();
1547         c->clipToImageBuffer(maskRect, maskImage.get());
1548         drawStyle->applyFillColor(c);
1549         c->fillRect(maskRect);
1550         c->restore();
1551 
1552         return;
1553     }
1554 #endif
1555 
1556     c->setTextDrawingMode(fill ? cTextFill : cTextStroke);
1557     c->drawBidiText(font, textRun, location);
1558 }
1559 
accessFont()1560 const Font& CanvasRenderingContext2D::accessFont()
1561 {
1562     if (!state().m_realizedFont)
1563         setFont(state().m_unparsedFont);
1564     return state().m_font;
1565 }
1566 
1567 } // namespace WebCore
1568