1// Aseprite
2// Copyright (C) 2019-2021 Igara Studio S.A.
3// Copyright (C) 2001-2016 David Capello
4//
5// This program is distributed under the terms of
6// the End-User License Agreement for Aseprite.
7
8#ifndef APP_UI_EDITOR_BRUSH_PREVIEW_H_INCLUDED
9#define APP_UI_EDITOR_BRUSH_PREVIEW_H_INCLUDED
10#pragma once
11
12#include "app/extra_cel.h"
13#include "doc/brush.h"
14#include "doc/color.h"
15#include "doc/frame.h"
16#include "doc/mask_boundaries.h"
17#include "gfx/color.h"
18#include "gfx/point.h"
19#include "gfx/rect.h"
20#include "gfx/region.h"
21#include "os/surface.h"
22#include "ui/cursor.h"
23
24#include <vector>
25
26namespace doc {
27 class Layer;
28 class Sprite;
29}
30
31namespace ui {
32 class Graphics;
33}
34
35namespace app {
36 class Editor;
37
38 class BrushPreview {
39 public:
40 // Brush type
41 enum {
42 // A simple cursor in the mouse position for drawing tools. The
43 // crosshair is painted in real-time with black and white
44 // depending on the pixel of the screen.
45 //
46 // |
47 // |
48 // --- * ---
49 // |
50 // |
51 CROSSHAIR = 1,
52
53 // Crosshair used in the selection tools in the sprite position.
54 //
55 // | |
56 // -- --
57 // *
58 // -- --
59 // | |
60 SELECTION_CROSSHAIR = 2,
61
62 // The boundaries of the brush used in the sprite position
63 // (depends on the shape of the brush generated with
64 // doc::MaskBoundaries).
65 BRUSH_BOUNDARIES = 4,
66
67 // Use a pre-defined native cursor that is a crosshair in the
68 // mouse position.
69 NATIVE_CROSSHAIR = 8,
70 };
71
72 static void destroyInternals();
73
74 BrushPreview(Editor* editor);
75 ~BrushPreview();
76
77 bool onScreen() const { return m_onScreen; }
78 const gfx::Point& screenPosition() const { return m_screenPosition; }
79
80 void show(const gfx::Point& screenPos);
81 void hide();
82 void redraw();
83 void discardBrushPreview();
84
85 void invalidateRegion(const gfx::Region& region);
86
87 private:
88 typedef void (BrushPreview::*PixelDelegate)(ui::Graphics*, const gfx::Point&, gfx::Color);
89
90 doc::BrushRef getCurrentBrush();
91 static doc::color_t getBrushColor(doc::Sprite* sprite, doc::Layer* layer);
92
93 void generateBoundaries();
94
95 // Creates a little native cursor to draw the CROSSHAIR
96 void createCrosshairCursor(ui::Graphics* g, const gfx::Color cursorColor);
97
98 void forEachBrushPixel(
99 ui::Graphics* g,
100 const gfx::Point& spritePos,
101 gfx::Color color,
102 PixelDelegate pixelDelegate);
103
104 void traceSelectionCrossPixels(ui::Graphics* g, const gfx::Point& pt, gfx::Color color, int thickness, PixelDelegate pixel);
105 void traceBrushBoundaries(ui::Graphics* g, gfx::Point pos, gfx::Color color, PixelDelegate pixel);
106
107 void savePixelDelegate(ui::Graphics* g, const gfx::Point& pt, gfx::Color color);
108 void drawPixelDelegate(ui::Graphics* g, const gfx::Point& pt, gfx::Color color);
109 void clearPixelDelegate(ui::Graphics* g, const gfx::Point& pt, gfx::Color color);
110
111 Editor* m_editor;
112 int m_type = CROSSHAIR;
113
114 // The brush preview shows the cross or brush boundaries as black
115 // & white negative.
116 bool m_blackAndWhiteNegative;
117
118 // The brush preview is on the screen.
119 bool m_onScreen = false;
120
121 bool m_withRealPreview = false;
122 gfx::Point m_screenPosition; // Position in the screen (view)
123 gfx::Point m_editorPosition; // Position in the editor (model)
124
125 // Information about current brush
126 doc::MaskBoundaries m_brushBoundaries;
127 int m_brushGen;
128
129 // True if we've modified pixels in the display surface
130 // (e.g. drawing the selection crosshair or the brush edges).
131 bool m_withModifiedPixels = false;
132 std::vector<gfx::Color> m_savedPixels;
133 int m_savedPixelsIterator;
134 int m_savedPixelsLimit;
135
136 gfx::Region m_clippingRegion;
137 gfx::Region m_oldClippingRegion;
138
139 // Information stored in show() and used in hide() to clear the
140 // brush preview in the exact same place.
141 gfx::Rect m_lastBounds;
142 doc::frame_t m_lastFrame;
143
144 ExtraCelRef m_extraCel;
145 };
146
147 class HideBrushPreview {
148 public:
149 HideBrushPreview(BrushPreview& brushPreview)
150 : m_brushPreview(brushPreview)
151 , m_oldScreenPosition(brushPreview.screenPosition())
152 , m_onScreen(brushPreview.onScreen()) {
153 if (m_onScreen)
154 m_brushPreview.hide();
155 }
156
157 ~HideBrushPreview() {
158 if (m_onScreen)
159 m_brushPreview.show(m_oldScreenPosition);
160 }
161
162 private:
163 BrushPreview& m_brushPreview;
164 gfx::Point m_oldScreenPosition;
165 bool m_onScreen;
166 };
167
168} // namespace app
169
170#endif
171