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