forked from SerenityOS/serenity
-
Notifications
You must be signed in to change notification settings - Fork 0
/
RecordingPainter.h
530 lines (430 loc) Β· 19.4 KB
/
RecordingPainter.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
/*
* Copyright (c) 2023, Aliaksandr Kalenik <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Forward.h>
#include <AK/NonnullRefPtr.h>
#include <AK/Utf8View.h>
#include <AK/Vector.h>
#include <LibGfx/AntiAliasingPainter.h>
#include <LibGfx/Color.h>
#include <LibGfx/Forward.h>
#include <LibGfx/Gradients.h>
#include <LibGfx/GrayscaleBitmap.h>
#include <LibGfx/PaintStyle.h>
#include <LibGfx/Painter.h>
#include <LibGfx/Palette.h>
#include <LibGfx/Point.h>
#include <LibGfx/Rect.h>
#include <LibGfx/Size.h>
#include <LibGfx/StylePainter.h>
#include <LibGfx/TextAlignment.h>
#include <LibGfx/TextDirection.h>
#include <LibGfx/TextElision.h>
#include <LibGfx/TextWrapping.h>
#include <LibWeb/Painting/BorderRadiiData.h>
#include <LibWeb/Painting/BorderRadiusCornerClipper.h>
#include <LibWeb/Painting/GradientData.h>
#include <LibWeb/Painting/PaintOuterBoxShadowParams.h>
namespace Web::Painting {
enum class CommandResult {
Continue,
SkipStackingContext,
};
struct DrawTextRun {
Color color;
Gfx::IntPoint baseline_start;
String string;
NonnullRefPtr<Gfx::Font> font;
Gfx::IntRect rect;
[[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
};
struct DrawText {
Gfx::IntRect rect;
String raw_text;
Gfx::TextAlignment alignment;
Color color;
Gfx::TextElision elision;
Gfx::TextWrapping wrapping;
Optional<NonnullRefPtr<Gfx::Font>> font {};
[[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
};
struct FillRect {
Gfx::IntRect rect;
Color color;
[[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
};
struct DrawScaledBitmap {
Gfx::IntRect dst_rect;
NonnullRefPtr<Gfx::Bitmap> bitmap;
Gfx::IntRect src_rect;
float opacity;
Gfx::Painter::ScalingMode scaling_mode;
[[nodiscard]] Gfx::IntRect bounding_rect() const { return dst_rect; }
};
struct SetClipRect {
Gfx::IntRect rect;
};
struct ClearClipRect { };
struct SetFont {
NonnullRefPtr<Gfx::Font> font;
};
struct PushStackingContext {
bool semitransparent_or_has_non_identity_transform;
bool has_fixed_position;
float opacity;
Gfx::FloatRect source_rect;
Gfx::FloatRect transformed_destination_rect;
Gfx::IntPoint painter_location;
};
struct PopStackingContext {
bool semitransparent_or_has_non_identity_transform;
Gfx::Painter::ScalingMode scaling_mode;
};
struct PushStackingContextWithMask {
Gfx::IntRect paint_rect;
};
struct PopStackingContextWithMask {
Gfx::IntRect paint_rect;
RefPtr<Gfx::Bitmap> mask_bitmap;
Gfx::Bitmap::MaskKind mask_kind;
float opacity;
};
struct PaintLinearGradient {
Gfx::IntRect gradient_rect;
LinearGradientData linear_gradient_data;
[[nodiscard]] Gfx::IntRect bounding_rect() const { return gradient_rect; }
};
struct PaintOuterBoxShadow {
PaintOuterBoxShadowParams outer_box_shadow_params;
[[nodiscard]] Gfx::IntRect bounding_rect() const;
};
struct PaintInnerBoxShadow {
PaintOuterBoxShadowParams outer_box_shadow_params;
};
struct PaintTextShadow {
int blur_radius;
Gfx::IntRect shadow_bounding_rect;
Gfx::IntRect text_rect;
String text;
NonnullRefPtr<Gfx::Font> font;
Color color;
int fragment_baseline;
Gfx::IntPoint draw_location;
[[nodiscard]] Gfx::IntRect bounding_rect() const { return { draw_location, shadow_bounding_rect.size() }; }
};
struct FillRectWithRoundedCorners {
Gfx::IntRect rect;
Color color;
Gfx::AntiAliasingPainter::CornerRadius top_left_radius;
Gfx::AntiAliasingPainter::CornerRadius top_right_radius;
Gfx::AntiAliasingPainter::CornerRadius bottom_left_radius;
Gfx::AntiAliasingPainter::CornerRadius bottom_right_radius;
Optional<Gfx::FloatPoint> aa_translation {};
[[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
};
struct FillPathUsingColor {
Gfx::IntRect path_bounding_rect;
Gfx::Path path;
Color color;
Gfx::Painter::WindingRule winding_rule;
Optional<Gfx::FloatPoint> aa_translation {};
[[nodiscard]] Gfx::IntRect bounding_rect() const { return path_bounding_rect; }
};
struct FillPathUsingPaintStyle {
Gfx::IntRect path_bounding_rect;
Gfx::Path path;
NonnullRefPtr<Gfx::PaintStyle> paint_style;
Gfx::Painter::WindingRule winding_rule;
float opacity;
Optional<Gfx::FloatPoint> aa_translation {};
[[nodiscard]] Gfx::IntRect bounding_rect() const { return path_bounding_rect; }
};
struct StrokePathUsingColor {
Gfx::IntRect path_bounding_rect;
Gfx::Path path;
Color color;
float thickness;
Optional<Gfx::FloatPoint> aa_translation {};
[[nodiscard]] Gfx::IntRect bounding_rect() const { return path_bounding_rect; }
};
struct StrokePathUsingPaintStyle {
Gfx::IntRect path_bounding_rect;
Gfx::Path path;
NonnullRefPtr<Gfx::PaintStyle> paint_style;
float thickness;
float opacity = 1.0f;
Optional<Gfx::FloatPoint> aa_translation {};
[[nodiscard]] Gfx::IntRect bounding_rect() const { return path_bounding_rect; }
};
struct DrawEllipse {
Gfx::IntRect rect;
Color color;
int thickness;
[[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
};
struct FillElipse {
Gfx::IntRect rect;
Color color;
Gfx::AntiAliasingPainter::BlendMode blend_mode;
[[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
};
struct DrawLine {
Color color;
Gfx::IntPoint from;
Gfx::IntPoint to;
int thickness;
Gfx::Painter::LineStyle style;
Color alternate_color;
};
struct DrawSignedDistanceField {
Gfx::IntRect rect;
Color color;
Gfx::GrayscaleBitmap sdf;
float smoothing;
[[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
};
struct PaintProgressbar {
Gfx::IntRect frame_rect;
Gfx::IntRect progress_rect;
Palette palette;
int min;
int max;
int value;
StringView text;
[[nodiscard]] Gfx::IntRect bounding_rect() const { return frame_rect; }
};
struct PaintFrame {
Gfx::IntRect rect;
Palette palette;
Gfx::FrameStyle style;
[[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
};
struct ApplyBackdropFilter {
Gfx::IntRect backdrop_region;
BorderRadiiData border_radii_data;
CSS::ResolvedBackdropFilter backdrop_filter;
[[nodiscard]] Gfx::IntRect bounding_rect() const { return backdrop_region; }
};
struct DrawRect {
Gfx::IntRect rect;
Color color;
bool rough;
[[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
};
struct PaintRadialGradient {
Gfx::IntRect rect;
RadialGradientData radial_gradient_data;
Gfx::IntPoint center;
Gfx::IntSize size;
[[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
};
struct PaintConicGradient {
Gfx::IntRect rect;
ConicGradientData conic_gradient_data;
Gfx::IntPoint position;
[[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
};
struct DrawTriangleWave {
Gfx::IntPoint p1;
Gfx::IntPoint p2;
Color color;
int amplitude;
int thickness;
};
struct SampleUnderCorners {
NonnullRefPtr<BorderRadiusCornerClipper> corner_clipper;
[[nodiscard]] Gfx::IntRect bounding_rect() const;
};
struct BlitCornerClipping {
NonnullRefPtr<BorderRadiusCornerClipper> corner_clipper;
[[nodiscard]] Gfx::IntRect bounding_rect() const;
};
using PaintingCommand = Variant<
DrawTextRun,
DrawText,
FillRect,
DrawScaledBitmap,
SetClipRect,
ClearClipRect,
SetFont,
PushStackingContext,
PopStackingContext,
PushStackingContextWithMask,
PopStackingContextWithMask,
PaintLinearGradient,
PaintRadialGradient,
PaintConicGradient,
PaintOuterBoxShadow,
PaintInnerBoxShadow,
PaintTextShadow,
FillRectWithRoundedCorners,
FillPathUsingColor,
FillPathUsingPaintStyle,
StrokePathUsingColor,
StrokePathUsingPaintStyle,
DrawEllipse,
FillElipse,
DrawLine,
DrawSignedDistanceField,
PaintProgressbar,
PaintFrame,
ApplyBackdropFilter,
DrawRect,
DrawTriangleWave,
SampleUnderCorners,
BlitCornerClipping>;
class PaintingCommandExecutor {
public:
virtual ~PaintingCommandExecutor() = default;
virtual CommandResult draw_text_run(Color const&, Gfx::IntPoint const& baseline_start, String const&, Gfx::Font const&) = 0;
virtual CommandResult draw_text(Gfx::IntRect const&, String const&, Gfx::TextAlignment alignment, Color const&, Gfx::TextElision, Gfx::TextWrapping, Optional<NonnullRefPtr<Gfx::Font>> const&) = 0;
virtual CommandResult fill_rect(Gfx::IntRect const&, Color const&) = 0;
virtual CommandResult draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bitmap const& bitmap, Gfx::IntRect const& src_rect, float opacity, Gfx::Painter::ScalingMode scaling_mode) = 0;
virtual CommandResult set_clip_rect(Gfx::IntRect const& rect) = 0;
virtual CommandResult clear_clip_rect() = 0;
virtual CommandResult set_font(Gfx::Font const& font) = 0;
virtual CommandResult push_stacking_context(bool semitransparent_or_has_non_identity_transform, float opacity, Gfx::FloatRect const& source_rect, Gfx::FloatRect const& transformed_destination_rect, Gfx::IntPoint const& painter_location) = 0;
virtual CommandResult pop_stacking_context(bool semitransparent_or_has_non_identity_transform, Gfx::Painter::ScalingMode scaling_mode) = 0;
virtual CommandResult push_stacking_context_with_mask(Gfx::IntRect const& paint_rect) = 0;
virtual CommandResult pop_stacking_context_with_mask(Gfx::IntRect const& paint_rect, RefPtr<Gfx::Bitmap> const& mask_bitmap, Gfx::Bitmap::MaskKind mask_kind, float opacity) = 0;
virtual CommandResult paint_linear_gradient(Gfx::IntRect const&, LinearGradientData const&) = 0;
virtual CommandResult paint_radial_gradient(Gfx::IntRect const& rect, RadialGradientData const&, Gfx::IntPoint const& center, Gfx::IntSize const& size) = 0;
virtual CommandResult paint_conic_gradient(Gfx::IntRect const& rect, ConicGradientData const&, Gfx::IntPoint const& position) = 0;
virtual CommandResult paint_outer_box_shadow(PaintOuterBoxShadowParams const&) = 0;
virtual CommandResult paint_inner_box_shadow(PaintOuterBoxShadowParams const&) = 0;
virtual CommandResult paint_text_shadow(int blur_radius, Gfx::IntRect const& shadow_bounding_rect, Gfx::IntRect const& text_rect, String const&, Gfx::Font const&, Color const&, int fragment_baseline, Gfx::IntPoint const& draw_location) = 0;
virtual CommandResult fill_rect_with_rounded_corners(Gfx::IntRect const& rect, Color const& color, Gfx::AntiAliasingPainter::CornerRadius const& top_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& top_right_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_right_radius, Optional<Gfx::FloatPoint> const& aa_translation) = 0;
virtual CommandResult fill_path_using_color(Gfx::Path const&, Color const& color, Gfx::Painter::WindingRule, Optional<Gfx::FloatPoint> const& aa_translation) = 0;
virtual CommandResult fill_path_using_paint_style(Gfx::Path const&, Gfx::PaintStyle const& paint_style, Gfx::Painter::WindingRule winding_rule, float opacity, Optional<Gfx::FloatPoint> const& aa_translation) = 0;
virtual CommandResult stroke_path_using_color(Gfx::Path const&, Color const& color, float thickness, Optional<Gfx::FloatPoint> const& aa_translation) = 0;
virtual CommandResult stroke_path_using_paint_style(Gfx::Path const&, Gfx::PaintStyle const& paint_style, float thickness, float opacity, Optional<Gfx::FloatPoint> const& aa_translation) = 0;
virtual CommandResult draw_ellipse(Gfx::IntRect const&, Color const&, int thickness) = 0;
virtual CommandResult fill_ellipse(Gfx::IntRect const&, Color const&, Gfx::AntiAliasingPainter::BlendMode blend_mode) = 0;
virtual CommandResult draw_line(Color const& color, Gfx::IntPoint const& from, Gfx::IntPoint const& to, int thickness, Gfx::Painter::LineStyle, Color const& alternate_color) = 0;
virtual CommandResult draw_signed_distance_field(Gfx::IntRect const& rect, Color const&, Gfx::GrayscaleBitmap const&, float smoothing) = 0;
virtual CommandResult paint_progressbar(Gfx::IntRect const& frame_rect, Gfx::IntRect const& progress_rect, Palette const& palette, int min, int max, int value, StringView const& text) = 0;
virtual CommandResult paint_frame(Gfx::IntRect const& rect, Palette const&, Gfx::FrameStyle) = 0;
virtual CommandResult apply_backdrop_filter(Gfx::IntRect const& backdrop_region, Web::CSS::ResolvedBackdropFilter const& backdrop_filter) = 0;
virtual CommandResult draw_rect(Gfx::IntRect const& rect, Color const&, bool rough) = 0;
virtual CommandResult draw_triangle_wave(Gfx::IntPoint const& p1, Gfx::IntPoint const& p2, Color const& color, int amplitude, int thickness) = 0;
virtual CommandResult sample_under_corners(BorderRadiusCornerClipper&) = 0;
virtual CommandResult blit_corner_clipping(BorderRadiusCornerClipper&) = 0;
virtual bool would_be_fully_clipped_by_painter(Gfx::IntRect) const = 0;
};
class RecordingPainter {
public:
void fill_rect(Gfx::IntRect const& rect, Color color);
struct FillPathUsingColorParams {
Gfx::Path path;
Gfx::Color color;
Gfx::Painter::WindingRule winding_rule = Gfx::Painter::WindingRule::EvenOdd;
Optional<Gfx::FloatPoint> translation = {};
};
void fill_path(FillPathUsingColorParams params);
struct FillPathUsingPaintStyleParams {
Gfx::Path path;
NonnullRefPtr<Gfx::PaintStyle> paint_style;
Gfx::Painter::WindingRule winding_rule = Gfx::Painter::WindingRule::EvenOdd;
float opacity;
Optional<Gfx::FloatPoint> translation = {};
};
void fill_path(FillPathUsingPaintStyleParams params);
struct StrokePathUsingColorParams {
Gfx::Path path;
Gfx::Color color;
float thickness;
Optional<Gfx::FloatPoint> translation = {};
};
void stroke_path(StrokePathUsingColorParams params);
struct StrokePathUsingPaintStyleParams {
Gfx::Path path;
NonnullRefPtr<Gfx::PaintStyle> paint_style;
float thickness;
float opacity;
Optional<Gfx::FloatPoint> translation = {};
};
void stroke_path(StrokePathUsingPaintStyleParams params);
void draw_ellipse(Gfx::IntRect const& a_rect, Color color, int thickness);
void fill_ellipse(Gfx::IntRect const& a_rect, Color color, Gfx::AntiAliasingPainter::BlendMode blend_mode = Gfx::AntiAliasingPainter::BlendMode::Normal);
void fill_rect_with_linear_gradient(Gfx::IntRect const& gradient_rect, LinearGradientData const& data);
void fill_rect_with_conic_gradient(Gfx::IntRect const& rect, ConicGradientData const& data, Gfx::IntPoint const& position);
void fill_rect_with_radial_gradient(Gfx::IntRect const& rect, RadialGradientData const& data, Gfx::IntPoint center, Gfx::IntSize size);
void draw_rect(Gfx::IntRect const& rect, Color color, bool rough = false);
void draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bitmap const& bitmap, Gfx::IntRect const& src_rect, float opacity = 1.0f, Gfx::Painter::ScalingMode scaling_mode = Gfx::Painter::ScalingMode::NearestNeighbor);
void draw_line(Gfx::IntPoint from, Gfx::IntPoint to, Color color, int thickness = 1, Gfx::Painter::LineStyle style = Gfx::Painter::LineStyle::Solid, Color alternate_color = Color::Transparent);
void draw_text(Gfx::IntRect const&, StringView, Gfx::Font const&, Gfx::TextAlignment = Gfx::TextAlignment::TopLeft, Color = Color::Black, Gfx::TextElision = Gfx::TextElision::None, Gfx::TextWrapping = Gfx::TextWrapping::DontWrap);
void draw_text(Gfx::IntRect const& rect, StringView raw_text, Gfx::TextAlignment alignment = Gfx::TextAlignment::TopLeft, Color color = Color::Black, Gfx::TextElision elision = Gfx::TextElision::None, Gfx::TextWrapping wrapping = Gfx::TextWrapping::DontWrap);
void draw_signed_distance_field(Gfx::IntRect const& dst_rect, Color color, Gfx::GrayscaleBitmap const& sdf, float smoothing);
// Streamlined text drawing routine that does no wrapping/elision/alignment.
void draw_text_run(Gfx::IntPoint baseline_start, Utf8View string, Gfx::Font const& font, Color color, Gfx::IntRect const& rect);
void add_clip_rect(Gfx::IntRect const& rect);
void translate(int dx, int dy);
void translate(Gfx::IntPoint delta);
void set_font(Gfx::Font const& font);
void save();
void restore();
struct PushStackingContextParams {
bool semitransparent_or_has_non_identity_transform;
bool has_fixed_position;
float opacity;
Gfx::FloatRect source_rect;
Gfx::FloatRect transformed_destination_rect;
Gfx::IntPoint painter_location;
};
void push_stacking_context(PushStackingContextParams params);
struct PopStackingContextParams {
bool semitransparent_or_has_non_identity_transform;
Gfx::Painter::ScalingMode scaling_mode;
};
void pop_stacking_context(PopStackingContextParams params);
void push_stacking_context_with_mask(Gfx::IntRect paint_rect);
void pop_stacking_context_with_mask(RefPtr<Gfx::Bitmap> mask_bitmap, Gfx::Bitmap::MaskKind mask_kind, Gfx::IntRect paint_rect, float opacity);
void sample_under_corners(NonnullRefPtr<BorderRadiusCornerClipper> corner_clipper);
void blit_corner_clipping(NonnullRefPtr<BorderRadiusCornerClipper> corner_clipper);
void paint_progressbar(Gfx::IntRect frame_rect, Gfx::IntRect progress_rect, Palette palette, int min, int max, int value, StringView text);
void paint_frame(Gfx::IntRect rect, Palette palette, Gfx::FrameStyle style);
void apply_backdrop_filter(Gfx::IntRect const& backdrop_region, BorderRadiiData const& border_radii_data, CSS::ResolvedBackdropFilter const& backdrop_filter);
void paint_outer_box_shadow_params(PaintOuterBoxShadowParams params);
void paint_inner_box_shadow_params(PaintOuterBoxShadowParams params);
void paint_text_shadow(int blur_radius, Gfx::IntRect bounding_rect, Gfx::IntRect text_rect, Utf8View text, Gfx::Font const& font, Color color, int fragment_baseline, Gfx::IntPoint draw_location);
void fill_rect_with_rounded_corners(Gfx::IntRect const& rect, Color color, Gfx::AntiAliasingPainter::CornerRadius top_left_radius, Gfx::AntiAliasingPainter::CornerRadius top_right_radius, Gfx::AntiAliasingPainter::CornerRadius bottom_right_radius, Gfx::AntiAliasingPainter::CornerRadius bottom_left_radius);
void fill_rect_with_rounded_corners(Gfx::IntRect const& a_rect, Color color, int radius);
void fill_rect_with_rounded_corners(Gfx::IntRect const& a_rect, Color color, int top_left_radius, int top_right_radius, int bottom_right_radius, int bottom_left_radius);
void draw_triangle_wave(Gfx::IntPoint a_p1, Gfx::IntPoint a_p2, Color color, int amplitude, int thickness);
void execute(PaintingCommandExecutor&);
RecordingPainter()
{
m_state_stack.append(State());
}
struct State {
Gfx::AffineTransform translation;
Optional<Gfx::IntRect> clip_rect;
};
State& state() { return m_state_stack.last(); }
State const& state() const { return m_state_stack.last(); }
private:
void push_command(PaintingCommand command)
{
m_painting_commands.append(command);
}
Vector<PaintingCommand> m_painting_commands;
Vector<State> m_state_stack;
};
class RecordingPainterStateSaver {
public:
explicit RecordingPainterStateSaver(RecordingPainter& painter)
: m_painter(painter)
{
m_painter.save();
}
~RecordingPainterStateSaver()
{
m_painter.restore();
}
private:
RecordingPainter& m_painter;
};
}