1/*
2 * Copyright (C) 2003, 2004, 2005, 2006, 2009, 2013 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 "BitmapImage.h"
31#include "FloatRoundedRect.h"
32#include "Gradient.h"
33#include "GraphicsContextImpl.h"
34#include "ImageBuffer.h"
35#include "IntRect.h"
36#include "RoundedRect.h"
37#include "TextRun.h"
38#include <wtf/text/TextStream.h>
39
40namespace WebCore {
41
42class TextRunIterator {
43public:
44 TextRunIterator()
45 : m_textRun(0)
46 , m_offset(0)
47 {
48 }
49
50 TextRunIterator(const TextRun* textRun, unsigned offset)
51 : m_textRun(textRun)
52 , m_offset(offset)
53 {
54 }
55
56 unsigned offset() const { return m_offset; }
57 void increment() { m_offset++; }
58 bool atEnd() const { return !m_textRun || m_offset >= m_textRun->length(); }
59 UChar current() const { return (*m_textRun)[m_offset]; }
60 UCharDirection direction() const { return atEnd() ? U_OTHER_NEUTRAL : u_charDirection(current()); }
61
62 bool operator==(const TextRunIterator& other)
63 {
64 return m_offset == other.m_offset && m_textRun == other.m_textRun;
65 }
66
67 bool operator!=(const TextRunIterator& other) { return !operator==(other); }
68
69private:
70 const TextRun* m_textRun;
71 unsigned m_offset;
72};
73
74#define CHECK_FOR_CHANGED_PROPERTY(flag, property) \
75 if ((m_changeFlags & GraphicsContextState::flag) && (m_state.property != state.property)) \
76 changeFlags |= GraphicsContextState::flag;
77
78GraphicsContextState::StateChangeFlags GraphicsContextStateChange::changesFromState(const GraphicsContextState& state) const
79{
80 GraphicsContextState::StateChangeFlags changeFlags = GraphicsContextState::NoChange;
81
82 CHECK_FOR_CHANGED_PROPERTY(StrokeGradientChange, strokeGradient);
83 CHECK_FOR_CHANGED_PROPERTY(StrokePatternChange, strokePattern);
84 CHECK_FOR_CHANGED_PROPERTY(FillGradientChange, fillGradient);
85 CHECK_FOR_CHANGED_PROPERTY(FillPatternChange, fillPattern);
86
87 if ((m_changeFlags & GraphicsContextState::ShadowChange)
88 && (m_state.shadowOffset != state.shadowOffset
89 || m_state.shadowBlur != state.shadowBlur
90 || m_state.shadowColor != state.shadowColor))
91 changeFlags |= GraphicsContextState::ShadowChange;
92
93 CHECK_FOR_CHANGED_PROPERTY(StrokeThicknessChange, strokeThickness);
94 CHECK_FOR_CHANGED_PROPERTY(TextDrawingModeChange, textDrawingMode);
95 CHECK_FOR_CHANGED_PROPERTY(StrokeColorChange, strokeColor);
96 CHECK_FOR_CHANGED_PROPERTY(FillColorChange, fillColor);
97 CHECK_FOR_CHANGED_PROPERTY(StrokeStyleChange, strokeStyle);
98 CHECK_FOR_CHANGED_PROPERTY(FillRuleChange, fillRule);
99 CHECK_FOR_CHANGED_PROPERTY(AlphaChange, alpha);
100
101 if ((m_changeFlags & (GraphicsContextState::CompositeOperationChange | GraphicsContextState::BlendModeChange))
102 && (m_state.compositeOperator != state.compositeOperator || m_state.blendMode != state.blendMode))
103 changeFlags |= (GraphicsContextState::CompositeOperationChange | GraphicsContextState::BlendModeChange);
104
105 CHECK_FOR_CHANGED_PROPERTY(ShouldAntialiasChange, shouldAntialias);
106 CHECK_FOR_CHANGED_PROPERTY(ShouldSmoothFontsChange, shouldSmoothFonts);
107 CHECK_FOR_CHANGED_PROPERTY(ShouldSubpixelQuantizeFontsChange, shouldSubpixelQuantizeFonts);
108 CHECK_FOR_CHANGED_PROPERTY(ShadowsIgnoreTransformsChange, shadowsIgnoreTransforms);
109 CHECK_FOR_CHANGED_PROPERTY(DrawLuminanceMaskChange, drawLuminanceMask);
110 CHECK_FOR_CHANGED_PROPERTY(ImageInterpolationQualityChange, imageInterpolationQuality);
111
112 return changeFlags;
113}
114
115void GraphicsContextStateChange::accumulate(const GraphicsContextState& state, GraphicsContextState::StateChangeFlags flags)
116{
117 // FIXME: This code should move to GraphicsContextState.
118 if (flags & GraphicsContextState::StrokeGradientChange)
119 m_state.strokeGradient = state.strokeGradient;
120
121 if (flags & GraphicsContextState::StrokePatternChange)
122 m_state.strokePattern = state.strokePattern;
123
124 if (flags & GraphicsContextState::FillGradientChange)
125 m_state.fillGradient = state.fillGradient;
126
127 if (flags & GraphicsContextState::FillPatternChange)
128 m_state.fillPattern = state.fillPattern;
129
130 if (flags & GraphicsContextState::ShadowChange) {
131 // FIXME: Deal with state.shadowsUseLegacyRadius.
132 m_state.shadowOffset = state.shadowOffset;
133 m_state.shadowBlur = state.shadowBlur;
134 m_state.shadowColor = state.shadowColor;
135 }
136
137 if (flags & GraphicsContextState::StrokeThicknessChange)
138 m_state.strokeThickness = state.strokeThickness;
139
140 if (flags & GraphicsContextState::TextDrawingModeChange)
141 m_state.textDrawingMode = state.textDrawingMode;
142
143 if (flags & GraphicsContextState::StrokeColorChange)
144 m_state.strokeColor = state.strokeColor;
145
146 if (flags & GraphicsContextState::FillColorChange)
147 m_state.fillColor = state.fillColor;
148
149 if (flags & GraphicsContextState::StrokeStyleChange)
150 m_state.strokeStyle = state.strokeStyle;
151
152 if (flags & GraphicsContextState::FillRuleChange)
153 m_state.fillRule = state.fillRule;
154
155 if (flags & GraphicsContextState::AlphaChange)
156 m_state.alpha = state.alpha;
157
158 if (flags & (GraphicsContextState::CompositeOperationChange | GraphicsContextState::BlendModeChange)) {
159 m_state.compositeOperator = state.compositeOperator;
160 m_state.blendMode = state.blendMode;
161 }
162
163 if (flags & GraphicsContextState::ShouldAntialiasChange)
164 m_state.shouldAntialias = state.shouldAntialias;
165
166 if (flags & GraphicsContextState::ShouldSmoothFontsChange)
167 m_state.shouldSmoothFonts = state.shouldSmoothFonts;
168
169 if (flags & GraphicsContextState::ShouldSubpixelQuantizeFontsChange)
170 m_state.shouldSubpixelQuantizeFonts = state.shouldSubpixelQuantizeFonts;
171
172 if (flags & GraphicsContextState::ShadowsIgnoreTransformsChange)
173 m_state.shadowsIgnoreTransforms = state.shadowsIgnoreTransforms;
174
175 if (flags & GraphicsContextState::DrawLuminanceMaskChange)
176 m_state.drawLuminanceMask = state.drawLuminanceMask;
177
178 if (flags & GraphicsContextState::ImageInterpolationQualityChange)
179 m_state.imageInterpolationQuality = state.imageInterpolationQuality;
180
181 m_changeFlags |= flags;
182}
183
184void GraphicsContextStateChange::apply(GraphicsContext& context) const
185{
186 if (m_changeFlags & GraphicsContextState::StrokeGradientChange)
187 context.setStrokeGradient(*m_state.strokeGradient);
188
189 if (m_changeFlags & GraphicsContextState::StrokePatternChange)
190 context.setStrokePattern(*m_state.strokePattern);
191
192 if (m_changeFlags & GraphicsContextState::FillGradientChange)
193 context.setFillGradient(*m_state.fillGradient);
194
195 if (m_changeFlags & GraphicsContextState::FillPatternChange)
196 context.setFillPattern(*m_state.fillPattern);
197
198 if (m_changeFlags & GraphicsContextState::ShadowChange) {
199#if USE(CG)
200 if (m_state.shadowsUseLegacyRadius)
201 context.setLegacyShadow(m_state.shadowOffset, m_state.shadowBlur, m_state.shadowColor);
202 else
203#endif
204 context.setShadow(m_state.shadowOffset, m_state.shadowBlur, m_state.shadowColor);
205 }
206
207 if (m_changeFlags & GraphicsContextState::StrokeThicknessChange)
208 context.setStrokeThickness(m_state.strokeThickness);
209
210 if (m_changeFlags & GraphicsContextState::TextDrawingModeChange)
211 context.setTextDrawingMode(m_state.textDrawingMode);
212
213 if (m_changeFlags & GraphicsContextState::StrokeColorChange)
214 context.setStrokeColor(m_state.strokeColor);
215
216 if (m_changeFlags & GraphicsContextState::FillColorChange)
217 context.setFillColor(m_state.fillColor);
218
219 if (m_changeFlags & GraphicsContextState::StrokeStyleChange)
220 context.setStrokeStyle(m_state.strokeStyle);
221
222 if (m_changeFlags & GraphicsContextState::FillRuleChange)
223 context.setFillRule(m_state.fillRule);
224
225 if (m_changeFlags & GraphicsContextState::AlphaChange)
226 context.setAlpha(m_state.alpha);
227
228 if (m_changeFlags & (GraphicsContextState::CompositeOperationChange | GraphicsContextState::BlendModeChange))
229 context.setCompositeOperation(m_state.compositeOperator, m_state.blendMode);
230
231 if (m_changeFlags & GraphicsContextState::ShouldAntialiasChange)
232 context.setShouldAntialias(m_state.shouldAntialias);
233
234 if (m_changeFlags & GraphicsContextState::ShouldSmoothFontsChange)
235 context.setShouldSmoothFonts(m_state.shouldSmoothFonts);
236
237 if (m_changeFlags & GraphicsContextState::ShouldSubpixelQuantizeFontsChange)
238 context.setShouldSubpixelQuantizeFonts(m_state.shouldSubpixelQuantizeFonts);
239
240 if (m_changeFlags & GraphicsContextState::ShadowsIgnoreTransformsChange)
241 context.setShadowsIgnoreTransforms(m_state.shadowsIgnoreTransforms);
242
243 if (m_changeFlags & GraphicsContextState::DrawLuminanceMaskChange)
244 context.setDrawLuminanceMask(m_state.drawLuminanceMask);
245
246 if (m_changeFlags & GraphicsContextState::ImageInterpolationQualityChange)
247 context.setImageInterpolationQuality(m_state.imageInterpolationQuality);
248}
249
250void GraphicsContextStateChange::dump(TextStream& ts) const
251{
252 ts.dumpProperty("change-flags", m_changeFlags);
253
254 if (m_changeFlags & GraphicsContextState::StrokeGradientChange)
255 ts.dumpProperty("stroke-gradient", m_state.strokeGradient.get());
256
257 if (m_changeFlags & GraphicsContextState::StrokePatternChange)
258 ts.dumpProperty("stroke-pattern", m_state.strokePattern.get());
259
260 if (m_changeFlags & GraphicsContextState::FillGradientChange)
261 ts.dumpProperty("fill-gradient", m_state.fillGradient.get());
262
263 if (m_changeFlags & GraphicsContextState::FillPatternChange)
264 ts.dumpProperty("fill-pattern", m_state.fillPattern.get());
265
266 if (m_changeFlags & GraphicsContextState::ShadowChange) {
267 ts.dumpProperty("shadow-blur", m_state.shadowBlur);
268 ts.dumpProperty("shadow-offset", m_state.shadowOffset);
269#if USE(CG)
270 ts.dumpProperty("shadows-use-legacy-radius", m_state.shadowsUseLegacyRadius);
271#endif
272 }
273
274 if (m_changeFlags & GraphicsContextState::StrokeThicknessChange)
275 ts.dumpProperty("stroke-thickness", m_state.strokeThickness);
276
277 if (m_changeFlags & GraphicsContextState::TextDrawingModeChange)
278 ts.dumpProperty("text-drawing-mode", m_state.textDrawingMode);
279
280 if (m_changeFlags & GraphicsContextState::StrokeColorChange)
281 ts.dumpProperty("stroke-color", m_state.strokeColor);
282
283 if (m_changeFlags & GraphicsContextState::FillColorChange)
284 ts.dumpProperty("fill-color", m_state.fillColor);
285
286 if (m_changeFlags & GraphicsContextState::StrokeStyleChange)
287 ts.dumpProperty("stroke-style", m_state.strokeStyle);
288
289 if (m_changeFlags & GraphicsContextState::FillRuleChange)
290 ts.dumpProperty("fill-rule", m_state.fillRule);
291
292 if (m_changeFlags & GraphicsContextState::AlphaChange)
293 ts.dumpProperty("alpha", m_state.alpha);
294
295 if (m_changeFlags & GraphicsContextState::CompositeOperationChange)
296 ts.dumpProperty("composite-operator", m_state.compositeOperator);
297
298 if (m_changeFlags & GraphicsContextState::BlendModeChange)
299 ts.dumpProperty("blend-mode", m_state.blendMode);
300
301 if (m_changeFlags & GraphicsContextState::ShouldAntialiasChange)
302 ts.dumpProperty("should-antialias", m_state.shouldAntialias);
303
304 if (m_changeFlags & GraphicsContextState::ShouldSmoothFontsChange)
305 ts.dumpProperty("should-smooth-fonts", m_state.shouldSmoothFonts);
306
307 if (m_changeFlags & GraphicsContextState::ShouldSubpixelQuantizeFontsChange)
308 ts.dumpProperty("should-subpixel-quantize-fonts", m_state.shouldSubpixelQuantizeFonts);
309
310 if (m_changeFlags & GraphicsContextState::ShadowsIgnoreTransformsChange)
311 ts.dumpProperty("shadows-ignore-transforms", m_state.shadowsIgnoreTransforms);
312
313 if (m_changeFlags & GraphicsContextState::DrawLuminanceMaskChange)
314 ts.dumpProperty("draw-luminance-mask", m_state.drawLuminanceMask);
315}
316
317TextStream& operator<<(TextStream& ts, const GraphicsContextStateChange& stateChange)
318{
319 stateChange.dump(ts);
320 return ts;
321}
322
323GraphicsContext::GraphicsContext(PaintInvalidationReasons paintInvalidationReasons)
324 : m_paintInvalidationReasons(paintInvalidationReasons)
325{
326}
327
328GraphicsContext::GraphicsContext(PlatformGraphicsContext* platformGraphicsContext)
329{
330 platformInit(platformGraphicsContext);
331}
332
333GraphicsContext::GraphicsContext(const GraphicsContextImplFactory& factoryFunction)
334 : m_impl(factoryFunction(*this))
335{
336}
337
338GraphicsContext::~GraphicsContext()
339{
340 ASSERT(m_stack.isEmpty());
341 ASSERT(!m_transparencyCount);
342 platformDestroy();
343}
344
345bool GraphicsContext::hasPlatformContext() const
346{
347 if (m_impl)
348 return m_impl->hasPlatformContext();
349 return !!m_data;
350}
351
352void GraphicsContext::save()
353{
354 if (paintingDisabled())
355 return;
356
357 m_stack.append(m_state);
358
359 if (m_impl) {
360 m_impl->save();
361 return;
362 }
363
364 savePlatformState();
365}
366
367void GraphicsContext::restore()
368{
369 if (paintingDisabled())
370 return;
371
372 if (m_stack.isEmpty()) {
373 LOG_ERROR("ERROR void GraphicsContext::restore() stack is empty");
374 return;
375 }
376
377 m_state = m_stack.last();
378 m_stack.removeLast();
379
380 // Make sure we deallocate the state stack buffer when it goes empty.
381 // Canvas elements will immediately save() again, but that goes into inline capacity.
382 if (m_stack.isEmpty())
383 m_stack.clear();
384
385 if (m_impl) {
386 m_impl->restore();
387 return;
388 }
389
390 restorePlatformState();
391}
392
393void GraphicsContext::drawRaisedEllipse(const FloatRect& rect, const Color& ellipseColor, const Color& shadowColor)
394{
395 if (paintingDisabled())
396 return;
397
398 save();
399
400 setStrokeColor(shadowColor);
401 setFillColor(shadowColor);
402
403 drawEllipse(FloatRect(rect.x(), rect.y() + 1, rect.width(), rect.height()));
404
405 setStrokeColor(ellipseColor);
406 setFillColor(ellipseColor);
407
408 drawEllipse(rect);
409
410 restore();
411}
412
413void GraphicsContext::setStrokeThickness(float thickness)
414{
415 m_state.strokeThickness = thickness;
416 if (m_impl) {
417 m_impl->updateState(m_state, GraphicsContextState::StrokeThicknessChange);
418 return;
419 }
420
421 setPlatformStrokeThickness(thickness);
422}
423
424void GraphicsContext::setStrokeStyle(StrokeStyle style)
425{
426 m_state.strokeStyle = style;
427 if (m_impl) {
428 m_impl->updateState(m_state, GraphicsContextState::StrokeStyleChange);
429 return;
430 }
431 setPlatformStrokeStyle(style);
432}
433
434void GraphicsContext::setStrokeColor(const Color& color)
435{
436 m_state.strokeColor = color;
437 m_state.strokeGradient = nullptr;
438 m_state.strokePattern = nullptr;
439 if (m_impl) {
440 m_impl->updateState(m_state, GraphicsContextState::StrokeColorChange);
441 return;
442 }
443 setPlatformStrokeColor(color);
444}
445
446void GraphicsContext::setShadow(const FloatSize& offset, float blur, const Color& color)
447{
448 m_state.shadowOffset = offset;
449 m_state.shadowBlur = blur;
450 m_state.shadowColor = color;
451#if USE(CG)
452 m_state.shadowsUseLegacyRadius = false;
453#endif
454 if (m_impl) {
455 m_impl->updateState(m_state, GraphicsContextState::ShadowChange);
456 return;
457 }
458 setPlatformShadow(offset, blur, color);
459}
460
461void GraphicsContext::setLegacyShadow(const FloatSize& offset, float blur, const Color& color)
462{
463 m_state.shadowOffset = offset;
464 m_state.shadowBlur = blur;
465 m_state.shadowColor = color;
466#if USE(CG)
467 m_state.shadowsUseLegacyRadius = true;
468#endif
469 if (m_impl) {
470 m_impl->updateState(m_state, GraphicsContextState::ShadowChange);
471 return;
472 }
473 setPlatformShadow(offset, blur, color);
474}
475
476void GraphicsContext::clearShadow()
477{
478 m_state.shadowOffset = FloatSize();
479 m_state.shadowBlur = 0;
480 m_state.shadowColor = Color();
481#if USE(CG)
482 m_state.shadowsUseLegacyRadius = false;
483#endif
484
485 if (m_impl) {
486 m_impl->clearShadow();
487 return;
488 }
489 clearPlatformShadow();
490}
491
492bool GraphicsContext::getShadow(FloatSize& offset, float& blur, Color& color) const
493{
494 offset = m_state.shadowOffset;
495 blur = m_state.shadowBlur;
496 color = m_state.shadowColor;
497
498 return hasShadow();
499}
500
501void GraphicsContext::setFillColor(const Color& color)
502{
503 m_state.fillColor = color;
504 m_state.fillGradient = nullptr;
505 m_state.fillPattern = nullptr;
506
507 if (m_impl) {
508 m_impl->updateState(m_state, GraphicsContextState::FillColorChange);
509 return;
510 }
511
512 setPlatformFillColor(color);
513}
514
515void GraphicsContext::setShadowsIgnoreTransforms(bool shadowsIgnoreTransforms)
516{
517 m_state.shadowsIgnoreTransforms = shadowsIgnoreTransforms;
518 if (m_impl)
519 m_impl->updateState(m_state, GraphicsContextState::ShadowsIgnoreTransformsChange);
520}
521
522void GraphicsContext::setShouldAntialias(bool shouldAntialias)
523{
524 m_state.shouldAntialias = shouldAntialias;
525
526 if (m_impl) {
527 m_impl->updateState(m_state, GraphicsContextState::ShouldAntialiasChange);
528 return;
529 }
530
531 setPlatformShouldAntialias(shouldAntialias);
532}
533
534void GraphicsContext::setShouldSmoothFonts(bool shouldSmoothFonts)
535{
536 m_state.shouldSmoothFonts = shouldSmoothFonts;
537
538 if (m_impl) {
539 m_impl->updateState(m_state, GraphicsContextState::ShouldSmoothFontsChange);
540 return;
541 }
542
543 setPlatformShouldSmoothFonts(shouldSmoothFonts);
544}
545
546void GraphicsContext::setShouldSubpixelQuantizeFonts(bool shouldSubpixelQuantizeFonts)
547{
548 m_state.shouldSubpixelQuantizeFonts = shouldSubpixelQuantizeFonts;
549 if (m_impl)
550 m_impl->updateState(m_state, GraphicsContextState::ShouldSubpixelQuantizeFontsChange);
551}
552
553void GraphicsContext::setImageInterpolationQuality(InterpolationQuality imageInterpolationQuality)
554{
555 m_state.imageInterpolationQuality = imageInterpolationQuality;
556
557 if (paintingDisabled())
558 return;
559
560 if (m_impl) {
561 m_impl->updateState(m_state, GraphicsContextState::ImageInterpolationQualityChange);
562 return;
563 }
564
565 setPlatformImageInterpolationQuality(imageInterpolationQuality);
566}
567
568void GraphicsContext::setStrokePattern(Ref<Pattern>&& pattern)
569{
570 m_state.strokeGradient = nullptr;
571 m_state.strokePattern = WTFMove(pattern);
572 if (m_impl)
573 m_impl->updateState(m_state, GraphicsContextState::StrokePatternChange);
574}
575
576void GraphicsContext::setFillPattern(Ref<Pattern>&& pattern)
577{
578 m_state.fillGradient = nullptr;
579 m_state.fillPattern = WTFMove(pattern);
580 if (m_impl)
581 m_impl->updateState(m_state, GraphicsContextState::FillPatternChange);
582}
583
584void GraphicsContext::setStrokeGradient(Ref<Gradient>&& gradient)
585{
586 m_state.strokeGradient = WTFMove(gradient);
587 m_state.strokePattern = nullptr;
588 if (m_impl)
589 m_impl->updateState(m_state, GraphicsContextState::StrokeGradientChange);
590}
591
592void GraphicsContext::setFillRule(WindRule fillRule)
593{
594 m_state.fillRule = fillRule;
595 if (m_impl)
596 m_impl->updateState(m_state, GraphicsContextState::FillRuleChange);
597}
598
599void GraphicsContext::setFillGradient(Ref<Gradient>&& gradient)
600{
601 m_state.fillGradient = WTFMove(gradient);
602 m_state.fillPattern = nullptr;
603 if (m_impl)
604 m_impl->updateState(m_state, GraphicsContextState::FillGradientChange); // FIXME: also fill pattern?
605}
606
607void GraphicsContext::beginTransparencyLayer(float opacity)
608{
609 if (m_impl) {
610 m_impl->beginTransparencyLayer(opacity);
611 return;
612 }
613 beginPlatformTransparencyLayer(opacity);
614 ++m_transparencyCount;
615}
616
617void GraphicsContext::endTransparencyLayer()
618{
619 if (m_impl) {
620 m_impl->endTransparencyLayer();
621 return;
622 }
623 endPlatformTransparencyLayer();
624 ASSERT(m_transparencyCount > 0);
625 --m_transparencyCount;
626}
627
628float GraphicsContext::drawText(const FontCascade& font, const TextRun& run, const FloatPoint& point, unsigned from, Optional<unsigned> to)
629{
630 if (paintingDisabled())
631 return 0;
632
633 // Display list recording for text content is done at glyphs level. See GraphicsContext::drawGlyphs.
634 return font.drawText(*this, run, point, from, to);
635}
636
637void GraphicsContext::drawGlyphs(const Font& font, const GlyphBuffer& buffer, unsigned from, unsigned numGlyphs, const FloatPoint& point, FontSmoothingMode fontSmoothingMode)
638{
639 if (paintingDisabled())
640 return;
641
642 if (m_impl) {
643 m_impl->drawGlyphs(font, buffer, from, numGlyphs, point, fontSmoothingMode);
644 return;
645 }
646
647 FontCascade::drawGlyphs(*this, font, buffer, from, numGlyphs, point, fontSmoothingMode);
648}
649
650void GraphicsContext::drawEmphasisMarks(const FontCascade& font, const TextRun& run, const AtomicString& mark, const FloatPoint& point, unsigned from, Optional<unsigned> to)
651{
652 if (paintingDisabled())
653 return;
654
655 font.drawEmphasisMarks(*this, run, mark, point, from, to);
656}
657
658void GraphicsContext::drawBidiText(const FontCascade& font, const TextRun& run, const FloatPoint& point, FontCascade::CustomFontNotReadyAction customFontNotReadyAction)
659{
660 if (paintingDisabled())
661 return;
662
663 BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver;
664 bidiResolver.setStatus(BidiStatus(run.direction(), run.directionalOverride()));
665 bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&run, 0));
666
667 // FIXME: This ownership should be reversed. We should pass BidiRunList
668 // to BidiResolver in createBidiRunsForLine.
669 BidiRunList<BidiCharacterRun>& bidiRuns = bidiResolver.runs();
670 bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length()));
671
672 if (!bidiRuns.runCount())
673 return;
674
675 FloatPoint currPoint = point;
676 BidiCharacterRun* bidiRun = bidiRuns.firstRun();
677 while (bidiRun) {
678 TextRun subrun = run.subRun(bidiRun->start(), bidiRun->stop() - bidiRun->start());
679 bool isRTL = bidiRun->level() % 2;
680 subrun.setDirection(isRTL ? TextDirection::RTL : TextDirection::LTR);
681 subrun.setDirectionalOverride(bidiRun->dirOverride(false));
682
683 float width = font.drawText(*this, subrun, currPoint, 0, WTF::nullopt, customFontNotReadyAction);
684 currPoint.move(width, 0);
685
686 bidiRun = bidiRun->next();
687 }
688
689 bidiRuns.clear();
690}
691
692ImageDrawResult GraphicsContext::drawImage(Image& image, const FloatPoint& destination, const ImagePaintingOptions& imagePaintingOptions)
693{
694 return drawImage(image, FloatRect(destination, image.size()), FloatRect(FloatPoint(), image.size()), imagePaintingOptions);
695}
696
697ImageDrawResult GraphicsContext::drawImage(Image& image, const FloatRect& destination, const ImagePaintingOptions& imagePaintingOptions)
698{
699#if PLATFORM(IOS_FAMILY)
700 FloatRect srcRect(FloatPoint(), image.originalSize());
701#else
702 FloatRect srcRect(FloatPoint(), image.size());
703#endif
704
705 return drawImage(image, destination, srcRect, imagePaintingOptions);
706}
707
708ImageDrawResult GraphicsContext::drawImage(Image& image, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& imagePaintingOptions)
709{
710 if (paintingDisabled())
711 return ImageDrawResult::DidNothing;
712
713 if (m_impl)
714 return m_impl->drawImage(image, destination, source, imagePaintingOptions);
715
716 InterpolationQualityMaintainer interpolationQualityForThisScope(*this, imagePaintingOptions.m_interpolationQuality);
717 return image.draw(*this, destination, source, imagePaintingOptions.m_compositeOperator, imagePaintingOptions.m_blendMode, imagePaintingOptions.m_decodingMode, imagePaintingOptions.m_orientationDescription);
718}
719
720ImageDrawResult GraphicsContext::drawTiledImage(Image& image, const FloatRect& destination, const FloatPoint& source, const FloatSize& tileSize, const FloatSize& spacing, const ImagePaintingOptions& imagePaintingOptions)
721{
722 if (paintingDisabled())
723 return ImageDrawResult::DidNothing;
724
725 if (m_impl)
726 return m_impl->drawTiledImage(image, destination, source, tileSize, spacing, imagePaintingOptions);
727
728 InterpolationQualityMaintainer interpolationQualityForThisScope(*this, imagePaintingOptions.m_interpolationQuality);
729 return image.drawTiled(*this, destination, source, tileSize, spacing, imagePaintingOptions.m_compositeOperator, imagePaintingOptions.m_blendMode, imagePaintingOptions.m_decodingMode);
730}
731
732ImageDrawResult GraphicsContext::drawTiledImage(Image& image, const FloatRect& destination, const FloatRect& source, const FloatSize& tileScaleFactor,
733 Image::TileRule hRule, Image::TileRule vRule, const ImagePaintingOptions& imagePaintingOptions)
734{
735 if (paintingDisabled())
736 return ImageDrawResult::DidNothing;
737
738 if (m_impl)
739 return m_impl->drawTiledImage(image, destination, source, tileScaleFactor, hRule, vRule, imagePaintingOptions);
740
741 if (hRule == Image::StretchTile && vRule == Image::StretchTile) {
742 // Just do a scale.
743 return drawImage(image, destination, source, imagePaintingOptions);
744 }
745
746 InterpolationQualityMaintainer interpolationQualityForThisScope(*this, imagePaintingOptions.m_interpolationQuality);
747 return image.drawTiled(*this, destination, source, tileScaleFactor, hRule, vRule, imagePaintingOptions.m_compositeOperator);
748}
749
750void GraphicsContext::drawImageBuffer(ImageBuffer& image, const FloatPoint& destination, const ImagePaintingOptions& imagePaintingOptions)
751{
752 drawImageBuffer(image, FloatRect(destination, image.logicalSize()), FloatRect(FloatPoint(), image.logicalSize()), imagePaintingOptions);
753}
754
755void GraphicsContext::drawImageBuffer(ImageBuffer& image, const FloatRect& destination, const ImagePaintingOptions& imagePaintingOptions)
756{
757 drawImageBuffer(image, destination, FloatRect(FloatPoint(), FloatSize(image.logicalSize())), imagePaintingOptions);
758}
759
760void GraphicsContext::drawImageBuffer(ImageBuffer& image, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& imagePaintingOptions)
761{
762 if (paintingDisabled())
763 return;
764
765 InterpolationQualityMaintainer interpolationQualityForThisScope(*this, imagePaintingOptions.m_interpolationQuality);
766 image.draw(*this, destination, source, imagePaintingOptions.m_compositeOperator, imagePaintingOptions.m_blendMode);
767}
768
769void GraphicsContext::drawConsumingImageBuffer(std::unique_ptr<ImageBuffer> image, const FloatPoint& destination, const ImagePaintingOptions& imagePaintingOptions)
770{
771 if (!image)
772 return;
773 IntSize imageLogicalSize = image->logicalSize();
774 drawConsumingImageBuffer(WTFMove(image), FloatRect(destination, imageLogicalSize), FloatRect(FloatPoint(), imageLogicalSize), imagePaintingOptions);
775}
776
777void GraphicsContext::drawConsumingImageBuffer(std::unique_ptr<ImageBuffer> image, const FloatRect& destination, const ImagePaintingOptions& imagePaintingOptions)
778{
779 if (!image)
780 return;
781 IntSize imageLogicalSize = image->logicalSize();
782 drawConsumingImageBuffer(WTFMove(image), destination, FloatRect(FloatPoint(), FloatSize(imageLogicalSize)), imagePaintingOptions);
783}
784
785void GraphicsContext::drawConsumingImageBuffer(std::unique_ptr<ImageBuffer> image, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& imagePaintingOptions)
786{
787 if (paintingDisabled() || !image)
788 return;
789
790 InterpolationQualityMaintainer interpolationQualityForThisScope(*this, imagePaintingOptions.m_interpolationQuality);
791 ImageBuffer::drawConsuming(WTFMove(image), *this, destination, source, imagePaintingOptions.m_compositeOperator, imagePaintingOptions.m_blendMode);
792}
793
794void GraphicsContext::clipRoundedRect(const FloatRoundedRect& rect)
795{
796 if (paintingDisabled())
797 return;
798
799 Path path;
800 path.addRoundedRect(rect);
801 clipPath(path);
802}
803
804void GraphicsContext::clipOutRoundedRect(const FloatRoundedRect& rect)
805{
806 if (paintingDisabled())
807 return;
808
809 if (!rect.isRounded()) {
810 clipOut(rect.rect());
811 return;
812 }
813
814 Path path;
815 path.addRoundedRect(rect);
816 clipOut(path);
817}
818
819#if !USE(CG) && !USE(DIRECT2D) && !USE(CAIRO)
820IntRect GraphicsContext::clipBounds() const
821{
822 ASSERT_NOT_REACHED();
823 return IntRect();
824}
825#endif
826
827void GraphicsContext::setTextDrawingMode(TextDrawingModeFlags mode)
828{
829 m_state.textDrawingMode = mode;
830 if (paintingDisabled())
831 return;
832
833 if (m_impl) {
834 m_impl->updateState(m_state, GraphicsContextState::TextDrawingModeChange);
835 return;
836 }
837 setPlatformTextDrawingMode(mode);
838}
839
840void GraphicsContext::fillRect(const FloatRect& rect, Gradient& gradient)
841{
842 if (paintingDisabled())
843 return;
844
845 if (m_impl) {
846 m_impl->fillRect(rect, gradient);
847 return;
848 }
849
850 gradient.fill(*this, rect);
851}
852
853void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, CompositeOperator op, BlendMode blendMode)
854{
855 if (paintingDisabled())
856 return;
857
858 if (m_impl) {
859 m_impl->fillRect(rect, color, op, blendMode);
860 return;
861 }
862
863 CompositeOperator previousOperator = compositeOperation();
864 setCompositeOperation(op, blendMode);
865 fillRect(rect, color);
866 setCompositeOperation(previousOperator);
867}
868
869void GraphicsContext::fillRoundedRect(const FloatRoundedRect& rect, const Color& color, BlendMode blendMode)
870{
871 if (paintingDisabled())
872 return;
873
874 if (m_impl) {
875 m_impl->fillRoundedRect(rect, color, blendMode);
876 return;
877 }
878
879 if (rect.isRounded()) {
880 setCompositeOperation(compositeOperation(), blendMode);
881 platformFillRoundedRect(rect, color);
882 setCompositeOperation(compositeOperation());
883 } else
884 fillRect(rect.rect(), color, compositeOperation(), blendMode);
885}
886
887#if !USE(CG) && !USE(DIRECT2D) && !USE(CAIRO)
888void GraphicsContext::fillRectWithRoundedHole(const IntRect& rect, const FloatRoundedRect& roundedHoleRect, const Color& color)
889{
890 if (paintingDisabled())
891 return;
892
893 Path path;
894 path.addRect(rect);
895
896 if (!roundedHoleRect.radii().isZero())
897 path.addRoundedRect(roundedHoleRect);
898 else
899 path.addRect(roundedHoleRect.rect());
900
901 WindRule oldFillRule = fillRule();
902 Color oldFillColor = fillColor();
903
904 setFillRule(WindRule::EvenOdd);
905 setFillColor(color);
906
907 fillPath(path);
908
909 setFillRule(oldFillRule);
910 setFillColor(oldFillColor);
911}
912#endif
913
914void GraphicsContext::setAlpha(float alpha)
915{
916 m_state.alpha = alpha;
917 if (m_impl) {
918 m_impl->updateState(m_state, GraphicsContextState::AlphaChange);
919 return;
920 }
921 setPlatformAlpha(alpha);
922}
923
924void GraphicsContext::setCompositeOperation(CompositeOperator compositeOperation, BlendMode blendMode)
925{
926 m_state.compositeOperator = compositeOperation;
927 m_state.blendMode = blendMode;
928 if (m_impl) {
929 m_impl->updateState(m_state, GraphicsContextState::CompositeOperationChange);
930 return;
931 }
932 setPlatformCompositeOperation(compositeOperation, blendMode);
933}
934
935void GraphicsContext::setDrawLuminanceMask(bool drawLuminanceMask)
936{
937 m_state.drawLuminanceMask = drawLuminanceMask;
938 if (m_impl)
939 m_impl->updateState(m_state, GraphicsContextState::DrawLuminanceMaskChange);
940}
941
942#if !USE(CG) && !USE(DIRECT2D)
943// Implement this if you want to go push the drawing mode into your native context immediately.
944void GraphicsContext::setPlatformTextDrawingMode(TextDrawingModeFlags)
945{
946}
947#endif
948
949#if !USE(CAIRO) && !USE(DIRECT2D)
950void GraphicsContext::setPlatformStrokeStyle(StrokeStyle)
951{
952}
953#endif
954
955#if !USE(CG) && !USE(DIRECT2D)
956void GraphicsContext::setPlatformShouldSmoothFonts(bool)
957{
958}
959#endif
960
961#if !USE(CG) && !USE(DIRECT2D) && !USE(CAIRO)
962bool GraphicsContext::isAcceleratedContext() const
963{
964 return false;
965}
966#endif
967
968void GraphicsContext::adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth, StrokeStyle penStyle)
969{
970 // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic
971 // works out. For example, with a border width of 3, WebKit will pass us (y1+y2)/2, e.g.,
972 // (50+53)/2 = 103/2 = 51 when we want 51.5. It is always true that an even width gave
973 // us a perfect position, but an odd width gave us a position that is off by exactly 0.5.
974 if (penStyle == DottedStroke || penStyle == DashedStroke) {
975 if (p1.x() == p2.x()) {
976 p1.setY(p1.y() + strokeWidth);
977 p2.setY(p2.y() - strokeWidth);
978 } else {
979 p1.setX(p1.x() + strokeWidth);
980 p2.setX(p2.x() - strokeWidth);
981 }
982 }
983
984 if (static_cast<int>(strokeWidth) % 2) { //odd
985 if (p1.x() == p2.x()) {
986 // We're a vertical line. Adjust our x.
987 p1.setX(p1.x() + 0.5f);
988 p2.setX(p2.x() + 0.5f);
989 } else {
990 // We're a horizontal line. Adjust our y.
991 p1.setY(p1.y() + 0.5f);
992 p2.setY(p2.y() + 0.5f);
993 }
994 }
995}
996
997#if !USE(CG) && !USE(DIRECT2D)
998void GraphicsContext::platformApplyDeviceScaleFactor(float)
999{
1000}
1001#endif
1002
1003void GraphicsContext::applyDeviceScaleFactor(float deviceScaleFactor)
1004{
1005 scale(deviceScaleFactor);
1006
1007 if (m_impl) {
1008 m_impl->applyDeviceScaleFactor(deviceScaleFactor);
1009 return;
1010 }
1011
1012 platformApplyDeviceScaleFactor(deviceScaleFactor);
1013}
1014
1015FloatSize GraphicsContext::scaleFactor() const
1016{
1017 AffineTransform transform = getCTM(GraphicsContext::DefinitelyIncludeDeviceScale);
1018 return FloatSize(transform.xScale(), transform.yScale());
1019}
1020
1021FloatSize GraphicsContext::scaleFactorForDrawing(const FloatRect& destRect, const FloatRect& srcRect) const
1022{
1023 AffineTransform transform = getCTM(GraphicsContext::DefinitelyIncludeDeviceScale);
1024 auto transformedDestRect = transform.mapRect(destRect);
1025 return transformedDestRect.size() / srcRect.size();
1026}
1027
1028void GraphicsContext::fillEllipse(const FloatRect& ellipse)
1029{
1030 if (m_impl) {
1031 m_impl->fillEllipse(ellipse);
1032 return;
1033 }
1034
1035 platformFillEllipse(ellipse);
1036}
1037
1038void GraphicsContext::strokeEllipse(const FloatRect& ellipse)
1039{
1040 if (m_impl) {
1041 m_impl->strokeEllipse(ellipse);
1042 return;
1043 }
1044
1045 platformStrokeEllipse(ellipse);
1046}
1047
1048void GraphicsContext::fillEllipseAsPath(const FloatRect& ellipse)
1049{
1050 Path path;
1051 path.addEllipse(ellipse);
1052 fillPath(path);
1053}
1054
1055void GraphicsContext::strokeEllipseAsPath(const FloatRect& ellipse)
1056{
1057 Path path;
1058 path.addEllipse(ellipse);
1059 strokePath(path);
1060}
1061
1062#if !USE(CG) && !USE(DIRECT2D)
1063void GraphicsContext::platformFillEllipse(const FloatRect& ellipse)
1064{
1065 if (paintingDisabled())
1066 return;
1067
1068 fillEllipseAsPath(ellipse);
1069}
1070
1071void GraphicsContext::platformStrokeEllipse(const FloatRect& ellipse)
1072{
1073 if (paintingDisabled())
1074 return;
1075
1076 strokeEllipseAsPath(ellipse);
1077}
1078#endif
1079
1080FloatRect GraphicsContext::computeUnderlineBoundsForText(const FloatRect& rect, bool printing)
1081{
1082 Color dummyColor;
1083 return computeLineBoundsAndAntialiasingModeForText(rect, printing, dummyColor);
1084}
1085
1086FloatRect GraphicsContext::computeLineBoundsAndAntialiasingModeForText(const FloatRect& rect, bool printing, Color& color)
1087{
1088 FloatPoint origin = rect.location();
1089 float thickness = std::max(rect.height(), 0.5f);
1090 if (printing)
1091 return FloatRect(origin, FloatSize(rect.width(), thickness));
1092
1093 AffineTransform transform = getCTM(GraphicsContext::DefinitelyIncludeDeviceScale);
1094 // Just compute scale in x dimension, assuming x and y scales are equal.
1095 float scale = transform.b() ? sqrtf(transform.a() * transform.a() + transform.b() * transform.b()) : transform.a();
1096 if (scale < 1.0) {
1097 // This code always draws a line that is at least one-pixel line high,
1098 // which tends to visually overwhelm text at small scales. To counter this
1099 // effect, an alpha is applied to the underline color when text is at small scales.
1100 static const float minimumUnderlineAlpha = 0.4f;
1101 float shade = scale > minimumUnderlineAlpha ? scale : minimumUnderlineAlpha;
1102 color = color.colorWithAlphaMultipliedBy(shade);
1103 }
1104
1105 FloatPoint devicePoint = transform.mapPoint(rect.location());
1106 // Visual overflow might occur here due to integral roundf/ceilf. visualOverflowForDecorations adjusts the overflow value for underline decoration.
1107 FloatPoint deviceOrigin = FloatPoint(roundf(devicePoint.x()), ceilf(devicePoint.y()));
1108 if (auto inverse = transform.inverse())
1109 origin = inverse.value().mapPoint(deviceOrigin);
1110 return FloatRect(origin, FloatSize(rect.width(), thickness));
1111}
1112
1113void GraphicsContext::applyState(const GraphicsContextState& state)
1114{
1115 setPlatformShadow(state.shadowOffset, state.shadowBlur, state.shadowColor);
1116 setPlatformStrokeThickness(state.strokeThickness);
1117 setPlatformTextDrawingMode(state.textDrawingMode);
1118 setPlatformStrokeColor(state.strokeColor);
1119 setPlatformFillColor(state.fillColor);
1120 setPlatformStrokeStyle(state.strokeStyle);
1121 setPlatformAlpha(state.alpha);
1122 setPlatformCompositeOperation(state.compositeOperator, state.blendMode);
1123 setPlatformShouldAntialias(state.shouldAntialias);
1124 setPlatformShouldSmoothFonts(state.shouldSmoothFonts);
1125}
1126
1127float GraphicsContext::dashedLineCornerWidthForStrokeWidth(float strokeWidth) const
1128{
1129 float thickness = strokeThickness();
1130 return strokeStyle() == DottedStroke ? thickness : std::min(2.0f * thickness, std::max(thickness, strokeWidth / 3.0f));
1131}
1132
1133float GraphicsContext::dashedLinePatternWidthForStrokeWidth(float strokeWidth) const
1134{
1135 float thickness = strokeThickness();
1136 return strokeStyle() == DottedStroke ? thickness : std::min(3.0f * thickness, std::max(thickness, strokeWidth / 3.0f));
1137}
1138
1139float GraphicsContext::dashedLinePatternOffsetForPatternAndStrokeWidth(float patternWidth, float strokeWidth) const
1140{
1141 // Pattern starts with full fill and ends with the empty fill.
1142 // 1. Let's start with the empty phase after the corner.
1143 // 2. Check if we've got odd or even number of patterns and whether they fully cover the line.
1144 // 3. In case of even number of patterns and/or remainder, move the pattern start position
1145 // so that the pattern is balanced between the corners.
1146 float patternOffset = patternWidth;
1147 int numberOfSegments = std::floor(strokeWidth / patternWidth);
1148 bool oddNumberOfSegments = numberOfSegments % 2;
1149 float remainder = strokeWidth - (numberOfSegments * patternWidth);
1150 if (oddNumberOfSegments && remainder)
1151 patternOffset -= remainder / 2.0f;
1152 else if (!oddNumberOfSegments) {
1153 if (remainder)
1154 patternOffset += patternOffset - (patternWidth + remainder) / 2.0f;
1155 else
1156 patternOffset += patternWidth / 2.0f;
1157 }
1158
1159 return patternOffset;
1160}
1161
1162Vector<FloatPoint> GraphicsContext::centerLineAndCutOffCorners(bool isVerticalLine, float cornerWidth, FloatPoint point1, FloatPoint point2) const
1163{
1164 // Center line and cut off corners for pattern painting.
1165 if (isVerticalLine) {
1166 float centerOffset = (point2.x() - point1.x()) / 2.0f;
1167 point1.move(centerOffset, cornerWidth);
1168 point2.move(-centerOffset, -cornerWidth);
1169 } else {
1170 float centerOffset = (point2.y() - point1.y()) / 2.0f;
1171 point1.move(cornerWidth, centerOffset);
1172 point2.move(-cornerWidth, -centerOffset);
1173 }
1174
1175 return { point1, point2 };
1176}
1177
1178#if !USE(CG)
1179bool GraphicsContext::supportsInternalLinks() const
1180{
1181 return false;
1182}
1183
1184void GraphicsContext::setDestinationForRect(const String&, const FloatRect&)
1185{
1186}
1187
1188void GraphicsContext::addDestinationAtPoint(const String&, const FloatPoint&)
1189{
1190}
1191#endif
1192
1193}
1194