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