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