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 "TransformationMatrix.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 "Frame.h"
46 #include "GraphicsContext.h"
47 #include "HTMLCanvasElement.h"
48 #include "HTMLImageElement.h"
49 #include "HTMLNames.h"
50 #include "ImageBuffer.h"
51 #include "ImageData.h"
52 #include "KURL.h"
53 #include "Page.h"
54 #include "RenderHTMLCanvas.h"
55 #include "SecurityOrigin.h"
56 #include "Settings.h"
57 #include "StrokeStyleApplier.h"
58 #include "TextMetrics.h"
59 #include "HTMLVideoElement.h"
60 #include <stdio.h>
61 #include <wtf/ByteArray.h>
62 #include <wtf/MathExtras.h>
63 #include <wtf/OwnPtr.h>
64 #include <wtf/UnusedParam.h>
65
66 using namespace std;
67
68 namespace WebCore {
69
70 using namespace HTMLNames;
71
72 static const char* const defaultFont = "10px sans-serif";
73
74
75 class CanvasStrokeStyleApplier : public StrokeStyleApplier {
76 public:
CanvasStrokeStyleApplier(CanvasRenderingContext2D * canvasContext)77 CanvasStrokeStyleApplier(CanvasRenderingContext2D* canvasContext)
78 : m_canvasContext(canvasContext)
79 {
80 }
81
strokeStyle(GraphicsContext * c)82 virtual void strokeStyle(GraphicsContext* c)
83 {
84 c->setStrokeThickness(m_canvasContext->lineWidth());
85 c->setLineCap(m_canvasContext->getLineCap());
86 c->setLineJoin(m_canvasContext->getLineJoin());
87 c->setMiterLimit(m_canvasContext->miterLimit());
88 }
89
90 private:
91 CanvasRenderingContext2D* m_canvasContext;
92 };
93
94
95
CanvasRenderingContext2D(HTMLCanvasElement * canvas)96 CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement* canvas)
97 : m_canvas(canvas)
98 , m_stateStack(1)
99 {
100 // Make sure that even if the drawingContext() has a different default
101 // thickness, it is in sync with the canvas thickness.
102 setLineWidth(lineWidth());
103 }
104
ref()105 void CanvasRenderingContext2D::ref()
106 {
107 m_canvas->ref();
108 }
109
deref()110 void CanvasRenderingContext2D::deref()
111 {
112 m_canvas->deref();
113 }
114
reset()115 void CanvasRenderingContext2D::reset()
116 {
117 m_stateStack.resize(1);
118 m_stateStack.first() = State();
119 }
120
State()121 CanvasRenderingContext2D::State::State()
122 : m_strokeStyle(CanvasStyle::create("black"))
123 , m_fillStyle(CanvasStyle::create("black"))
124 , m_lineWidth(1)
125 , m_lineCap(ButtCap)
126 , m_lineJoin(MiterJoin)
127 , m_miterLimit(10)
128 , m_shadowBlur(0)
129 , m_shadowColor("black")
130 , m_globalAlpha(1)
131 , m_globalComposite(CompositeSourceOver)
132 , m_invertibleCTM(true)
133 , m_textAlign(StartTextAlign)
134 , m_textBaseline(AlphabeticTextBaseline)
135 , m_unparsedFont(defaultFont)
136 , m_realizedFont(false)
137 {
138 }
139
save()140 void CanvasRenderingContext2D::save()
141 {
142 ASSERT(m_stateStack.size() >= 1);
143 m_stateStack.append(state());
144 GraphicsContext* c = drawingContext();
145 if (!c)
146 return;
147 c->save();
148 }
149
restore()150 void CanvasRenderingContext2D::restore()
151 {
152 ASSERT(m_stateStack.size() >= 1);
153 if (m_stateStack.size() <= 1)
154 return;
155 m_path.transform(state().m_transform);
156 m_stateStack.removeLast();
157 m_path.transform(state().m_transform.inverse());
158 GraphicsContext* c = drawingContext();
159 if (!c)
160 return;
161 c->restore();
162 }
163
strokeStyle() const164 CanvasStyle* CanvasRenderingContext2D::strokeStyle() const
165 {
166 return state().m_strokeStyle.get();
167 }
168
setStrokeStyle(PassRefPtr<CanvasStyle> style)169 void CanvasRenderingContext2D::setStrokeStyle(PassRefPtr<CanvasStyle> style)
170 {
171 if (!style)
172 return;
173
174 if (m_canvas->originClean()) {
175 if (CanvasPattern* pattern = style->canvasPattern()) {
176 if (!pattern->originClean())
177 m_canvas->setOriginTainted();
178 }
179 }
180
181 state().m_strokeStyle = style;
182 GraphicsContext* c = drawingContext();
183 if (!c)
184 return;
185 state().m_strokeStyle->applyStrokeColor(c);
186 }
187
fillStyle() const188 CanvasStyle* CanvasRenderingContext2D::fillStyle() const
189 {
190 return state().m_fillStyle.get();
191 }
192
setFillStyle(PassRefPtr<CanvasStyle> style)193 void CanvasRenderingContext2D::setFillStyle(PassRefPtr<CanvasStyle> style)
194 {
195 if (!style)
196 return;
197
198 if (m_canvas->originClean()) {
199 if (CanvasPattern* pattern = style->canvasPattern()) {
200 if (!pattern->originClean())
201 m_canvas->setOriginTainted();
202 }
203 }
204
205 state().m_fillStyle = style;
206 GraphicsContext* c = drawingContext();
207 if (!c)
208 return;
209 state().m_fillStyle->applyFillColor(c);
210 }
211
lineWidth() const212 float CanvasRenderingContext2D::lineWidth() const
213 {
214 return state().m_lineWidth;
215 }
216
setLineWidth(float width)217 void CanvasRenderingContext2D::setLineWidth(float width)
218 {
219 if (!(width > 0))
220 return;
221 state().m_lineWidth = width;
222 GraphicsContext* c = drawingContext();
223 if (!c)
224 return;
225 c->setStrokeThickness(width);
226 }
227
lineCap() const228 String CanvasRenderingContext2D::lineCap() const
229 {
230 return lineCapName(state().m_lineCap);
231 }
232
setLineCap(const String & s)233 void CanvasRenderingContext2D::setLineCap(const String& s)
234 {
235 LineCap cap;
236 if (!parseLineCap(s, cap))
237 return;
238 state().m_lineCap = cap;
239 GraphicsContext* c = drawingContext();
240 if (!c)
241 return;
242 c->setLineCap(cap);
243 }
244
lineJoin() const245 String CanvasRenderingContext2D::lineJoin() const
246 {
247 return lineJoinName(state().m_lineJoin);
248 }
249
setLineJoin(const String & s)250 void CanvasRenderingContext2D::setLineJoin(const String& s)
251 {
252 LineJoin join;
253 if (!parseLineJoin(s, join))
254 return;
255 state().m_lineJoin = join;
256 GraphicsContext* c = drawingContext();
257 if (!c)
258 return;
259 c->setLineJoin(join);
260 }
261
miterLimit() const262 float CanvasRenderingContext2D::miterLimit() const
263 {
264 return state().m_miterLimit;
265 }
266
setMiterLimit(float limit)267 void CanvasRenderingContext2D::setMiterLimit(float limit)
268 {
269 if (!(limit > 0))
270 return;
271 state().m_miterLimit = limit;
272 GraphicsContext* c = drawingContext();
273 if (!c)
274 return;
275 c->setMiterLimit(limit);
276 }
277
shadowOffsetX() const278 float CanvasRenderingContext2D::shadowOffsetX() const
279 {
280 return state().m_shadowOffset.width();
281 }
282
setShadowOffsetX(float x)283 void CanvasRenderingContext2D::setShadowOffsetX(float x)
284 {
285 state().m_shadowOffset.setWidth(x);
286 applyShadow();
287 }
288
shadowOffsetY() const289 float CanvasRenderingContext2D::shadowOffsetY() const
290 {
291 return state().m_shadowOffset.height();
292 }
293
setShadowOffsetY(float y)294 void CanvasRenderingContext2D::setShadowOffsetY(float y)
295 {
296 state().m_shadowOffset.setHeight(y);
297 applyShadow();
298 }
299
shadowBlur() const300 float CanvasRenderingContext2D::shadowBlur() const
301 {
302 return state().m_shadowBlur;
303 }
304
setShadowBlur(float blur)305 void CanvasRenderingContext2D::setShadowBlur(float blur)
306 {
307 state().m_shadowBlur = blur;
308 applyShadow();
309 }
310
shadowColor() const311 String CanvasRenderingContext2D::shadowColor() const
312 {
313 // FIXME: What should this return if you called setShadow with a non-string color?
314 return state().m_shadowColor;
315 }
316
setShadowColor(const String & color)317 void CanvasRenderingContext2D::setShadowColor(const String& color)
318 {
319 state().m_shadowColor = color;
320 applyShadow();
321 }
322
globalAlpha() const323 float CanvasRenderingContext2D::globalAlpha() const
324 {
325 return state().m_globalAlpha;
326 }
327
setGlobalAlpha(float alpha)328 void CanvasRenderingContext2D::setGlobalAlpha(float alpha)
329 {
330 if (!(alpha >= 0 && alpha <= 1))
331 return;
332 state().m_globalAlpha = alpha;
333 GraphicsContext* c = drawingContext();
334 if (!c)
335 return;
336 c->setAlpha(alpha);
337 }
338
globalCompositeOperation() const339 String CanvasRenderingContext2D::globalCompositeOperation() const
340 {
341 return compositeOperatorName(state().m_globalComposite);
342 }
343
setGlobalCompositeOperation(const String & operation)344 void CanvasRenderingContext2D::setGlobalCompositeOperation(const String& operation)
345 {
346 CompositeOperator op;
347 if (!parseCompositeOperator(operation, op))
348 return;
349 state().m_globalComposite = op;
350 GraphicsContext* c = drawingContext();
351 if (!c)
352 return;
353 c->setCompositeOperation(op);
354 }
355
scale(float sx,float sy)356 void CanvasRenderingContext2D::scale(float sx, float sy)
357 {
358 GraphicsContext* c = drawingContext();
359 if (!c)
360 return;
361 if (!state().m_invertibleCTM)
362 return;
363
364 TransformationMatrix newTransform = state().m_transform;
365 newTransform.scaleNonUniform(sx, sy);
366 if (!newTransform.isInvertible()) {
367 state().m_invertibleCTM = false;
368 return;
369 }
370
371 state().m_transform = newTransform;
372 c->scale(FloatSize(sx, sy));
373 m_path.transform(TransformationMatrix().scaleNonUniform(1.0/sx, 1.0/sy));
374 }
375
rotate(float angleInRadians)376 void CanvasRenderingContext2D::rotate(float angleInRadians)
377 {
378 GraphicsContext* c = drawingContext();
379 if (!c)
380 return;
381 if (!state().m_invertibleCTM)
382 return;
383
384 TransformationMatrix newTransform = state().m_transform;
385 newTransform.rotate(angleInRadians / piDouble * 180.0);
386 if (!newTransform.isInvertible()) {
387 state().m_invertibleCTM = false;
388 return;
389 }
390
391 state().m_transform = newTransform;
392 c->rotate(angleInRadians);
393 m_path.transform(TransformationMatrix().rotate(-angleInRadians / piDouble * 180.0));
394 }
395
translate(float tx,float ty)396 void CanvasRenderingContext2D::translate(float tx, float ty)
397 {
398 GraphicsContext* c = drawingContext();
399 if (!c)
400 return;
401 if (!state().m_invertibleCTM)
402 return;
403
404 TransformationMatrix newTransform = state().m_transform;
405 newTransform.translate(tx, ty);
406 if (!newTransform.isInvertible()) {
407 state().m_invertibleCTM = false;
408 return;
409 }
410
411 state().m_transform = newTransform;
412 c->translate(tx, ty);
413 m_path.transform(TransformationMatrix().translate(-tx, -ty));
414 }
415
transform(float m11,float m12,float m21,float m22,float dx,float dy)416 void CanvasRenderingContext2D::transform(float m11, float m12, float m21, float m22, float dx, float dy)
417 {
418 GraphicsContext* c = drawingContext();
419 if (!c)
420 return;
421 if (!state().m_invertibleCTM)
422 return;
423
424 // HTML5 3.14.11.1 -- ignore any calls that pass non-finite numbers
425 if (!isfinite(m11) | !isfinite(m21) | !isfinite(dx) |
426 !isfinite(m12) | !isfinite(m22) | !isfinite(dy))
427 return;
428
429 TransformationMatrix transform(m11, m12, m21, m22, dx, dy);
430 TransformationMatrix newTransform = transform * state().m_transform;
431 if (!newTransform.isInvertible()) {
432 state().m_invertibleCTM = false;
433 return;
434 }
435
436 state().m_transform = newTransform;
437 c->concatCTM(transform);
438 m_path.transform(transform.inverse());
439 }
440
setTransform(float m11,float m12,float m21,float m22,float dx,float dy)441 void CanvasRenderingContext2D::setTransform(float m11, float m12, float m21, float m22, float dx, float dy)
442 {
443 GraphicsContext* c = drawingContext();
444 if (!c)
445 return;
446
447 // HTML5 3.14.11.1 -- ignore any calls that pass non-finite numbers
448 if (!isfinite(m11) | !isfinite(m21) | !isfinite(dx) |
449 !isfinite(m12) | !isfinite(m22) | !isfinite(dy))
450 return;
451
452 TransformationMatrix ctm = state().m_transform;
453 if (!ctm.isInvertible())
454 return;
455 c->concatCTM(c->getCTM().inverse());
456 c->concatCTM(m_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 = m_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->clip(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 TransformationMatrix 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));
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)));
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));
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));
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));
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));
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 RefPtr<SecurityOrigin> origin = SecurityOrigin::create(url);
945 if (!m_canvas->document()->securityOrigin()->canAccess(origin.get()))
946 m_canvas->setOriginTainted();
947 }
948
checkOrigin(const String & url)949 void CanvasRenderingContext2D::checkOrigin(const String& url)
950 {
951 RefPtr<SecurityOrigin> origin = SecurityOrigin::createFromString(url);
952 if (!m_canvas->document()->securityOrigin()->canAccess(origin.get()))
953 m_canvas->setOriginTainted();
954 }
955
drawImage(HTMLImageElement * image,float x,float y)956 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, float x, float y)
957 {
958 ASSERT(image);
959 IntSize s = size(image);
960 ExceptionCode ec;
961 drawImage(image, x, y, s.width(), s.height(), ec);
962 }
963
drawImage(HTMLImageElement * image,float x,float y,float width,float height,ExceptionCode & ec)964 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image,
965 float x, float y, float width, float height, ExceptionCode& ec)
966 {
967 ASSERT(image);
968 IntSize s = size(image);
969 drawImage(image, FloatRect(0, 0, s.width(), s.height()), FloatRect(x, y, width, height), ec);
970 }
971
drawImage(HTMLImageElement * image,const FloatRect & srcRect,const FloatRect & dstRect,ExceptionCode & ec)972 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, const FloatRect& srcRect, const FloatRect& dstRect,
973 ExceptionCode& ec)
974 {
975 ASSERT(image);
976
977 ec = 0;
978
979 FloatRect imageRect = FloatRect(FloatPoint(), size(image));
980 if (!imageRect.contains(normalizeRect(srcRect)) || srcRect.width() == 0 || srcRect.height() == 0) {
981 ec = INDEX_SIZE_ERR;
982 return;
983 }
984
985 if (!dstRect.width() || !dstRect.height())
986 return;
987
988 GraphicsContext* c = drawingContext();
989 if (!c)
990 return;
991 if (!state().m_invertibleCTM)
992 return;
993
994 CachedImage* cachedImage = image->cachedImage();
995 if (!cachedImage)
996 return;
997
998 if (m_canvas->originClean())
999 checkOrigin(cachedImage->response().url());
1000
1001 if (m_canvas->originClean() && !cachedImage->image()->hasSingleSecurityOrigin())
1002 m_canvas->setOriginTainted();
1003
1004 FloatRect sourceRect = c->roundToDevicePixels(srcRect);
1005 FloatRect destRect = c->roundToDevicePixels(dstRect);
1006 willDraw(destRect);
1007 c->drawImage(cachedImage->image(), destRect, sourceRect, state().m_globalComposite);
1008 }
1009
drawImage(HTMLCanvasElement * canvas,float x,float y)1010 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas, float x, float y)
1011 {
1012 ASSERT(canvas);
1013 ExceptionCode ec;
1014 drawImage(canvas, x, y, canvas->width(), canvas->height(), ec);
1015 }
1016
drawImage(HTMLCanvasElement * canvas,float x,float y,float width,float height,ExceptionCode & ec)1017 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas,
1018 float x, float y, float width, float height, ExceptionCode& ec)
1019 {
1020 ASSERT(canvas);
1021 drawImage(canvas, FloatRect(0, 0, canvas->width(), canvas->height()), FloatRect(x, y, width, height), ec);
1022 }
1023
drawImage(HTMLCanvasElement * canvas,const FloatRect & srcRect,const FloatRect & dstRect,ExceptionCode & ec)1024 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas, const FloatRect& srcRect,
1025 const FloatRect& dstRect, ExceptionCode& ec)
1026 {
1027 ASSERT(canvas);
1028
1029 ec = 0;
1030
1031 FloatRect srcCanvasRect = FloatRect(FloatPoint(), canvas->size());
1032 if (!srcCanvasRect.contains(normalizeRect(srcRect)) || srcRect.width() == 0 || srcRect.height() == 0) {
1033 ec = INDEX_SIZE_ERR;
1034 return;
1035 }
1036
1037 if (!dstRect.width() || !dstRect.height())
1038 return;
1039
1040 GraphicsContext* c = drawingContext();
1041 if (!c)
1042 return;
1043 if (!state().m_invertibleCTM)
1044 return;
1045
1046 FloatRect sourceRect = c->roundToDevicePixels(srcRect);
1047 FloatRect destRect = c->roundToDevicePixels(dstRect);
1048
1049 // FIXME: Do this through platform-independent GraphicsContext API.
1050 ImageBuffer* buffer = canvas->buffer();
1051 if (!buffer)
1052 return;
1053
1054 if (!canvas->originClean())
1055 m_canvas->setOriginTainted();
1056
1057 c->drawImage(buffer->image(), destRect, sourceRect, state().m_globalComposite);
1058 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.
1059 // FIXME: Arguably willDraw should become didDraw and occur after drawing calls and not before them to avoid problems like this.
1060 }
1061
1062 #if ENABLE(VIDEO)
drawImage(HTMLVideoElement * video,float x,float y)1063 void CanvasRenderingContext2D::drawImage(HTMLVideoElement* video, float x, float y)
1064 {
1065 ASSERT(video);
1066 IntSize s = size(video);
1067 ExceptionCode ec;
1068 drawImage(video, x, y, s.width(), s.height(), ec);
1069 }
1070
drawImage(HTMLVideoElement * video,float x,float y,float width,float height,ExceptionCode & ec)1071 void CanvasRenderingContext2D::drawImage(HTMLVideoElement* video,
1072 float x, float y, float width, float height, ExceptionCode& ec)
1073 {
1074 ASSERT(video);
1075 IntSize s = size(video);
1076 drawImage(video, FloatRect(0, 0, s.width(), s.height()), FloatRect(x, y, width, height), ec);
1077 }
1078
drawImage(HTMLVideoElement * video,const FloatRect & srcRect,const FloatRect & dstRect,ExceptionCode & ec)1079 void CanvasRenderingContext2D::drawImage(HTMLVideoElement* video, const FloatRect& srcRect, const FloatRect& dstRect,
1080 ExceptionCode& ec)
1081 {
1082 ASSERT(video);
1083
1084 ec = 0;
1085 FloatRect videoRect = FloatRect(FloatPoint(), size(video));
1086 if (!videoRect.contains(normalizeRect(srcRect)) || srcRect.width() == 0 || srcRect.height() == 0) {
1087 ec = INDEX_SIZE_ERR;
1088 return;
1089 }
1090
1091 if (!dstRect.width() || !dstRect.height())
1092 return;
1093
1094 GraphicsContext* c = drawingContext();
1095 if (!c)
1096 return;
1097 if (!state().m_invertibleCTM)
1098 return;
1099
1100 if (m_canvas->originClean())
1101 checkOrigin(video->currentSrc());
1102
1103 if (m_canvas->originClean() && !video->hasSingleSecurityOrigin())
1104 m_canvas->setOriginTainted();
1105
1106 FloatRect sourceRect = c->roundToDevicePixels(srcRect);
1107 FloatRect destRect = c->roundToDevicePixels(dstRect);
1108 willDraw(destRect);
1109
1110 c->save();
1111 c->clip(destRect);
1112 c->translate(destRect.x(), destRect.y());
1113 c->scale(FloatSize(destRect.width()/sourceRect.width(), destRect.height()/sourceRect.height()));
1114 c->translate(-sourceRect.x(), -sourceRect.y());
1115 video->paintCurrentFrameInContext(c, IntRect(IntPoint(), size(video)));
1116 c->restore();
1117 }
1118 #endif
1119
1120 // 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)1121 void CanvasRenderingContext2D::drawImageFromRect(HTMLImageElement* image,
1122 float sx, float sy, float sw, float sh,
1123 float dx, float dy, float dw, float dh,
1124 const String& compositeOperation)
1125 {
1126 if (!image)
1127 return;
1128
1129 CachedImage* cachedImage = image->cachedImage();
1130 if (!cachedImage)
1131 return;
1132
1133 if (m_canvas->originClean())
1134 checkOrigin(cachedImage->response().url());
1135
1136 if (m_canvas->originClean() && !cachedImage->image()->hasSingleSecurityOrigin())
1137 m_canvas->setOriginTainted();
1138
1139 GraphicsContext* c = drawingContext();
1140 if (!c)
1141 return;
1142 if (!state().m_invertibleCTM)
1143 return;
1144
1145 CompositeOperator op;
1146 if (!parseCompositeOperator(compositeOperation, op))
1147 op = CompositeSourceOver;
1148
1149 FloatRect destRect = FloatRect(dx, dy, dw, dh);
1150 willDraw(destRect);
1151 c->drawImage(cachedImage->image(), destRect, FloatRect(sx, sy, sw, sh), op);
1152 }
1153
setAlpha(float alpha)1154 void CanvasRenderingContext2D::setAlpha(float alpha)
1155 {
1156 setGlobalAlpha(alpha);
1157 }
1158
setCompositeOperation(const String & operation)1159 void CanvasRenderingContext2D::setCompositeOperation(const String& operation)
1160 {
1161 setGlobalCompositeOperation(operation);
1162 }
1163
prepareGradientForDashboard(CanvasGradient * gradient) const1164 void CanvasRenderingContext2D::prepareGradientForDashboard(CanvasGradient* gradient) const
1165 {
1166 #if ENABLE(DASHBOARD_SUPPORT)
1167 if (Settings* settings = m_canvas->document()->settings())
1168 if (settings->usesDashboardBackwardCompatibilityMode())
1169 gradient->setDashboardCompatibilityMode();
1170 #else
1171 UNUSED_PARAM(gradient);
1172 #endif
1173 }
1174
createLinearGradient(float x0,float y0,float x1,float y1,ExceptionCode & ec)1175 PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createLinearGradient(float x0, float y0, float x1, float y1, ExceptionCode& ec)
1176 {
1177 if (!isfinite(x0) || !isfinite(y0) || !isfinite(x1) || !isfinite(y1)) {
1178 ec = NOT_SUPPORTED_ERR;
1179 return 0;
1180 }
1181
1182 PassRefPtr<CanvasGradient> gradient = CanvasGradient::create(FloatPoint(x0, y0), FloatPoint(x1, y1));
1183 prepareGradientForDashboard(gradient.get());
1184 return gradient;
1185 }
1186
createRadialGradient(float x0,float y0,float r0,float x1,float y1,float r1,ExceptionCode & ec)1187 PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createRadialGradient(float x0, float y0, float r0, float x1, float y1, float r1, ExceptionCode& ec)
1188 {
1189 if (!isfinite(x0) || !isfinite(y0) || !isfinite(r0) ||
1190 !isfinite(x1) || !isfinite(y1) || !isfinite(r1)) {
1191 ec = NOT_SUPPORTED_ERR;
1192 return 0;
1193 }
1194 PassRefPtr<CanvasGradient> gradient = CanvasGradient::create(FloatPoint(x0, y0), r0, FloatPoint(x1, y1), r1);
1195 prepareGradientForDashboard(gradient.get());
1196 return gradient;
1197 }
1198
createPattern(HTMLImageElement * image,const String & repetitionType,ExceptionCode & ec)1199 PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLImageElement* image,
1200 const String& repetitionType, ExceptionCode& ec)
1201 {
1202 bool repeatX, repeatY;
1203 ec = 0;
1204 CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec);
1205 if (ec)
1206 return 0;
1207
1208 if (!image->complete()) {
1209 ec = INVALID_STATE_ERR;
1210 return 0;
1211 }
1212
1213 CachedImage* cachedImage = image->cachedImage();
1214 if (!cachedImage || !image->cachedImage()->image())
1215 return CanvasPattern::create(Image::nullImage(), repeatX, repeatY, true);
1216
1217 RefPtr<SecurityOrigin> origin = SecurityOrigin::createFromString(cachedImage->url());
1218 bool originClean = m_canvas->document()->securityOrigin()->canAccess(origin.get());
1219 return CanvasPattern::create(cachedImage->image(), repeatX, repeatY, originClean);
1220 }
1221
createPattern(HTMLCanvasElement * canvas,const String & repetitionType,ExceptionCode & ec)1222 PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLCanvasElement* canvas,
1223 const String& repetitionType, ExceptionCode& ec)
1224 {
1225 if (!canvas->width() || !canvas->height()) {
1226 ec = INVALID_STATE_ERR;
1227 return 0;
1228 }
1229
1230 bool repeatX, repeatY;
1231 ec = 0;
1232 CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec);
1233 if (ec)
1234 return 0;
1235 return CanvasPattern::create(canvas->buffer()->image(), repeatX, repeatY, canvas->originClean());
1236 }
1237
willDraw(const FloatRect & r,unsigned options)1238 void CanvasRenderingContext2D::willDraw(const FloatRect& r, unsigned options)
1239 {
1240 GraphicsContext* c = drawingContext();
1241 if (!c)
1242 return;
1243 if (!state().m_invertibleCTM)
1244 return;
1245
1246 FloatRect dirtyRect = r;
1247 if (options & CanvasWillDrawApplyTransform) {
1248 TransformationMatrix ctm = state().m_transform;
1249 dirtyRect = ctm.mapRect(r);
1250 }
1251
1252 if (options & CanvasWillDrawApplyShadow) {
1253 // The shadow gets applied after transformation
1254 FloatRect shadowRect(dirtyRect);
1255 shadowRect.move(state().m_shadowOffset);
1256 shadowRect.inflate(state().m_shadowBlur);
1257 dirtyRect.unite(shadowRect);
1258 }
1259
1260 if (options & CanvasWillDrawApplyClip) {
1261 // FIXME: apply the current clip to the rectangle. Unfortunately we can't get the clip
1262 // back out of the GraphicsContext, so to take clip into account for incremental painting,
1263 // we'd have to keep the clip path around.
1264 }
1265
1266 m_canvas->willDraw(dirtyRect);
1267 }
1268
drawingContext() const1269 GraphicsContext* CanvasRenderingContext2D::drawingContext() const
1270 {
1271 return m_canvas->drawingContext();
1272 }
1273
createEmptyImageData(const IntSize & size)1274 static PassRefPtr<ImageData> createEmptyImageData(const IntSize& size)
1275 {
1276 RefPtr<ImageData> data = ImageData::create(size.width(), size.height());
1277 memset(data->data()->data()->data(), 0, data->data()->data()->length());
1278 return data.get();
1279 }
1280
createImageData(float sw,float sh) const1281 PassRefPtr<ImageData> CanvasRenderingContext2D::createImageData(float sw, float sh) const
1282 {
1283 FloatSize unscaledSize(sw, sh);
1284 IntSize scaledSize = m_canvas->convertLogicalToDevice(unscaledSize);
1285 if (scaledSize.width() < 1)
1286 scaledSize.setWidth(1);
1287 if (scaledSize.height() < 1)
1288 scaledSize.setHeight(1);
1289
1290 return createEmptyImageData(scaledSize);
1291 }
1292
getImageData(float sx,float sy,float sw,float sh,ExceptionCode & ec) const1293 PassRefPtr<ImageData> CanvasRenderingContext2D::getImageData(float sx, float sy, float sw, float sh, ExceptionCode& ec) const
1294 {
1295 if (!m_canvas->originClean()) {
1296 ec = SECURITY_ERR;
1297 return 0;
1298 }
1299
1300 FloatRect unscaledRect(sx, sy, sw, sh);
1301 IntRect scaledRect = m_canvas->convertLogicalToDevice(unscaledRect);
1302 if (scaledRect.width() < 1)
1303 scaledRect.setWidth(1);
1304 if (scaledRect.height() < 1)
1305 scaledRect.setHeight(1);
1306 ImageBuffer* buffer = m_canvas ? m_canvas->buffer() : 0;
1307 if (!buffer)
1308 return createEmptyImageData(scaledRect.size());
1309 return buffer->getImageData(scaledRect);
1310 }
1311
putImageData(ImageData * data,float dx,float dy,ExceptionCode & ec)1312 void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, ExceptionCode& ec)
1313 {
1314 if (!data) {
1315 ec = TYPE_MISMATCH_ERR;
1316 return;
1317 }
1318 putImageData(data, dx, dy, 0, 0, data->width(), data->height(), ec);
1319 }
1320
putImageData(ImageData * data,float dx,float dy,float dirtyX,float dirtyY,float dirtyWidth,float dirtyHeight,ExceptionCode & ec)1321 void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, float dirtyX, float dirtyY,
1322 float dirtyWidth, float dirtyHeight, ExceptionCode& ec)
1323 {
1324 if (!data) {
1325 ec = TYPE_MISMATCH_ERR;
1326 return;
1327 }
1328 if (!isfinite(dx) || !isfinite(dy) || !isfinite(dirtyX) ||
1329 !isfinite(dirtyY) || !isfinite(dirtyWidth) || !isfinite(dirtyHeight)) {
1330 ec = INDEX_SIZE_ERR;
1331 return;
1332 }
1333
1334 ImageBuffer* buffer = m_canvas->buffer();
1335 if (!buffer)
1336 return;
1337
1338 if (dirtyWidth < 0) {
1339 dirtyX += dirtyWidth;
1340 dirtyWidth = -dirtyWidth;
1341 }
1342
1343 if (dirtyHeight < 0) {
1344 dirtyY += dirtyHeight;
1345 dirtyHeight = -dirtyHeight;
1346 }
1347
1348 FloatRect clipRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
1349 clipRect.intersect(IntRect(0, 0, data->width(), data->height()));
1350 IntSize destOffset(static_cast<int>(dx), static_cast<int>(dy));
1351 IntRect sourceRect = enclosingIntRect(clipRect);
1352 sourceRect.move(destOffset);
1353 sourceRect.intersect(IntRect(IntPoint(), buffer->size()));
1354 if (sourceRect.isEmpty())
1355 return;
1356 willDraw(sourceRect, 0); // ignore transform, shadow and clip
1357 sourceRect.move(-destOffset);
1358 IntPoint destPoint(destOffset.width(), destOffset.height());
1359
1360 buffer->putImageData(data, sourceRect, destPoint);
1361 }
1362
font() const1363 String CanvasRenderingContext2D::font() const
1364 {
1365 return state().m_unparsedFont;
1366 }
1367
setFont(const String & newFont)1368 void CanvasRenderingContext2D::setFont(const String& newFont)
1369 {
1370 RefPtr<CSSMutableStyleDeclaration> tempDecl = CSSMutableStyleDeclaration::create();
1371 CSSParser parser(!m_canvas->document()->inCompatMode()); // Use the parse mode of the canvas' document when parsing CSS.
1372
1373 String declarationText("font: ");
1374 declarationText += newFont;
1375 parser.parseDeclaration(tempDecl.get(), declarationText);
1376 if (!tempDecl->length())
1377 return;
1378
1379 // The parse succeeded.
1380 state().m_unparsedFont = newFont;
1381
1382 // Map the <canvas> font into the text style. If the font uses keywords like larger/smaller, these will work
1383 // relative to the canvas.
1384 RefPtr<RenderStyle> newStyle = RenderStyle::create();
1385 if (m_canvas->computedStyle())
1386 newStyle->setFontDescription(m_canvas->computedStyle()->fontDescription());
1387
1388 // Now map the font property into the style.
1389 CSSStyleSelector* styleSelector = m_canvas->document()->styleSelector();
1390 styleSelector->applyPropertyToStyle(CSSPropertyFont, tempDecl->getPropertyCSSValue(CSSPropertyFont).get(), newStyle.get());
1391
1392 state().m_font = newStyle->font();
1393 state().m_font.update(styleSelector->fontSelector());
1394 state().m_realizedFont = true;
1395 }
1396
textAlign() const1397 String CanvasRenderingContext2D::textAlign() const
1398 {
1399 return textAlignName(state().m_textAlign);
1400 }
1401
setTextAlign(const String & s)1402 void CanvasRenderingContext2D::setTextAlign(const String& s)
1403 {
1404 TextAlign align;
1405 if (!parseTextAlign(s, align))
1406 return;
1407 state().m_textAlign = align;
1408 }
1409
textBaseline() const1410 String CanvasRenderingContext2D::textBaseline() const
1411 {
1412 return textBaselineName(state().m_textBaseline);
1413 }
1414
setTextBaseline(const String & s)1415 void CanvasRenderingContext2D::setTextBaseline(const String& s)
1416 {
1417 TextBaseline baseline;
1418 if (!parseTextBaseline(s, baseline))
1419 return;
1420 state().m_textBaseline = baseline;
1421 }
1422
fillText(const String & text,float x,float y)1423 void CanvasRenderingContext2D::fillText(const String& text, float x, float y)
1424 {
1425 drawTextInternal(text, x, y, true);
1426 }
1427
fillText(const String & text,float x,float y,float maxWidth)1428 void CanvasRenderingContext2D::fillText(const String& text, float x, float y, float maxWidth)
1429 {
1430 drawTextInternal(text, x, y, true, maxWidth, true);
1431 }
1432
strokeText(const String & text,float x,float y)1433 void CanvasRenderingContext2D::strokeText(const String& text, float x, float y)
1434 {
1435 drawTextInternal(text, x, y, false);
1436 }
1437
strokeText(const String & text,float x,float y,float maxWidth)1438 void CanvasRenderingContext2D::strokeText(const String& text, float x, float y, float maxWidth)
1439 {
1440 drawTextInternal(text, x, y, false, maxWidth, true);
1441 }
1442
measureText(const String & text)1443 PassRefPtr<TextMetrics> CanvasRenderingContext2D::measureText(const String& text)
1444 {
1445 RefPtr<TextMetrics> metrics = TextMetrics::create();
1446 metrics->setWidth(accessFont().width(TextRun(text.characters(), text.length())));
1447 return metrics;
1448 }
1449
drawTextInternal(const String & text,float x,float y,bool fill,float,bool)1450 void CanvasRenderingContext2D::drawTextInternal(const String& text, float x, float y, bool fill, float /*maxWidth*/, bool /*useMaxWidth*/)
1451 {
1452 GraphicsContext* c = drawingContext();
1453 if (!c)
1454 return;
1455 if (!state().m_invertibleCTM)
1456 return;
1457
1458 const Font& font = accessFont();
1459
1460 // FIXME: Handle maxWidth.
1461 // FIXME: Need to turn off font smoothing.
1462
1463 bool rtl = m_canvas->computedStyle() ? m_canvas->computedStyle()->direction() == RTL : false;
1464 bool override = m_canvas->computedStyle() ? m_canvas->computedStyle()->unicodeBidi() == Override : false;
1465
1466 unsigned length = text.length();
1467 const UChar* string = text.characters();
1468 TextRun textRun(string, length, 0, 0, 0, rtl, override, false, false);
1469
1470 // Draw the item text at the correct point.
1471 FloatPoint location(x, y);
1472 switch (state().m_textBaseline) {
1473 case TopTextBaseline:
1474 case HangingTextBaseline:
1475 location.setY(y + font.ascent());
1476 break;
1477 case BottomTextBaseline:
1478 case IdeographicTextBaseline:
1479 location.setY(y - font.descent());
1480 break;
1481 case MiddleTextBaseline:
1482 location.setY(y - font.descent() + font.height() / 2);
1483 break;
1484 case AlphabeticTextBaseline:
1485 default:
1486 // Do nothing.
1487 break;
1488 }
1489
1490 float width = font.width(TextRun(text, false, 0, 0, rtl, override));
1491
1492 TextAlign align = state().m_textAlign;
1493 if (align == StartTextAlign)
1494 align = rtl ? RightTextAlign : LeftTextAlign;
1495 else if (align == EndTextAlign)
1496 align = rtl ? LeftTextAlign : RightTextAlign;
1497
1498 switch (align) {
1499 case CenterTextAlign:
1500 location.setX(location.x() - width / 2);
1501 break;
1502 case RightTextAlign:
1503 location.setX(location.x() - width);
1504 break;
1505 default:
1506 break;
1507 }
1508
1509 // The slop built in to this mask rect matches the heuristic used in FontCGWin.cpp for GDI text.
1510 FloatRect textRect = FloatRect(location.x() - font.height() / 2, location.y() - font.ascent() - font.lineGap(),
1511 width + font.height(), font.lineSpacing());
1512 if (!fill)
1513 textRect.inflate(c->strokeThickness() / 2);
1514
1515 if (fill)
1516 m_canvas->willDraw(textRect);
1517 else {
1518 // When stroking text, pointy miters can extend outside of textRect, so we
1519 // punt and dirty the whole canvas.
1520 m_canvas->willDraw(FloatRect(0, 0, m_canvas->width(), m_canvas->height()));
1521 }
1522
1523 #if PLATFORM(CG)
1524 CanvasStyle* drawStyle = fill ? state().m_fillStyle.get() : state().m_strokeStyle.get();
1525 if (drawStyle->canvasGradient() || drawStyle->canvasPattern()) {
1526 // FIXME: The rect is not big enough for miters on stroked text.
1527 IntRect maskRect = enclosingIntRect(textRect);
1528
1529 OwnPtr<ImageBuffer> maskImage = ImageBuffer::create(maskRect.size());
1530
1531 GraphicsContext* maskImageContext = maskImage->context();
1532
1533 if (fill)
1534 maskImageContext->setFillColor(Color::black);
1535 else {
1536 maskImageContext->setStrokeColor(Color::black);
1537 maskImageContext->setStrokeThickness(c->strokeThickness());
1538 }
1539
1540 maskImageContext->setTextDrawingMode(fill ? cTextFill : cTextStroke);
1541 maskImageContext->translate(-maskRect.x(), -maskRect.y());
1542
1543 maskImageContext->drawBidiText(font, textRun, location);
1544
1545 c->save();
1546 c->clipToImageBuffer(maskRect, maskImage.get());
1547 drawStyle->applyFillColor(c);
1548 c->fillRect(maskRect);
1549 c->restore();
1550
1551 return;
1552 }
1553 #endif
1554
1555 c->setTextDrawingMode(fill ? cTextFill : cTextStroke);
1556 c->drawBidiText(font, textRun, location);
1557 }
1558
accessFont()1559 const Font& CanvasRenderingContext2D::accessFont()
1560 {
1561 if (!state().m_realizedFont)
1562 setFont(state().m_unparsedFont);
1563 return state().m_font;
1564 }
1565
1566 } // namespace WebCore
1567