Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(722)

Side by Side Diff: sky/engine/core/html/canvas/CanvasRenderingContext2D.cpp

Issue 1001913003: Remove <canvas> (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 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 * Copyright (C) 2012, 2013 Intel Corporation. All rights reserved.
9 * Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
21 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
28 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include "sky/engine/config.h"
34 #include "sky/engine/core/html/canvas/CanvasRenderingContext2D.h"
35
36 #include "gen/sky/core/CSSPropertyNames.h"
37 #include "sky/engine/bindings/exception_messages.h"
38 #include "sky/engine/bindings/exception_state.h"
39 #include "sky/engine/bindings/exception_state_placeholder.h"
40 #include "sky/engine/core/css/CSSFontSelector.h"
41 #include "sky/engine/core/css/StylePropertySet.h"
42 #include "sky/engine/core/css/parser/BisonCSSParser.h"
43 #include "sky/engine/core/css/resolver/StyleResolver.h"
44 #include "sky/engine/core/dom/ExceptionCode.h"
45 #include "sky/engine/core/dom/StyleEngine.h"
46 #include "sky/engine/core/events/Event.h"
47 #include "sky/engine/core/fetch/ImageResource.h"
48 #include "sky/engine/core/frame/ImageBitmap.h"
49 #include "sky/engine/core/html/HTMLCanvasElement.h"
50 #include "sky/engine/core/html/HTMLImageElement.h"
51 #include "sky/engine/core/html/ImageData.h"
52 #include "sky/engine/core/html/TextMetrics.h"
53 #include "sky/engine/core/html/canvas/CanvasGradient.h"
54 #include "sky/engine/core/html/canvas/CanvasPattern.h"
55 #include "sky/engine/core/html/canvas/CanvasStyle.h"
56 #include "sky/engine/core/html/canvas/Path2D.h"
57 #include "sky/engine/core/rendering/RenderImage.h"
58 #include "sky/engine/core/rendering/RenderLayer.h"
59 #include "sky/engine/core/rendering/RenderTheme.h"
60 #include "sky/engine/platform/fonts/FontCache.h"
61 #include "sky/engine/platform/geometry/FloatQuad.h"
62 #include "sky/engine/platform/graphics/DrawLooperBuilder.h"
63 #include "sky/engine/platform/graphics/GraphicsContextStateSaver.h"
64 #include "sky/engine/platform/text/TextRun.h"
65 #include "sky/engine/wtf/CheckedArithmetic.h"
66 #include "sky/engine/wtf/MathExtras.h"
67 #include "sky/engine/wtf/OwnPtr.h"
68 #include "sky/engine/wtf/Uint8ClampedArray.h"
69 #include "sky/engine/wtf/text/StringBuilder.h"
70
71 namespace blink {
72
73 static const int defaultFontSize = 10;
74 static const char defaultFontFamily[] = "sans-serif";
75 static const char defaultFont[] = "10px sans-serif";
76 static const char inherit[] = "inherit";
77 static const char rtl[] = "rtl";
78 static const char ltr[] = "ltr";
79 static const double TryRestoreContextInterval = 0.5;
80 static const unsigned MaxTryRestoreContextAttempts = 4;
81
82 static bool contextLostRestoredEventsEnabled()
83 {
84 return RuntimeEnabledFeatures::experimentalCanvasFeaturesEnabled();
85 }
86
87 CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement* canvas, co nst Canvas2DContextAttributes* attrs)
88 : CanvasRenderingContext(canvas)
89 , m_isContextLost(false)
90 , m_contextRestorable(true)
91 , m_storageMode(!attrs ? PersistentStorage : attrs->parsedStorage())
92 , m_tryRestoreContextAttemptCount(0)
93 , m_dispatchContextLostEventTimer(this, &CanvasRenderingContext2D::dispatchC ontextLostEvent)
94 , m_dispatchContextRestoredEventTimer(this, &CanvasRenderingContext2D::dispa tchContextRestoredEvent)
95 , m_tryRestoreContextEventTimer(this, &CanvasRenderingContext2D::tryRestoreC ontextEvent)
96 {
97 m_stateStack.append(adoptPtr(new State()));
98 }
99
100 void CanvasRenderingContext2D::unwindStateStack()
101 {
102 if (size_t stackSize = m_stateStack.size()) {
103 if (GraphicsContext* context = canvas()->existingDrawingContext()) {
104 while (--stackSize)
105 context->restore();
106 }
107 }
108 }
109
110 CanvasRenderingContext2D::~CanvasRenderingContext2D()
111 {
112 }
113
114 void CanvasRenderingContext2D::validateStateStack()
115 {
116 #if ENABLE(ASSERT)
117 GraphicsContext* context = canvas()->existingDrawingContext();
118 if (context && !context->contextDisabled())
119 ASSERT(context->saveCount() == m_stateStack.size());
120 #endif
121 }
122
123 bool CanvasRenderingContext2D::isAccelerated() const
124 {
125 if (!canvas()->hasImageBuffer())
126 return false;
127 GraphicsContext* context = drawingContext();
128 return context && context->isAccelerated();
129 }
130
131 bool CanvasRenderingContext2D::isContextLost() const
132 {
133 return m_isContextLost;
134 }
135
136 void CanvasRenderingContext2D::loseContext()
137 {
138 if (m_isContextLost)
139 return;
140 m_isContextLost = true;
141 m_dispatchContextLostEventTimer.startOneShot(0, FROM_HERE);
142 }
143
144 void CanvasRenderingContext2D::restoreContext()
145 {
146 if (!m_contextRestorable)
147 return;
148 // This code path is for restoring from an eviction
149 // Restoring from surface failure is handled internally
150 ASSERT(m_isContextLost && !canvas()->hasImageBuffer());
151
152 if (canvas()->buffer()) {
153 if (contextLostRestoredEventsEnabled()) {
154 m_dispatchContextRestoredEventTimer.startOneShot(0, FROM_HERE);
155 } else {
156 // legacy synchronous context restoration.
157 reset();
158 m_isContextLost = false;
159 }
160 }
161 }
162
163 void CanvasRenderingContext2D::dispatchContextLostEvent(Timer<CanvasRenderingCon text2D>*)
164 {
165 if (contextLostRestoredEventsEnabled()) {
166 RefPtr<Event> event = Event::createCancelable(EventTypeNames::contextlos t);
167 canvas()->dispatchEvent(event);
168 if (event->defaultPrevented()) {
169 m_contextRestorable = false;
170 }
171 }
172
173 // If an image buffer is present, it means the context was not lost due to
174 // an eviction, but rather due to a surface failure (gpu context lost?)
175 if (m_contextRestorable && canvas()->hasImageBuffer()) {
176 m_tryRestoreContextAttemptCount = 0;
177 m_tryRestoreContextEventTimer.startRepeating(TryRestoreContextInterval, FROM_HERE);
178 }
179 }
180
181 void CanvasRenderingContext2D::tryRestoreContextEvent(Timer<CanvasRenderingConte xt2D>* timer)
182 {
183 if (!m_isContextLost) {
184 // Canvas was already restored (possibly thanks to a resize), so stop tr ying.
185 m_tryRestoreContextEventTimer.stop();
186 return;
187 }
188 if (canvas()->hasImageBuffer() && canvas()->buffer()->restoreSurface()) {
189 m_tryRestoreContextEventTimer.stop();
190 dispatchContextRestoredEvent(0);
191 }
192
193 if (++m_tryRestoreContextAttemptCount > MaxTryRestoreContextAttempts)
194 canvas()->discardImageBuffer();
195
196 if (!canvas()->hasImageBuffer()) {
197 // final attempt: allocate a brand new image buffer instead of restoring
198 timer->stop();
199 if (canvas()->buffer())
200 dispatchContextRestoredEvent(0);
201 }
202 }
203
204 void CanvasRenderingContext2D::dispatchContextRestoredEvent(Timer<CanvasRenderin gContext2D>*)
205 {
206 if (!m_isContextLost)
207 return;
208 reset();
209 m_isContextLost = false;
210 if (contextLostRestoredEventsEnabled()) {
211 RefPtr<Event> event(Event::create(EventTypeNames::contextrestored));
212 canvas()->dispatchEvent(event);
213 }
214 }
215
216 void CanvasRenderingContext2D::reset()
217 {
218 validateStateStack();
219 unwindStateStack();
220 m_stateStack.resize(1);
221 m_stateStack.first() = adoptPtr(new State());
222 m_path.clear();
223 validateStateStack();
224 }
225
226 // Important: Several of these properties are also stored in GraphicsContext's
227 // StrokeData. The default values that StrokeData uses may not the same values
228 // that the canvas 2d spec specifies. Make sure to sync the initial state of the
229 // GraphicsContext in HTMLCanvasElement::createImageBuffer()!
230 CanvasRenderingContext2D::State::State()
231 : m_unrealizedSaveCount(0)
232 , m_strokeStyle(CanvasStyle::createFromRGBA(Color::black))
233 , m_fillStyle(CanvasStyle::createFromRGBA(Color::black))
234 , m_lineWidth(1)
235 , m_lineCap(ButtCap)
236 , m_lineJoin(MiterJoin)
237 , m_miterLimit(10)
238 , m_shadowBlur(0)
239 , m_shadowColor(Color::transparent)
240 , m_invertibleCTM(true)
241 , m_lineDashOffset(0)
242 , m_imageSmoothingEnabled(true)
243 , m_textAlign(StartTextAlign)
244 , m_textBaseline(AlphabeticTextBaseline)
245 , m_direction(DirectionInherit)
246 , m_unparsedFont(defaultFont)
247 , m_realizedFont(false)
248 , m_hasClip(false)
249 {
250 }
251
252 CanvasRenderingContext2D::State::State(const State& other)
253 : CSSFontSelectorClient()
254 , m_unrealizedSaveCount(other.m_unrealizedSaveCount)
255 , m_unparsedStrokeColor(other.m_unparsedStrokeColor)
256 , m_unparsedFillColor(other.m_unparsedFillColor)
257 , m_strokeStyle(other.m_strokeStyle)
258 , m_fillStyle(other.m_fillStyle)
259 , m_lineWidth(other.m_lineWidth)
260 , m_lineCap(other.m_lineCap)
261 , m_lineJoin(other.m_lineJoin)
262 , m_miterLimit(other.m_miterLimit)
263 , m_shadowOffset(other.m_shadowOffset)
264 , m_shadowBlur(other.m_shadowBlur)
265 , m_shadowColor(other.m_shadowColor)
266 , m_transform(other.m_transform)
267 , m_invertibleCTM(other.m_invertibleCTM)
268 , m_lineDashOffset(other.m_lineDashOffset)
269 , m_imageSmoothingEnabled(other.m_imageSmoothingEnabled)
270 , m_textAlign(other.m_textAlign)
271 , m_textBaseline(other.m_textBaseline)
272 , m_direction(other.m_direction)
273 , m_unparsedFont(other.m_unparsedFont)
274 , m_font(other.m_font)
275 , m_realizedFont(other.m_realizedFont)
276 , m_hasClip(other.m_hasClip)
277 {
278 if (m_realizedFont)
279 static_cast<CSSFontSelector*>(m_font.fontSelector())->registerForInvalid ationCallbacks(this);
280 }
281
282 CanvasRenderingContext2D::State& CanvasRenderingContext2D::State::operator=(cons t State& other)
283 {
284 if (this == &other)
285 return *this;
286
287 #if !ENABLE(OILPAN)
288 if (m_realizedFont)
289 static_cast<CSSFontSelector*>(m_font.fontSelector())->unregisterForInval idationCallbacks(this);
290 #endif
291
292 m_unrealizedSaveCount = other.m_unrealizedSaveCount;
293 m_unparsedStrokeColor = other.m_unparsedStrokeColor;
294 m_unparsedFillColor = other.m_unparsedFillColor;
295 m_strokeStyle = other.m_strokeStyle;
296 m_fillStyle = other.m_fillStyle;
297 m_lineWidth = other.m_lineWidth;
298 m_lineCap = other.m_lineCap;
299 m_lineJoin = other.m_lineJoin;
300 m_miterLimit = other.m_miterLimit;
301 m_shadowOffset = other.m_shadowOffset;
302 m_shadowBlur = other.m_shadowBlur;
303 m_shadowColor = other.m_shadowColor;
304 m_transform = other.m_transform;
305 m_invertibleCTM = other.m_invertibleCTM;
306 m_imageSmoothingEnabled = other.m_imageSmoothingEnabled;
307 m_textAlign = other.m_textAlign;
308 m_textBaseline = other.m_textBaseline;
309 m_direction = other.m_direction;
310 m_unparsedFont = other.m_unparsedFont;
311 m_font = other.m_font;
312 m_realizedFont = other.m_realizedFont;
313 m_hasClip = other.m_hasClip;
314
315 if (m_realizedFont)
316 static_cast<CSSFontSelector*>(m_font.fontSelector())->registerForInvalid ationCallbacks(this);
317
318 return *this;
319 }
320
321 CanvasRenderingContext2D::State::~State()
322 {
323 #if !ENABLE(OILPAN)
324 if (m_realizedFont)
325 static_cast<CSSFontSelector*>(m_font.fontSelector())->unregisterForInval idationCallbacks(this);
326 #endif
327 }
328
329 void CanvasRenderingContext2D::State::fontsNeedUpdate(CSSFontSelector* fontSelec tor)
330 {
331 ASSERT_ARG(fontSelector, fontSelector == m_font.fontSelector());
332 ASSERT(m_realizedFont);
333
334 m_font.update(fontSelector);
335 }
336
337 void CanvasRenderingContext2D::realizeSaves(GraphicsContext* context)
338 {
339 validateStateStack();
340 if (state().m_unrealizedSaveCount) {
341 ASSERT(m_stateStack.size() >= 1);
342 // Reduce the current state's unrealized count by one now,
343 // to reflect the fact we are saving one state.
344 m_stateStack.last()->m_unrealizedSaveCount--;
345 m_stateStack.append(adoptPtr(new State(state())));
346 // Set the new state's unrealized count to 0, because it has no outstand ing saves.
347 // We need to do this explicitly because the copy constructor and operat or= used
348 // by the Vector operations copy the unrealized count from the previous state (in
349 // turn necessary to support correct resizing and unwinding of the stack ).
350 m_stateStack.last()->m_unrealizedSaveCount = 0;
351 if (!context)
352 context = drawingContext();
353 if (context)
354 context->save();
355 validateStateStack();
356 }
357 }
358
359 void CanvasRenderingContext2D::restore()
360 {
361 validateStateStack();
362 if (state().m_unrealizedSaveCount) {
363 // We never realized the save, so just record that it was unnecessary.
364 --m_stateStack.last()->m_unrealizedSaveCount;
365 return;
366 }
367 ASSERT(m_stateStack.size() >= 1);
368 if (m_stateStack.size() <= 1)
369 return;
370 m_path.transform(state().m_transform);
371 m_stateStack.removeLast();
372 m_path.transform(state().m_transform.inverse());
373 GraphicsContext* c = drawingContext();
374 if (c)
375 c->restore();
376 validateStateStack();
377 }
378
379 CanvasStyle* CanvasRenderingContext2D::strokeStyle() const
380 {
381 return state().m_strokeStyle.get();
382 }
383
384 void CanvasRenderingContext2D::setStrokeStyle(PassRefPtr<CanvasStyle> prpStyle)
385 {
386 RefPtr<CanvasStyle> style = prpStyle;
387
388 if (!style)
389 return;
390
391 if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentColor(*style ))
392 return;
393
394 if (style->isCurrentColor()) {
395 if (style->hasOverrideAlpha())
396 style = CanvasStyle::createFromRGBA(colorWithOverrideAlpha(currentCo lor(canvas()), style->overrideAlpha()));
397 else
398 style = CanvasStyle::createFromRGBA(currentColor(canvas()));
399 }
400
401 GraphicsContext* c = drawingContext();
402 realizeSaves(c);
403 modifiableState().m_strokeStyle = style.release();
404 if (!c)
405 return;
406 state().m_strokeStyle->applyStrokeColor(c);
407 modifiableState().m_unparsedStrokeColor = String();
408 }
409
410 CanvasStyle* CanvasRenderingContext2D::fillStyle() const
411 {
412 return state().m_fillStyle.get();
413 }
414
415 void CanvasRenderingContext2D::setFillStyle(PassRefPtr<CanvasStyle> prpStyle)
416 {
417 RefPtr<CanvasStyle> style = prpStyle;
418
419 if (!style)
420 return;
421
422 if (state().m_fillStyle && state().m_fillStyle->isEquivalentColor(*style))
423 return;
424
425 if (style->isCurrentColor()) {
426 if (style->hasOverrideAlpha())
427 style = CanvasStyle::createFromRGBA(colorWithOverrideAlpha(currentCo lor(canvas()), style->overrideAlpha()));
428 else
429 style = CanvasStyle::createFromRGBA(currentColor(canvas()));
430 }
431
432 GraphicsContext* c = drawingContext();
433 realizeSaves(c);
434 modifiableState().m_fillStyle = style.release();
435 if (!c)
436 return;
437 state().m_fillStyle->applyFillColor(c);
438 modifiableState().m_unparsedFillColor = String();
439 }
440
441 float CanvasRenderingContext2D::lineWidth() const
442 {
443 return state().m_lineWidth;
444 }
445
446 void CanvasRenderingContext2D::setLineWidth(float width)
447 {
448 if (!(std::isfinite(width) && width > 0))
449 return;
450 if (state().m_lineWidth == width)
451 return;
452 GraphicsContext* c = drawingContext();
453 realizeSaves(c);
454 modifiableState().m_lineWidth = width;
455 if (!c)
456 return;
457 c->setStrokeThickness(width);
458 }
459
460 String CanvasRenderingContext2D::lineCap() const
461 {
462 return lineCapName(state().m_lineCap);
463 }
464
465 void CanvasRenderingContext2D::setLineCap(const String& s)
466 {
467 LineCap cap;
468 if (!parseLineCap(s, cap))
469 return;
470 if (state().m_lineCap == cap)
471 return;
472 GraphicsContext* c = drawingContext();
473 realizeSaves(c);
474 modifiableState().m_lineCap = cap;
475 if (!c)
476 return;
477 c->setLineCap(cap);
478 }
479
480 String CanvasRenderingContext2D::lineJoin() const
481 {
482 return lineJoinName(state().m_lineJoin);
483 }
484
485 void CanvasRenderingContext2D::setLineJoin(const String& s)
486 {
487 LineJoin join;
488 if (!parseLineJoin(s, join))
489 return;
490 if (state().m_lineJoin == join)
491 return;
492 GraphicsContext* c = drawingContext();
493 realizeSaves(c);
494 modifiableState().m_lineJoin = join;
495 if (!c)
496 return;
497 c->setLineJoin(join);
498 }
499
500 float CanvasRenderingContext2D::miterLimit() const
501 {
502 return state().m_miterLimit;
503 }
504
505 void CanvasRenderingContext2D::setMiterLimit(float limit)
506 {
507 if (!(std::isfinite(limit) && limit > 0))
508 return;
509 if (state().m_miterLimit == limit)
510 return;
511 GraphicsContext* c = drawingContext();
512 realizeSaves(c);
513 modifiableState().m_miterLimit = limit;
514 if (!c)
515 return;
516 c->setMiterLimit(limit);
517 }
518
519 float CanvasRenderingContext2D::shadowOffsetX() const
520 {
521 return state().m_shadowOffset.width();
522 }
523
524 void CanvasRenderingContext2D::setShadowOffsetX(float x)
525 {
526 if (!std::isfinite(x))
527 return;
528 if (state().m_shadowOffset.width() == x)
529 return;
530 realizeSaves(0);
531 modifiableState().m_shadowOffset.setWidth(x);
532 applyShadow();
533 }
534
535 float CanvasRenderingContext2D::shadowOffsetY() const
536 {
537 return state().m_shadowOffset.height();
538 }
539
540 void CanvasRenderingContext2D::setShadowOffsetY(float y)
541 {
542 if (!std::isfinite(y))
543 return;
544 if (state().m_shadowOffset.height() == y)
545 return;
546 realizeSaves(0);
547 modifiableState().m_shadowOffset.setHeight(y);
548 applyShadow();
549 }
550
551 float CanvasRenderingContext2D::shadowBlur() const
552 {
553 return state().m_shadowBlur;
554 }
555
556 void CanvasRenderingContext2D::setShadowBlur(float blur)
557 {
558 if (!(std::isfinite(blur) && blur >= 0))
559 return;
560 if (state().m_shadowBlur == blur)
561 return;
562 realizeSaves(0);
563 modifiableState().m_shadowBlur = blur;
564 applyShadow();
565 }
566
567 String CanvasRenderingContext2D::shadowColor() const
568 {
569 return Color(state().m_shadowColor).serialized();
570 }
571
572 void CanvasRenderingContext2D::setShadowColor(const String& color)
573 {
574 RGBA32 rgba;
575 if (!parseColorOrCurrentColor(rgba, color, canvas()))
576 return;
577 if (state().m_shadowColor == rgba)
578 return;
579 realizeSaves(0);
580 modifiableState().m_shadowColor = rgba;
581 applyShadow();
582 }
583
584 const Vector<float>& CanvasRenderingContext2D::getLineDash() const
585 {
586 return state().m_lineDash;
587 }
588
589 static bool lineDashSequenceIsValid(const Vector<float>& dash)
590 {
591 for (size_t i = 0; i < dash.size(); i++) {
592 if (!std::isfinite(dash[i]) || dash[i] < 0)
593 return false;
594 }
595 return true;
596 }
597
598 void CanvasRenderingContext2D::setLineDash(const Vector<float>& dash)
599 {
600 if (!lineDashSequenceIsValid(dash))
601 return;
602
603 realizeSaves(0);
604 modifiableState().m_lineDash = dash;
605 // Spec requires the concatenation of two copies the dash list when the
606 // number of elements is odd
607 if (dash.size() % 2)
608 modifiableState().m_lineDash.appendVector(dash);
609
610 applyLineDash();
611 }
612
613 float CanvasRenderingContext2D::lineDashOffset() const
614 {
615 return state().m_lineDashOffset;
616 }
617
618 void CanvasRenderingContext2D::setLineDashOffset(float offset)
619 {
620 if (!std::isfinite(offset) || state().m_lineDashOffset == offset)
621 return;
622
623 realizeSaves(0);
624 modifiableState().m_lineDashOffset = offset;
625 applyLineDash();
626 }
627
628 void CanvasRenderingContext2D::applyLineDash() const
629 {
630 GraphicsContext* c = drawingContext();
631 if (!c)
632 return;
633 DashArray convertedLineDash(state().m_lineDash.size());
634 for (size_t i = 0; i < state().m_lineDash.size(); ++i)
635 convertedLineDash[i] = static_cast<DashArrayElement>(state().m_lineDash[ i]);
636 c->setLineDash(convertedLineDash, state().m_lineDashOffset);
637 }
638
639 void CanvasRenderingContext2D::scale(float sx, float sy)
640 {
641 GraphicsContext* c = drawingContext();
642 if (!c)
643 return;
644 if (!state().m_invertibleCTM)
645 return;
646
647 if (!std::isfinite(sx) | !std::isfinite(sy))
648 return;
649
650 AffineTransform newTransform = state().m_transform;
651 newTransform.scaleNonUniform(sx, sy);
652 if (state().m_transform == newTransform)
653 return;
654
655 realizeSaves(c);
656
657 if (!newTransform.isInvertible()) {
658 modifiableState().m_invertibleCTM = false;
659 return;
660 }
661
662 modifiableState().m_transform = newTransform;
663 c->scale(sx, sy);
664 m_path.transform(AffineTransform().scaleNonUniform(1.0 / sx, 1.0 / sy));
665 }
666
667 void CanvasRenderingContext2D::rotate(float angleInRadians)
668 {
669 GraphicsContext* c = drawingContext();
670 if (!c)
671 return;
672 if (!state().m_invertibleCTM)
673 return;
674
675 if (!std::isfinite(angleInRadians))
676 return;
677
678 AffineTransform newTransform = state().m_transform;
679 newTransform.rotateRadians(angleInRadians);
680 if (state().m_transform == newTransform)
681 return;
682
683 realizeSaves(c);
684
685 if (!newTransform.isInvertible()) {
686 modifiableState().m_invertibleCTM = false;
687 return;
688 }
689
690 modifiableState().m_transform = newTransform;
691 c->rotate(angleInRadians);
692 m_path.transform(AffineTransform().rotateRadians(-angleInRadians));
693 }
694
695 void CanvasRenderingContext2D::translate(float tx, float ty)
696 {
697 GraphicsContext* c = drawingContext();
698 if (!c)
699 return;
700 if (!state().m_invertibleCTM)
701 return;
702
703 if (!std::isfinite(tx) | !std::isfinite(ty))
704 return;
705
706 AffineTransform newTransform = state().m_transform;
707 newTransform.translate(tx, ty);
708 if (state().m_transform == newTransform)
709 return;
710
711 realizeSaves(c);
712
713 if (!newTransform.isInvertible()) {
714 modifiableState().m_invertibleCTM = false;
715 return;
716 }
717
718 modifiableState().m_transform = newTransform;
719 c->translate(tx, ty);
720 m_path.transform(AffineTransform().translate(-tx, -ty));
721 }
722
723 void CanvasRenderingContext2D::transform(float m11, float m12, float m21, float m22, float dx, float dy)
724 {
725 GraphicsContext* c = drawingContext();
726 if (!c)
727 return;
728 if (!state().m_invertibleCTM)
729 return;
730
731 if (!std::isfinite(m11) | !std::isfinite(m21) | !std::isfinite(dx) | !std::i sfinite(m12) | !std::isfinite(m22) | !std::isfinite(dy))
732 return;
733
734 AffineTransform transform(m11, m12, m21, m22, dx, dy);
735 AffineTransform newTransform = state().m_transform * transform;
736 if (state().m_transform == newTransform)
737 return;
738
739 realizeSaves(c);
740
741 modifiableState().m_transform = newTransform;
742 if (!newTransform.isInvertible()) {
743 modifiableState().m_invertibleCTM = false;
744 return;
745 }
746
747 c->concatCTM(transform);
748 m_path.transform(transform.inverse());
749 }
750
751 void CanvasRenderingContext2D::resetTransform()
752 {
753 GraphicsContext* c = drawingContext();
754 if (!c)
755 return;
756
757 AffineTransform ctm = state().m_transform;
758 bool invertibleCTM = state().m_invertibleCTM;
759 // It is possible that CTM is identity while CTM is not invertible.
760 // When CTM becomes non-invertible, realizeSaves() can make CTM identity.
761 if (ctm.isIdentity() && invertibleCTM)
762 return;
763
764 realizeSaves(c);
765 // resetTransform() resolves the non-invertible CTM state.
766 modifiableState().m_transform.makeIdentity();
767 modifiableState().m_invertibleCTM = true;
768 c->setCTM(canvas()->baseTransform());
769
770 if (invertibleCTM)
771 m_path.transform(ctm);
772 // When else, do nothing because all transform methods didn't update m_path when CTM became non-invertible.
773 // It means that resetTransform() restores m_path just before CTM became non -invertible.
774 }
775
776 void CanvasRenderingContext2D::setTransform(float m11, float m12, float m21, flo at m22, float dx, float dy)
777 {
778 GraphicsContext* c = drawingContext();
779 if (!c)
780 return;
781
782 if (!std::isfinite(m11) | !std::isfinite(m21) | !std::isfinite(dx) | !std::i sfinite(m12) | !std::isfinite(m22) | !std::isfinite(dy))
783 return;
784
785 resetTransform();
786 transform(m11, m12, m21, m22, dx, dy);
787 }
788
789 String CanvasRenderingContext2D::strokeColor()
790 {
791 return strokeStyle()->color();
792 }
793
794 void CanvasRenderingContext2D::setStrokeColor(const String& color)
795 {
796 if (color == state().m_unparsedStrokeColor)
797 return;
798 realizeSaves(0);
799 setStrokeStyle(CanvasStyle::createFromString(color));
800 modifiableState().m_unparsedStrokeColor = color;
801 }
802
803 void CanvasRenderingContext2D::setStrokeColor(float grayLevel)
804 {
805 if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentRGBA(grayLev el, grayLevel, grayLevel, 1.0f))
806 return;
807 setStrokeStyle(CanvasStyle::createFromGrayLevelWithAlpha(grayLevel, 1.0f));
808 }
809
810 void CanvasRenderingContext2D::setStrokeColor(const String& color, float alpha)
811 {
812 setStrokeStyle(CanvasStyle::createFromStringWithOverrideAlpha(color, alpha)) ;
813 }
814
815 void CanvasRenderingContext2D::setStrokeColor(float grayLevel, float alpha)
816 {
817 if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentRGBA(grayLev el, grayLevel, grayLevel, alpha))
818 return;
819 setStrokeStyle(CanvasStyle::createFromGrayLevelWithAlpha(grayLevel, alpha));
820 }
821
822 void CanvasRenderingContext2D::setStrokeColor(float r, float g, float b, float a )
823 {
824 if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentRGBA(r, g, b , a))
825 return;
826 setStrokeStyle(CanvasStyle::createFromRGBAChannels(r, g, b, a));
827 }
828
829 void CanvasRenderingContext2D::setStrokeColor(float c, float m, float y, float k , float a)
830 {
831 if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentCMYKA(c, m, y, k, a))
832 return;
833 setStrokeStyle(CanvasStyle::createFromCMYKAChannels(c, m, y, k, a));
834 }
835
836 String CanvasRenderingContext2D::fillColor()
837 {
838 return fillStyle()->color();
839 }
840
841 void CanvasRenderingContext2D::setFillColor(const String& color)
842 {
843 if (color == state().m_unparsedFillColor)
844 return;
845 realizeSaves(0);
846 setFillStyle(CanvasStyle::createFromString(color));
847 modifiableState().m_unparsedFillColor = color;
848 }
849
850 void CanvasRenderingContext2D::setFillColor(float grayLevel)
851 {
852 if (state().m_fillStyle && state().m_fillStyle->isEquivalentRGBA(grayLevel, grayLevel, grayLevel, 1.0f))
853 return;
854 setFillStyle(CanvasStyle::createFromGrayLevelWithAlpha(grayLevel, 1.0f));
855 }
856
857 void CanvasRenderingContext2D::setFillColor(const String& color, float alpha)
858 {
859 setFillStyle(CanvasStyle::createFromStringWithOverrideAlpha(color, alpha));
860 }
861
862 void CanvasRenderingContext2D::setFillColor(float grayLevel, float alpha)
863 {
864 if (state().m_fillStyle && state().m_fillStyle->isEquivalentRGBA(grayLevel, grayLevel, grayLevel, alpha))
865 return;
866 setFillStyle(CanvasStyle::createFromGrayLevelWithAlpha(grayLevel, alpha));
867 }
868
869 void CanvasRenderingContext2D::setFillColor(float r, float g, float b, float a)
870 {
871 if (state().m_fillStyle && state().m_fillStyle->isEquivalentRGBA(r, g, b, a) )
872 return;
873 setFillStyle(CanvasStyle::createFromRGBAChannels(r, g, b, a));
874 }
875
876 void CanvasRenderingContext2D::setFillColor(float c, float m, float y, float k, float a)
877 {
878 if (state().m_fillStyle && state().m_fillStyle->isEquivalentCMYKA(c, m, y, k , a))
879 return;
880 setFillStyle(CanvasStyle::createFromCMYKAChannels(c, m, y, k, a));
881 }
882
883 void CanvasRenderingContext2D::beginPath()
884 {
885 m_path.clear();
886 }
887
888 static bool validateRectForCanvas(float& x, float& y, float& width, float& heigh t)
889 {
890 if (!std::isfinite(x) | !std::isfinite(y) | !std::isfinite(width) | !std::is finite(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
909 static WindRule parseWinding(const String& windingRuleString)
910 {
911 if (windingRuleString == "nonzero")
912 return RULE_NONZERO;
913 if (windingRuleString == "evenodd")
914 return RULE_EVENODD;
915
916 ASSERT_NOT_REACHED();
917 return RULE_EVENODD;
918 }
919
920 void CanvasRenderingContext2D::fillInternal(const Path& path, const String& wind ingRuleString)
921 {
922 if (path.isEmpty()) {
923 return;
924 }
925 GraphicsContext* c = drawingContext();
926 if (!c) {
927 return;
928 }
929 if (!state().m_invertibleCTM) {
930 return;
931 }
932 FloatRect clipBounds;
933 if (!c->getTransformedClipBounds(&clipBounds)) {
934 return;
935 }
936
937 // If gradient size is zero, then paint nothing.
938 Gradient* gradient = c->fillGradient();
939 if (gradient && gradient->isZeroSize()) {
940 return;
941 }
942
943 WindRule windRule = c->fillRule();
944 c->setFillRule(parseWinding(windingRuleString));
945
946 FloatRect dirtyRect;
947 if (computeDirtyRect(path.boundingRect(), clipBounds, &dirtyRect)) {
948 c->fillPath(path);
949 didDraw(dirtyRect);
950 }
951
952 c->setFillRule(windRule);
953 }
954
955 void CanvasRenderingContext2D::fill(const String& windingRuleString)
956 {
957 fillInternal(m_path, windingRuleString);
958 }
959
960 void CanvasRenderingContext2D::fill(Path2D* domPath, const String& windingRuleSt ring)
961 {
962 fillInternal(domPath->path(), windingRuleString);
963 }
964
965 void CanvasRenderingContext2D::strokeInternal(const Path& path)
966 {
967 if (path.isEmpty()) {
968 return;
969 }
970 GraphicsContext* c = drawingContext();
971 if (!c) {
972 return;
973 }
974 if (!state().m_invertibleCTM) {
975 return;
976 }
977 FloatRect clipBounds;
978 if (!c->getTransformedClipBounds(&clipBounds))
979 return;
980
981 // If gradient size is zero, then paint nothing.
982 Gradient* gradient = c->strokeGradient();
983 if (gradient && gradient->isZeroSize()) {
984 return;
985 }
986
987 FloatRect bounds = path.boundingRect();
988 inflateStrokeRect(bounds);
989 FloatRect dirtyRect;
990 if (computeDirtyRect(bounds, clipBounds, &dirtyRect)) {
991 c->strokePath(path);
992 didDraw(dirtyRect);
993 }
994 }
995
996 void CanvasRenderingContext2D::stroke()
997 {
998 strokeInternal(m_path);
999 }
1000
1001 void CanvasRenderingContext2D::stroke(Path2D* domPath)
1002 {
1003 strokeInternal(domPath->path());
1004 }
1005
1006 void CanvasRenderingContext2D::clipInternal(const Path& path, const String& wind ingRuleString)
1007 {
1008 GraphicsContext* c = drawingContext();
1009 if (!c) {
1010 return;
1011 }
1012 if (!state().m_invertibleCTM) {
1013 return;
1014 }
1015
1016 realizeSaves(c);
1017 c->canvasClip(path, parseWinding(windingRuleString));
1018 modifiableState().m_hasClip = true;
1019 }
1020
1021 void CanvasRenderingContext2D::clip(const String& windingRuleString)
1022 {
1023 clipInternal(m_path, windingRuleString);
1024 }
1025
1026 void CanvasRenderingContext2D::clip(Path2D* domPath, const String& windingRuleSt ring)
1027 {
1028 clipInternal(domPath->path(), windingRuleString);
1029 }
1030
1031 bool CanvasRenderingContext2D::isPointInPath(const float x, const float y, const String& windingRuleString)
1032 {
1033 return isPointInPathInternal(m_path, x, y, windingRuleString);
1034 }
1035
1036 bool CanvasRenderingContext2D::isPointInPath(Path2D* domPath, const float x, con st float y, const String& windingRuleString)
1037 {
1038 return isPointInPathInternal(domPath->path(), x, y, windingRuleString);
1039 }
1040
1041 bool CanvasRenderingContext2D::isPointInPathInternal(const Path& path, const flo at x, const float y, const String& windingRuleString)
1042 {
1043 GraphicsContext* c = drawingContext();
1044 if (!c)
1045 return false;
1046 if (!state().m_invertibleCTM)
1047 return false;
1048
1049 FloatPoint point(x, y);
1050 AffineTransform ctm = state().m_transform;
1051 FloatPoint transformedPoint = ctm.inverse().mapPoint(point);
1052 if (!std::isfinite(transformedPoint.x()) || !std::isfinite(transformedPoint. y()))
1053 return false;
1054
1055 return path.contains(transformedPoint, parseWinding(windingRuleString));
1056 }
1057
1058 bool CanvasRenderingContext2D::isPointInStroke(const float x, const float y)
1059 {
1060 return isPointInStrokeInternal(m_path, x, y);
1061 }
1062
1063 bool CanvasRenderingContext2D::isPointInStroke(Path2D* domPath, const float x, c onst float y)
1064 {
1065 return isPointInStrokeInternal(domPath->path(), x, y);
1066 }
1067
1068 bool CanvasRenderingContext2D::isPointInStrokeInternal(const Path& path, const f loat x, const float y)
1069 {
1070 GraphicsContext* c = drawingContext();
1071 if (!c)
1072 return false;
1073 if (!state().m_invertibleCTM)
1074 return false;
1075
1076 FloatPoint point(x, y);
1077 AffineTransform ctm = state().m_transform;
1078 FloatPoint transformedPoint = ctm.inverse().mapPoint(point);
1079 if (!std::isfinite(transformedPoint.x()) || !std::isfinite(transformedPoint. y()))
1080 return false;
1081
1082 StrokeData strokeData;
1083 strokeData.setThickness(lineWidth());
1084 strokeData.setLineCap(getLineCap());
1085 strokeData.setLineJoin(getLineJoin());
1086 strokeData.setMiterLimit(miterLimit());
1087 strokeData.setLineDash(getLineDash(), lineDashOffset());
1088 return path.strokeContains(transformedPoint, strokeData);
1089 }
1090
1091 void CanvasRenderingContext2D::clearRect(float x, float y, float width, float he ight)
1092 {
1093 if (!validateRectForCanvas(x, y, width, height))
1094 return;
1095 GraphicsContext* context = drawingContext();
1096 if (!context)
1097 return;
1098 if (!state().m_invertibleCTM)
1099 return;
1100 FloatRect rect(x, y, width, height);
1101
1102 FloatRect dirtyRect;
1103 if (!computeDirtyRect(rect, &dirtyRect))
1104 return;
1105
1106 bool saved = false;
1107 if (shouldDrawShadows()) {
1108 context->save();
1109 saved = true;
1110 context->clearShadow();
1111 }
1112 context->clearRect(rect);
1113 if (m_hitRegionManager)
1114 m_hitRegionManager->removeHitRegionsInRect(rect, state().m_transform);
1115 if (saved)
1116 context->restore();
1117
1118 validateStateStack();
1119 didDraw(dirtyRect);
1120 }
1121
1122 void CanvasRenderingContext2D::fillRect(float x, float y, float width, float hei ght)
1123 {
1124 if (!validateRectForCanvas(x, y, width, height))
1125 return;
1126
1127 GraphicsContext* c = drawingContext();
1128 if (!c)
1129 return;
1130 if (!state().m_invertibleCTM)
1131 return;
1132 FloatRect clipBounds;
1133 if (!c->getTransformedClipBounds(&clipBounds))
1134 return;
1135
1136 // from the HTML5 Canvas spec:
1137 // If x0 = x1 and y0 = y1, then the linear gradient must paint nothing
1138 // If x0 = x1 and y0 = y1 and r0 = r1, then the radial gradient must paint n othing
1139 Gradient* gradient = c->fillGradient();
1140 if (gradient && gradient->isZeroSize())
1141 return;
1142
1143 FloatRect rect(x, y, width, height);
1144 if (rectContainsTransformedRect(rect, clipBounds)) {
1145 c->fillRect(rect);
1146 didDraw(clipBounds);
1147 } else {
1148 FloatRect dirtyRect;
1149 if (computeDirtyRect(rect, clipBounds, &dirtyRect)) {
1150 c->fillRect(rect);
1151 didDraw(dirtyRect);
1152 }
1153 }
1154 }
1155
1156 void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float h eight)
1157 {
1158 if (!validateRectForCanvas(x, y, width, height))
1159 return;
1160
1161 if (!(state().m_lineWidth >= 0))
1162 return;
1163
1164 GraphicsContext* c = drawingContext();
1165 if (!c)
1166 return;
1167 if (!state().m_invertibleCTM)
1168 return;
1169 FloatRect clipBounds;
1170 if (!c->getTransformedClipBounds(&clipBounds))
1171 return;
1172
1173 // If gradient size is zero, then paint nothing.
1174 Gradient* gradient = c->strokeGradient();
1175 if (gradient && gradient->isZeroSize())
1176 return;
1177
1178 FloatRect rect(x, y, width, height);
1179
1180 FloatRect boundingRect = rect;
1181 boundingRect.inflate(state().m_lineWidth / 2);
1182 FloatRect dirtyRect;
1183 if (computeDirtyRect(boundingRect, clipBounds, &dirtyRect)) {
1184 c->strokeRect(rect);
1185 didDraw(dirtyRect);
1186 }
1187 }
1188
1189 void CanvasRenderingContext2D::setShadow(float width, float height, float blur)
1190 {
1191 setShadow(FloatSize(width, height), blur, Color::transparent);
1192 }
1193
1194 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color)
1195 {
1196 RGBA32 rgba;
1197 if (!parseColorOrCurrentColor(rgba, color, canvas()))
1198 return;
1199 setShadow(FloatSize(width, height), blur, rgba);
1200 }
1201
1202 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel)
1203 {
1204 setShadow(FloatSize(width, height), blur, makeRGBA32FromFloats(grayLevel, gr ayLevel, grayLevel, 1));
1205 }
1206
1207 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color, float alpha)
1208 {
1209 RGBA32 rgba;
1210 if (!parseColorOrCurrentColor(rgba, color, canvas()))
1211 return;
1212 setShadow(FloatSize(width, height), blur, colorWithOverrideAlpha(rgba, alpha ));
1213 }
1214
1215 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel, float alpha)
1216 {
1217 setShadow(FloatSize(width, height), blur, makeRGBA32FromFloats(grayLevel, gr ayLevel, grayLevel, alpha));
1218 }
1219
1220 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float r, float g, float b, float a)
1221 {
1222 setShadow(FloatSize(width, height), blur, makeRGBA32FromFloats(r, g, b, a));
1223 }
1224
1225 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float c, float m, float y, float k, float a)
1226 {
1227 setShadow(FloatSize(width, height), blur, makeRGBAFromCMYKA(c, m, y, k, a));
1228 }
1229
1230 void CanvasRenderingContext2D::clearShadow()
1231 {
1232 setShadow(FloatSize(), 0, Color::transparent);
1233 }
1234
1235 void CanvasRenderingContext2D::setShadow(const FloatSize& offset, float blur, RG BA32 color)
1236 {
1237 if (state().m_shadowOffset == offset && state().m_shadowBlur == blur && stat e().m_shadowColor == color)
1238 return;
1239 bool wasDrawingShadows = shouldDrawShadows();
1240 realizeSaves(0);
1241 modifiableState().m_shadowOffset = offset;
1242 modifiableState().m_shadowBlur = blur;
1243 modifiableState().m_shadowColor = color;
1244 if (!wasDrawingShadows && !shouldDrawShadows())
1245 return;
1246 applyShadow();
1247 }
1248
1249 void CanvasRenderingContext2D::applyShadow()
1250 {
1251 GraphicsContext* c = drawingContext();
1252 if (!c)
1253 return;
1254
1255 if (shouldDrawShadows()) {
1256 c->setShadow(state().m_shadowOffset, state().m_shadowBlur, state().m_sha dowColor,
1257 DrawLooperBuilder::ShadowIgnoresTransforms);
1258 } else {
1259 c->clearShadow();
1260 }
1261 }
1262
1263 bool CanvasRenderingContext2D::shouldDrawShadows() const
1264 {
1265 return alphaChannel(state().m_shadowColor) && (state().m_shadowBlur || !stat e().m_shadowOffset.isZero());
1266 }
1267
1268 static inline FloatRect normalizeRect(const FloatRect& rect)
1269 {
1270 return FloatRect(std::min(rect.x(), rect.maxX()),
1271 std::min(rect.y(), rect.maxY()),
1272 std::max(rect.width(), -rect.width()),
1273 std::max(rect.height(), -rect.height()));
1274 }
1275
1276 static inline void clipRectsToImageRect(const FloatRect& imageRect, FloatRect* s rcRect, FloatRect* dstRect)
1277 {
1278 if (imageRect.contains(*srcRect))
1279 return;
1280
1281 // Compute the src to dst transform
1282 FloatSize scale(dstRect->size().width() / srcRect->size().width(), dstRect-> size().height() / srcRect->size().height());
1283 FloatPoint scaledSrcLocation = srcRect->location();
1284 scaledSrcLocation.scale(scale.width(), scale.height());
1285 FloatSize offset = dstRect->location() - scaledSrcLocation;
1286
1287 srcRect->intersect(imageRect);
1288
1289 // To clip the destination rectangle in the same proportion, transform the c lipped src rect
1290 *dstRect = *srcRect;
1291 dstRect->scale(scale.width(), scale.height());
1292 dstRect->move(offset);
1293 }
1294
1295 void CanvasRenderingContext2D::drawImage(CanvasImageSource* imageSource, float x , float y, ExceptionState& exceptionState)
1296 {
1297 FloatSize destRectSize = imageSource->defaultDestinationSize();
1298 drawImage(imageSource, x, y, destRectSize.width(), destRectSize.height(), ex ceptionState);
1299 }
1300
1301 void CanvasRenderingContext2D::drawImage(CanvasImageSource* imageSource,
1302 float x, float y, float width, float height, ExceptionState& exceptionState)
1303 {
1304 FloatSize sourceRectSize = imageSource->sourceSize();
1305 drawImage(imageSource, 0, 0, sourceRectSize.width(), sourceRectSize.height() , x, y, width, height, exceptionState);
1306 }
1307
1308 void CanvasRenderingContext2D::drawImage(CanvasImageSource* imageSource,
1309 float sx, float sy, float sw, float sh,
1310 float dx, float dy, float dw, float dh, ExceptionState& exceptionState)
1311 {
1312 GraphicsContext* c = drawingContext(); // Do not exit yet if !c because we m ay need to throw exceptions first
1313 CompositeOperator op = c ? c->compositeOperation() : CompositeSourceOver;
1314 blink::WebBlendMode blendMode = c ? c->blendModeOperation() : blink::WebBlen dModeNormal;
1315 drawImageInternal(imageSource, sx, sy, sw, sh, dx, dy, dw, dh, exceptionStat e, op, blendMode, c);
1316 }
1317
1318 void CanvasRenderingContext2D::drawImageInternal(CanvasImageSource* imageSource,
1319 float sx, float sy, float sw, float sh,
1320 float dx, float dy, float dw, float dh, ExceptionState& exceptionState,
1321 CompositeOperator op, blink::WebBlendMode blendMode, GraphicsContext* c)
1322 {
1323 RefPtr<Image> image;
1324 SourceImageStatus sourceImageStatus = InvalidSourceImageStatus;
1325 if (!imageSource->isVideoElement()) {
1326 SourceImageMode mode = canvas() == imageSource ? CopySourceImageIfVolati le : DontCopySourceImage; // Thunking for ==
1327 image = imageSource->getSourceImageForCanvas(mode, &sourceImageStatus);
1328 if (sourceImageStatus == UndecodableSourceImageStatus)
1329 exceptionState.ThrowDOMException(InvalidStateError, "The HTMLImageEl ement provided is in the 'broken' state.");
1330 if (!image || !image->width() || !image->height())
1331 return;
1332 }
1333
1334 if (!c)
1335 c = drawingContext();
1336 if (!c)
1337 return;
1338
1339 if (!state().m_invertibleCTM)
1340 return;
1341
1342 if (!std::isfinite(dx) || !std::isfinite(dy) || !std::isfinite(dw) || !std:: isfinite(dh)
1343 || !std::isfinite(sx) || !std::isfinite(sy) || !std::isfinite(sw) || !st d::isfinite(sh)
1344 || !dw || !dh || !sw || !sh)
1345 return;
1346
1347 FloatRect clipBounds;
1348 if (!c->getTransformedClipBounds(&clipBounds))
1349 return;
1350
1351 FloatRect srcRect = normalizeRect(FloatRect(sx, sy, sw, sh));
1352 FloatRect dstRect = normalizeRect(FloatRect(dx, dy, dw, dh));
1353
1354 clipRectsToImageRect(FloatRect(FloatPoint(), imageSource->sourceSize()), &sr cRect, &dstRect);
1355
1356 imageSource->adjustDrawRects(&srcRect, &dstRect);
1357
1358 if (srcRect.isEmpty())
1359 return;
1360
1361 FloatRect dirtyRect = clipBounds;
1362 if (rectContainsTransformedRect(dstRect, clipBounds)) {
1363 c->drawImage(image.get(), dstRect, srcRect, op, blendMode);
1364 } else {
1365 FloatRect dirtyRect;
1366 computeDirtyRect(dstRect, clipBounds, &dirtyRect);
1367 c->drawImage(image.get(), dstRect, srcRect, op, blendMode);
1368 }
1369
1370 if (sourceImageStatus == ExternalSourceImageStatus && isAccelerated() && can vas()->buffer())
1371 canvas()->buffer()->flush();
1372
1373 didDraw(dirtyRect);
1374 }
1375
1376 void CanvasRenderingContext2D::drawImageFromRect(HTMLImageElement* image,
1377 float sx, float sy, float sw, float sh,
1378 float dx, float dy, float dw, float dh,
1379 const String& compositeOperation)
1380 {
1381 if (!image)
1382 return;
1383 CompositeOperator op;
1384 blink::WebBlendMode blendOp = blink::WebBlendModeNormal;
1385 if (!parseCompositeAndBlendOperator(compositeOperation, op, blendOp) || blen dOp != blink::WebBlendModeNormal)
1386 op = CompositeSourceOver;
1387
1388 drawImageInternal(image, sx, sy, sw, sh, dx, dy, dw, dh, IGNORE_EXCEPTION, o p, blendOp);
1389 }
1390
1391 void CanvasRenderingContext2D::clearCanvas()
1392 {
1393 FloatRect canvasRect(0, 0, canvas()->width(), canvas()->height());
1394 GraphicsContext* c = drawingContext();
1395 if (!c)
1396 return;
1397
1398 c->save();
1399 c->setCTM(canvas()->baseTransform());
1400 c->clearRect(canvasRect);
1401 c->restore();
1402 }
1403
1404 bool CanvasRenderingContext2D::rectContainsTransformedRect(const FloatRect& rect , const FloatRect& transformedRect) const
1405 {
1406 FloatQuad quad(rect);
1407 FloatQuad transformedQuad(transformedRect);
1408 return state().m_transform.mapQuad(quad).containsQuad(transformedQuad);
1409 }
1410
1411 PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createLinearGradient(float x0, float y0, float x1, float y1)
1412 {
1413 RefPtr<CanvasGradient> gradient = CanvasGradient::create(FloatPoint(x0, y0), FloatPoint(x1, y1));
1414 return gradient.release();
1415 }
1416
1417 PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createRadialGradient(float x0, float y0, float r0, float x1, float y1, float r1, ExceptionState& exceptionS tate)
1418 {
1419 if (r0 < 0 || r1 < 0) {
1420 exceptionState.ThrowDOMException(IndexSizeError, String::format("The %s provided is less than 0.", r0 < 0 ? "r0" : "r1"));
1421 return nullptr;
1422 }
1423
1424 RefPtr<CanvasGradient> gradient = CanvasGradient::create(FloatPoint(x0, y0), r0, FloatPoint(x1, y1), r1);
1425 return gradient.release();
1426 }
1427
1428 PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(CanvasImageSou rce* imageSource,
1429 const String& repetitionType, ExceptionState& exceptionState)
1430 {
1431 Pattern::RepeatMode repeatMode = CanvasPattern::parseRepetitionType(repetiti onType, exceptionState);
1432 if (exceptionState.had_exception())
1433 return nullptr;
1434
1435 SourceImageStatus status;
1436 RefPtr<Image> imageForRendering = imageSource->getSourceImageForCanvas(CopyS ourceImageIfVolatile, &status);
1437
1438 switch (status) {
1439 case NormalSourceImageStatus:
1440 break;
1441 case ZeroSizeCanvasSourceImageStatus:
1442 exceptionState.ThrowDOMException(InvalidStateError, String::format("The canvas %s is 0.", imageSource->sourceSize().width() ? "height" : "width"));
1443 return nullptr;
1444 case UndecodableSourceImageStatus:
1445 exceptionState.ThrowDOMException(InvalidStateError, "Source image is in the 'broken' state.");
1446 return nullptr;
1447 case InvalidSourceImageStatus:
1448 imageForRendering = Image::nullImage();
1449 break;
1450 case IncompleteSourceImageStatus:
1451 return nullptr;
1452 default:
1453 case ExternalSourceImageStatus: // should not happen when mode is CopySource ImageIfVolatile
1454 ASSERT_NOT_REACHED();
1455 return nullptr;
1456 }
1457 ASSERT(imageForRendering);
1458
1459 return CanvasPattern::create(imageForRendering.release(), repeatMode);
1460 }
1461
1462 bool CanvasRenderingContext2D::computeDirtyRect(const FloatRect& localRect, Floa tRect* dirtyRect)
1463 {
1464 FloatRect clipBounds;
1465 if (!drawingContext()->getTransformedClipBounds(&clipBounds))
1466 return false;
1467 return computeDirtyRect(localRect, clipBounds, dirtyRect);
1468 }
1469
1470 bool CanvasRenderingContext2D::computeDirtyRect(const FloatRect& localRect, cons t FloatRect& transformedClipBounds, FloatRect* dirtyRect)
1471 {
1472 FloatRect canvasRect = state().m_transform.mapRect(localRect);
1473
1474 if (alphaChannel(state().m_shadowColor)) {
1475 FloatRect shadowRect(canvasRect);
1476 shadowRect.move(state().m_shadowOffset);
1477 shadowRect.inflate(state().m_shadowBlur);
1478 canvasRect.unite(shadowRect);
1479 }
1480
1481 canvasRect.intersect(transformedClipBounds);
1482 if (canvasRect.isEmpty())
1483 return false;
1484
1485 if (dirtyRect)
1486 *dirtyRect = canvasRect;
1487
1488 return true;
1489 }
1490
1491 void CanvasRenderingContext2D::didDraw(const FloatRect& dirtyRect)
1492 {
1493 if (dirtyRect.isEmpty())
1494 return;
1495
1496 canvas()->didDraw(dirtyRect);
1497 }
1498
1499 GraphicsContext* CanvasRenderingContext2D::drawingContext() const
1500 {
1501 if (isContextLost())
1502 return 0;
1503 return canvas()->drawingContext();
1504 }
1505
1506 static PassRefPtr<ImageData> createEmptyImageData(const IntSize& size)
1507 {
1508 if (RefPtr<ImageData> data = ImageData::create(size)) {
1509 data->data()->zeroFill();
1510 return data.release();
1511 }
1512
1513 return nullptr;
1514 }
1515
1516 PassRefPtr<ImageData> CanvasRenderingContext2D::createImageData(PassRefPtr<Image Data> imageData) const
1517 {
1518 return createEmptyImageData(imageData->size());
1519 }
1520
1521 PassRefPtr<ImageData> CanvasRenderingContext2D::createImageData(float sw, float sh, ExceptionState& exceptionState) const
1522 {
1523 if (!sw || !sh) {
1524 exceptionState.ThrowDOMException(IndexSizeError, String::format("The sou rce %s is 0.", sw ? "height" : "width"));
1525 return nullptr;
1526 }
1527
1528 FloatSize logicalSize(fabs(sw), fabs(sh));
1529 if (!logicalSize.isExpressibleAsIntSize())
1530 return nullptr;
1531
1532 IntSize size = expandedIntSize(logicalSize);
1533 if (size.width() < 1)
1534 size.setWidth(1);
1535 if (size.height() < 1)
1536 size.setHeight(1);
1537
1538 return createEmptyImageData(size);
1539 }
1540
1541 PassRefPtr<ImageData> CanvasRenderingContext2D::getImageData(float sx, float sy, float sw, float sh, ExceptionState& exceptionState) const
1542 {
1543 if (!sw || !sh)
1544 exceptionState.ThrowDOMException(IndexSizeError, String::format("The sou rce %s is 0.", sw ? "height" : "width"));
1545
1546 if (exceptionState.had_exception())
1547 return nullptr;
1548
1549 if (sw < 0) {
1550 sx += sw;
1551 sw = -sw;
1552 }
1553 if (sh < 0) {
1554 sy += sh;
1555 sh = -sh;
1556 }
1557
1558 FloatRect logicalRect(sx, sy, sw, sh);
1559 if (logicalRect.width() < 1)
1560 logicalRect.setWidth(1);
1561 if (logicalRect.height() < 1)
1562 logicalRect.setHeight(1);
1563 if (!logicalRect.isExpressibleAsIntRect())
1564 return nullptr;
1565
1566 IntRect imageDataRect = enclosingIntRect(logicalRect);
1567 ImageBuffer* buffer = canvas()->buffer();
1568 if (!buffer || isContextLost())
1569 return createEmptyImageData(imageDataRect.size());
1570
1571 RefPtr<Uint8ClampedArray> byteArray = buffer->getImageData(Unmultiplied, ima geDataRect);
1572 if (!byteArray)
1573 return nullptr;
1574
1575 return ImageData::create(imageDataRect.size(), byteArray.release());
1576 }
1577
1578 void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy)
1579 {
1580 putImageData(data, dx, dy, 0, 0, data->width(), data->height());
1581 }
1582
1583 void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, float dirtyX, float dirtyY, float dirtyWidth, float dirtyHeight)
1584 {
1585 ImageBuffer* buffer = canvas()->buffer();
1586 if (!buffer)
1587 return;
1588
1589 if (dirtyWidth < 0) {
1590 dirtyX += dirtyWidth;
1591 dirtyWidth = -dirtyWidth;
1592 }
1593
1594 if (dirtyHeight < 0) {
1595 dirtyY += dirtyHeight;
1596 dirtyHeight = -dirtyHeight;
1597 }
1598
1599 FloatRect clipRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
1600 clipRect.intersect(IntRect(0, 0, data->width(), data->height()));
1601 IntSize destOffset(static_cast<int>(dx), static_cast<int>(dy));
1602 IntRect destRect = enclosingIntRect(clipRect);
1603 destRect.move(destOffset);
1604 destRect.intersect(IntRect(IntPoint(), buffer->size()));
1605 if (destRect.isEmpty())
1606 return;
1607 IntRect sourceRect(destRect);
1608 sourceRect.move(-destOffset);
1609
1610 buffer->putByteArray(Unmultiplied, data->data(), IntSize(data->width(), data ->height()), sourceRect, IntPoint(destOffset));
1611
1612 didDraw(destRect);
1613 }
1614
1615 String CanvasRenderingContext2D::font() const
1616 {
1617 if (!state().m_realizedFont)
1618 return defaultFont;
1619
1620 StringBuilder serializedFont;
1621 const FontDescription& fontDescription = state().m_font.fontDescription();
1622
1623 if (fontDescription.style() == FontStyleItalic)
1624 serializedFont.appendLiteral("italic ");
1625 if (fontDescription.weight() == FontWeightBold)
1626 serializedFont.appendLiteral("bold ");
1627 if (fontDescription.variant() == FontVariantSmallCaps)
1628 serializedFont.appendLiteral("small-caps ");
1629
1630 serializedFont.appendNumber(fontDescription.computedPixelSize());
1631 serializedFont.appendLiteral("px");
1632
1633 const FontFamily& firstFontFamily = fontDescription.family();
1634 for (const FontFamily* fontFamily = &firstFontFamily; fontFamily; fontFamily = fontFamily->next()) {
1635 if (fontFamily != &firstFontFamily)
1636 serializedFont.append(',');
1637
1638 // FIXME: We should append family directly to serializedFont rather than building a temporary string.
1639 String family = fontFamily->family();
1640 if (family.startsWith("-webkit-"))
1641 family = family.substring(8);
1642 if (family.contains(' '))
1643 family = "\"" + family + "\"";
1644
1645 serializedFont.append(' ');
1646 serializedFont.append(family);
1647 }
1648
1649 return serializedFont.toString();
1650 }
1651
1652 void CanvasRenderingContext2D::setFont(const String& newFont)
1653 {
1654 // The style resolution required for rendering text is not available in fram e-less documents.
1655 if (!canvas()->document().frame())
1656 return;
1657
1658 MutableStylePropertyMap::iterator i = m_fetchedFonts.find(newFont);
1659 RefPtr<MutableStylePropertySet> parsedStyle = i != m_fetchedFonts.end() ? i- >value : nullptr;
1660
1661 if (!parsedStyle) {
1662 parsedStyle = MutableStylePropertySet::create();
1663 BisonCSSParser::parseValue(parsedStyle.get(), CSSPropertyFont, newFont, HTMLStandardMode, 0);
1664 m_fetchedFonts.add(newFont, parsedStyle);
1665 }
1666 if (parsedStyle->isEmpty())
1667 return;
1668
1669 String fontValue = parsedStyle->getPropertyValue(CSSPropertyFont);
1670
1671 // According to http://lists.w3.org/Archives/Public/public-html/2009Jul/0947 .html,
1672 // the "inherit" and "initial" values must be ignored.
1673 if (fontValue == "inherit" || fontValue == "initial")
1674 return;
1675
1676 // The parse succeeded.
1677 String newFontSafeCopy(newFont); // Create a string copy since newFont can b e deleted inside realizeSaves.
1678 realizeSaves(0);
1679 modifiableState().m_unparsedFont = newFontSafeCopy;
1680
1681 // Map the <canvas> font into the text style. If the font uses keywords like larger/smaller, these will work
1682 // relative to the canvas.
1683 RefPtr<RenderStyle> newStyle = RenderStyle::create();
1684 if (RenderStyle* computedStyle = canvas()->computedStyle()) {
1685 FontDescription elementFontDescription(computedStyle->fontDescription()) ;
1686 // Reset the computed size to avoid inheriting the zoom factor from the <canvas> element.
1687 elementFontDescription.setComputedSize(elementFontDescription.specifiedS ize());
1688 newStyle->setFontDescription(elementFontDescription);
1689 } else {
1690 FontFamily fontFamily;
1691 fontFamily.setFamily(defaultFontFamily);
1692
1693 FontDescription defaultFontDescription;
1694 defaultFontDescription.setFamily(fontFamily);
1695 defaultFontDescription.setSpecifiedSize(defaultFontSize);
1696 defaultFontDescription.setComputedSize(defaultFontSize);
1697
1698 newStyle->setFontDescription(defaultFontDescription);
1699 }
1700
1701 newStyle->font().update(newStyle->font().fontSelector());
1702
1703 // Now map the font property longhands into the style.
1704 CSSPropertyValue properties[] = {
1705 CSSPropertyValue(CSSPropertyFontFamily, *parsedStyle),
1706 CSSPropertyValue(CSSPropertyFontStyle, *parsedStyle),
1707 CSSPropertyValue(CSSPropertyFontVariant, *parsedStyle),
1708 CSSPropertyValue(CSSPropertyFontWeight, *parsedStyle),
1709 CSSPropertyValue(CSSPropertyFontSize, *parsedStyle),
1710 CSSPropertyValue(CSSPropertyLineHeight, *parsedStyle),
1711 };
1712
1713 StyleResolver& styleResolver = canvas()->document().styleResolver();
1714 styleResolver.applyPropertiesToStyle(properties, WTF_ARRAY_LENGTH(properties ), newStyle.get());
1715
1716 #if !ENABLE(OILPAN)
1717 if (state().m_realizedFont)
1718 static_cast<CSSFontSelector*>(state().m_font.fontSelector())->unregister ForInvalidationCallbacks(&modifiableState());
1719 #endif
1720 modifiableState().m_font = newStyle->font();
1721 modifiableState().m_font.update(canvas()->document().styleEngine()->fontSele ctor());
1722 modifiableState().m_realizedFont = true;
1723 canvas()->document().styleEngine()->fontSelector()->registerForInvalidationC allbacks(&modifiableState());
1724 }
1725
1726 String CanvasRenderingContext2D::textAlign() const
1727 {
1728 return textAlignName(state().m_textAlign);
1729 }
1730
1731 void CanvasRenderingContext2D::setTextAlign(const String& s)
1732 {
1733 TextAlign align;
1734 if (!parseTextAlign(s, align))
1735 return;
1736 if (state().m_textAlign == align)
1737 return;
1738 realizeSaves(0);
1739 modifiableState().m_textAlign = align;
1740 }
1741
1742 String CanvasRenderingContext2D::textBaseline() const
1743 {
1744 return textBaselineName(state().m_textBaseline);
1745 }
1746
1747 void CanvasRenderingContext2D::setTextBaseline(const String& s)
1748 {
1749 TextBaseline baseline;
1750 if (!parseTextBaseline(s, baseline))
1751 return;
1752 if (state().m_textBaseline == baseline)
1753 return;
1754 realizeSaves(0);
1755 modifiableState().m_textBaseline = baseline;
1756 }
1757
1758 inline TextDirection CanvasRenderingContext2D::toTextDirection(Direction directi on, RenderStyle** computedStyle) const
1759 {
1760 RenderStyle* style = (computedStyle || direction == DirectionInherit) ? canv as()->computedStyle() : nullptr;
1761 if (computedStyle)
1762 *computedStyle = style;
1763 switch (direction) {
1764 case DirectionInherit:
1765 return style ? style->direction() : LTR;
1766 case DirectionRTL:
1767 return RTL;
1768 case DirectionLTR:
1769 return LTR;
1770 }
1771 ASSERT_NOT_REACHED();
1772 return LTR;
1773 }
1774
1775 String CanvasRenderingContext2D::direction() const
1776 {
1777 if (state().m_direction == DirectionInherit)
1778 canvas()->document().updateRenderTreeIfNeeded();
1779 return toTextDirection(state().m_direction) == RTL ? rtl : ltr;
1780 }
1781
1782 void CanvasRenderingContext2D::setDirection(const String& directionString)
1783 {
1784 Direction direction;
1785 if (directionString == inherit)
1786 direction = DirectionInherit;
1787 else if (directionString == rtl)
1788 direction = DirectionRTL;
1789 else if (directionString == ltr)
1790 direction = DirectionLTR;
1791 else
1792 return;
1793
1794 if (state().m_direction == direction)
1795 return;
1796
1797 realizeSaves(0);
1798 modifiableState().m_direction = direction;
1799 }
1800
1801 void CanvasRenderingContext2D::fillText(const String& text, float x, float y)
1802 {
1803 drawTextInternal(text, x, y, true);
1804 }
1805
1806 void CanvasRenderingContext2D::fillText(const String& text, float x, float y, fl oat maxWidth)
1807 {
1808 drawTextInternal(text, x, y, true, maxWidth, true);
1809 }
1810
1811 void CanvasRenderingContext2D::strokeText(const String& text, float x, float y)
1812 {
1813 drawTextInternal(text, x, y, false);
1814 }
1815
1816 void CanvasRenderingContext2D::strokeText(const String& text, float x, float y, float maxWidth)
1817 {
1818 drawTextInternal(text, x, y, false, maxWidth, true);
1819 }
1820
1821 static inline bool isSpaceCharacter(UChar c)
1822 {
1823 // According to specification all space characters should be replaced with 0 x0020 space character.
1824 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-el ement.html#text-preparation-algorithm
1825 // The space characters according to specification are : U+0020, U+0009, U+0 00A, U+000C, and U+000D.
1826 // http://www.whatwg.org/specs/web-apps/current-work/multipage/common-micros yntaxes.html#space-character
1827 // This function returns true for 0x000B also, so that this is backward comp atible.
1828 // Otherwise, the test tests/canvas/philip/tests/2d.text.draw.space.collapse .space.html will fail
1829 return c == 0x0009 || c == 0x000A || c == 0x000B || c == 0x000C || c == 0x00 0D;
1830 }
1831
1832 static String normalizeSpaces(const String& text)
1833 {
1834 unsigned textLength = text.length();
1835 Vector<UChar> charVector(textLength);
1836
1837 for (unsigned i = 0; i < textLength; i++) {
1838 if (isSpaceCharacter(text[i]))
1839 charVector[i] = ' ';
1840 else
1841 charVector[i] = text[i];
1842 }
1843
1844 return String(charVector);
1845 }
1846
1847 PassRefPtr<TextMetrics> CanvasRenderingContext2D::measureText(const String& text )
1848 {
1849 RefPtr<TextMetrics> metrics = TextMetrics::create();
1850
1851 // The style resolution required for rendering text is not available in fram e-less documents.
1852 if (!canvas()->document().frame())
1853 return metrics.release();
1854
1855 FontCachePurgePreventer fontCachePurgePreventer;
1856 canvas()->document().updateRenderTreeIfNeeded();
1857 const Font& font = accessFont();
1858 String normalizedText = normalizeSpaces(text);
1859 const TextRun textRun(normalizedText);
1860 FloatRect textBounds = font.selectionRectForText(textRun, FloatPoint(), font .fontDescription().computedSize(), 0, -1, true);
1861
1862 // x direction
1863 metrics->setWidth(font.width(textRun));
1864 metrics->setActualBoundingBoxLeft(-textBounds.x());
1865 metrics->setActualBoundingBoxRight(textBounds.maxX());
1866
1867 // y direction
1868 const FontMetrics& fontMetrics = font.fontMetrics();
1869 const float ascent = fontMetrics.floatAscent();
1870 const float descent = fontMetrics.floatDescent();
1871 const float baselineY = getFontBaseline(fontMetrics);
1872
1873 metrics->setFontBoundingBoxAscent(ascent - baselineY);
1874 metrics->setFontBoundingBoxDescent(descent + baselineY);
1875 metrics->setActualBoundingBoxAscent(-textBounds.y() - baselineY);
1876 metrics->setActualBoundingBoxDescent(textBounds.maxY() + baselineY);
1877
1878 // Note : top/bottom and ascend/descend are currently the same, so there's n o difference
1879 // between the EM box's top and bottom and the font's ascend and desc end
1880 metrics->setEmHeightAscent(0);
1881 metrics->setEmHeightDescent(0);
1882
1883 metrics->setHangingBaseline(-0.8f * ascent + baselineY);
1884 metrics->setAlphabeticBaseline(baselineY);
1885 metrics->setIdeographicBaseline(descent + baselineY);
1886 return metrics.release();
1887 }
1888
1889 void CanvasRenderingContext2D::drawTextInternal(const String& text, float x, flo at y, bool fill, float maxWidth, bool useMaxWidth)
1890 {
1891 // The style resolution required for rendering text is not available in fram e-less documents.
1892 if (!canvas()->document().frame())
1893 return;
1894
1895 // accessFont needs the style to be up to date, but updating style can cause script to run,
1896 // which can free the GraphicsContext, so update style before grabbing the G raphicsContext.
1897 // TODO(esprehn): This isn't needed in sky.
1898 canvas()->document().updateRenderTreeIfNeeded();
1899
1900 GraphicsContext* c = drawingContext();
1901 if (!c)
1902 return;
1903 if (!state().m_invertibleCTM)
1904 return;
1905 if (!std::isfinite(x) | !std::isfinite(y))
1906 return;
1907 if (useMaxWidth && (!std::isfinite(maxWidth) || maxWidth <= 0))
1908 return;
1909
1910 // If gradient size is zero, then paint nothing.
1911 Gradient* gradient = c->strokeGradient();
1912 if (!fill && gradient && gradient->isZeroSize())
1913 return;
1914
1915 gradient = c->fillGradient();
1916 if (fill && gradient && gradient->isZeroSize())
1917 return;
1918
1919 FontCachePurgePreventer fontCachePurgePreventer;
1920
1921 const Font& font = accessFont();
1922 const FontMetrics& fontMetrics = font.fontMetrics();
1923 String normalizedText = normalizeSpaces(text);
1924
1925 // FIXME: Need to turn off font smoothing.
1926
1927 RenderStyle* computedStyle;
1928 TextDirection direction = toTextDirection(state().m_direction, &computedStyl e);
1929 bool isRTL = direction == RTL;
1930 bool override = computedStyle ? isOverride(computedStyle->unicodeBidi()) : f alse;
1931
1932 TextRun textRun(normalizedText, 0, 0, TextRun::AllowTrailingExpansion, direc tion, override, true);
1933 // Draw the item text at the correct point.
1934 FloatPoint location(x, y + getFontBaseline(fontMetrics));
1935
1936 float fontWidth = font.width(TextRun(normalizedText, 0, 0, TextRun::AllowTra ilingExpansion, direction, override));
1937
1938 useMaxWidth = (useMaxWidth && maxWidth < fontWidth);
1939 float width = useMaxWidth ? maxWidth : fontWidth;
1940
1941 TextAlign align = state().m_textAlign;
1942 if (align == StartTextAlign)
1943 align = isRTL ? RightTextAlign : LeftTextAlign;
1944 else if (align == EndTextAlign)
1945 align = isRTL ? LeftTextAlign : RightTextAlign;
1946
1947 switch (align) {
1948 case CenterTextAlign:
1949 location.setX(location.x() - width / 2);
1950 break;
1951 case RightTextAlign:
1952 location.setX(location.x() - width);
1953 break;
1954 default:
1955 break;
1956 }
1957
1958 // The slop built in to this mask rect matches the heuristic used in FontCGW in.cpp for GDI text.
1959 TextRunPaintInfo textRunPaintInfo(textRun);
1960 textRunPaintInfo.bounds = FloatRect(location.x() - fontMetrics.height() / 2,
1961 location.y() - fontMetrics.ascent() - fo ntMetrics.lineGap(),
1962 width + fontMetrics.height(),
1963 fontMetrics.lineSpacing());
1964 if (!fill)
1965 inflateStrokeRect(textRunPaintInfo.bounds);
1966
1967 c->setTextDrawingMode(fill ? TextModeFill : TextModeStroke);
1968
1969 GraphicsContextStateSaver stateSaver(*c);
1970 if (useMaxWidth) {
1971 c->translate(location.x(), location.y());
1972 // We draw when fontWidth is 0 so compositing operations (eg, a "copy" o p) still work.
1973 c->scale((fontWidth > 0 ? (width / fontWidth) : 0), 1);
1974 location = FloatPoint();
1975 }
1976
1977 FloatRect clipBounds;
1978 if (!c->getTransformedClipBounds(&clipBounds)) {
1979 return;
1980 }
1981
1982 FloatRect dirtyRect;
1983 if (computeDirtyRect(textRunPaintInfo.bounds, clipBounds, &dirtyRect)) {
1984 c->drawBidiText(font, textRunPaintInfo, location, Font::UseFallbackIfFon tNotReady);
1985 didDraw(dirtyRect);
1986 }
1987 }
1988
1989 void CanvasRenderingContext2D::inflateStrokeRect(FloatRect& rect) const
1990 {
1991 // Fast approximation of the stroke's bounding rect.
1992 // This yields a slightly oversized rect but is very fast
1993 // compared to Path::strokeBoundingRect().
1994 static const float root2 = sqrtf(2);
1995 float delta = state().m_lineWidth / 2;
1996 if (state().m_lineJoin == MiterJoin)
1997 delta *= state().m_miterLimit;
1998 else if (state().m_lineCap == SquareCap)
1999 delta *= root2;
2000
2001 rect.inflate(delta);
2002 }
2003
2004 const Font& CanvasRenderingContext2D::accessFont()
2005 {
2006 // This needs style to be up to date, but can't assert so because drawTextIn ternal
2007 // can invalidate style before this is called (e.g. drawingContext invalidat es style).
2008 if (!state().m_realizedFont)
2009 setFont(state().m_unparsedFont);
2010 return state().m_font;
2011 }
2012
2013 int CanvasRenderingContext2D::getFontBaseline(const FontMetrics& fontMetrics) co nst
2014 {
2015 switch (state().m_textBaseline) {
2016 case TopTextBaseline:
2017 return fontMetrics.ascent();
2018 case HangingTextBaseline:
2019 // According to http://wiki.apache.org/xmlgraphics-fop/LineLayout/Alignm entHandling
2020 // "FOP (Formatting Objects Processor) puts the hanging baseline at 80% of the ascender height"
2021 return (fontMetrics.ascent() * 4) / 5;
2022 case BottomTextBaseline:
2023 case IdeographicTextBaseline:
2024 return -fontMetrics.descent();
2025 case MiddleTextBaseline:
2026 return -fontMetrics.descent() + fontMetrics.height() / 2;
2027 case AlphabeticTextBaseline:
2028 default:
2029 // Do nothing.
2030 break;
2031 }
2032 return 0;
2033 }
2034
2035 void CanvasRenderingContext2D::setIsHidden(bool hidden)
2036 {
2037 ImageBuffer* buffer = canvas()->buffer();
2038 if (buffer)
2039 buffer->setIsHidden(hidden);
2040 }
2041
2042 blink::WebLayer* CanvasRenderingContext2D::platformLayer() const
2043 {
2044 return canvas()->buffer() ? canvas()->buffer()->platformLayer() : 0;
2045 }
2046
2047 bool CanvasRenderingContext2D::imageSmoothingEnabled() const
2048 {
2049 return state().m_imageSmoothingEnabled;
2050 }
2051
2052 void CanvasRenderingContext2D::setImageSmoothingEnabled(bool enabled)
2053 {
2054 if (enabled == state().m_imageSmoothingEnabled)
2055 return;
2056
2057 GraphicsContext* c = drawingContext();
2058 realizeSaves(c);
2059 modifiableState().m_imageSmoothingEnabled = enabled;
2060 if (c)
2061 c->setImageInterpolationQuality(enabled ? CanvasDefaultInterpolationQual ity : InterpolationNone);
2062 }
2063
2064 PassRefPtr<Canvas2DContextAttributes> CanvasRenderingContext2D::getContextAttrib utes() const
2065 {
2066 RefPtr<Canvas2DContextAttributes> attributes = Canvas2DContextAttributes::cr eate();
2067 return attributes.release();
2068 }
2069
2070 void CanvasRenderingContext2D::drawFocusIfNeeded(Element* element)
2071 {
2072 drawFocusIfNeededInternal(m_path, element);
2073 }
2074
2075 void CanvasRenderingContext2D::drawFocusIfNeeded(Path2D* path2d, Element* elemen t)
2076 {
2077 drawFocusIfNeededInternal(path2d->path(), element);
2078 }
2079
2080 void CanvasRenderingContext2D::drawFocusIfNeededInternal(const Path& path, Eleme nt* element)
2081 {
2082 if (!focusRingCallIsValid(path, element))
2083 return;
2084
2085 // Note: we need to check document->focusedElement() rather than just callin g
2086 // element->focused(), because element->focused() isn't updated until after
2087 // focus events fire.
2088 if (element->document().focusedElement() == element)
2089 drawFocusRing(path);
2090 }
2091
2092 bool CanvasRenderingContext2D::focusRingCallIsValid(const Path& path, Element* e lement)
2093 {
2094 ASSERT(element);
2095 if (!state().m_invertibleCTM)
2096 return false;
2097 if (path.isEmpty())
2098 return false;
2099 if (!element->isDescendantOf(canvas()))
2100 return false;
2101
2102 return true;
2103 }
2104
2105 void CanvasRenderingContext2D::drawFocusRing(const Path& path)
2106 {
2107 GraphicsContext* c = drawingContext();
2108 if (!c)
2109 return;
2110
2111 // These should match the style defined in html.css.
2112 Color focusRingColor = RenderTheme::theme().focusRingColor();
2113 const int focusRingWidth = 5;
2114 const int focusRingOutline = 0;
2115
2116 // We need to add focusRingWidth to dirtyRect.
2117 StrokeData strokeData;
2118 strokeData.setThickness(focusRingWidth);
2119
2120 FloatRect dirtyRect;
2121 if (!computeDirtyRect(path.strokeBoundingRect(strokeData), &dirtyRect))
2122 return;
2123
2124 c->save();
2125 c->setAlphaAsFloat(1.0);
2126 c->clearShadow();
2127 c->setCompositeOperation(CompositeSourceOver, blink::WebBlendModeNormal);
2128 c->drawFocusRing(path, focusRingWidth, focusRingOutline, focusRingColor);
2129 c->restore();
2130 validateStateStack();
2131 didDraw(dirtyRect);
2132 }
2133
2134 void CanvasRenderingContext2D::addHitRegion(ExceptionState& exceptionState)
2135 {
2136 HitRegionOptions passOptions;
2137
2138 RefPtr<Path2D> path2d;
2139 Path hitRegionPath = path2d ? path2d->path() : m_path;
2140
2141 FloatRect clipBounds;
2142 GraphicsContext* context = drawingContext();
2143
2144 if (hitRegionPath.isEmpty() || !context || !state().m_invertibleCTM
2145 || !context->getTransformedClipBounds(&clipBounds)) {
2146 exceptionState.ThrowDOMException(NotSupportedError, "The specified path has no pixels.");
2147 return;
2148 }
2149
2150 hitRegionPath.transform(state().m_transform);
2151
2152 if (hasClip()) {
2153 // FIXME: The hit regions should take clipping region into account.
2154 // However, we have no way to get the region from canvas state stack by now.
2155 // See http://crbug.com/387057
2156 exceptionState.ThrowDOMException(NotSupportedError, "The specified path has no pixels.");
2157 return;
2158 }
2159
2160 passOptions.path = hitRegionPath;
2161
2162 // FIXME(Dictionary): Way to specify fillRule
2163 passOptions.fillRule = RULE_NONZERO;
2164
2165 addHitRegionInternal(passOptions, exceptionState);
2166 }
2167
2168 void CanvasRenderingContext2D::addHitRegionInternal(const HitRegionOptions& opti ons, ExceptionState& exceptionState)
2169 {
2170 if (!m_hitRegionManager)
2171 m_hitRegionManager = HitRegionManager::create();
2172
2173 // Remove previous region (with id or control)
2174 m_hitRegionManager->removeHitRegionById(options.id);
2175 m_hitRegionManager->removeHitRegionByControl(options.control.get());
2176
2177 RefPtr<HitRegion> hitRegion = HitRegion::create(options);
2178 hitRegion->updateAccessibility(canvas());
2179 m_hitRegionManager->addHitRegion(hitRegion.release());
2180 }
2181
2182 void CanvasRenderingContext2D::removeHitRegion(const String& id)
2183 {
2184 if (m_hitRegionManager)
2185 m_hitRegionManager->removeHitRegionById(id);
2186 }
2187
2188 void CanvasRenderingContext2D::clearHitRegions()
2189 {
2190 if (m_hitRegionManager)
2191 m_hitRegionManager->removeAllHitRegions();
2192 }
2193
2194 HitRegion* CanvasRenderingContext2D::hitRegionAtPoint(const LayoutPoint& point)
2195 {
2196 if (m_hitRegionManager)
2197 return m_hitRegionManager->getHitRegionAtPoint(point);
2198
2199 return 0;
2200 }
2201
2202 unsigned CanvasRenderingContext2D::hitRegionsCount() const
2203 {
2204 if (m_hitRegionManager)
2205 return m_hitRegionManager->getHitRegionsCount();
2206
2207 return 0;
2208 }
2209
2210 } // namespace blink
OLDNEW
« no previous file with comments | « sky/engine/core/html/canvas/CanvasRenderingContext2D.h ('k') | sky/engine/core/html/canvas/CanvasRenderingContext2D.idl » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698