1 /*
2 * Copyright (C) 2003, 2004, 2005, 2006, 2009 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include "config.h"
27 #include "GraphicsContext.h"
28
29 #include "BidiResolver.h"
30 #include "Font.h"
31 #include "Generator.h"
32 #include "GraphicsContextPrivate.h"
33
34 using namespace std;
35
36 namespace WebCore {
37
38 class TextRunIterator {
39 public:
TextRunIterator()40 TextRunIterator()
41 : m_textRun(0)
42 , m_offset(0)
43 {
44 }
45
TextRunIterator(const TextRun * textRun,unsigned offset)46 TextRunIterator(const TextRun* textRun, unsigned offset)
47 : m_textRun(textRun)
48 , m_offset(offset)
49 {
50 }
51
TextRunIterator(const TextRunIterator & other)52 TextRunIterator(const TextRunIterator& other)
53 : m_textRun(other.m_textRun)
54 , m_offset(other.m_offset)
55 {
56 }
57
offset() const58 unsigned offset() const { return m_offset; }
increment()59 void increment() { m_offset++; }
atEnd() const60 bool atEnd() const { return !m_textRun || m_offset >= m_textRun->length(); }
current() const61 UChar current() const { return (*m_textRun)[m_offset]; }
direction() const62 WTF::Unicode::Direction direction() const { return atEnd() ? WTF::Unicode::OtherNeutral : WTF::Unicode::direction(current()); }
63
operator ==(const TextRunIterator & other)64 bool operator==(const TextRunIterator& other)
65 {
66 return m_offset == other.m_offset && m_textRun == other.m_textRun;
67 }
68
operator !=(const TextRunIterator & other)69 bool operator!=(const TextRunIterator& other) { return !operator==(other); }
70
71 private:
72 const TextRun* m_textRun;
73 int m_offset;
74 };
75
createGraphicsContextPrivate()76 GraphicsContextPrivate* GraphicsContext::createGraphicsContextPrivate()
77 {
78 return new GraphicsContextPrivate;
79 }
80
destroyGraphicsContextPrivate(GraphicsContextPrivate * deleteMe)81 void GraphicsContext::destroyGraphicsContextPrivate(GraphicsContextPrivate* deleteMe)
82 {
83 delete deleteMe;
84 }
85
save()86 void GraphicsContext::save()
87 {
88 if (paintingDisabled())
89 return;
90
91 m_common->stack.append(m_common->state);
92
93 savePlatformState();
94 }
95
restore()96 void GraphicsContext::restore()
97 {
98 if (paintingDisabled())
99 return;
100
101 if (m_common->stack.isEmpty()) {
102 LOG_ERROR("ERROR void GraphicsContext::restore() stack is empty");
103 return;
104 }
105 m_common->state = m_common->stack.last();
106 m_common->stack.removeLast();
107
108 restorePlatformState();
109 }
110
setStrokeThickness(float thickness)111 void GraphicsContext::setStrokeThickness(float thickness)
112 {
113 m_common->state.strokeThickness = thickness;
114 setPlatformStrokeThickness(thickness);
115 }
116
setStrokeStyle(const StrokeStyle & style)117 void GraphicsContext::setStrokeStyle(const StrokeStyle& style)
118 {
119 m_common->state.strokeStyle = style;
120 setPlatformStrokeStyle(style);
121 }
122
setStrokeColor(const Color & color,ColorSpace colorSpace)123 void GraphicsContext::setStrokeColor(const Color& color, ColorSpace colorSpace)
124 {
125 m_common->state.strokeColor = color;
126 m_common->state.strokeColorSpace = colorSpace;
127 m_common->state.strokeGradient.clear();
128 m_common->state.strokePattern.clear();
129 setPlatformStrokeColor(color, colorSpace);
130 }
131
setShadow(const IntSize & size,int blur,const Color & color,ColorSpace colorSpace)132 void GraphicsContext::setShadow(const IntSize& size, int blur, const Color& color, ColorSpace colorSpace)
133 {
134 m_common->state.shadowSize = size;
135 m_common->state.shadowBlur = blur;
136 m_common->state.shadowColor = color;
137 setPlatformShadow(size, blur, color, colorSpace);
138 }
139
clearShadow()140 void GraphicsContext::clearShadow()
141 {
142 m_common->state.shadowSize = IntSize();
143 m_common->state.shadowBlur = 0;
144 m_common->state.shadowColor = Color();
145 clearPlatformShadow();
146 }
147
getShadow(IntSize & size,int & blur,Color & color) const148 bool GraphicsContext::getShadow(IntSize& size, int& blur, Color& color) const
149 {
150 size = m_common->state.shadowSize;
151 blur = m_common->state.shadowBlur;
152 color = m_common->state.shadowColor;
153
154 return color.isValid() && color.alpha() && (blur || size.width() || size.height());
155 }
156
strokeThickness() const157 float GraphicsContext::strokeThickness() const
158 {
159 return m_common->state.strokeThickness;
160 }
161
strokeStyle() const162 StrokeStyle GraphicsContext::strokeStyle() const
163 {
164 return m_common->state.strokeStyle;
165 }
166
strokeColor() const167 Color GraphicsContext::strokeColor() const
168 {
169 return m_common->state.strokeColor;
170 }
171
strokeColorSpace() const172 ColorSpace GraphicsContext::strokeColorSpace() const
173 {
174 return m_common->state.strokeColorSpace;
175 }
176
fillRule() const177 WindRule GraphicsContext::fillRule() const
178 {
179 return m_common->state.fillRule;
180 }
181
setFillRule(WindRule fillRule)182 void GraphicsContext::setFillRule(WindRule fillRule)
183 {
184 m_common->state.fillRule = fillRule;
185 }
186
setFillColor(const Color & color,ColorSpace colorSpace)187 void GraphicsContext::setFillColor(const Color& color, ColorSpace colorSpace)
188 {
189 m_common->state.fillColor = color;
190 m_common->state.fillColorSpace = colorSpace;
191 m_common->state.fillGradient.clear();
192 m_common->state.fillPattern.clear();
193 setPlatformFillColor(color, colorSpace);
194 }
195
fillColor() const196 Color GraphicsContext::fillColor() const
197 {
198 return m_common->state.fillColor;
199 }
200
fillColorSpace() const201 ColorSpace GraphicsContext::fillColorSpace() const
202 {
203 return m_common->state.fillColorSpace;
204 }
205
setShouldAntialias(bool b)206 void GraphicsContext::setShouldAntialias(bool b)
207 {
208 m_common->state.shouldAntialias = b;
209 setPlatformShouldAntialias(b);
210 }
211
shouldAntialias() const212 bool GraphicsContext::shouldAntialias() const
213 {
214 return m_common->state.shouldAntialias;
215 }
216
setStrokePattern(PassRefPtr<Pattern> pattern)217 void GraphicsContext::setStrokePattern(PassRefPtr<Pattern> pattern)
218 {
219 ASSERT(pattern);
220 if (!pattern) {
221 setStrokeColor(Color::black, DeviceColorSpace);
222 return;
223 }
224 m_common->state.strokeGradient.clear();
225 m_common->state.strokePattern = pattern;
226 setPlatformStrokePattern(m_common->state.strokePattern.get());
227 }
228
setFillPattern(PassRefPtr<Pattern> pattern)229 void GraphicsContext::setFillPattern(PassRefPtr<Pattern> pattern)
230 {
231 ASSERT(pattern);
232 if (!pattern) {
233 setFillColor(Color::black, DeviceColorSpace);
234 return;
235 }
236 m_common->state.fillGradient.clear();
237 m_common->state.fillPattern = pattern;
238 setPlatformFillPattern(m_common->state.fillPattern.get());
239 }
240
setStrokeGradient(PassRefPtr<Gradient> gradient)241 void GraphicsContext::setStrokeGradient(PassRefPtr<Gradient> gradient)
242 {
243 ASSERT(gradient);
244 if (!gradient) {
245 setStrokeColor(Color::black, DeviceColorSpace);
246 return;
247 }
248 m_common->state.strokeGradient = gradient;
249 m_common->state.strokePattern.clear();
250 setPlatformStrokeGradient(m_common->state.strokeGradient.get());
251 }
252
setFillGradient(PassRefPtr<Gradient> gradient)253 void GraphicsContext::setFillGradient(PassRefPtr<Gradient> gradient)
254 {
255 ASSERT(gradient);
256 if (!gradient) {
257 setFillColor(Color::black, DeviceColorSpace);
258 return;
259 }
260 m_common->state.fillGradient = gradient;
261 m_common->state.fillPattern.clear();
262 setPlatformFillGradient(m_common->state.fillGradient.get());
263 }
264
fillGradient() const265 Gradient* GraphicsContext::fillGradient() const
266 {
267 return m_common->state.fillGradient.get();
268 }
269
strokeGradient() const270 Gradient* GraphicsContext::strokeGradient() const
271 {
272 return m_common->state.strokeGradient.get();
273 }
274
fillPattern() const275 Pattern* GraphicsContext::fillPattern() const
276 {
277 return m_common->state.fillPattern.get();
278 }
279
strokePattern() const280 Pattern* GraphicsContext::strokePattern() const
281 {
282 return m_common->state.strokePattern.get();
283 }
284
setShadowsIgnoreTransforms(bool ignoreTransforms)285 void GraphicsContext::setShadowsIgnoreTransforms(bool ignoreTransforms)
286 {
287 m_common->state.shadowsIgnoreTransforms = ignoreTransforms;
288 }
289
updatingControlTints() const290 bool GraphicsContext::updatingControlTints() const
291 {
292 return m_common->m_updatingControlTints;
293 }
294
setUpdatingControlTints(bool b)295 void GraphicsContext::setUpdatingControlTints(bool b)
296 {
297 setPaintingDisabled(b);
298 m_common->m_updatingControlTints = b;
299 }
300
setPaintingDisabled(bool f)301 void GraphicsContext::setPaintingDisabled(bool f)
302 {
303 m_common->state.paintingDisabled = f;
304 }
305
paintingDisabled() const306 bool GraphicsContext::paintingDisabled() const
307 {
308 return m_common->state.paintingDisabled;
309 }
310
drawImage(Image * image,ColorSpace styleColorSpace,const IntPoint & p,CompositeOperator op)311 void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const IntPoint& p, CompositeOperator op)
312 {
313 drawImage(image, styleColorSpace, p, IntRect(0, 0, -1, -1), op);
314 }
315
drawImage(Image * image,ColorSpace styleColorSpace,const IntRect & r,CompositeOperator op,bool useLowQualityScale)316 void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const IntRect& r, CompositeOperator op, bool useLowQualityScale)
317 {
318 drawImage(image, styleColorSpace, r, IntRect(0, 0, -1, -1), op, useLowQualityScale);
319 }
320
drawImage(Image * image,ColorSpace styleColorSpace,const IntPoint & dest,const IntRect & srcRect,CompositeOperator op)321 void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const IntPoint& dest, const IntRect& srcRect, CompositeOperator op)
322 {
323 drawImage(image, styleColorSpace, IntRect(dest, srcRect.size()), srcRect, op);
324 }
325
drawImage(Image * image,ColorSpace styleColorSpace,const IntRect & dest,const IntRect & srcRect,CompositeOperator op,bool useLowQualityScale)326 void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const IntRect& dest, const IntRect& srcRect, CompositeOperator op, bool useLowQualityScale)
327 {
328 drawImage(image, styleColorSpace, FloatRect(dest), srcRect, op, useLowQualityScale);
329 }
330
331 #if !OS(WINCE) || PLATFORM(QT)
drawText(const Font & font,const TextRun & run,const IntPoint & point,int from,int to)332 void GraphicsContext::drawText(const Font& font, const TextRun& run, const IntPoint& point, int from, int to)
333 {
334 if (paintingDisabled())
335 return;
336
337 font.drawText(this, run, point, from, to);
338 }
339 #endif
340
drawBidiText(const Font & font,const TextRun & run,const FloatPoint & point)341 void GraphicsContext::drawBidiText(const Font& font, const TextRun& run, const FloatPoint& point)
342 {
343 if (paintingDisabled())
344 return;
345
346 BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver;
347 WTF::Unicode::Direction paragraphDirection = run.ltr() ? WTF::Unicode::LeftToRight : WTF::Unicode::RightToLeft;
348
349 bidiResolver.setStatus(BidiStatus(paragraphDirection, paragraphDirection, paragraphDirection, BidiContext::create(run.ltr() ? 0 : 1, paragraphDirection, run.directionalOverride())));
350
351 bidiResolver.setPosition(TextRunIterator(&run, 0));
352 bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length()));
353
354 if (!bidiResolver.runCount())
355 return;
356
357 FloatPoint currPoint = point;
358 BidiCharacterRun* bidiRun = bidiResolver.firstRun();
359 while (bidiRun) {
360
361 TextRun subrun = run;
362 subrun.setText(run.data(bidiRun->start()), bidiRun->stop() - bidiRun->start());
363 subrun.setRTL(bidiRun->level() % 2);
364 subrun.setDirectionalOverride(bidiRun->dirOverride(false));
365
366 font.drawText(this, subrun, currPoint);
367
368 bidiRun = bidiRun->next();
369 // FIXME: Have Font::drawText return the width of what it drew so that we don't have to re-measure here.
370 if (bidiRun)
371 currPoint.move(font.floatWidth(subrun), 0.f);
372 }
373
374 bidiResolver.deleteRuns();
375 }
376
drawHighlightForText(const Font & font,const TextRun & run,const IntPoint & point,int h,const Color & backgroundColor,ColorSpace colorSpace,int from,int to)377 void GraphicsContext::drawHighlightForText(const Font& font, const TextRun& run, const IntPoint& point, int h, const Color& backgroundColor, ColorSpace colorSpace, int from, int to)
378 {
379 if (paintingDisabled())
380 return;
381
382 fillRect(font.selectionRectForText(run, point, h, from, to), backgroundColor, colorSpace);
383 }
384
drawImage(Image * image,ColorSpace styleColorSpace,const FloatRect & dest,const FloatRect & src,CompositeOperator op,bool useLowQualityScale)385 void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const FloatRect& dest, const FloatRect& src, CompositeOperator op, bool useLowQualityScale)
386 {
387 if (paintingDisabled() || !image)
388 return;
389
390 float tsw = src.width();
391 float tsh = src.height();
392 float tw = dest.width();
393 float th = dest.height();
394
395 if (tsw == -1)
396 tsw = image->width();
397 if (tsh == -1)
398 tsh = image->height();
399
400 if (tw == -1)
401 tw = image->width();
402 if (th == -1)
403 th = image->height();
404
405 if (useLowQualityScale) {
406 save();
407 setImageInterpolationQuality(InterpolationNone);
408 }
409 image->draw(this, FloatRect(dest.location(), FloatSize(tw, th)), FloatRect(src.location(), FloatSize(tsw, tsh)), styleColorSpace, op);
410 if (useLowQualityScale)
411 restore();
412 }
413
drawTiledImage(Image * image,ColorSpace styleColorSpace,const IntRect & rect,const IntPoint & srcPoint,const IntSize & tileSize,CompositeOperator op,bool useLowQualityScale)414 void GraphicsContext::drawTiledImage(Image* image, ColorSpace styleColorSpace, const IntRect& rect, const IntPoint& srcPoint, const IntSize& tileSize, CompositeOperator op, bool useLowQualityScale)
415 {
416 if (paintingDisabled() || !image)
417 return;
418 if (useLowQualityScale) {
419 save();
420 setImageInterpolationQuality(InterpolationLow);
421 }
422 image->drawTiled(this, rect, srcPoint, tileSize, styleColorSpace, op);
423 if (useLowQualityScale)
424 restore();
425 }
426
drawTiledImage(Image * image,ColorSpace styleColorSpace,const IntRect & dest,const IntRect & srcRect,Image::TileRule hRule,Image::TileRule vRule,CompositeOperator op,bool useLowQualityScale)427 void GraphicsContext::drawTiledImage(Image* image, ColorSpace styleColorSpace, const IntRect& dest, const IntRect& srcRect, Image::TileRule hRule, Image::TileRule vRule, CompositeOperator op, bool useLowQualityScale)
428 {
429 if (paintingDisabled() || !image)
430 return;
431
432 if (useLowQualityScale) {
433 save();
434 setImageInterpolationQuality(InterpolationLow);
435 }
436 if (hRule == Image::StretchTile && vRule == Image::StretchTile)
437 // Just do a scale.
438 drawImage(image, styleColorSpace, dest, srcRect, op);
439 else
440 image->drawTiled(this, dest, srcRect, hRule, vRule, styleColorSpace, op);
441 if (useLowQualityScale)
442 restore();
443 }
444
addRoundedRectClip(const IntRect & rect,const IntSize & topLeft,const IntSize & topRight,const IntSize & bottomLeft,const IntSize & bottomRight)445 void GraphicsContext::addRoundedRectClip(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight,
446 const IntSize& bottomLeft, const IntSize& bottomRight)
447 {
448 if (paintingDisabled())
449 return;
450
451 clip(Path::createRoundedRectangle(rect, topLeft, topRight, bottomLeft, bottomRight));
452 }
453
clipOutRoundedRect(const IntRect & rect,const IntSize & topLeft,const IntSize & topRight,const IntSize & bottomLeft,const IntSize & bottomRight)454 void GraphicsContext::clipOutRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight,
455 const IntSize& bottomLeft, const IntSize& bottomRight)
456 {
457 if (paintingDisabled())
458 return;
459
460 clipOut(Path::createRoundedRectangle(rect, topLeft, topRight, bottomLeft, bottomRight));
461 }
462
textDrawingMode()463 int GraphicsContext::textDrawingMode()
464 {
465 return m_common->state.textDrawingMode;
466 }
467
setTextDrawingMode(int mode)468 void GraphicsContext::setTextDrawingMode(int mode)
469 {
470 m_common->state.textDrawingMode = mode;
471 if (paintingDisabled())
472 return;
473 setPlatformTextDrawingMode(mode);
474 }
475
fillRect(const FloatRect & rect,Generator & generator)476 void GraphicsContext::fillRect(const FloatRect& rect, Generator& generator)
477 {
478 if (paintingDisabled())
479 return;
480 generator.fill(this, rect);
481 }
482
483 #if !(PLATFORM(SKIA) && !PLATFORM(ANDROID))
setPlatformFillGradient(Gradient *)484 void GraphicsContext::setPlatformFillGradient(Gradient*)
485 {
486 }
487
setPlatformFillPattern(Pattern *)488 void GraphicsContext::setPlatformFillPattern(Pattern*)
489 {
490 }
491
setPlatformStrokeGradient(Gradient *)492 void GraphicsContext::setPlatformStrokeGradient(Gradient*)
493 {
494 }
495
setPlatformStrokePattern(Pattern *)496 void GraphicsContext::setPlatformStrokePattern(Pattern*)
497 {
498 }
499 #endif
500
501 #if !PLATFORM(CG) && !(PLATFORM(SKIA) && !PLATFORM(ANDROID))
502 // Implement this if you want to go ahead and push the drawing mode into your native context
503 // immediately.
setPlatformTextDrawingMode(int mode)504 void GraphicsContext::setPlatformTextDrawingMode(int mode)
505 {
506 }
507 #endif
508
509 #if !PLATFORM(QT) && !PLATFORM(CAIRO) && !(PLATFORM(SKIA) && !PLATFORM(ANDROID)) && !PLATFORM(HAIKU) && !PLATFORM(OPENVG)
setPlatformStrokeStyle(const StrokeStyle &)510 void GraphicsContext::setPlatformStrokeStyle(const StrokeStyle&)
511 {
512 }
513 #endif
514
adjustLineToPixelBoundaries(FloatPoint & p1,FloatPoint & p2,float strokeWidth,const StrokeStyle & penStyle)515 void GraphicsContext::adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth, const StrokeStyle& penStyle)
516 {
517 // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic
518 // works out. For example, with a border width of 3, WebKit will pass us (y1+y2)/2, e.g.,
519 // (50+53)/2 = 103/2 = 51 when we want 51.5. It is always true that an even width gave
520 // us a perfect position, but an odd width gave us a position that is off by exactly 0.5.
521 if (penStyle == DottedStroke || penStyle == DashedStroke) {
522 if (p1.x() == p2.x()) {
523 p1.setY(p1.y() + strokeWidth);
524 p2.setY(p2.y() - strokeWidth);
525 } else {
526 p1.setX(p1.x() + strokeWidth);
527 p2.setX(p2.x() - strokeWidth);
528 }
529 }
530
531 if (static_cast<int>(strokeWidth) % 2) { //odd
532 if (p1.x() == p2.x()) {
533 // We're a vertical line. Adjust our x.
534 p1.setX(p1.x() + 0.5f);
535 p2.setX(p2.x() + 0.5f);
536 } else {
537 // We're a horizontal line. Adjust our y.
538 p1.setY(p1.y() + 0.5f);
539 p2.setY(p2.y() + 0.5f);
540 }
541 }
542 }
543
544 }
545