1 | /* |
2 | * Copyright (C) 2018 Metrological Group B.V. |
3 | * Copyright (C) 2018 Igalia S.L. |
4 | * |
5 | * Redistribution and use in source and binary forms, with or without |
6 | * modification, are permitted provided that the following conditions |
7 | * are met: |
8 | * |
9 | * 1. Redistributions of source code must retain the above copyright |
10 | * notice, this list of conditions and the following disclaimer. |
11 | * 2. Redistributions in binary form must reproduce the above |
12 | * copyright notice, this list of conditions and the following |
13 | * disclaimer in the documentation and/or other materials provided |
14 | * with the distribution. |
15 | * |
16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
17 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
18 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
19 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
20 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
21 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
22 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
23 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
24 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
27 | */ |
28 | |
29 | #include "config.h" |
30 | #include "NicosiaCairoOperationRecorder.h" |
31 | |
32 | #include "CairoOperations.h" |
33 | #include "FloatRoundedRect.h" |
34 | #include "ImageBuffer.h" |
35 | #include "NicosiaPaintingOperationReplayCairo.h" |
36 | #include <type_traits> |
37 | #include <wtf/text/TextStream.h> |
38 | |
39 | namespace Nicosia { |
40 | using namespace WebCore; |
41 | |
42 | PlatformContextCairo& contextForReplay(PaintingOperationReplay& operationReplay) |
43 | { |
44 | return static_cast<PaintingOperationReplayCairo&>(operationReplay).platformContext; |
45 | } |
46 | |
47 | template<typename... Args> |
48 | struct OperationData { |
49 | template<std::size_t I> |
50 | auto arg() const -> std::tuple_element_t<I, const std::tuple<Args...>>& |
51 | { |
52 | return std::get<I>(arguments); |
53 | } |
54 | |
55 | std::tuple<Args...> arguments; |
56 | }; |
57 | |
58 | template<> struct OperationData<> { }; |
59 | |
60 | template<typename T, typename... Args> |
61 | auto createCommand(Args&&... arguments) -> std::enable_if_t<std::is_base_of<OperationData<std::decay_t<Args>...>, T>::value, std::unique_ptr<PaintingOperation>> |
62 | { |
63 | auto* command = new T(); |
64 | command->arguments = std::make_tuple(std::forward<Args>(arguments)...); |
65 | return std::unique_ptr<PaintingOperation>(command); |
66 | } |
67 | |
68 | template<typename T> |
69 | auto createCommand() -> std::enable_if_t<std::is_base_of<OperationData<>, T>::value, std::unique_ptr<PaintingOperation>> |
70 | { |
71 | return std::make_unique<T>(); |
72 | } |
73 | |
74 | CairoOperationRecorder::CairoOperationRecorder(GraphicsContext& context, PaintingOperations& commandList) |
75 | : GraphicsContextImpl(context, FloatRect { }, AffineTransform { }) |
76 | , m_commandList(commandList) |
77 | { |
78 | m_stateStack.append({ { }, { }, FloatRect::infiniteRect() }); |
79 | } |
80 | |
81 | void CairoOperationRecorder::updateState(const GraphicsContextState& state, GraphicsContextState::StateChangeFlags flags) |
82 | { |
83 | if (flags & GraphicsContextState::StrokeThicknessChange) { |
84 | struct StrokeThicknessChange final : PaintingOperation, OperationData<float> { |
85 | virtual ~StrokeThicknessChange() = default; |
86 | |
87 | void execute(PaintingOperationReplay& replayer) override |
88 | { |
89 | Cairo::State::setStrokeThickness(contextForReplay(replayer), arg<0>()); |
90 | } |
91 | |
92 | void dump(TextStream& ts) override |
93 | { |
94 | ts << indent << "StrokeThicknessChange<>\n" ; |
95 | } |
96 | }; |
97 | |
98 | append(createCommand<StrokeThicknessChange>(state.strokeThickness)); |
99 | } |
100 | |
101 | if (flags & GraphicsContextState::StrokeStyleChange) { |
102 | struct StrokeStyleChange final : PaintingOperation, OperationData<StrokeStyle> { |
103 | virtual ~StrokeStyleChange() = default; |
104 | |
105 | void execute(PaintingOperationReplay& replayer) override |
106 | { |
107 | Cairo::State::setStrokeStyle(contextForReplay(replayer), arg<0>()); |
108 | } |
109 | |
110 | void dump(TextStream& ts) override |
111 | { |
112 | ts << indent << "StrokeStyleChange<>\n" ; |
113 | } |
114 | }; |
115 | |
116 | append(createCommand<StrokeStyleChange>(state.strokeStyle)); |
117 | } |
118 | |
119 | if (flags & GraphicsContextState::CompositeOperationChange) { |
120 | struct CompositeOperationChange final : PaintingOperation, OperationData<CompositeOperator, BlendMode> { |
121 | virtual ~CompositeOperationChange() = default; |
122 | |
123 | void execute(PaintingOperationReplay& replayer) override |
124 | { |
125 | Cairo::State::setCompositeOperation(contextForReplay(replayer), arg<0>(), arg<1>()); |
126 | } |
127 | |
128 | void dump(TextStream& ts) override |
129 | { |
130 | ts << indent << "CompositeOperationChange<>\n" ; |
131 | } |
132 | }; |
133 | |
134 | append(createCommand<CompositeOperationChange>(state.compositeOperator, state.blendMode)); |
135 | } |
136 | |
137 | if (flags & GraphicsContextState::ShouldAntialiasChange) { |
138 | struct ShouldAntialiasChange final : PaintingOperation, OperationData<bool> { |
139 | virtual ~ShouldAntialiasChange() = default; |
140 | |
141 | void execute(PaintingOperationReplay& replayer) override |
142 | { |
143 | Cairo::State::setShouldAntialias(contextForReplay(replayer), arg<0>()); |
144 | } |
145 | |
146 | void dump(TextStream& ts) override |
147 | { |
148 | ts << indent << "ShouldAntialiasChange<>\n" ; |
149 | } |
150 | }; |
151 | |
152 | append(createCommand<ShouldAntialiasChange>(state.shouldAntialias)); |
153 | } |
154 | } |
155 | |
156 | void CairoOperationRecorder::clearShadow() |
157 | { |
158 | } |
159 | |
160 | void CairoOperationRecorder::setLineCap(LineCap lineCap) |
161 | { |
162 | struct SetLineCap final : PaintingOperation, OperationData<LineCap> { |
163 | virtual ~SetLineCap() = default; |
164 | |
165 | void execute(PaintingOperationReplay& replayer) override |
166 | { |
167 | Cairo::setLineCap(contextForReplay(replayer), arg<0>()); |
168 | } |
169 | |
170 | void dump(TextStream& ts) override |
171 | { |
172 | ts << indent << "SetLineCap<>\n" ; |
173 | } |
174 | }; |
175 | |
176 | append(createCommand<SetLineCap>(lineCap)); |
177 | } |
178 | |
179 | void CairoOperationRecorder::setLineDash(const DashArray& dashes, float dashOffset) |
180 | { |
181 | struct SetLineDash final : PaintingOperation, OperationData<DashArray, float> { |
182 | virtual ~SetLineDash() = default; |
183 | |
184 | void execute(PaintingOperationReplay& replayer) override |
185 | { |
186 | Cairo::setLineDash(contextForReplay(replayer), arg<0>(), arg<1>()); |
187 | } |
188 | |
189 | void dump(TextStream& ts) override |
190 | { |
191 | ts << indent << "SetLineDash<>\n" ; |
192 | } |
193 | }; |
194 | |
195 | append(createCommand<SetLineDash>(dashes, dashOffset)); |
196 | } |
197 | |
198 | void CairoOperationRecorder::setLineJoin(LineJoin lineJoin) |
199 | { |
200 | struct SetLineJoin final : PaintingOperation, OperationData<LineJoin> { |
201 | virtual ~SetLineJoin() = default; |
202 | |
203 | void execute(PaintingOperationReplay& replayer) override |
204 | { |
205 | Cairo::setLineJoin(contextForReplay(replayer), arg<0>()); |
206 | } |
207 | |
208 | void dump(TextStream& ts) override |
209 | { |
210 | ts << indent << "SetLineJoin<>\n" ; |
211 | } |
212 | }; |
213 | |
214 | append(createCommand<SetLineJoin>(lineJoin)); |
215 | } |
216 | |
217 | void CairoOperationRecorder::setMiterLimit(float miterLimit) |
218 | { |
219 | struct SetMiterLimit final : PaintingOperation, OperationData<float> { |
220 | virtual ~SetMiterLimit() = default; |
221 | |
222 | void execute(PaintingOperationReplay& replayer) override |
223 | { |
224 | Cairo::setMiterLimit(contextForReplay(replayer), arg<0>()); |
225 | } |
226 | |
227 | void dump(TextStream& ts) override |
228 | { |
229 | ts << indent << "SetMiterLimit<>\n" ; |
230 | } |
231 | }; |
232 | |
233 | append(createCommand<SetMiterLimit>(miterLimit)); |
234 | } |
235 | |
236 | void CairoOperationRecorder::fillRect(const FloatRect& rect) |
237 | { |
238 | struct FillRect final : PaintingOperation, OperationData<FloatRect, Cairo::FillSource, Cairo::ShadowState> { |
239 | virtual ~FillRect() = default; |
240 | |
241 | void execute(PaintingOperationReplay& replayer) override |
242 | { |
243 | Cairo::fillRect(contextForReplay(replayer), arg<0>(), arg<1>(), arg<2>()); |
244 | } |
245 | |
246 | void dump(TextStream& ts) override |
247 | { |
248 | ts << indent << "FillRect<>\n" ; |
249 | } |
250 | }; |
251 | |
252 | auto& state = graphicsContext().state(); |
253 | append(createCommand<FillRect>(rect, Cairo::FillSource(state), Cairo::ShadowState(state))); |
254 | } |
255 | |
256 | void CairoOperationRecorder::fillRect(const FloatRect& rect, const Color& color) |
257 | { |
258 | struct FillRect final : PaintingOperation, OperationData<FloatRect, Color, Cairo::ShadowState> { |
259 | virtual ~FillRect() = default; |
260 | |
261 | void execute(PaintingOperationReplay& replayer) override |
262 | { |
263 | Cairo::fillRect(contextForReplay(replayer), arg<0>(), arg<1>(), arg<2>()); |
264 | } |
265 | |
266 | void dump(TextStream& ts) override |
267 | { |
268 | ts << indent << "FillRect<>\n" ; |
269 | } |
270 | }; |
271 | |
272 | append(createCommand<FillRect>(rect, color, Cairo::ShadowState(graphicsContext().state()))); |
273 | } |
274 | |
275 | void CairoOperationRecorder::fillRect(const FloatRect& rect, Gradient& gradient) |
276 | { |
277 | struct FillRect final : PaintingOperation, OperationData<FloatRect, RefPtr<cairo_pattern_t>> { |
278 | virtual ~FillRect() = default; |
279 | |
280 | void execute(PaintingOperationReplay& replayer) override |
281 | { |
282 | auto& platformContext = contextForReplay(replayer); |
283 | Cairo::save(platformContext); |
284 | Cairo::fillRect(platformContext, arg<0>(), arg<1>().get()); |
285 | Cairo::restore(platformContext); |
286 | } |
287 | |
288 | void dump(TextStream& ts) override |
289 | { |
290 | ts << indent << "FillRect<>\n" ; |
291 | } |
292 | }; |
293 | |
294 | append(createCommand<FillRect>(rect, adoptRef(gradient.createPlatformGradient(1.0)))); |
295 | } |
296 | |
297 | void CairoOperationRecorder::fillRect(const FloatRect& rect, const Color& color, CompositeOperator compositeOperator, BlendMode blendMode) |
298 | { |
299 | struct FillRect final : PaintingOperation, OperationData<FloatRect, Color, CompositeOperator, BlendMode, Cairo::ShadowState, CompositeOperator> { |
300 | virtual ~FillRect() = default; |
301 | |
302 | void execute(PaintingOperationReplay& replayer) override |
303 | { |
304 | auto& platformContext = contextForReplay(replayer); |
305 | |
306 | Cairo::State::setCompositeOperation(platformContext, arg<2>(), arg<3>()); |
307 | Cairo::fillRect(platformContext, arg<0>(), arg<1>(), arg<4>()); |
308 | Cairo::State::setCompositeOperation(platformContext, arg<5>(), BlendMode::Normal); |
309 | } |
310 | |
311 | void dump(TextStream& ts) override |
312 | { |
313 | ts << indent << "FillRect<>\n" ; |
314 | } |
315 | }; |
316 | |
317 | auto& state = graphicsContext().state(); |
318 | append(createCommand<FillRect>(rect, color, compositeOperator, blendMode, Cairo::ShadowState(state), state.compositeOperator)); |
319 | } |
320 | |
321 | void CairoOperationRecorder::fillRoundedRect(const FloatRoundedRect& roundedRect, const Color& color, BlendMode blendMode) |
322 | { |
323 | struct FillRoundedRect final : PaintingOperation, OperationData<FloatRoundedRect, Color, CompositeOperator, BlendMode, Cairo::ShadowState> { |
324 | virtual ~FillRoundedRect() = default; |
325 | |
326 | void execute(PaintingOperationReplay& replayer) override |
327 | { |
328 | auto& platformContext = contextForReplay(replayer); |
329 | |
330 | Cairo::State::setCompositeOperation(platformContext, arg<2>(), arg<3>()); |
331 | |
332 | auto& rect = arg<0>(); |
333 | if (rect.isRounded()) |
334 | Cairo::fillRoundedRect(platformContext, rect, arg<1>(), arg<4>()); |
335 | else |
336 | Cairo::fillRect(platformContext, rect.rect(), arg<1>(), arg<4>()); |
337 | |
338 | Cairo::State::setCompositeOperation(platformContext, arg<2>(), BlendMode::Normal); |
339 | } |
340 | |
341 | void dump(TextStream& ts) override |
342 | { |
343 | ts << indent << "FillRoundedRect<>\n" ; |
344 | } |
345 | }; |
346 | |
347 | auto& state = graphicsContext().state(); |
348 | append(createCommand<FillRoundedRect>(roundedRect, color, state.compositeOperator, blendMode, Cairo::ShadowState(state))); |
349 | } |
350 | |
351 | void CairoOperationRecorder::fillRectWithRoundedHole(const FloatRect& rect, const FloatRoundedRect& roundedHoleRect, const Color& color) |
352 | { |
353 | struct FillRectWithRoundedHole final : PaintingOperation, OperationData<FloatRect, FloatRoundedRect, Cairo::ShadowState> { |
354 | virtual ~FillRectWithRoundedHole() = default; |
355 | |
356 | void execute(PaintingOperationReplay& replayer) override |
357 | { |
358 | Cairo::fillRectWithRoundedHole(contextForReplay(replayer), arg<0>(), arg<1>(), { }, arg<2>()); |
359 | } |
360 | |
361 | void dump(TextStream& ts) override |
362 | { |
363 | ts << indent << "FillRectWithRoundedHole<>\n" ; |
364 | } |
365 | }; |
366 | |
367 | UNUSED_PARAM(color); |
368 | append(createCommand<FillRectWithRoundedHole>(rect, roundedHoleRect, Cairo::ShadowState(graphicsContext().state()))); |
369 | } |
370 | |
371 | void CairoOperationRecorder::fillPath(const Path& path) |
372 | { |
373 | struct FillPath final : PaintingOperation, OperationData<Path, Cairo::FillSource, Cairo::ShadowState> { |
374 | virtual ~FillPath() = default; |
375 | |
376 | void execute(PaintingOperationReplay& replayer) override |
377 | { |
378 | Cairo::fillPath(contextForReplay(replayer), arg<0>(), arg<1>(), arg<2>()); |
379 | } |
380 | |
381 | void dump(TextStream& ts) override |
382 | { |
383 | ts << indent << "FillPath<>\n" ; |
384 | } |
385 | }; |
386 | |
387 | auto& state = graphicsContext().state(); |
388 | append(createCommand<FillPath>(path, Cairo::FillSource(state), Cairo::ShadowState(state))); |
389 | } |
390 | |
391 | void CairoOperationRecorder::fillEllipse(const FloatRect& rect) |
392 | { |
393 | struct FillEllipse final : PaintingOperation, OperationData<FloatRect, Cairo::FillSource, Cairo::ShadowState> { |
394 | virtual ~FillEllipse() = default; |
395 | |
396 | void execute(PaintingOperationReplay& replayer) override |
397 | { |
398 | Path path; |
399 | path.addEllipse(arg<0>()); |
400 | Cairo::fillPath(contextForReplay(replayer), path, arg<1>(), arg<2>()); |
401 | } |
402 | |
403 | void dump(TextStream& ts) override |
404 | { |
405 | ts << indent << "FillEllipse<>\n" ; |
406 | } |
407 | }; |
408 | |
409 | auto& state = graphicsContext().state(); |
410 | append(createCommand<FillEllipse>(rect, Cairo::FillSource(state), Cairo::ShadowState(state))); |
411 | } |
412 | |
413 | void CairoOperationRecorder::strokeRect(const FloatRect& rect, float lineWidth) |
414 | { |
415 | struct StrokeRect final : PaintingOperation, OperationData<FloatRect, float, Cairo::StrokeSource, Cairo::ShadowState> { |
416 | virtual ~StrokeRect() = default; |
417 | |
418 | void execute(PaintingOperationReplay& replayer) override |
419 | { |
420 | Cairo::strokeRect(contextForReplay(replayer), arg<0>(), arg<1>(), arg<2>(), arg<3>()); |
421 | } |
422 | |
423 | void dump(TextStream& ts) override |
424 | { |
425 | ts << indent << "StrokeRect<>\n" ; |
426 | } |
427 | }; |
428 | |
429 | auto& state = graphicsContext().state(); |
430 | append(createCommand<StrokeRect>(rect, lineWidth, Cairo::StrokeSource(state), Cairo::ShadowState(state))); |
431 | } |
432 | |
433 | void CairoOperationRecorder::strokePath(const Path& path) |
434 | { |
435 | struct StrokePath final : PaintingOperation, OperationData<Path, Cairo::StrokeSource, Cairo::ShadowState> { |
436 | virtual ~StrokePath() = default; |
437 | |
438 | void execute(PaintingOperationReplay& replayer) override |
439 | { |
440 | Cairo::strokePath(contextForReplay(replayer), arg<0>(), arg<1>(), arg<2>()); |
441 | } |
442 | |
443 | void dump(TextStream& ts) override |
444 | { |
445 | ts << indent << "StrokePath<>\n" ; |
446 | } |
447 | }; |
448 | |
449 | auto& state = graphicsContext().state(); |
450 | append(createCommand<StrokePath>(path, Cairo::StrokeSource(state), Cairo::ShadowState(state))); |
451 | } |
452 | |
453 | void CairoOperationRecorder::strokeEllipse(const FloatRect& rect) |
454 | { |
455 | struct StrokeEllipse final : PaintingOperation, OperationData<FloatRect, Cairo::StrokeSource, Cairo::ShadowState> { |
456 | virtual ~StrokeEllipse() = default; |
457 | |
458 | void execute(PaintingOperationReplay& replayer) override |
459 | { |
460 | Path path; |
461 | path.addEllipse(arg<0>()); |
462 | Cairo::strokePath(contextForReplay(replayer), path, arg<1>(), arg<2>()); |
463 | } |
464 | |
465 | void dump(TextStream& ts) override |
466 | { |
467 | ts << indent << "StrokeEllipse<>\n" ; |
468 | } |
469 | }; |
470 | |
471 | auto& state = graphicsContext().state(); |
472 | append(createCommand<StrokeEllipse>(rect, Cairo::StrokeSource(state), Cairo::ShadowState(state))); |
473 | } |
474 | |
475 | void CairoOperationRecorder::clearRect(const FloatRect& rect) |
476 | { |
477 | struct ClearRect final : PaintingOperation, OperationData<FloatRect> { |
478 | virtual ~ClearRect() = default; |
479 | |
480 | void execute(PaintingOperationReplay& replayer) override |
481 | { |
482 | Cairo::clearRect(contextForReplay(replayer), arg<0>()); |
483 | } |
484 | |
485 | void dump(TextStream& ts) override |
486 | { |
487 | ts << indent << "ClearRect<>\n" ; |
488 | } |
489 | }; |
490 | |
491 | append(createCommand<ClearRect>(rect)); |
492 | } |
493 | |
494 | void CairoOperationRecorder::drawGlyphs(const Font& font, const GlyphBuffer& glyphBuffer, unsigned from, unsigned numGlyphs, const FloatPoint& point, FontSmoothingMode fontSmoothing) |
495 | { |
496 | struct DrawGlyphs final : PaintingOperation, OperationData<Cairo::FillSource, Cairo::StrokeSource, Cairo::ShadowState, FloatPoint, RefPtr<cairo_scaled_font_t>, float, Vector<cairo_glyph_t>, float, TextDrawingModeFlags, float, FloatSize, Color> { |
497 | virtual ~DrawGlyphs() = default; |
498 | |
499 | void execute(PaintingOperationReplay& replayer) override |
500 | { |
501 | Cairo::drawGlyphs(contextForReplay(replayer), arg<0>(), arg<1>(), arg<2>(), arg<3>(), arg<4>().get(), |
502 | arg<5>(), arg<6>(), arg<7>(), arg<8>(), arg<9>(), arg<10>(), arg<11>()); |
503 | } |
504 | |
505 | void dump(TextStream& ts) override |
506 | { |
507 | ts << indent << "DrawGlyphs<>\n" ; |
508 | } |
509 | }; |
510 | |
511 | UNUSED_PARAM(fontSmoothing); |
512 | if (!font.platformData().size()) |
513 | return; |
514 | |
515 | auto xOffset = point.x(); |
516 | Vector<cairo_glyph_t> glyphs(numGlyphs); |
517 | { |
518 | ASSERT(from + numGlyphs <= glyphBuffer.size()); |
519 | auto* glyphsData = glyphBuffer.glyphs(from); |
520 | auto* advances = glyphBuffer.advances(from); |
521 | |
522 | auto yOffset = point.y(); |
523 | for (size_t i = 0; i < numGlyphs; ++i) { |
524 | glyphs[i] = { glyphsData[i], xOffset, yOffset }; |
525 | xOffset += advances[i].width(); |
526 | } |
527 | } |
528 | |
529 | auto& state = graphicsContext().state(); |
530 | append(createCommand<DrawGlyphs>(Cairo::FillSource(state), Cairo::StrokeSource(state), |
531 | Cairo::ShadowState(state), point, |
532 | RefPtr<cairo_scaled_font_t>(font.platformData().scaledFont()), |
533 | font.syntheticBoldOffset(), WTFMove(glyphs), xOffset, state.textDrawingMode, |
534 | state.strokeThickness, state.shadowOffset, state.shadowColor)); |
535 | } |
536 | |
537 | ImageDrawResult CairoOperationRecorder::drawImage(Image& image, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& imagePaintingOptions) |
538 | { |
539 | return GraphicsContextImpl::drawImageImpl(graphicsContext(), image, destination, source, imagePaintingOptions); |
540 | } |
541 | |
542 | ImageDrawResult CairoOperationRecorder::drawTiledImage(Image& image, const FloatRect& destination, const FloatPoint& source, const FloatSize& tileSize, const FloatSize& spacing, const ImagePaintingOptions& imagePaintingOptions) |
543 | { |
544 | return GraphicsContextImpl::drawTiledImageImpl(graphicsContext(), image, destination, source, tileSize, spacing, imagePaintingOptions); |
545 | } |
546 | |
547 | ImageDrawResult CairoOperationRecorder::drawTiledImage(Image& image, const FloatRect& destination, const FloatRect& source, const FloatSize& tileScaleFactor, Image::TileRule hRule, Image::TileRule vRule, const ImagePaintingOptions& imagePaintingOptions) |
548 | { |
549 | return GraphicsContextImpl::drawTiledImageImpl(graphicsContext(), image, destination, source, tileScaleFactor, hRule, vRule, imagePaintingOptions); |
550 | } |
551 | |
552 | void CairoOperationRecorder::drawNativeImage(const NativeImagePtr& image, const FloatSize& imageSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator compositeOperator, BlendMode blendMode, ImageOrientation orientation) |
553 | { |
554 | struct DrawNativeImage final : PaintingOperation, OperationData<RefPtr<cairo_surface_t>, FloatRect, FloatRect, CompositeOperator, BlendMode, ImageOrientation, InterpolationQuality, float, Cairo::ShadowState> { |
555 | virtual ~DrawNativeImage() = default; |
556 | |
557 | void execute(PaintingOperationReplay& replayer) override |
558 | { |
559 | Cairo::drawNativeImage(contextForReplay(replayer), arg<0>().get(), arg<1>(), arg<2>(), arg<3>(), arg<4>(), arg<5>(), arg<6>(), arg<7>(), arg<8>()); |
560 | } |
561 | |
562 | void dump(TextStream& ts) override |
563 | { |
564 | ts << indent << "DrawNativeImage<>\n" ; |
565 | } |
566 | }; |
567 | |
568 | UNUSED_PARAM(imageSize); |
569 | auto& state = graphicsContext().state(); |
570 | append(createCommand<DrawNativeImage>(RefPtr<cairo_surface_t>(image.get()), destRect, srcRect, compositeOperator, blendMode, orientation, state.imageInterpolationQuality, state.alpha, Cairo::ShadowState(state))); |
571 | } |
572 | |
573 | void CairoOperationRecorder::drawPattern(Image& image, const FloatRect& destRect, const FloatRect& tileRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator compositeOperator, BlendMode blendMode) |
574 | { |
575 | struct DrawPattern final : PaintingOperation, OperationData<RefPtr<cairo_surface_t>, IntSize, FloatRect, FloatRect, AffineTransform, FloatPoint, CompositeOperator, BlendMode> { |
576 | virtual ~DrawPattern() = default; |
577 | |
578 | void execute(PaintingOperationReplay& replayer) override |
579 | { |
580 | Cairo::drawPattern(contextForReplay(replayer), arg<0>().get(), arg<1>(), arg<2>(), arg<3>(), arg<4>(), arg<5>(), arg<6>(), arg<7>()); |
581 | } |
582 | |
583 | void dump(TextStream& ts) override |
584 | { |
585 | ts << indent << "DrawPattern<>\n" ; |
586 | } |
587 | }; |
588 | |
589 | UNUSED_PARAM(spacing); |
590 | if (auto surface = image.nativeImageForCurrentFrame()) |
591 | append(createCommand<DrawPattern>(WTFMove(surface), IntSize(image.size()), destRect, tileRect, patternTransform, phase, compositeOperator, blendMode)); |
592 | } |
593 | |
594 | void CairoOperationRecorder::drawRect(const FloatRect& rect, float borderThickness) |
595 | { |
596 | struct DrawRect final : PaintingOperation, OperationData<FloatRect, float, Color, StrokeStyle, Color> { |
597 | virtual ~DrawRect() = default; |
598 | |
599 | void execute(PaintingOperationReplay& replayer) override |
600 | { |
601 | Cairo::drawRect(contextForReplay(replayer), arg<0>(), arg<1>(), arg<2>(), arg<3>(), arg<4>()); |
602 | } |
603 | |
604 | void dump(TextStream& ts) override |
605 | { |
606 | ts << indent << "DrawRect<>\n" ; |
607 | } |
608 | }; |
609 | |
610 | auto& state = graphicsContext().state(); |
611 | append(createCommand<DrawRect>(rect, borderThickness, state.fillColor, state.strokeStyle, state.strokeColor)); |
612 | } |
613 | |
614 | void CairoOperationRecorder::drawLine(const FloatPoint& point1, const FloatPoint& point2) |
615 | { |
616 | struct DrawLine final : PaintingOperation, OperationData<FloatPoint, FloatPoint, StrokeStyle, Color, float, bool> { |
617 | virtual ~DrawLine() = default; |
618 | |
619 | void execute(PaintingOperationReplay& replayer) override |
620 | { |
621 | Cairo::drawLine(contextForReplay(replayer), arg<0>(), arg<1>(), arg<2>(), arg<3>(), arg<4>(), arg<5>()); |
622 | } |
623 | |
624 | void dump(TextStream& ts) override |
625 | { |
626 | ts << indent << "DrawLine<>\n" ; |
627 | } |
628 | }; |
629 | |
630 | auto& state = graphicsContext().state(); |
631 | append(createCommand<DrawLine>(point1, point2, state.strokeStyle, state.strokeColor, state.strokeThickness, state.shouldAntialias)); |
632 | } |
633 | |
634 | void CairoOperationRecorder::drawLinesForText(const FloatPoint& point, float thickness, const DashArray& widths, bool printing, bool doubleUnderlines) |
635 | { |
636 | struct DrawLinesForText final : PaintingOperation, OperationData<FloatPoint, float, DashArray, bool, bool, Color> { |
637 | virtual ~DrawLinesForText() = default; |
638 | |
639 | void execute(PaintingOperationReplay& replayer) override |
640 | { |
641 | Cairo::drawLinesForText(contextForReplay(replayer), arg<0>(), arg<1>(), arg<2>(), arg<3>(), arg<4>(), arg<5>()); |
642 | } |
643 | |
644 | void dump(TextStream& ts) override |
645 | { |
646 | ts << indent << "DrawLinesForText<>\n" ; |
647 | } |
648 | }; |
649 | |
650 | auto& state = graphicsContext().state(); |
651 | append(createCommand<DrawLinesForText>(point, thickness, widths, printing, doubleUnderlines, state.strokeColor)); |
652 | } |
653 | |
654 | void CairoOperationRecorder::drawDotsForDocumentMarker(const FloatRect& rect, DocumentMarkerLineStyle style) |
655 | { |
656 | struct DrawDotsForDocumentMarker final : PaintingOperation, OperationData<FloatRect, DocumentMarkerLineStyle> { |
657 | virtual ~DrawDotsForDocumentMarker() = default; |
658 | |
659 | void execute(PaintingOperationReplay& replayer) override |
660 | { |
661 | Cairo::drawDotsForDocumentMarker(contextForReplay(replayer), arg<0>(), arg<1>()); |
662 | } |
663 | |
664 | void dump(TextStream& ts) override |
665 | { |
666 | ts << indent << "DrawDotsForDocumentMarker<>\n" ; |
667 | } |
668 | }; |
669 | |
670 | append(createCommand<DrawDotsForDocumentMarker>(rect, style)); |
671 | } |
672 | |
673 | void CairoOperationRecorder::drawEllipse(const FloatRect& rect) |
674 | { |
675 | struct DrawEllipse final : PaintingOperation, OperationData<FloatRect, Color, StrokeStyle, Color, float> { |
676 | virtual ~DrawEllipse() = default; |
677 | |
678 | void execute(PaintingOperationReplay& replayer) override |
679 | { |
680 | Cairo::drawEllipse(contextForReplay(replayer), arg<0>(), arg<1>(), arg<2>(), arg<3>(), arg<4>()); |
681 | } |
682 | |
683 | void dump(TextStream& ts) override |
684 | { |
685 | ts << indent << "DrawEllipse<>\n" ; |
686 | } |
687 | }; |
688 | |
689 | auto& state = graphicsContext().state(); |
690 | append(createCommand<DrawEllipse>(rect, state.fillColor, state.strokeStyle, state.strokeColor, state.strokeThickness)); |
691 | } |
692 | |
693 | void CairoOperationRecorder::drawPath(const Path&) |
694 | { |
695 | } |
696 | |
697 | void CairoOperationRecorder::drawFocusRing(const Path& path, float width, float offset, const Color& color) |
698 | { |
699 | struct DrawFocusRing final : PaintingOperation, OperationData<Path, float, Color> { |
700 | virtual ~DrawFocusRing() = default; |
701 | |
702 | void execute(PaintingOperationReplay& replayer) override |
703 | { |
704 | Cairo::drawFocusRing(contextForReplay(replayer), arg<0>(), arg<1>(), arg<2>()); |
705 | } |
706 | |
707 | void dump(TextStream& ts) override |
708 | { |
709 | ts << indent << "DrawFocusRing<>\n" ; |
710 | } |
711 | }; |
712 | |
713 | UNUSED_PARAM(offset); |
714 | append(createCommand<DrawFocusRing>(path, width, color)); |
715 | } |
716 | |
717 | void CairoOperationRecorder::drawFocusRing(const Vector<FloatRect>& rects, float width, float offset, const Color& color) |
718 | { |
719 | struct DrawFocusRing final : PaintingOperation, OperationData<Vector<FloatRect>, float, Color> { |
720 | virtual ~DrawFocusRing() = default; |
721 | |
722 | void execute(PaintingOperationReplay& replayer) override |
723 | { |
724 | Cairo::drawFocusRing(contextForReplay(replayer), arg<0>(), arg<1>(), arg<2>()); |
725 | } |
726 | |
727 | void dump(TextStream& ts) override |
728 | { |
729 | ts << indent << "DrawFocusRing<>\n" ; |
730 | } |
731 | }; |
732 | |
733 | UNUSED_PARAM(offset); |
734 | append(createCommand<DrawFocusRing>(rects, width, color)); |
735 | } |
736 | |
737 | void CairoOperationRecorder::save() |
738 | { |
739 | struct Save final : PaintingOperation, OperationData<> { |
740 | virtual ~Save() = default; |
741 | |
742 | void execute(PaintingOperationReplay& replayer) override |
743 | { |
744 | Cairo::save(contextForReplay(replayer)); |
745 | } |
746 | |
747 | void dump(TextStream& ts) override |
748 | { |
749 | ts << indent << "Save<>\n" ; |
750 | } |
751 | }; |
752 | |
753 | append(createCommand<Save>()); |
754 | |
755 | m_stateStack.append(m_stateStack.last()); |
756 | } |
757 | |
758 | void CairoOperationRecorder::restore() |
759 | { |
760 | struct Restore final : PaintingOperation, OperationData<> { |
761 | virtual ~Restore() = default; |
762 | |
763 | void execute(PaintingOperationReplay& replayer) override |
764 | { |
765 | Cairo::restore(contextForReplay(replayer)); |
766 | } |
767 | |
768 | void dump(TextStream& ts) override |
769 | { |
770 | ts << indent << "Restore<>\n" ; |
771 | } |
772 | }; |
773 | |
774 | append(createCommand<Restore>()); |
775 | |
776 | ASSERT(!m_stateStack.isEmpty()); |
777 | m_stateStack.removeLast(); |
778 | if (m_stateStack.isEmpty()) |
779 | m_stateStack.clear(); |
780 | } |
781 | |
782 | void CairoOperationRecorder::translate(float x, float y) |
783 | { |
784 | struct Translate final : PaintingOperation, OperationData<float, float> { |
785 | virtual ~Translate() = default; |
786 | |
787 | void execute(PaintingOperationReplay& replayer) override |
788 | { |
789 | Cairo::translate(contextForReplay(replayer), arg<0>(), arg<1>()); |
790 | } |
791 | |
792 | void dump(TextStream& ts) override |
793 | { |
794 | ts << indent << "Translate<>\n" ; |
795 | } |
796 | }; |
797 | |
798 | append(createCommand<Translate>(x, y)); |
799 | |
800 | { |
801 | auto& state = m_stateStack.last(); |
802 | state.ctm.translate(x, y); |
803 | |
804 | AffineTransform t; |
805 | t.translate(-x, -y); |
806 | state.ctmInverse = t * state.ctmInverse; |
807 | } |
808 | } |
809 | |
810 | void CairoOperationRecorder::rotate(float angleInRadians) |
811 | { |
812 | struct Rotate final : PaintingOperation, OperationData<float> { |
813 | virtual ~Rotate() = default; |
814 | |
815 | void execute(PaintingOperationReplay& replayer) override |
816 | { |
817 | Cairo::rotate(contextForReplay(replayer), arg<0>()); |
818 | } |
819 | |
820 | void dump(TextStream& ts) override |
821 | { |
822 | ts << indent << "Rotate<>\n" ; |
823 | } |
824 | }; |
825 | |
826 | append(createCommand<Rotate>(angleInRadians)); |
827 | |
828 | { |
829 | auto& state = m_stateStack.last(); |
830 | state.ctm.rotate(angleInRadians); |
831 | |
832 | AffineTransform t; |
833 | t.rotate(angleInRadians); |
834 | state.ctmInverse = t * state.ctmInverse; |
835 | } |
836 | } |
837 | |
838 | void CairoOperationRecorder::scale(const FloatSize& size) |
839 | { |
840 | struct Scale final : PaintingOperation, OperationData<FloatSize> { |
841 | virtual ~Scale() = default; |
842 | |
843 | void execute(PaintingOperationReplay& replayer) override |
844 | { |
845 | Cairo::scale(contextForReplay(replayer), arg<0>()); |
846 | } |
847 | |
848 | void dump(TextStream& ts) override |
849 | { |
850 | ts << indent << "Scale<>\n" ; |
851 | } |
852 | }; |
853 | |
854 | append(createCommand<Scale>(size)); |
855 | |
856 | { |
857 | auto& state = m_stateStack.last(); |
858 | state.ctm.scale(size.width(), size.height()); |
859 | |
860 | AffineTransform t; |
861 | t.scale(1 / size.width(), 1 / size.height()); |
862 | state.ctmInverse = t * state.ctmInverse; |
863 | } |
864 | } |
865 | |
866 | void CairoOperationRecorder::concatCTM(const AffineTransform& transform) |
867 | { |
868 | struct ConcatCTM final : PaintingOperation, OperationData<AffineTransform> { |
869 | virtual ~ConcatCTM() = default; |
870 | |
871 | void execute(PaintingOperationReplay& replayer) override |
872 | { |
873 | Cairo::concatCTM(contextForReplay(replayer), arg<0>()); |
874 | } |
875 | |
876 | void dump(TextStream& ts) override |
877 | { |
878 | ts << indent << "ConcatCTM<>\n" ; |
879 | } |
880 | }; |
881 | |
882 | auto inverse = transform.inverse(); |
883 | if (!inverse) |
884 | return; |
885 | |
886 | append(createCommand<ConcatCTM>(transform)); |
887 | |
888 | auto& state = m_stateStack.last(); |
889 | state.ctm *= transform; |
890 | state.ctmInverse = inverse.value() * state.ctmInverse; |
891 | } |
892 | |
893 | void CairoOperationRecorder::setCTM(const AffineTransform& transform) |
894 | { |
895 | struct SetCTM final : PaintingOperation, OperationData<AffineTransform> { |
896 | virtual ~SetCTM() = default; |
897 | |
898 | void execute(PaintingOperationReplay& replayer) override |
899 | { |
900 | Cairo::State::setCTM(contextForReplay(replayer), arg<0>()); |
901 | } |
902 | |
903 | void dump(TextStream& ts) override |
904 | { |
905 | ts << indent << "SetCTM<>\n" ; |
906 | } |
907 | }; |
908 | |
909 | auto inverse = transform.inverse(); |
910 | if (!inverse) |
911 | return; |
912 | |
913 | append(createCommand<SetCTM>(transform)); |
914 | |
915 | auto& state = m_stateStack.last(); |
916 | state.ctm = transform; |
917 | state.ctmInverse = inverse.value(); |
918 | } |
919 | |
920 | AffineTransform CairoOperationRecorder::getCTM(GraphicsContext::IncludeDeviceScale) |
921 | { |
922 | return m_stateStack.last().ctm; |
923 | } |
924 | |
925 | void CairoOperationRecorder::beginTransparencyLayer(float opacity) |
926 | { |
927 | struct BeginTransparencyLayer final : PaintingOperation, OperationData<float> { |
928 | virtual ~BeginTransparencyLayer() = default; |
929 | |
930 | void execute(PaintingOperationReplay& replayer) override |
931 | { |
932 | Cairo::beginTransparencyLayer(contextForReplay(replayer), arg<0>()); |
933 | } |
934 | |
935 | void dump(TextStream& ts) override |
936 | { |
937 | ts << indent << "BeginTransparencyLayer<>\n" ; |
938 | } |
939 | }; |
940 | |
941 | append(createCommand<BeginTransparencyLayer>(opacity)); |
942 | } |
943 | |
944 | void CairoOperationRecorder::endTransparencyLayer() |
945 | { |
946 | struct EndTransparencyLayer final : PaintingOperation, OperationData<> { |
947 | virtual ~EndTransparencyLayer() = default; |
948 | |
949 | void execute(PaintingOperationReplay& replayer) override |
950 | { |
951 | Cairo::endTransparencyLayer(contextForReplay(replayer)); |
952 | } |
953 | |
954 | void dump(TextStream& ts) override |
955 | { |
956 | ts << indent << "EndTransparencyLayer<>\n" ; |
957 | } |
958 | }; |
959 | |
960 | append(createCommand<EndTransparencyLayer>()); |
961 | } |
962 | |
963 | void CairoOperationRecorder::clip(const FloatRect& rect) |
964 | { |
965 | struct Clip final : PaintingOperation, OperationData<FloatRect> { |
966 | virtual ~Clip() = default; |
967 | |
968 | void execute(PaintingOperationReplay& replayer) override |
969 | { |
970 | Cairo::clip(contextForReplay(replayer), arg<0>()); |
971 | } |
972 | |
973 | void dump(TextStream& ts) override |
974 | { |
975 | ts << indent << "Clip<>\n" ; |
976 | } |
977 | }; |
978 | |
979 | append(createCommand<Clip>(rect)); |
980 | |
981 | { |
982 | auto& state = m_stateStack.last(); |
983 | state.clipBounds.intersect(state.ctm.mapRect(rect)); |
984 | } |
985 | } |
986 | |
987 | void CairoOperationRecorder::clipOut(const FloatRect& rect) |
988 | { |
989 | struct ClipOut final : PaintingOperation, OperationData<FloatRect> { |
990 | virtual ~ClipOut() = default; |
991 | |
992 | void execute(PaintingOperationReplay& replayer) override |
993 | { |
994 | Cairo::clipOut(contextForReplay(replayer), arg<0>()); |
995 | } |
996 | |
997 | void dump(TextStream& ts) override |
998 | { |
999 | ts << indent << "ClipOut<>\n" ; |
1000 | } |
1001 | }; |
1002 | |
1003 | append(createCommand<ClipOut>(rect)); |
1004 | } |
1005 | |
1006 | void CairoOperationRecorder::clipOut(const Path& path) |
1007 | { |
1008 | struct ClipOut final : PaintingOperation, OperationData<Path> { |
1009 | virtual ~ClipOut() = default; |
1010 | |
1011 | void execute(PaintingOperationReplay& replayer) override |
1012 | { |
1013 | Cairo::clipOut(contextForReplay(replayer), arg<0>()); |
1014 | } |
1015 | |
1016 | void dump(TextStream& ts) override |
1017 | { |
1018 | ts << indent << "ClipOut<>\n" ; |
1019 | } |
1020 | }; |
1021 | |
1022 | append(createCommand<ClipOut>(path)); |
1023 | } |
1024 | |
1025 | void CairoOperationRecorder::clipPath(const Path& path, WindRule clipRule) |
1026 | { |
1027 | struct ClipPath final : PaintingOperation, OperationData<Path, WindRule> { |
1028 | virtual ~ClipPath() = default; |
1029 | |
1030 | void execute(PaintingOperationReplay& replayer) override |
1031 | { |
1032 | Cairo::clipPath(contextForReplay(replayer), arg<0>(), arg<1>()); |
1033 | } |
1034 | |
1035 | void dump(TextStream& ts) override |
1036 | { |
1037 | ts << indent << "ClipPath<>\n" ; |
1038 | } |
1039 | }; |
1040 | |
1041 | append(createCommand<ClipPath>(path, clipRule)); |
1042 | |
1043 | { |
1044 | auto& state = m_stateStack.last(); |
1045 | state.clipBounds.intersect(state.ctm.mapRect(path.fastBoundingRect())); |
1046 | } |
1047 | } |
1048 | |
1049 | IntRect CairoOperationRecorder::clipBounds() |
1050 | { |
1051 | auto& state = m_stateStack.last(); |
1052 | return enclosingIntRect(state.ctmInverse.mapRect(state.clipBounds)); |
1053 | } |
1054 | |
1055 | void CairoOperationRecorder::clipToImageBuffer(ImageBuffer& buffer, const FloatRect& destRect) |
1056 | { |
1057 | struct ClipToImageBuffer final: PaintingOperation, OperationData<RefPtr<cairo_surface_t>, FloatRect> { |
1058 | virtual ~ClipToImageBuffer() = default; |
1059 | |
1060 | void execute(PaintingOperationReplay& replayer) override |
1061 | { |
1062 | Cairo::clipToImageBuffer(contextForReplay(replayer), arg<0>().get(), arg<1>()); |
1063 | } |
1064 | |
1065 | void dump(TextStream& ts) override |
1066 | { |
1067 | ts << indent << "ClipToImageBuffer<>\n" ; |
1068 | } |
1069 | }; |
1070 | |
1071 | RefPtr<Image> image = buffer.copyImage(DontCopyBackingStore); |
1072 | if (!image) |
1073 | return; |
1074 | |
1075 | if (auto surface = image->nativeImageForCurrentFrame()) |
1076 | append(createCommand<ClipToImageBuffer>(RefPtr<cairo_surface_t>(surface.get()), destRect)); |
1077 | } |
1078 | |
1079 | void CairoOperationRecorder::applyDeviceScaleFactor(float) |
1080 | { |
1081 | } |
1082 | |
1083 | FloatRect CairoOperationRecorder::roundToDevicePixels(const FloatRect& rect, GraphicsContext::RoundingMode) |
1084 | { |
1085 | return rect; |
1086 | } |
1087 | |
1088 | void CairoOperationRecorder::append(std::unique_ptr<PaintingOperation>&& command) |
1089 | { |
1090 | m_commandList.append(WTFMove(command)); |
1091 | } |
1092 | |
1093 | } // namespace Nicosia |
1094 | |