OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. | |
3 * Copyright (C) 2007 Trolltech ASA | |
4 * Copyright (C) 2007 Alp Toker <alp@atoker.com> | |
5 * Copyright (C) 2008 Eric Seidel <eric@webkit.org> | |
6 * | |
7 * Redistribution and use in source and binary forms, with or without | |
8 * modification, are permitted provided that the following conditions | |
9 * are met: | |
10 * 1. Redistributions of source code must retain the above copyright | |
11 * notice, this list of conditions and the following disclaimer. | |
12 * 2. Redistributions in binary form must reproduce the above copyright | |
13 * notice, this list of conditions and the following disclaimer in the | |
14 * documentation and/or other materials provided with the distribution. | |
15 * | |
16 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY | |
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR | |
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
24 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
27 */ | |
28 | |
29 #include "config.h" | |
30 #include "CanvasRenderingContext2D.h" | |
31 | |
32 #include "AffineTransform.h" | |
33 #include "CSSParser.h" | |
34 #include "CachedImage.h" | |
35 #include "CanvasGradient.h" | |
36 #include "CanvasPattern.h" | |
37 #include "CanvasPixelArray.h" | |
38 #include "CanvasStyle.h" | |
39 #include "CSSPropertyNames.h" | |
40 #include "CSSStyleSelector.h" | |
41 #include "Document.h" | |
42 #include "ExceptionCode.h" | |
43 #include "FloatConversion.h" | |
44 #include "Frame.h" | |
45 #include "GraphicsContext.h" | |
46 #include "HTMLCanvasElement.h" | |
47 #include "HTMLImageElement.h" | |
48 #include "HTMLNames.h" | |
49 #include "ImageBuffer.h" | |
50 #include "ImageData.h" | |
51 #include "KURL.h" | |
52 #include "NotImplemented.h" | |
53 #include "Page.h" | |
54 #include "RenderHTMLCanvas.h" | |
55 #include "SecurityOrigin.h" | |
56 #include "Settings.h" | |
57 #include "TextMetrics.h" | |
58 #include <kjs/interpreter.h> | |
59 #include <stdio.h> | |
60 #include <wtf/MathExtras.h> | |
61 | |
62 using namespace std; | |
63 | |
64 namespace WebCore { | |
65 | |
66 using namespace HTMLNames; | |
67 | |
68 const char* defaultFont = "10px sans-serif"; | |
69 | |
70 CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement* canvas) | |
71 : m_canvas(canvas) | |
72 , m_stateStack(1) | |
73 #if USE(V8) | |
74 , m_peer(0) | |
75 #endif | |
76 { | |
77 } | |
78 | |
79 void CanvasRenderingContext2D::ref() | |
80 { | |
81 m_canvas->ref(); | |
82 } | |
83 | |
84 void CanvasRenderingContext2D::deref() | |
85 { | |
86 m_canvas->deref(); | |
87 } | |
88 | |
89 void CanvasRenderingContext2D::reset() | |
90 { | |
91 m_stateStack.resize(1); | |
92 m_stateStack.first() = State(); | |
93 } | |
94 | |
95 CanvasRenderingContext2D::State::State() | |
96 : m_strokeStyle(CanvasStyle::create("black")) | |
97 , m_fillStyle(CanvasStyle::create("black")) | |
98 , m_lineWidth(1) | |
99 , m_lineCap(ButtCap) | |
100 , m_lineJoin(MiterJoin) | |
101 , m_miterLimit(10) | |
102 , m_shadowBlur(0) | |
103 , m_shadowColor("black") | |
104 , m_globalAlpha(1) | |
105 , m_globalComposite(CompositeSourceOver) | |
106 , m_textAlign(StartTextAlign) | |
107 , m_textBaseline(AlphabeticTextBaseline) | |
108 , m_unparsedFont(defaultFont) | |
109 , m_realizedFont(false) | |
110 { | |
111 } | |
112 | |
113 void CanvasRenderingContext2D::save() | |
114 { | |
115 ASSERT(m_stateStack.size() >= 1); | |
116 m_stateStack.append(state()); | |
117 GraphicsContext* c = drawingContext(); | |
118 if (!c) | |
119 return; | |
120 c->save(); | |
121 } | |
122 | |
123 void CanvasRenderingContext2D::restore() | |
124 { | |
125 ASSERT(m_stateStack.size() >= 1); | |
126 if (m_stateStack.size() <= 1) | |
127 return; | |
128 m_path.transform(state().m_transform); | |
129 m_stateStack.removeLast(); | |
130 m_path.transform(state().m_transform.inverse()); | |
131 GraphicsContext* c = drawingContext(); | |
132 if (!c) | |
133 return; | |
134 c->restore(); | |
135 } | |
136 | |
137 CanvasStyle* CanvasRenderingContext2D::strokeStyle() const | |
138 { | |
139 return state().m_strokeStyle.get(); | |
140 } | |
141 | |
142 void CanvasRenderingContext2D::setStrokeStyle(PassRefPtr<CanvasStyle> style) | |
143 { | |
144 if (!style) | |
145 return; | |
146 | |
147 if (m_canvas->originClean()) { | |
148 if (CanvasPattern* pattern = style->canvasPattern()) { | |
149 if (!pattern->originClean()) | |
150 m_canvas->setOriginTainted(); | |
151 } | |
152 } | |
153 | |
154 state().m_strokeStyle = style; | |
155 GraphicsContext* c = drawingContext(); | |
156 if (!c) | |
157 return; | |
158 state().m_strokeStyle->applyStrokeColor(c); | |
159 } | |
160 | |
161 CanvasStyle* CanvasRenderingContext2D::fillStyle() const | |
162 { | |
163 return state().m_fillStyle.get(); | |
164 } | |
165 | |
166 void CanvasRenderingContext2D::setFillStyle(PassRefPtr<CanvasStyle> style) | |
167 { | |
168 if (!style) | |
169 return; | |
170 | |
171 if (m_canvas->originClean()) { | |
172 if (CanvasPattern* pattern = style->canvasPattern()) { | |
173 if (!pattern->originClean()) | |
174 m_canvas->setOriginTainted(); | |
175 } | |
176 } | |
177 | |
178 state().m_fillStyle = style; | |
179 GraphicsContext* c = drawingContext(); | |
180 if (!c) | |
181 return; | |
182 state().m_fillStyle->applyFillColor(c); | |
183 } | |
184 | |
185 float CanvasRenderingContext2D::lineWidth() const | |
186 { | |
187 return state().m_lineWidth; | |
188 } | |
189 | |
190 void CanvasRenderingContext2D::setLineWidth(float width) | |
191 { | |
192 if (!(width > 0)) | |
193 return; | |
194 state().m_lineWidth = width; | |
195 GraphicsContext* c = drawingContext(); | |
196 if (!c) | |
197 return; | |
198 c->setStrokeThickness(width); | |
199 } | |
200 | |
201 String CanvasRenderingContext2D::lineCap() const | |
202 { | |
203 return lineCapName(state().m_lineCap); | |
204 } | |
205 | |
206 void CanvasRenderingContext2D::setLineCap(const String& s) | |
207 { | |
208 LineCap cap; | |
209 if (!parseLineCap(s, cap)) | |
210 return; | |
211 state().m_lineCap = cap; | |
212 GraphicsContext* c = drawingContext(); | |
213 if (!c) | |
214 return; | |
215 c->setLineCap(cap); | |
216 } | |
217 | |
218 String CanvasRenderingContext2D::lineJoin() const | |
219 { | |
220 return lineJoinName(state().m_lineJoin); | |
221 } | |
222 | |
223 void CanvasRenderingContext2D::setLineJoin(const String& s) | |
224 { | |
225 LineJoin join; | |
226 if (!parseLineJoin(s, join)) | |
227 return; | |
228 state().m_lineJoin = join; | |
229 GraphicsContext* c = drawingContext(); | |
230 if (!c) | |
231 return; | |
232 c->setLineJoin(join); | |
233 } | |
234 | |
235 float CanvasRenderingContext2D::miterLimit() const | |
236 { | |
237 return state().m_miterLimit; | |
238 } | |
239 | |
240 void CanvasRenderingContext2D::setMiterLimit(float limit) | |
241 { | |
242 if (!(limit > 0)) | |
243 return; | |
244 state().m_miterLimit = limit; | |
245 GraphicsContext* c = drawingContext(); | |
246 if (!c) | |
247 return; | |
248 c->setMiterLimit(limit); | |
249 } | |
250 | |
251 float CanvasRenderingContext2D::shadowOffsetX() const | |
252 { | |
253 return state().m_shadowOffset.width(); | |
254 } | |
255 | |
256 void CanvasRenderingContext2D::setShadowOffsetX(float x) | |
257 { | |
258 state().m_shadowOffset.setWidth(x); | |
259 applyShadow(); | |
260 } | |
261 | |
262 float CanvasRenderingContext2D::shadowOffsetY() const | |
263 { | |
264 return state().m_shadowOffset.height(); | |
265 } | |
266 | |
267 void CanvasRenderingContext2D::setShadowOffsetY(float y) | |
268 { | |
269 state().m_shadowOffset.setHeight(y); | |
270 applyShadow(); | |
271 } | |
272 | |
273 float CanvasRenderingContext2D::shadowBlur() const | |
274 { | |
275 return state().m_shadowBlur; | |
276 } | |
277 | |
278 void CanvasRenderingContext2D::setShadowBlur(float blur) | |
279 { | |
280 state().m_shadowBlur = blur; | |
281 applyShadow(); | |
282 } | |
283 | |
284 String CanvasRenderingContext2D::shadowColor() const | |
285 { | |
286 // FIXME: What should this return if you called setShadow with a non-string
color? | |
287 return state().m_shadowColor; | |
288 } | |
289 | |
290 void CanvasRenderingContext2D::setShadowColor(const String& color) | |
291 { | |
292 state().m_shadowColor = color; | |
293 applyShadow(); | |
294 } | |
295 | |
296 float CanvasRenderingContext2D::globalAlpha() const | |
297 { | |
298 return state().m_globalAlpha; | |
299 } | |
300 | |
301 void CanvasRenderingContext2D::setGlobalAlpha(float alpha) | |
302 { | |
303 if (!(alpha >= 0 && alpha <= 1)) | |
304 return; | |
305 state().m_globalAlpha = alpha; | |
306 GraphicsContext* c = drawingContext(); | |
307 if (!c) | |
308 return; | |
309 c->setAlpha(alpha); | |
310 } | |
311 | |
312 String CanvasRenderingContext2D::globalCompositeOperation() const | |
313 { | |
314 return compositeOperatorName(state().m_globalComposite); | |
315 } | |
316 | |
317 void CanvasRenderingContext2D::setGlobalCompositeOperation(const String& operati
on) | |
318 { | |
319 CompositeOperator op; | |
320 if (!parseCompositeOperator(operation, op)) | |
321 return; | |
322 state().m_globalComposite = op; | |
323 GraphicsContext* c = drawingContext(); | |
324 if (!c) | |
325 return; | |
326 c->setCompositeOperation(op); | |
327 } | |
328 | |
329 void CanvasRenderingContext2D::scale(float sx, float sy) | |
330 { | |
331 GraphicsContext* c = drawingContext(); | |
332 if (!c) | |
333 return; | |
334 c->scale(FloatSize(sx, sy)); | |
335 state().m_transform.scale(sx, sy); | |
336 m_path.transform(AffineTransform().scale(1.0/sx, 1.0/sy)); | |
337 } | |
338 | |
339 void CanvasRenderingContext2D::rotate(float angleInRadians) | |
340 { | |
341 GraphicsContext* c = drawingContext(); | |
342 if (!c) | |
343 return; | |
344 c->rotate(angleInRadians); | |
345 state().m_transform.rotate(angleInRadians / piDouble * 180.0); | |
346 m_path.transform(AffineTransform().rotate(-angleInRadians / piDouble * 180.0
)); | |
347 } | |
348 | |
349 void CanvasRenderingContext2D::translate(float tx, float ty) | |
350 { | |
351 GraphicsContext* c = drawingContext(); | |
352 if (!c) | |
353 return; | |
354 c->translate(tx, ty); | |
355 state().m_transform.translate(tx, ty); | |
356 m_path.transform(AffineTransform().translate(-tx, -ty)); | |
357 } | |
358 | |
359 void CanvasRenderingContext2D::transform(float m11, float m12, float m21, float
m22, float dx, float dy) | |
360 { | |
361 GraphicsContext* c = drawingContext(); | |
362 if (!c) | |
363 return; | |
364 | |
365 // HTML5 3.14.11.1 -- ignore any calls that pass non-finite numbers | |
366 if (!isfinite(m11) | !isfinite(m21) | !isfinite(dx) | | |
367 !isfinite(m12) | !isfinite(m22) | !isfinite(dy)) | |
368 return; | |
369 AffineTransform transform(m11, m12, m21, m22, dx, dy); | |
370 c->concatCTM(transform); | |
371 state().m_transform.multiply(transform); | |
372 m_path.transform(transform.inverse()); | |
373 } | |
374 | |
375 void CanvasRenderingContext2D::setStrokeColor(const String& color) | |
376 { | |
377 setStrokeStyle(CanvasStyle::create(color)); | |
378 } | |
379 | |
380 void CanvasRenderingContext2D::setStrokeColor(float grayLevel) | |
381 { | |
382 setStrokeStyle(CanvasStyle::create(grayLevel, 1)); | |
383 } | |
384 | |
385 void CanvasRenderingContext2D::setStrokeColor(const String& color, float alpha) | |
386 { | |
387 setStrokeStyle(CanvasStyle::create(color, alpha)); | |
388 } | |
389 | |
390 void CanvasRenderingContext2D::setStrokeColor(float grayLevel, float alpha) | |
391 { | |
392 setStrokeStyle(CanvasStyle::create(grayLevel, alpha)); | |
393 } | |
394 | |
395 void CanvasRenderingContext2D::setStrokeColor(float r, float g, float b, float a
) | |
396 { | |
397 setStrokeStyle(CanvasStyle::create(r, g, b, a)); | |
398 } | |
399 | |
400 void CanvasRenderingContext2D::setStrokeColor(float c, float m, float y, float k
, float a) | |
401 { | |
402 setStrokeStyle(CanvasStyle::create(c, m, y, k, a)); | |
403 } | |
404 | |
405 void CanvasRenderingContext2D::setFillColor(const String& color) | |
406 { | |
407 setFillStyle(CanvasStyle::create(color)); | |
408 } | |
409 | |
410 void CanvasRenderingContext2D::setFillColor(float grayLevel) | |
411 { | |
412 setFillStyle(CanvasStyle::create(grayLevel, 1)); | |
413 } | |
414 | |
415 void CanvasRenderingContext2D::setFillColor(const String& color, float alpha) | |
416 { | |
417 setFillStyle(CanvasStyle::create(color, 1)); | |
418 } | |
419 | |
420 void CanvasRenderingContext2D::setFillColor(float grayLevel, float alpha) | |
421 { | |
422 setFillStyle(CanvasStyle::create(grayLevel, alpha)); | |
423 } | |
424 | |
425 void CanvasRenderingContext2D::setFillColor(float r, float g, float b, float a) | |
426 { | |
427 setFillStyle(CanvasStyle::create(r, g, b, a)); | |
428 } | |
429 | |
430 void CanvasRenderingContext2D::setFillColor(float c, float m, float y, float k,
float a) | |
431 { | |
432 setFillStyle(CanvasStyle::create(c, m, y, k, a)); | |
433 } | |
434 | |
435 void CanvasRenderingContext2D::beginPath() | |
436 { | |
437 m_path.clear(); | |
438 } | |
439 | |
440 void CanvasRenderingContext2D::closePath() | |
441 { | |
442 m_path.closeSubpath(); | |
443 } | |
444 | |
445 void CanvasRenderingContext2D::moveTo(float x, float y) | |
446 { | |
447 if (!isfinite(x) | !isfinite(y)) | |
448 return; | |
449 m_path.moveTo(FloatPoint(x, y)); | |
450 } | |
451 | |
452 void CanvasRenderingContext2D::lineTo(float x, float y) | |
453 { | |
454 if (!isfinite(x) | !isfinite(y)) | |
455 return; | |
456 m_path.addLineTo(FloatPoint(x, y)); | |
457 } | |
458 | |
459 void CanvasRenderingContext2D::quadraticCurveTo(float cpx, float cpy, float x, f
loat y) | |
460 { | |
461 if (!isfinite(cpx) | !isfinite(cpy) | !isfinite(x) | !isfinite(y)) | |
462 return; | |
463 m_path.addQuadCurveTo(FloatPoint(cpx, cpy), FloatPoint(x, y)); | |
464 } | |
465 | |
466 void CanvasRenderingContext2D::bezierCurveTo(float cp1x, float cp1y, float cp2x,
float cp2y, float x, float y) | |
467 { | |
468 if (!isfinite(cp1x) | !isfinite(cp1y) | !isfinite(cp2x) | !isfinite(cp2y) |
!isfinite(x) | !isfinite(y)) | |
469 return; | |
470 m_path.addBezierCurveTo(FloatPoint(cp1x, cp1y), FloatPoint(cp2x, cp2y), Floa
tPoint(x, y)); | |
471 } | |
472 | |
473 void CanvasRenderingContext2D::arcTo(float x0, float y0, float x1, float y1, flo
at r, ExceptionCode& ec) | |
474 { | |
475 ec = 0; | |
476 if (!isfinite(x0) | !isfinite(y0) | !isfinite(x1) | !isfinite(y1) | !isfinit
e(r)) | |
477 return; | |
478 | |
479 if (r < 0) { | |
480 ec = INDEX_SIZE_ERR; | |
481 return; | |
482 } | |
483 | |
484 m_path.addArcTo(FloatPoint(x0, y0), FloatPoint(x1, y1), r); | |
485 } | |
486 | |
487 void CanvasRenderingContext2D::arc(float x, float y, float r, float sa, float ea
, bool anticlockwise, ExceptionCode& ec) | |
488 { | |
489 ec = 0; | |
490 if (!isfinite(x) | !isfinite(y) | !isfinite(r) | !isfinite(sa) | !isfinite(e
a)) | |
491 return; | |
492 | |
493 if (r < 0) { | |
494 ec = INDEX_SIZE_ERR; | |
495 return; | |
496 } | |
497 | |
498 m_path.addArc(FloatPoint(x, y), r, sa, ea, anticlockwise); | |
499 } | |
500 | |
501 static bool validateRectForCanvas(float& x, float& y, float& width, float& heigh
t) | |
502 { | |
503 if (!isfinite(x) | !isfinite(y) | !isfinite(width) | !isfinite(height)) | |
504 return false; | |
505 | |
506 if (width < 0) { | |
507 width = -width; | |
508 x -= width; | |
509 } | |
510 | |
511 if (height < 0) { | |
512 height = -height; | |
513 y -= height; | |
514 } | |
515 | |
516 return true; | |
517 } | |
518 | |
519 void CanvasRenderingContext2D::rect(float x, float y, float width, float height) | |
520 { | |
521 if (!validateRectForCanvas(x, y, width, height)) | |
522 return; | |
523 | |
524 m_path.addRect(FloatRect(x, y, width, height)); | |
525 } | |
526 | |
527 #if ENABLE(DASHBOARD_SUPPORT) | |
528 void CanvasRenderingContext2D::clearPathForDashboardBackwardCompatibilityMode() | |
529 { | |
530 if (Settings* settings = m_canvas->document()->settings()) | |
531 if (settings->usesDashboardBackwardCompatibilityMode()) | |
532 m_path.clear(); | |
533 } | |
534 #endif | |
535 | |
536 void CanvasRenderingContext2D::fill() | |
537 { | |
538 GraphicsContext* c = drawingContext(); | |
539 if (!c) | |
540 return; | |
541 | |
542 c->beginPath(); | |
543 c->addPath(m_path); | |
544 if (!m_path.isEmpty()) | |
545 willDraw(m_path.boundingRect()); | |
546 | |
547 c->fillPath(); | |
548 | |
549 #if ENABLE(DASHBOARD_SUPPORT) | |
550 clearPathForDashboardBackwardCompatibilityMode(); | |
551 #endif | |
552 } | |
553 | |
554 void CanvasRenderingContext2D::stroke() | |
555 { | |
556 GraphicsContext* c = drawingContext(); | |
557 if (!c) | |
558 return; | |
559 c->beginPath(); | |
560 c->addPath(m_path); | |
561 | |
562 if (!m_path.isEmpty()) { | |
563 // FIXME: This is insufficient, need to use CGContextReplacePathWithStro
kedPath to expand to required bounds | |
564 float lineWidth = state().m_lineWidth; | |
565 float inset = lineWidth / 2; | |
566 FloatRect boundingRect = m_path.boundingRect(); | |
567 boundingRect.inflate(inset); | |
568 willDraw(boundingRect); | |
569 } | |
570 | |
571 c->strokePath(); | |
572 | |
573 #if ENABLE(DASHBOARD_SUPPORT) | |
574 clearPathForDashboardBackwardCompatibilityMode(); | |
575 #endif | |
576 } | |
577 | |
578 void CanvasRenderingContext2D::clip() | |
579 { | |
580 GraphicsContext* c = drawingContext(); | |
581 if (!c) | |
582 return; | |
583 c->clip(m_path); | |
584 #if ENABLE(DASHBOARD_SUPPORT) | |
585 clearPathForDashboardBackwardCompatibilityMode(); | |
586 #endif | |
587 } | |
588 | |
589 bool CanvasRenderingContext2D::isPointInPath(const float x, const float y) | |
590 { | |
591 GraphicsContext* c = drawingContext(); | |
592 if (!c) | |
593 return false; | |
594 FloatPoint point(x, y); | |
595 // We have to invert the current transform to ensure we correctly handle the | |
596 // transforms applied to the current path. | |
597 AffineTransform ctm = state().m_transform; | |
598 if (!ctm.isInvertible()) | |
599 return false; | |
600 FloatPoint transformedPoint = ctm.inverse().mapPoint(point); | |
601 return m_path.contains(transformedPoint); | |
602 } | |
603 | |
604 void CanvasRenderingContext2D::clearRect(float x, float y, float width, float he
ight) | |
605 { | |
606 if (!validateRectForCanvas(x, y, width, height)) | |
607 return; | |
608 GraphicsContext* c = drawingContext(); | |
609 if (!c) | |
610 return; | |
611 FloatRect rect(x, y, width, height); | |
612 willDraw(rect); | |
613 c->clearRect(rect); | |
614 } | |
615 | |
616 void CanvasRenderingContext2D::fillRect(float x, float y, float width, float hei
ght) | |
617 { | |
618 if (!validateRectForCanvas(x, y, width, height)) | |
619 return; | |
620 | |
621 GraphicsContext* c = drawingContext(); | |
622 if (!c) | |
623 return; | |
624 | |
625 FloatRect rect(x, y, width, height); | |
626 willDraw(rect); | |
627 | |
628 c->save(); | |
629 c->fillRect(rect); | |
630 c->restore(); | |
631 } | |
632 | |
633 void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float h
eight) | |
634 { | |
635 if (!validateRectForCanvas(x, y, width, height)) | |
636 return; | |
637 strokeRect(x, y, width, height, state().m_lineWidth); | |
638 } | |
639 | |
640 void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float h
eight, float lineWidth) | |
641 { | |
642 if (!validateRectForCanvas(x, y, width, height)) | |
643 return; | |
644 | |
645 if (!(lineWidth >= 0)) | |
646 return; | |
647 | |
648 GraphicsContext* c = drawingContext(); | |
649 if (!c) | |
650 return; | |
651 | |
652 FloatRect rect(x, y, width, height); | |
653 | |
654 FloatRect boundingRect = rect; | |
655 boundingRect.inflate(lineWidth / 2); | |
656 willDraw(boundingRect); | |
657 | |
658 c->strokeRect(rect, lineWidth); | |
659 } | |
660 | |
661 #if PLATFORM(CG) | |
662 static inline CGSize adjustedShadowSize(CGFloat width, CGFloat height) | |
663 { | |
664 // Work around <rdar://problem/5539388> by ensuring that shadow offsets will
get truncated | |
665 // to the desired integer. | |
666 static const CGFloat extraShadowOffset = narrowPrecisionToCGFloat(1.0 / 128)
; | |
667 if (width > 0) | |
668 width += extraShadowOffset; | |
669 else if (width < 0) | |
670 width -= extraShadowOffset; | |
671 | |
672 if (height > 0) | |
673 height += extraShadowOffset; | |
674 else if (height < 0) | |
675 height -= extraShadowOffset; | |
676 | |
677 return CGSizeMake(width, height); | |
678 } | |
679 #endif | |
680 | |
681 void CanvasRenderingContext2D::setShadow(float width, float height, float blur) | |
682 { | |
683 state().m_shadowOffset = FloatSize(width, height); | |
684 state().m_shadowBlur = blur; | |
685 state().m_shadowColor = ""; | |
686 applyShadow(); | |
687 } | |
688 | |
689 void CanvasRenderingContext2D::setShadow(float width, float height, float blur,
const String& color) | |
690 { | |
691 state().m_shadowOffset = FloatSize(width, height); | |
692 state().m_shadowBlur = blur; | |
693 state().m_shadowColor = color; | |
694 applyShadow(); | |
695 } | |
696 | |
697 void CanvasRenderingContext2D::setShadow(float width, float height, float blur,
float grayLevel) | |
698 { | |
699 state().m_shadowOffset = FloatSize(width, height); | |
700 state().m_shadowBlur = blur; | |
701 state().m_shadowColor = ""; | |
702 | |
703 GraphicsContext* c = drawingContext(); | |
704 if (!c) | |
705 return; | |
706 // FIXME: Do this through platform-independent GraphicsContext API. | |
707 #if PLATFORM(CG) | |
708 const CGFloat components[2] = { grayLevel, 1 }; | |
709 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray(); | |
710 CGColorRef color = CGColorCreate(colorSpace, components); | |
711 CGColorSpaceRelease(colorSpace); | |
712 CGContextSetShadowWithColor(c->platformContext(), adjustedShadowSize(width,
-height), blur, color); | |
713 CGColorRelease(color); | |
714 #endif | |
715 } | |
716 | |
717 void CanvasRenderingContext2D::setShadow(float width, float height, float blur,
const String& color, float alpha) | |
718 { | |
719 state().m_shadowOffset = FloatSize(width, height); | |
720 state().m_shadowBlur = blur; | |
721 state().m_shadowColor = color; | |
722 | |
723 GraphicsContext* c = drawingContext(); | |
724 if (!c) | |
725 return; | |
726 // FIXME: Do this through platform-independent GraphicsContext API. | |
727 #if PLATFORM(CG) | |
728 RGBA32 rgba = 0; // default is transparent black | |
729 CSSParser::parseColor(rgba, color); | |
730 const CGFloat components[4] = { | |
731 ((rgba >> 16) & 0xFF) / 255.0f, | |
732 ((rgba >> 8) & 0xFF) / 255.0f, | |
733 (rgba & 0xFF) / 255.0f, | |
734 alpha | |
735 }; | |
736 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); | |
737 CGColorRef shadowColor = CGColorCreate(colorSpace, components); | |
738 CGColorSpaceRelease(colorSpace); | |
739 CGContextSetShadowWithColor(c->platformContext(), adjustedShadowSize(width,
-height), blur, shadowColor); | |
740 CGColorRelease(shadowColor); | |
741 #endif | |
742 } | |
743 | |
744 void CanvasRenderingContext2D::setShadow(float width, float height, float blur,
float grayLevel, float alpha) | |
745 { | |
746 state().m_shadowOffset = FloatSize(width, height); | |
747 state().m_shadowBlur = blur; | |
748 state().m_shadowColor = ""; | |
749 | |
750 GraphicsContext* c = drawingContext(); | |
751 if (!c) | |
752 return; | |
753 // FIXME: Do this through platform-independent GraphicsContext API. | |
754 #if PLATFORM(CG) | |
755 const CGFloat components[2] = { grayLevel, alpha }; | |
756 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray(); | |
757 CGColorRef color = CGColorCreate(colorSpace, components); | |
758 CGColorSpaceRelease(colorSpace); | |
759 CGContextSetShadowWithColor(c->platformContext(), adjustedShadowSize(width,
-height), blur, color); | |
760 CGColorRelease(color); | |
761 #endif | |
762 } | |
763 | |
764 void CanvasRenderingContext2D::setShadow(float width, float height, float blur,
float r, float g, float b, float a) | |
765 { | |
766 state().m_shadowOffset = FloatSize(width, height); | |
767 state().m_shadowBlur = blur; | |
768 state().m_shadowColor = ""; | |
769 | |
770 GraphicsContext* c = drawingContext(); | |
771 if (!c) | |
772 return; | |
773 // FIXME: Do this through platform-independent GraphicsContext API. | |
774 #if PLATFORM(CG) | |
775 const CGFloat components[4] = { r, g, b, a }; | |
776 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); | |
777 CGColorRef shadowColor = CGColorCreate(colorSpace, components); | |
778 CGColorSpaceRelease(colorSpace); | |
779 CGContextSetShadowWithColor(c->platformContext(), adjustedShadowSize(width,
-height), blur, shadowColor); | |
780 CGColorRelease(shadowColor); | |
781 #endif | |
782 } | |
783 | |
784 void CanvasRenderingContext2D::setShadow(float width, float height, float blur,
float c, float m, float y, float k, float a) | |
785 { | |
786 state().m_shadowOffset = FloatSize(width, height); | |
787 state().m_shadowBlur = blur; | |
788 state().m_shadowColor = ""; | |
789 | |
790 GraphicsContext* dc = drawingContext(); | |
791 if (!dc) | |
792 return; | |
793 // FIXME: Do this through platform-independent GraphicsContext API. | |
794 #if PLATFORM(CG) | |
795 const CGFloat components[5] = { c, m, y, k, a }; | |
796 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceCMYK(); | |
797 CGColorRef shadowColor = CGColorCreate(colorSpace, components); | |
798 CGColorSpaceRelease(colorSpace); | |
799 CGContextSetShadowWithColor(dc->platformContext(), adjustedShadowSize(width,
-height), blur, shadowColor); | |
800 CGColorRelease(shadowColor); | |
801 #endif | |
802 } | |
803 | |
804 void CanvasRenderingContext2D::clearShadow() | |
805 { | |
806 state().m_shadowOffset = FloatSize(); | |
807 state().m_shadowBlur = 0; | |
808 state().m_shadowColor = ""; | |
809 applyShadow(); | |
810 } | |
811 | |
812 void CanvasRenderingContext2D::applyShadow() | |
813 { | |
814 GraphicsContext* c = drawingContext(); | |
815 if (!c) | |
816 return; | |
817 // FIXME: Do this through platform-independent GraphicsContext API. | |
818 #if PLATFORM(CG) | |
819 RGBA32 rgba = 0; // default is transparent black | |
820 if (!state().m_shadowColor.isEmpty()) | |
821 CSSParser::parseColor(rgba, state().m_shadowColor); | |
822 const CGFloat components[4] = { | |
823 ((rgba >> 16) & 0xFF) / 255.0f, | |
824 ((rgba >> 8) & 0xFF) / 255.0f, | |
825 (rgba & 0xFF) / 255.0f, | |
826 ((rgba >> 24) & 0xFF) / 255.0f | |
827 }; | |
828 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); | |
829 CGColorRef color = CGColorCreate(colorSpace, components); | |
830 CGColorSpaceRelease(colorSpace); | |
831 | |
832 CGContextSetShadowWithColor(c->platformContext(), adjustedShadowSize(state()
.m_shadowOffset.width(), -state().m_shadowOffset.height()), state().m_shadowBlur
, color); | |
833 CGColorRelease(color); | |
834 #endif | |
835 } | |
836 | |
837 static IntSize size(HTMLImageElement* image) | |
838 { | |
839 if (CachedImage* cachedImage = image->cachedImage()) | |
840 return cachedImage->imageSize(1.0f); // FIXME: Not sure about this. | |
841 return IntSize(); | |
842 } | |
843 | |
844 static inline FloatRect normalizeRect(const FloatRect& rect) | |
845 { | |
846 return FloatRect(min(rect.x(), rect.right()), | |
847 min(rect.y(), rect.bottom()), | |
848 max(rect.width(), -rect.width()), | |
849 max(rect.height(), -rect.height())); | |
850 } | |
851 | |
852 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, float x, float
y) | |
853 { | |
854 ASSERT(image); | |
855 IntSize s = size(image); | |
856 ExceptionCode ec; | |
857 drawImage(image, x, y, s.width(), s.height(), ec); | |
858 } | |
859 | |
860 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, | |
861 float x, float y, float width, float height, ExceptionCode& ec) | |
862 { | |
863 ASSERT(image); | |
864 IntSize s = size(image); | |
865 drawImage(image, FloatRect(0, 0, s.width(), s.height()), FloatRect(x, y, wid
th, height), ec); | |
866 } | |
867 | |
868 void CanvasRenderingContext2D::checkOrigin(const KURL& url) | |
869 { | |
870 RefPtr<SecurityOrigin> origin = SecurityOrigin::create(url); | |
871 if (!m_canvas->document()->securityOrigin()->canAccess(origin.get())) | |
872 m_canvas->setOriginTainted(); | |
873 } | |
874 | |
875 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, const FloatRec
t& srcRect, const FloatRect& dstRect, | |
876 ExceptionCode& ec) | |
877 { | |
878 ASSERT(image); | |
879 | |
880 ec = 0; | |
881 | |
882 FloatRect imageRect = FloatRect(FloatPoint(), size(image)); | |
883 if (!imageRect.contains(normalizeRect(srcRect)) || srcRect.width() == 0 || s
rcRect.height() == 0) { | |
884 ec = INDEX_SIZE_ERR; | |
885 return; | |
886 } | |
887 | |
888 if (!dstRect.width() || !dstRect.height()) | |
889 return; | |
890 | |
891 GraphicsContext* c = drawingContext(); | |
892 if (!c) | |
893 return; | |
894 | |
895 CachedImage* cachedImage = image->cachedImage(); | |
896 if (!cachedImage) | |
897 return; | |
898 | |
899 if (m_canvas->originClean()) | |
900 checkOrigin(cachedImage->response().url()); | |
901 | |
902 if (m_canvas->originClean() && !cachedImage->image()->hasSingleSecurityOrigi
n()) | |
903 m_canvas->setOriginTainted(); | |
904 | |
905 FloatRect sourceRect = c->roundToDevicePixels(srcRect); | |
906 FloatRect destRect = c->roundToDevicePixels(dstRect); | |
907 willDraw(destRect); | |
908 c->drawImage(cachedImage->image(), destRect, sourceRect, state().m_globalCom
posite); | |
909 } | |
910 | |
911 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas, float x, flo
at y) | |
912 { | |
913 ASSERT(canvas); | |
914 ExceptionCode ec; | |
915 drawImage(canvas, x, y, canvas->width(), canvas->height(), ec); | |
916 } | |
917 | |
918 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas, | |
919 float x, float y, float width, float height, ExceptionCode& ec) | |
920 { | |
921 ASSERT(canvas); | |
922 drawImage(canvas, FloatRect(0, 0, canvas->width(), canvas->height()), FloatR
ect(x, y, width, height), ec); | |
923 } | |
924 | |
925 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas, const FloatR
ect& srcRect, | |
926 const FloatRect& dstRect, ExceptionCode& ec) | |
927 { | |
928 ASSERT(canvas); | |
929 | |
930 ec = 0; | |
931 | |
932 FloatRect srcCanvasRect = FloatRect(FloatPoint(), canvas->size()); | |
933 if (!srcCanvasRect.contains(normalizeRect(srcRect)) || srcRect.width() == 0
|| srcRect.height() == 0) { | |
934 ec = INDEX_SIZE_ERR; | |
935 return; | |
936 } | |
937 | |
938 if (!dstRect.width() || !dstRect.height()) | |
939 return; | |
940 | |
941 GraphicsContext* c = drawingContext(); | |
942 if (!c) | |
943 return; | |
944 | |
945 FloatRect sourceRect = c->roundToDevicePixels(srcRect); | |
946 FloatRect destRect = c->roundToDevicePixels(dstRect); | |
947 | |
948 // FIXME: Do this through platform-independent GraphicsContext API. | |
949 ImageBuffer* buffer = canvas->buffer(); | |
950 if (!buffer) | |
951 return; | |
952 | |
953 if (!canvas->originClean()) | |
954 m_canvas->setOriginTainted(); | |
955 | |
956 c->drawImage(buffer->image(), destRect, sourceRect, state().m_globalComposit
e); | |
957 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. | |
958 // FIXME: Arguably willDraw should become didDraw and oc
cur after drawing calls and not before them to avoid problems like this. | |
959 } | |
960 | |
961 // FIXME: Why isn't this just another overload of drawImage? Why have a differen
t name? | |
962 void CanvasRenderingContext2D::drawImageFromRect(HTMLImageElement* image, | |
963 float sx, float sy, float sw, float sh, | |
964 float dx, float dy, float dw, float dh, | |
965 const String& compositeOperation) | |
966 { | |
967 if (!image) | |
968 return; | |
969 | |
970 CachedImage* cachedImage = image->cachedImage(); | |
971 if (!cachedImage) | |
972 return; | |
973 | |
974 if (m_canvas->originClean()) | |
975 checkOrigin(cachedImage->response().url()); | |
976 | |
977 if (m_canvas->originClean() && !cachedImage->image()->hasSingleSecurityOrigi
n()) | |
978 m_canvas->setOriginTainted(); | |
979 | |
980 GraphicsContext* c = drawingContext(); | |
981 if (!c) | |
982 return; | |
983 | |
984 CompositeOperator op; | |
985 if (!parseCompositeOperator(compositeOperation, op)) | |
986 op = CompositeSourceOver; | |
987 | |
988 FloatRect destRect = FloatRect(dx, dy, dw, dh); | |
989 willDraw(destRect); | |
990 c->drawImage(cachedImage->image(), destRect, FloatRect(sx, sy, sw, sh), op); | |
991 } | |
992 | |
993 void CanvasRenderingContext2D::setAlpha(float alpha) | |
994 { | |
995 setGlobalAlpha(alpha); | |
996 } | |
997 | |
998 void CanvasRenderingContext2D::setCompositeOperation(const String& operation) | |
999 { | |
1000 setGlobalCompositeOperation(operation); | |
1001 } | |
1002 | |
1003 PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createLinearGradient(float
x0, float y0, float x1, float y1) | |
1004 { | |
1005 return CanvasGradient::create(FloatPoint(x0, y0), FloatPoint(x1, y1)); | |
1006 } | |
1007 | |
1008 PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createRadialGradient(float
x0, float y0, float r0, float x1, float y1, float r1) | |
1009 { | |
1010 return CanvasGradient::create(FloatPoint(x0, y0), r0, FloatPoint(x1, y1), r1
); | |
1011 } | |
1012 | |
1013 PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLImageEleme
nt* image, | |
1014 const String& repetitionType, ExceptionCode& ec) | |
1015 { | |
1016 bool repeatX, repeatY; | |
1017 ec = 0; | |
1018 CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec); | |
1019 if (ec) | |
1020 return 0; | |
1021 | |
1022 if (!image->complete()) { | |
1023 ec = INVALID_STATE_ERR; | |
1024 return 0; | |
1025 } | |
1026 | |
1027 CachedImage* cachedImage = image->cachedImage(); | |
1028 if (!cachedImage || !image->cachedImage()->image()) | |
1029 return CanvasPattern::create(Image::nullImage(), repeatX, repeatY, true)
; | |
1030 | |
1031 KURL url(cachedImage->url()); | |
1032 RefPtr<SecurityOrigin> origin = SecurityOrigin::create(url); | |
1033 bool originClean = m_canvas->document()->securityOrigin()->canAccess(origin.
get()); | |
1034 return CanvasPattern::create(cachedImage->image(), repeatX, repeatY, originC
lean); | |
1035 } | |
1036 | |
1037 PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLCanvasElem
ent* canvas, | |
1038 const String& repetitionType, ExceptionCode& ec) | |
1039 { | |
1040 bool repeatX, repeatY; | |
1041 ec = 0; | |
1042 CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec); | |
1043 if (ec) | |
1044 return 0; | |
1045 return CanvasPattern::create(canvas->buffer()->image(), repeatX, repeatY, ca
nvas->originClean()); | |
1046 } | |
1047 | |
1048 void CanvasRenderingContext2D::willDraw(const FloatRect& r) | |
1049 { | |
1050 GraphicsContext* c = drawingContext(); | |
1051 if (!c) | |
1052 return; | |
1053 | |
1054 m_canvas->willDraw(c->getCTM().mapRect(r)); | |
1055 } | |
1056 | |
1057 GraphicsContext* CanvasRenderingContext2D::drawingContext() const | |
1058 { | |
1059 return m_canvas->drawingContext(); | |
1060 } | |
1061 | |
1062 static PassRefPtr<ImageData> createEmptyImageData(const IntSize& size) | |
1063 { | |
1064 PassRefPtr<ImageData> data = ImageData::create(size.width(), size.height()); | |
1065 memset(data->data()->data().data(), 0, data->data()->length()); | |
1066 return data; | |
1067 } | |
1068 | |
1069 PassRefPtr<ImageData> CanvasRenderingContext2D::createImageData(float sw, float
sh) const | |
1070 { | |
1071 FloatSize unscaledSize(sw, sh); | |
1072 IntSize scaledSize = m_canvas->convertLogicalToDevice(unscaledSize); | |
1073 if (scaledSize.width() < 1) | |
1074 scaledSize.setWidth(1); | |
1075 if (scaledSize.height() < 1) | |
1076 scaledSize.setHeight(1); | |
1077 | |
1078 return createEmptyImageData(scaledSize); | |
1079 } | |
1080 | |
1081 PassRefPtr<ImageData> CanvasRenderingContext2D::getImageData(float sx, float sy,
float sw, float sh, ExceptionCode& ec) const | |
1082 { | |
1083 if (!m_canvas->originClean()) { | |
1084 ec = SECURITY_ERR; | |
1085 return 0; | |
1086 } | |
1087 | |
1088 FloatRect unscaledRect(sx, sy, sw, sh); | |
1089 IntRect scaledRect = m_canvas->convertLogicalToDevice(unscaledRect); | |
1090 if (scaledRect.width() < 1) | |
1091 scaledRect.setWidth(1); | |
1092 if (scaledRect.height() < 1) | |
1093 scaledRect.setHeight(1); | |
1094 ImageBuffer* buffer = m_canvas ? m_canvas->buffer() : 0; | |
1095 if (!buffer) | |
1096 return createEmptyImageData(scaledRect.size()); | |
1097 return buffer->getImageData(scaledRect); | |
1098 } | |
1099 | |
1100 void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy,
ExceptionCode& ec) | |
1101 { | |
1102 if (!data) { | |
1103 ec = TYPE_MISMATCH_ERR; | |
1104 return; | |
1105 } | |
1106 putImageData(data, dx, dy, 0, 0, data->width(), data->height(), ec); | |
1107 } | |
1108 | |
1109 void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy,
float dirtyX, float dirtyY, | |
1110 float dirtyWidth, float dirtyHeight,
ExceptionCode& ec) | |
1111 { | |
1112 if (!data) { | |
1113 ec = TYPE_MISMATCH_ERR; | |
1114 return; | |
1115 } | |
1116 if (!isfinite(dx) || !isfinite(dy) || !isfinite(dirtyX) || | |
1117 !isfinite(dirtyY) || !isfinite(dirtyWidth) || !isfinite(dirtyHeight)) { | |
1118 ec = INDEX_SIZE_ERR; | |
1119 return; | |
1120 } | |
1121 | |
1122 ImageBuffer* buffer = m_canvas->buffer(); | |
1123 if (!buffer) | |
1124 return; | |
1125 | |
1126 if (dirtyWidth < 0) { | |
1127 dirtyX += dirtyWidth; | |
1128 dirtyWidth = -dirtyWidth; | |
1129 } | |
1130 | |
1131 if (dirtyHeight < 0) { | |
1132 dirtyY += dirtyHeight; | |
1133 dirtyHeight = -dirtyHeight; | |
1134 } | |
1135 | |
1136 FloatRect clipRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight); | |
1137 clipRect.intersect(IntRect(0, 0, data->width(), data->height())); | |
1138 IntSize destOffset(static_cast<int>(dx), static_cast<int>(dy)); | |
1139 IntRect sourceRect = enclosingIntRect(clipRect); | |
1140 sourceRect.move(destOffset); | |
1141 sourceRect.intersect(IntRect(IntPoint(), buffer->size())); | |
1142 if (sourceRect.isEmpty()) | |
1143 return; | |
1144 willDraw(sourceRect); | |
1145 sourceRect.move(-destOffset); | |
1146 IntPoint destPoint(destOffset.width(), destOffset.height()); | |
1147 | |
1148 buffer->putImageData(data, sourceRect, destPoint); | |
1149 } | |
1150 | |
1151 String CanvasRenderingContext2D::font() const | |
1152 { | |
1153 return state().m_unparsedFont; | |
1154 } | |
1155 | |
1156 void CanvasRenderingContext2D::setFont(const String& newFont) | |
1157 { | |
1158 RefPtr<CSSMutableStyleDeclaration> tempDecl = CSSMutableStyleDeclaration::cr
eate(); | |
1159 CSSParser parser(!m_canvas->document()->inCompatMode()); // Use the parse mo
de of the canvas' document when parsing CSS. | |
1160 | |
1161 String declarationText("font: "); | |
1162 declarationText += newFont; | |
1163 parser.parseDeclaration(tempDecl.get(), declarationText); | |
1164 if (!tempDecl->length()) | |
1165 return; | |
1166 | |
1167 // The parse succeeded. | |
1168 state().m_unparsedFont = newFont; | |
1169 | |
1170 // Map the <canvas> font into the text style. If the font uses keywords like
larger/smaller, these will work | |
1171 // relative to the canvas. | |
1172 RenderArena* arena = m_canvas->document()->renderArena(); | |
1173 RenderStyle* newStyle = new (arena) RenderStyle(); | |
1174 newStyle->ref(); | |
1175 if (m_canvas->computedStyle()) | |
1176 newStyle->setFontDescription(m_canvas->computedStyle()->fontDescription(
)); | |
1177 | |
1178 // Now map the font property into the style. | |
1179 CSSStyleSelector* styleSelector = m_canvas->document()->styleSelector(); | |
1180 styleSelector->applyPropertyToStyle(CSSPropertyFont, tempDecl->getPropertyCS
SValue(CSSPropertyFont).get(), newStyle); | |
1181 | |
1182 state().m_font = newStyle->font(); | |
1183 state().m_font.update(styleSelector->fontSelector()); | |
1184 state().m_realizedFont = true; | |
1185 | |
1186 newStyle->deref(arena); | |
1187 | |
1188 // Set the font in the graphics context. | |
1189 GraphicsContext* c = drawingContext(); | |
1190 if (!c) | |
1191 return; | |
1192 c->setFont(state().m_font); | |
1193 } | |
1194 | |
1195 String CanvasRenderingContext2D::textAlign() const | |
1196 { | |
1197 return textAlignName(state().m_textAlign); | |
1198 } | |
1199 | |
1200 void CanvasRenderingContext2D::setTextAlign(const String& s) | |
1201 { | |
1202 TextAlign align; | |
1203 if (!parseTextAlign(s, align)) | |
1204 return; | |
1205 state().m_textAlign = align; | |
1206 } | |
1207 | |
1208 String CanvasRenderingContext2D::textBaseline() const | |
1209 { | |
1210 return textBaselineName(state().m_textBaseline); | |
1211 } | |
1212 | |
1213 void CanvasRenderingContext2D::setTextBaseline(const String& s) | |
1214 { | |
1215 TextBaseline baseline; | |
1216 if (!parseTextBaseline(s, baseline)) | |
1217 return; | |
1218 state().m_textBaseline = baseline; | |
1219 } | |
1220 | |
1221 void CanvasRenderingContext2D::fillText(const String& text, float x, float y) | |
1222 { | |
1223 drawTextInternal(text, x, y, true); | |
1224 } | |
1225 | |
1226 void CanvasRenderingContext2D::fillText(const String& text, float x, float y, fl
oat maxWidth) | |
1227 { | |
1228 drawTextInternal(text, x, y, true, maxWidth, true); | |
1229 } | |
1230 | |
1231 void CanvasRenderingContext2D::strokeText(const String& text, float x, float y) | |
1232 { | |
1233 drawTextInternal(text, x, y, false); | |
1234 } | |
1235 | |
1236 void CanvasRenderingContext2D::strokeText(const String& text, float x, float y,
float maxWidth) | |
1237 { | |
1238 drawTextInternal(text, x, y, false, maxWidth, true); | |
1239 } | |
1240 | |
1241 PassRefPtr<TextMetrics> CanvasRenderingContext2D::measureText(const String& text
) | |
1242 { | |
1243 RefPtr<TextMetrics> metrics = TextMetrics::create(); | |
1244 metrics->setWidth(accessFont().width(TextRun(text.characters(), text.length(
)))); | |
1245 return metrics; | |
1246 } | |
1247 | |
1248 void CanvasRenderingContext2D::drawTextInternal(const String& text, float x, flo
at y, bool fill, float maxWidth, bool useMaxWidth) | |
1249 { | |
1250 GraphicsContext* c = drawingContext(); | |
1251 if (!c) | |
1252 return; | |
1253 | |
1254 const Font& font = accessFont(); | |
1255 | |
1256 // FIXME: Handle maxWidth. | |
1257 // FIXME: Need to turn off font smoothing. | |
1258 | |
1259 bool rtl = m_canvas->computedStyle() ? m_canvas->computedStyle()->direction(
) == RTL : false; | |
1260 bool override = m_canvas->computedStyle() ? m_canvas->computedStyle()->unico
deBidi() == Override : false; | |
1261 | |
1262 unsigned length = text.length(); | |
1263 const UChar* string = text.characters(); | |
1264 TextRun textRun(string, length, 0, 0, 0, rtl, override, false, false); | |
1265 | |
1266 // Draw the item text at the correct point. | |
1267 FloatPoint location(x, y); | |
1268 switch (state().m_textBaseline) { | |
1269 case TopTextBaseline: | |
1270 case HangingTextBaseline: | |
1271 location.setY(y + font.ascent()); | |
1272 break; | |
1273 case BottomTextBaseline: | |
1274 case IdeographicTextBaseline: | |
1275 location.setY(y - font.descent()); | |
1276 break; | |
1277 case MiddleTextBaseline: | |
1278 location.setY(y - font.descent() + font.height() / 2); | |
1279 break; | |
1280 case AlphabeticTextBaseline: | |
1281 default: | |
1282 // Do nothing. | |
1283 break; | |
1284 } | |
1285 | |
1286 float width = font.width(TextRun(text, false, 0, 0, rtl, override)); | |
1287 | |
1288 TextAlign align = state().m_textAlign; | |
1289 if (align == StartTextAlign) | |
1290 align = rtl ? RightTextAlign : LeftTextAlign; | |
1291 else if (align == EndTextAlign) | |
1292 align = rtl ? LeftTextAlign : RightTextAlign; | |
1293 | |
1294 switch (align) { | |
1295 case CenterTextAlign: | |
1296 location.setX(location.x() - width / 2); | |
1297 break; | |
1298 case RightTextAlign: | |
1299 location.setX(location.x() - width); | |
1300 break; | |
1301 default: | |
1302 break; | |
1303 } | |
1304 | |
1305 // The slop built in to this mask rect matches the heuristic used in FontCGW
in.cpp for GDI text. | |
1306 FloatRect textRect = FloatRect(location.x() - font.height() / 2, location.y(
) - font.ascent() - font.lineGap(), | |
1307 width + font.height(), font.lineSpacing()); | |
1308 if (!fill) | |
1309 textRect.inflate(c->strokeThickness() / 2); | |
1310 | |
1311 CanvasStyle* drawStyle = fill ? state().m_fillStyle.get() : state().m_stroke
Style.get(); | |
1312 if (drawStyle->canvasGradient() || drawStyle->canvasPattern()) { | |
1313 IntRect maskRect = enclosingIntRect(textRect); | |
1314 | |
1315 auto_ptr<ImageBuffer> maskImage = ImageBuffer::create(maskRect.size(), f
alse); | |
1316 | |
1317 GraphicsContext* maskImageContext = maskImage->context(); | |
1318 | |
1319 if (fill) | |
1320 maskImageContext->setFillColor(Color::black); | |
1321 else { | |
1322 maskImageContext->setStrokeColor(Color::black); | |
1323 maskImageContext->setStrokeThickness(c->strokeThickness()); | |
1324 } | |
1325 | |
1326 maskImageContext->setTextDrawingMode(fill ? cTextFill : cTextStroke); | |
1327 maskImageContext->translate(-maskRect.x(), -maskRect.y()); | |
1328 | |
1329 maskImageContext->setFont(font); | |
1330 maskImageContext->drawBidiText(textRun, location); | |
1331 | |
1332 c->save(); | |
1333 c->clipToImageBuffer(maskRect, maskImage.get()); | |
1334 drawStyle->applyFillColor(c); | |
1335 c->fillRect(maskRect); | |
1336 c->restore(); | |
1337 | |
1338 return; | |
1339 } | |
1340 | |
1341 c->setTextDrawingMode(fill ? cTextFill : cTextStroke); | |
1342 c->drawBidiText(textRun, location); | |
1343 } | |
1344 | |
1345 const Font& CanvasRenderingContext2D::accessFont() | |
1346 { | |
1347 if (!state().m_realizedFont) | |
1348 setFont(state().m_unparsedFont); | |
1349 return state().m_font; | |
1350 } | |
1351 | |
1352 } // namespace WebCore | |
OLD | NEW |