1 | // Aseprite |
2 | // Copyright (C) 2019-2020 Igara Studio S.A. |
3 | // Copyright (C) 2001-2018 David Capello |
4 | // |
5 | // This program is distributed under the terms of |
6 | // the End-User License Agreement for Aseprite. |
7 | |
8 | #ifdef HAVE_CONFIG_H |
9 | #include "config.h" |
10 | #endif |
11 | |
12 | #include "app/color_picker.h" |
13 | |
14 | #include "app/doc.h" |
15 | #include "app/pref/preferences.h" |
16 | #include "app/site.h" |
17 | #include "app/util/wrap_point.h" |
18 | #include "doc/cel.h" |
19 | #include "doc/image.h" |
20 | #include "doc/layer_tilemap.h" |
21 | #include "doc/primitives.h" |
22 | #include "doc/sprite.h" |
23 | #include "doc/tileset.h" |
24 | #include "gfx/point.h" |
25 | #include "render/get_sprite_pixel.h" |
26 | |
27 | #define PICKER_TRACE(...) // TRACE |
28 | |
29 | namespace app { |
30 | |
31 | namespace { |
32 | |
33 | const int kOpacityThreshold = 1; |
34 | |
35 | bool get_cel_pixel(const Cel* cel, |
36 | const double x, |
37 | const double y, |
38 | const frame_t frame, |
39 | color_t& output) |
40 | { |
41 | gfx::RectF celBounds; |
42 | if (cel->layer()->isReference()) |
43 | celBounds = cel->boundsF(); |
44 | else |
45 | celBounds = cel->bounds(); |
46 | |
47 | const doc::Image* image = cel->image(); |
48 | gfx::PointF pos(x, y); |
49 | if (!celBounds.contains(pos)) |
50 | return false; |
51 | |
52 | // For tilemaps |
53 | if (image->pixelFormat() == IMAGE_TILEMAP) { |
54 | ASSERT(cel->layer()->isTilemap()); |
55 | |
56 | auto layerTilemap = static_cast<doc::LayerTilemap*>(cel->layer()); |
57 | doc::Grid grid = layerTilemap->tileset()->grid(); |
58 | grid.origin(grid.origin() + cel->position()); |
59 | |
60 | gfx::Point tilePos = grid.canvasToTile(gfx::Point(pos)); |
61 | PICKER_TRACE("PICKER: tilePos=(%d %d)\n" , tilePos.x,tilePos.y); |
62 | if (!image->bounds().contains(tilePos)) |
63 | return false; |
64 | |
65 | const doc::tile_index ti = |
66 | get_pixel(image, tilePos.x, tilePos.y); |
67 | |
68 | PICKER_TRACE("PICKER: tile index=%d\n" , ti); |
69 | |
70 | doc::ImageRef tile = layerTilemap->tileset()->get(ti); |
71 | if (!tile) |
72 | return false; |
73 | |
74 | const gfx::Point ipos = |
75 | gfx::Point(pos) - grid.tileToCanvas(tilePos); |
76 | |
77 | PICKER_TRACE("PICKER: ipos=%d %d\n" , ipos.x, ipos.y); |
78 | |
79 | output = get_pixel(tile.get(), ipos.x, ipos.y); |
80 | PICKER_TRACE("PICKER: output=%d\n" , output); |
81 | return true; |
82 | } |
83 | // Regular images |
84 | else { |
85 | pos.x = (pos.x-celBounds.x)*image->width()/celBounds.w; |
86 | pos.y = (pos.y-celBounds.y)*image->height()/celBounds.h; |
87 | const gfx::Point ipos(pos); |
88 | if (!image->bounds().contains(ipos)) |
89 | return false; |
90 | |
91 | output = get_pixel(image, ipos.x, ipos.y); |
92 | return true; |
93 | } |
94 | } |
95 | |
96 | } |
97 | |
98 | ColorPicker::ColorPicker() |
99 | : m_tile(doc::notile) |
100 | , m_alpha(0) |
101 | , m_layer(nullptr) |
102 | { |
103 | } |
104 | |
105 | void ColorPicker::pickColor(const Site& site, |
106 | const gfx::PointF& _pos, |
107 | const render::Projection& proj, |
108 | const Mode mode) |
109 | { |
110 | const doc::Sprite* sprite = site.sprite(); |
111 | gfx::PointF pos = _pos; |
112 | |
113 | m_alpha = 255; |
114 | m_color = app::Color::fromMask(); |
115 | |
116 | // Check tiled mode |
117 | if (sprite && site.document()) { |
118 | auto doc = static_cast<const Doc*>(site.document()); |
119 | DocumentPreferences& docPref = Preferences::instance().document(doc); |
120 | |
121 | pos = wrap_pointF(docPref.tiled.mode(), |
122 | site.sprite()->size(), pos); |
123 | } |
124 | |
125 | // Get the color from the image |
126 | switch (mode) { |
127 | |
128 | // Pick from the composed image |
129 | case FromComposition: { |
130 | doc::CelList cels; |
131 | sprite->pickCels(pos.x, pos.y, site.frame(), kOpacityThreshold, |
132 | sprite->allVisibleLayers(), cels); |
133 | if (!cels.empty()) |
134 | m_layer = cels.front()->layer(); |
135 | |
136 | if (site.tilemapMode() == TilemapMode::Tiles) { |
137 | if (cels.empty() || !cels.front()->image()->isTilemap()) |
138 | return; |
139 | |
140 | const gfx::Point tilePos = site.grid().canvasToTile(gfx::Point(pos)); |
141 | if (cels.front()->image()->bounds().contains(tilePos)) { |
142 | m_tile = doc::get_pixel(cels.front()->image(), tilePos.x, tilePos.y); |
143 | m_color = app::Color::fromIndex(m_tile); |
144 | } |
145 | } |
146 | else if (site.tilemapMode() == TilemapMode::Pixels) { |
147 | m_color = app::Color::fromImage( |
148 | sprite->pixelFormat(), |
149 | render::get_sprite_pixel(sprite, pos.x, pos.y, |
150 | site.frame(), proj, |
151 | Preferences::instance().experimental.newBlend())); |
152 | } |
153 | break; |
154 | } |
155 | |
156 | // Pick from the current layer |
157 | case FromActiveLayer: { |
158 | const Cel* cel = site.cel(); |
159 | if (!cel) |
160 | return; |
161 | |
162 | if (site.tilemapMode() == TilemapMode::Tiles) { |
163 | const gfx::Point tilePos = site.grid().canvasToTile(gfx::Point(pos)); |
164 | if (cel->image()->bounds().contains(tilePos)) { |
165 | m_tile = doc::get_pixel(cel->image(), tilePos.x, tilePos.y); |
166 | m_color = app::Color::fromIndex(m_tile); |
167 | } |
168 | } |
169 | else if (site.tilemapMode() == TilemapMode::Pixels) { |
170 | doc::color_t imageColor; |
171 | if (!get_cel_pixel(cel, pos.x, pos.y, |
172 | site.frame(), imageColor)) |
173 | return; |
174 | |
175 | doc::PixelFormat pixelFormat = |
176 | (cel->layer()->isTilemap() ? sprite->pixelFormat(): |
177 | cel->image()->pixelFormat()); |
178 | switch (pixelFormat) { |
179 | case IMAGE_RGB: |
180 | m_alpha = doc::rgba_geta(imageColor); |
181 | break; |
182 | case IMAGE_GRAYSCALE: |
183 | m_alpha = doc::graya_geta(imageColor); |
184 | break; |
185 | } |
186 | |
187 | m_color = app::Color::fromImage(pixelFormat, imageColor); |
188 | m_layer = const_cast<Layer*>(site.layer()); |
189 | } |
190 | break; |
191 | } |
192 | |
193 | case FromFirstReferenceLayer: { |
194 | doc::CelList cels; |
195 | sprite->pickCels(pos.x, pos.y, site.frame(), kOpacityThreshold, |
196 | sprite->allVisibleReferenceLayers(), cels); |
197 | |
198 | for (const Cel* cel : cels) { |
199 | doc::color_t imageColor; |
200 | if (get_cel_pixel(cel, pos.x, pos.y, |
201 | site.frame(), imageColor)) { |
202 | m_color = app::Color::fromImage( |
203 | cel->image()->pixelFormat(), imageColor); |
204 | m_layer = cel->layer(); |
205 | break; |
206 | } |
207 | } |
208 | break; |
209 | } |
210 | } |
211 | } |
212 | |
213 | } // namespace app |
214 | |