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
39namespace Nicosia {
40using namespace WebCore;
41
42PlatformContextCairo& contextForReplay(PaintingOperationReplay& operationReplay)
43{
44 return static_cast<PaintingOperationReplayCairo&>(operationReplay).platformContext;
45}
46
47template<typename... Args>
48struct 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
58template<> struct OperationData<> { };
59
60template<typename T, typename... Args>
61auto 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
68template<typename T>
69auto 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
74CairoOperationRecorder::CairoOperationRecorder(GraphicsContext& context, PaintingOperations& commandList)
75 : GraphicsContextImpl(context, FloatRect { }, AffineTransform { })
76 , m_commandList(commandList)
77{
78 m_stateStack.append({ { }, { }, FloatRect::infiniteRect() });
79}
80
81void 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
156void CairoOperationRecorder::clearShadow()
157{
158}
159
160void 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
179void 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
198void 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
217void 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
236void 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
256void 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
275void 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
297void 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
321void 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
351void 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
371void 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
391void 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
413void 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
433void 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
453void 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
475void 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
494void 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
537ImageDrawResult 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
542ImageDrawResult 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
547ImageDrawResult 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
552void 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
573void 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
594void 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
614void 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
634void 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
654void 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
673void 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
693void CairoOperationRecorder::drawPath(const Path&)
694{
695}
696
697void 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
717void 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
737void 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
758void 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
782void 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
810void 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
838void 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
866void 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
893void 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
920AffineTransform CairoOperationRecorder::getCTM(GraphicsContext::IncludeDeviceScale)
921{
922 return m_stateStack.last().ctm;
923}
924
925void 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
944void 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
963void 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
987void 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
1006void 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
1025void 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
1049IntRect CairoOperationRecorder::clipBounds()
1050{
1051 auto& state = m_stateStack.last();
1052 return enclosingIntRect(state.ctmInverse.mapRect(state.clipBounds));
1053}
1054
1055void 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
1079void CairoOperationRecorder::applyDeviceScaleFactor(float)
1080{
1081}
1082
1083FloatRect CairoOperationRecorder::roundToDevicePixels(const FloatRect& rect, GraphicsContext::RoundingMode)
1084{
1085 return rect;
1086}
1087
1088void CairoOperationRecorder::append(std::unique_ptr<PaintingOperation>&& command)
1089{
1090 m_commandList.append(WTFMove(command));
1091}
1092
1093} // namespace Nicosia
1094