1 /*
2 * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, 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 COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include "config.h"
27 #include "GraphicsContext.h"
28
29 #include "BidiResolver.h"
30 #include "Generator.h"
31 #include "GraphicsContextPrivate.h"
32 #include "Font.h"
33 #include "NotImplemented.h"
34
35 using namespace std;
36
37 namespace WebCore {
38
39 class TextRunIterator {
40 public:
TextRunIterator()41 TextRunIterator()
42 : m_textRun(0)
43 , m_offset(0)
44 {
45 }
46
TextRunIterator(const TextRun * textRun,unsigned offset)47 TextRunIterator(const TextRun* textRun, unsigned offset)
48 : m_textRun(textRun)
49 , m_offset(offset)
50 {
51 }
52
TextRunIterator(const TextRunIterator & other)53 TextRunIterator(const TextRunIterator& other)
54 : m_textRun(other.m_textRun)
55 , m_offset(other.m_offset)
56 {
57 }
58
offset() const59 unsigned offset() const { return m_offset; }
increment()60 void increment() { m_offset++; }
atEnd() const61 bool atEnd() const { return !m_textRun || m_offset >= m_textRun->length(); }
current() const62 UChar current() const { return (*m_textRun)[m_offset]; }
direction() const63 WTF::Unicode::Direction direction() const { return atEnd() ? WTF::Unicode::OtherNeutral : WTF::Unicode::direction(current()); }
64
operator ==(const TextRunIterator & other)65 bool operator==(const TextRunIterator& other)
66 {
67 return m_offset == other.m_offset && m_textRun == other.m_textRun;
68 }
69
operator !=(const TextRunIterator & other)70 bool operator!=(const TextRunIterator& other) { return !operator==(other); }
71
72 private:
73 const TextRun* m_textRun;
74 int m_offset;
75 };
76
createGraphicsContextPrivate()77 GraphicsContextPrivate* GraphicsContext::createGraphicsContextPrivate()
78 {
79 return new GraphicsContextPrivate;
80 }
81
destroyGraphicsContextPrivate(GraphicsContextPrivate * deleteMe)82 void GraphicsContext::destroyGraphicsContextPrivate(GraphicsContextPrivate* deleteMe)
83 {
84 delete deleteMe;
85 }
86
save()87 void GraphicsContext::save()
88 {
89 if (paintingDisabled())
90 return;
91
92 m_common->stack.append(m_common->state);
93
94 savePlatformState();
95 }
96
restore()97 void GraphicsContext::restore()
98 {
99 if (paintingDisabled())
100 return;
101
102 if (m_common->stack.isEmpty()) {
103 LOG_ERROR("ERROR void GraphicsContext::restore() stack is empty");
104 return;
105 }
106 m_common->state = m_common->stack.last();
107 m_common->stack.removeLast();
108
109 restorePlatformState();
110 }
111
setStrokeThickness(float thickness)112 void GraphicsContext::setStrokeThickness(float thickness)
113 {
114 m_common->state.strokeThickness = thickness;
115 setPlatformStrokeThickness(thickness);
116 }
117
setStrokeStyle(const StrokeStyle & style)118 void GraphicsContext::setStrokeStyle(const StrokeStyle& style)
119 {
120 m_common->state.strokeStyle = style;
121 setPlatformStrokeStyle(style);
122 }
123
setStrokeColor(const Color & color)124 void GraphicsContext::setStrokeColor(const Color& color)
125 {
126 m_common->state.strokeColorSpace = SolidColorSpace;
127 m_common->state.strokeColor = color;
128 setPlatformStrokeColor(color);
129 }
130
setShadow(const IntSize & size,int blur,const Color & color)131 void GraphicsContext::setShadow(const IntSize& size, int blur, const Color& color)
132 {
133 m_common->state.shadowSize = size;
134 m_common->state.shadowBlur = blur;
135 m_common->state.shadowColor = color;
136 setPlatformShadow(size, blur, color);
137 }
138
clearShadow()139 void GraphicsContext::clearShadow()
140 {
141 m_common->state.shadowSize = IntSize();
142 m_common->state.shadowBlur = 0;
143 m_common->state.shadowColor = Color();
144 clearPlatformShadow();
145 }
146
getShadow(IntSize & size,int & blur,Color & color) const147 bool GraphicsContext::getShadow(IntSize& size, int& blur, Color& color) const
148 {
149 size = m_common->state.shadowSize;
150 blur = m_common->state.shadowBlur;
151 color = m_common->state.shadowColor;
152
153 return color.isValid() && color.alpha() && (blur || size.width() || size.height());
154 }
155
strokeThickness() const156 float GraphicsContext::strokeThickness() const
157 {
158 return m_common->state.strokeThickness;
159 }
160
strokeStyle() const161 StrokeStyle GraphicsContext::strokeStyle() const
162 {
163 return m_common->state.strokeStyle;
164 }
165
strokeColor() const166 Color GraphicsContext::strokeColor() const
167 {
168 return m_common->state.strokeColor;
169 }
170
fillRule() const171 WindRule GraphicsContext::fillRule() const
172 {
173 return m_common->state.fillRule;
174 }
175
setFillRule(WindRule fillRule)176 void GraphicsContext::setFillRule(WindRule fillRule)
177 {
178 m_common->state.fillRule = fillRule;
179 }
180
spreadMethod() const181 GradientSpreadMethod GraphicsContext::spreadMethod() const
182 {
183 return m_common->state.spreadMethod;
184 }
185
setSpreadMethod(GradientSpreadMethod spreadMethod)186 void GraphicsContext::setSpreadMethod(GradientSpreadMethod spreadMethod)
187 {
188 m_common->state.spreadMethod = spreadMethod;
189 }
190
setFillColor(const Color & color)191 void GraphicsContext::setFillColor(const Color& color)
192 {
193 m_common->state.fillColorSpace = SolidColorSpace;
194 m_common->state.fillColor = color;
195 setPlatformFillColor(color);
196 }
197
fillColor() const198 Color GraphicsContext::fillColor() const
199 {
200 return m_common->state.fillColor;
201 }
202
setShouldAntialias(bool b)203 void GraphicsContext::setShouldAntialias(bool b)
204 {
205 m_common->state.shouldAntialias = b;
206 setPlatformShouldAntialias(b);
207 }
208
shouldAntialias() const209 bool GraphicsContext::shouldAntialias() const
210 {
211 return m_common->state.shouldAntialias;
212 }
213
setStrokePattern(PassRefPtr<Pattern> pattern)214 void GraphicsContext::setStrokePattern(PassRefPtr<Pattern> pattern)
215 {
216 ASSERT(pattern);
217 if (!pattern) {
218 setStrokeColor(Color::black);
219 return;
220 }
221 m_common->state.strokeColorSpace = PatternColorSpace;
222 m_common->state.strokePattern = pattern;
223 }
224
setFillPattern(PassRefPtr<Pattern> pattern)225 void GraphicsContext::setFillPattern(PassRefPtr<Pattern> pattern)
226 {
227 ASSERT(pattern);
228 if (!pattern) {
229 setFillColor(Color::black);
230 return;
231 }
232 m_common->state.fillColorSpace = PatternColorSpace;
233 m_common->state.fillPattern = pattern;
234 }
235
setStrokeGradient(PassRefPtr<Gradient> gradient)236 void GraphicsContext::setStrokeGradient(PassRefPtr<Gradient> gradient)
237 {
238 ASSERT(gradient);
239 if (!gradient) {
240 setStrokeColor(Color::black);
241 return;
242 }
243 m_common->state.strokeColorSpace = GradientColorSpace;
244 m_common->state.strokeGradient = gradient;
245 }
246
setFillGradient(PassRefPtr<Gradient> gradient)247 void GraphicsContext::setFillGradient(PassRefPtr<Gradient> gradient)
248 {
249 ASSERT(gradient);
250 if (!gradient) {
251 setFillColor(Color::black);
252 return;
253 }
254 m_common->state.fillColorSpace = GradientColorSpace;
255 m_common->state.fillGradient = gradient;
256 }
257
setShadowsIgnoreTransforms(bool ignoreTransforms)258 void GraphicsContext::setShadowsIgnoreTransforms(bool ignoreTransforms)
259 {
260 m_common->state.shadowsIgnoreTransforms = ignoreTransforms;
261 }
262
updatingControlTints() const263 bool GraphicsContext::updatingControlTints() const
264 {
265 return m_common->m_updatingControlTints;
266 }
267
setUpdatingControlTints(bool b)268 void GraphicsContext::setUpdatingControlTints(bool b)
269 {
270 setPaintingDisabled(b);
271 m_common->m_updatingControlTints = b;
272 }
273
setPaintingDisabled(bool f)274 void GraphicsContext::setPaintingDisabled(bool f)
275 {
276 m_common->state.paintingDisabled = f;
277 }
278
paintingDisabled() const279 bool GraphicsContext::paintingDisabled() const
280 {
281 return m_common->state.paintingDisabled;
282 }
283
drawImage(Image * image,const IntPoint & p,CompositeOperator op)284 void GraphicsContext::drawImage(Image* image, const IntPoint& p, CompositeOperator op)
285 {
286 drawImage(image, p, IntRect(0, 0, -1, -1), op);
287 }
288
drawImage(Image * image,const IntRect & r,CompositeOperator op,bool useLowQualityScale)289 void GraphicsContext::drawImage(Image* image, const IntRect& r, CompositeOperator op, bool useLowQualityScale)
290 {
291 drawImage(image, r, IntRect(0, 0, -1, -1), op, useLowQualityScale);
292 }
293
drawImage(Image * image,const IntPoint & dest,const IntRect & srcRect,CompositeOperator op)294 void GraphicsContext::drawImage(Image* image, const IntPoint& dest, const IntRect& srcRect, CompositeOperator op)
295 {
296 drawImage(image, IntRect(dest, srcRect.size()), srcRect, op);
297 }
298
drawImage(Image * image,const IntRect & dest,const IntRect & srcRect,CompositeOperator op,bool useLowQualityScale)299 void GraphicsContext::drawImage(Image* image, const IntRect& dest, const IntRect& srcRect, CompositeOperator op, bool useLowQualityScale)
300 {
301 drawImage(image, FloatRect(dest), srcRect, op, useLowQualityScale);
302 }
303
drawText(const Font & font,const TextRun & run,const IntPoint & point,int from,int to)304 void GraphicsContext::drawText(const Font& font, const TextRun& run, const IntPoint& point, int from, int to)
305 {
306 if (paintingDisabled())
307 return;
308
309 font.drawText(this, run, point, from, to);
310 }
311
drawBidiText(const Font & font,const TextRun & run,const FloatPoint & point)312 void GraphicsContext::drawBidiText(const Font& font, const TextRun& run, const FloatPoint& point)
313 {
314 if (paintingDisabled())
315 return;
316
317 BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver;
318 WTF::Unicode::Direction paragraphDirection = run.ltr() ? WTF::Unicode::LeftToRight : WTF::Unicode::RightToLeft;
319
320 bidiResolver.setStatus(BidiStatus(paragraphDirection, paragraphDirection, paragraphDirection, new BidiContext(run.ltr() ? 0 : 1, paragraphDirection, run.directionalOverride())));
321
322 bidiResolver.setPosition(TextRunIterator(&run, 0));
323 bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length()));
324
325 if (!bidiResolver.runCount())
326 return;
327
328 FloatPoint currPoint = point;
329 BidiCharacterRun* bidiRun = bidiResolver.firstRun();
330 while (bidiRun) {
331
332 TextRun subrun = run;
333 subrun.setText(run.data(bidiRun->start()), bidiRun->stop() - bidiRun->start());
334 subrun.setRTL(bidiRun->level() % 2);
335 subrun.setDirectionalOverride(bidiRun->dirOverride(false));
336
337 font.drawText(this, subrun, currPoint);
338
339 bidiRun = bidiRun->next();
340 // FIXME: Have Font::drawText return the width of what it drew so that we don't have to re-measure here.
341 if (bidiRun)
342 currPoint.move(font.floatWidth(subrun), 0.f);
343 }
344
345 bidiResolver.deleteRuns();
346 }
347
drawHighlightForText(const Font & font,const TextRun & run,const IntPoint & point,int h,const Color & backgroundColor,int from,int to)348 void GraphicsContext::drawHighlightForText(const Font& font, const TextRun& run, const IntPoint& point, int h, const Color& backgroundColor, int from, int to)
349 {
350 if (paintingDisabled())
351 return;
352
353 fillRect(font.selectionRectForText(run, point, h, from, to), backgroundColor);
354 }
355
initFocusRing(int width,int offset)356 void GraphicsContext::initFocusRing(int width, int offset)
357 {
358 if (paintingDisabled())
359 return;
360 clearFocusRing();
361
362 m_common->m_focusRingWidth = width;
363 m_common->m_focusRingOffset = offset;
364 }
365
clearFocusRing()366 void GraphicsContext::clearFocusRing()
367 {
368 m_common->m_focusRingRects.clear();
369 }
370
focusRingBoundingRect()371 IntRect GraphicsContext::focusRingBoundingRect()
372 {
373 IntRect result = IntRect(0, 0, 0, 0);
374
375 const Vector<IntRect>& rects = focusRingRects();
376 unsigned rectCount = rects.size();
377 for (unsigned i = 0; i < rectCount; i++)
378 result.unite(rects[i]);
379
380 return result;
381 }
382
addFocusRingRect(const IntRect & rect)383 void GraphicsContext::addFocusRingRect(const IntRect& rect)
384 {
385 if (paintingDisabled() || rect.isEmpty())
386 return;
387 m_common->m_focusRingRects.append(rect);
388 }
389
focusRingWidth() const390 int GraphicsContext::focusRingWidth() const
391 {
392 return m_common->m_focusRingWidth;
393 }
394
focusRingOffset() const395 int GraphicsContext::focusRingOffset() const
396 {
397 return m_common->m_focusRingOffset;
398 }
399
focusRingRects() const400 const Vector<IntRect>& GraphicsContext::focusRingRects() const
401 {
402 return m_common->m_focusRingRects;
403 }
404
drawImage(Image * image,const FloatRect & dest,const FloatRect & src,CompositeOperator op,bool useLowQualityScale)405 void GraphicsContext::drawImage(Image* image, const FloatRect& dest, const FloatRect& src, CompositeOperator op, bool useLowQualityScale)
406 {
407 if (paintingDisabled() || !image)
408 return;
409
410 float tsw = src.width();
411 float tsh = src.height();
412 float tw = dest.width();
413 float th = dest.height();
414
415 if (tsw == -1)
416 tsw = image->width();
417 if (tsh == -1)
418 tsh = image->height();
419
420 if (tw == -1)
421 tw = image->width();
422 if (th == -1)
423 th = image->height();
424
425 if (useLowQualityScale) {
426 save();
427 setImageInterpolationQuality(InterpolationNone);
428 }
429 image->draw(this, FloatRect(dest.location(), FloatSize(tw, th)), FloatRect(src.location(), FloatSize(tsw, tsh)), op);
430 if (useLowQualityScale)
431 restore();
432 }
433
drawTiledImage(Image * image,const IntRect & rect,const IntPoint & srcPoint,const IntSize & tileSize,CompositeOperator op)434 void GraphicsContext::drawTiledImage(Image* image, const IntRect& rect, const IntPoint& srcPoint, const IntSize& tileSize, CompositeOperator op)
435 {
436 if (paintingDisabled() || !image)
437 return;
438
439 image->drawTiled(this, rect, srcPoint, tileSize, op);
440 }
441
drawTiledImage(Image * image,const IntRect & dest,const IntRect & srcRect,Image::TileRule hRule,Image::TileRule vRule,CompositeOperator op)442 void GraphicsContext::drawTiledImage(Image* image, const IntRect& dest, const IntRect& srcRect, Image::TileRule hRule, Image::TileRule vRule, CompositeOperator op)
443 {
444 if (paintingDisabled() || !image)
445 return;
446
447 if (hRule == Image::StretchTile && vRule == Image::StretchTile)
448 // Just do a scale.
449 return drawImage(image, dest, srcRect, op);
450
451 image->drawTiled(this, dest, srcRect, hRule, vRule, op);
452 }
453
addRoundedRectClip(const IntRect & rect,const IntSize & topLeft,const IntSize & topRight,const IntSize & bottomLeft,const IntSize & bottomRight)454 void GraphicsContext::addRoundedRectClip(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight,
455 const IntSize& bottomLeft, const IntSize& bottomRight)
456 {
457 if (paintingDisabled())
458 return;
459
460 clip(Path::createRoundedRectangle(rect, topLeft, topRight, bottomLeft, bottomRight));
461 }
462
clipOutRoundedRect(const IntRect & rect,const IntSize & topLeft,const IntSize & topRight,const IntSize & bottomLeft,const IntSize & bottomRight)463 void GraphicsContext::clipOutRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight,
464 const IntSize& bottomLeft, const IntSize& bottomRight)
465 {
466 if (paintingDisabled())
467 return;
468
469 clipOut(Path::createRoundedRectangle(rect, topLeft, topRight, bottomLeft, bottomRight));
470 }
471
textDrawingMode()472 int GraphicsContext::textDrawingMode()
473 {
474 return m_common->state.textDrawingMode;
475 }
476
setTextDrawingMode(int mode)477 void GraphicsContext::setTextDrawingMode(int mode)
478 {
479 m_common->state.textDrawingMode = mode;
480 if (paintingDisabled())
481 return;
482 setPlatformTextDrawingMode(mode);
483 }
484
fillRect(const FloatRect & rect,Generator & generator)485 void GraphicsContext::fillRect(const FloatRect& rect, Generator& generator)
486 {
487 if (paintingDisabled())
488 return;
489 generator.fill(this, rect);
490 }
491
492 #if !PLATFORM(CG) && !PLATFORM(SKIA)
493 // Implement this if you want to go ahead and push the drawing mode into your native context
494 // immediately.
setPlatformTextDrawingMode(int mode)495 void GraphicsContext::setPlatformTextDrawingMode(int mode)
496 {
497 }
498 #endif
499
500 #if !PLATFORM(QT) && !PLATFORM(CAIRO) && !PLATFORM(SKIA)
setPlatformStrokeStyle(const StrokeStyle &)501 void GraphicsContext::setPlatformStrokeStyle(const StrokeStyle&)
502 {
503 }
504 #endif
505
506 }
507