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 | |
40 | namespace WebCore { |
41 | |
42 | class TextRunIterator { |
43 | public: |
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 | |
69 | private: |
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 | |
78 | GraphicsContextState::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 | |
115 | void 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 | |
184 | void 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 | |
250 | void 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 | |
317 | TextStream& operator<<(TextStream& ts, const GraphicsContextStateChange& stateChange) |
318 | { |
319 | stateChange.dump(ts); |
320 | return ts; |
321 | } |
322 | |
323 | GraphicsContext::GraphicsContext(PaintInvalidationReasons paintInvalidationReasons) |
324 | : m_paintInvalidationReasons(paintInvalidationReasons) |
325 | { |
326 | } |
327 | |
328 | GraphicsContext::GraphicsContext(PlatformGraphicsContext* platformGraphicsContext) |
329 | { |
330 | platformInit(platformGraphicsContext); |
331 | } |
332 | |
333 | GraphicsContext::GraphicsContext(const GraphicsContextImplFactory& factoryFunction) |
334 | : m_impl(factoryFunction(*this)) |
335 | { |
336 | } |
337 | |
338 | GraphicsContext::~GraphicsContext() |
339 | { |
340 | ASSERT(m_stack.isEmpty()); |
341 | ASSERT(!m_transparencyCount); |
342 | platformDestroy(); |
343 | } |
344 | |
345 | bool GraphicsContext::hasPlatformContext() const |
346 | { |
347 | if (m_impl) |
348 | return m_impl->hasPlatformContext(); |
349 | return !!m_data; |
350 | } |
351 | |
352 | void 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 | |
367 | void 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 | |
393 | void 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 | |
413 | void 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 | |
424 | void 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 | |
434 | void 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 | |
446 | void 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 | |
461 | void 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 | |
476 | void 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 | |
492 | bool 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 | |
501 | void 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 | |
515 | void GraphicsContext::setShadowsIgnoreTransforms(bool shadowsIgnoreTransforms) |
516 | { |
517 | m_state.shadowsIgnoreTransforms = shadowsIgnoreTransforms; |
518 | if (m_impl) |
519 | m_impl->updateState(m_state, GraphicsContextState::ShadowsIgnoreTransformsChange); |
520 | } |
521 | |
522 | void 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 | |
534 | void 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 | |
546 | void GraphicsContext::setShouldSubpixelQuantizeFonts(bool shouldSubpixelQuantizeFonts) |
547 | { |
548 | m_state.shouldSubpixelQuantizeFonts = shouldSubpixelQuantizeFonts; |
549 | if (m_impl) |
550 | m_impl->updateState(m_state, GraphicsContextState::ShouldSubpixelQuantizeFontsChange); |
551 | } |
552 | |
553 | void 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 | |
568 | void 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 | |
576 | void 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 | |
584 | void 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 | |
592 | void GraphicsContext::setFillRule(WindRule fillRule) |
593 | { |
594 | m_state.fillRule = fillRule; |
595 | if (m_impl) |
596 | m_impl->updateState(m_state, GraphicsContextState::FillRuleChange); |
597 | } |
598 | |
599 | void 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 | |
607 | void 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 | |
617 | void 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 | |
628 | float 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 | |
637 | void 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 | |
650 | void 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 | |
658 | void 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 | |
692 | ImageDrawResult 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 | |
697 | ImageDrawResult 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 | |
708 | ImageDrawResult 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 | |
720 | ImageDrawResult 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 | |
732 | ImageDrawResult 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 | |
750 | void 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 | |
755 | void GraphicsContext::drawImageBuffer(ImageBuffer& image, const FloatRect& destination, const ImagePaintingOptions& imagePaintingOptions) |
756 | { |
757 | drawImageBuffer(image, destination, FloatRect(FloatPoint(), FloatSize(image.logicalSize())), imagePaintingOptions); |
758 | } |
759 | |
760 | void 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 | |
769 | void 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 | |
777 | void 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 | |
785 | void 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 | |
794 | void GraphicsContext::clipRoundedRect(const FloatRoundedRect& rect) |
795 | { |
796 | if (paintingDisabled()) |
797 | return; |
798 | |
799 | Path path; |
800 | path.addRoundedRect(rect); |
801 | clipPath(path); |
802 | } |
803 | |
804 | void 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) |
820 | IntRect GraphicsContext::clipBounds() const |
821 | { |
822 | ASSERT_NOT_REACHED(); |
823 | return IntRect(); |
824 | } |
825 | #endif |
826 | |
827 | void 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 | |
840 | void 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 | |
853 | void 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 | |
869 | void 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) |
888 | void 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 | |
914 | void 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 | |
924 | void 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 | |
935 | void 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. |
944 | void GraphicsContext::setPlatformTextDrawingMode(TextDrawingModeFlags) |
945 | { |
946 | } |
947 | #endif |
948 | |
949 | #if !USE(CAIRO) && !USE(DIRECT2D) |
950 | void GraphicsContext::setPlatformStrokeStyle(StrokeStyle) |
951 | { |
952 | } |
953 | #endif |
954 | |
955 | #if !USE(CG) && !USE(DIRECT2D) |
956 | void GraphicsContext::setPlatformShouldSmoothFonts(bool) |
957 | { |
958 | } |
959 | #endif |
960 | |
961 | #if !USE(CG) && !USE(DIRECT2D) && !USE(CAIRO) |
962 | bool GraphicsContext::isAcceleratedContext() const |
963 | { |
964 | return false; |
965 | } |
966 | #endif |
967 | |
968 | void 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) |
998 | void GraphicsContext::platformApplyDeviceScaleFactor(float) |
999 | { |
1000 | } |
1001 | #endif |
1002 | |
1003 | void 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 | |
1015 | FloatSize GraphicsContext::scaleFactor() const |
1016 | { |
1017 | AffineTransform transform = getCTM(GraphicsContext::DefinitelyIncludeDeviceScale); |
1018 | return FloatSize(transform.xScale(), transform.yScale()); |
1019 | } |
1020 | |
1021 | FloatSize 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 | |
1028 | void GraphicsContext::fillEllipse(const FloatRect& ellipse) |
1029 | { |
1030 | if (m_impl) { |
1031 | m_impl->fillEllipse(ellipse); |
1032 | return; |
1033 | } |
1034 | |
1035 | platformFillEllipse(ellipse); |
1036 | } |
1037 | |
1038 | void GraphicsContext::strokeEllipse(const FloatRect& ellipse) |
1039 | { |
1040 | if (m_impl) { |
1041 | m_impl->strokeEllipse(ellipse); |
1042 | return; |
1043 | } |
1044 | |
1045 | platformStrokeEllipse(ellipse); |
1046 | } |
1047 | |
1048 | void GraphicsContext::fillEllipseAsPath(const FloatRect& ellipse) |
1049 | { |
1050 | Path path; |
1051 | path.addEllipse(ellipse); |
1052 | fillPath(path); |
1053 | } |
1054 | |
1055 | void GraphicsContext::strokeEllipseAsPath(const FloatRect& ellipse) |
1056 | { |
1057 | Path path; |
1058 | path.addEllipse(ellipse); |
1059 | strokePath(path); |
1060 | } |
1061 | |
1062 | #if !USE(CG) && !USE(DIRECT2D) |
1063 | void GraphicsContext::platformFillEllipse(const FloatRect& ellipse) |
1064 | { |
1065 | if (paintingDisabled()) |
1066 | return; |
1067 | |
1068 | fillEllipseAsPath(ellipse); |
1069 | } |
1070 | |
1071 | void GraphicsContext::platformStrokeEllipse(const FloatRect& ellipse) |
1072 | { |
1073 | if (paintingDisabled()) |
1074 | return; |
1075 | |
1076 | strokeEllipseAsPath(ellipse); |
1077 | } |
1078 | #endif |
1079 | |
1080 | FloatRect GraphicsContext::computeUnderlineBoundsForText(const FloatRect& rect, bool printing) |
1081 | { |
1082 | Color dummyColor; |
1083 | return computeLineBoundsAndAntialiasingModeForText(rect, printing, dummyColor); |
1084 | } |
1085 | |
1086 | FloatRect 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 | |
1113 | void 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 | |
1127 | float 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 | |
1133 | float 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 | |
1139 | float 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 | |
1162 | Vector<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) |
1179 | bool GraphicsContext::supportsInternalLinks() const |
1180 | { |
1181 | return false; |
1182 | } |
1183 | |
1184 | void GraphicsContext::setDestinationForRect(const String&, const FloatRect&) |
1185 | { |
1186 | } |
1187 | |
1188 | void GraphicsContext::addDestinationAtPoint(const String&, const FloatPoint&) |
1189 | { |
1190 | } |
1191 | #endif |
1192 | |
1193 | } |
1194 | |