1 | // Aseprite |
2 | // Copyright (C) 2018-2022 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/cmd/assign_color_profile.h" |
13 | #include "app/cmd/convert_color_profile.h" |
14 | #include "app/cmd/set_pixel_ratio.h" |
15 | #include "app/cmd/set_user_data.h" |
16 | #include "app/color.h" |
17 | #include "app/commands/command.h" |
18 | #include "app/context_access.h" |
19 | #include "app/doc_api.h" |
20 | #include "app/i18n/strings.h" |
21 | #include "app/modules/gui.h" |
22 | #include "app/pref/preferences.h" |
23 | #include "app/tx.h" |
24 | #include "app/ui/color_button.h" |
25 | #include "app/ui/user_data_view.h" |
26 | #include "app/util/pixel_ratio.h" |
27 | #include "base/mem_utils.h" |
28 | #include "doc/image.h" |
29 | #include "doc/palette.h" |
30 | #include "doc/sprite.h" |
31 | #include "doc/user_data.h" |
32 | #include "fmt/format.h" |
33 | #include "os/color_space.h" |
34 | #include "os/system.h" |
35 | #include "ui/ui.h" |
36 | |
37 | #include "sprite_properties.xml.h" |
38 | |
39 | namespace app { |
40 | |
41 | using namespace ui; |
42 | |
43 | class SpritePropertiesWindow : public app::gen::SpriteProperties { |
44 | public: |
45 | SpritePropertiesWindow(Sprite* sprite) |
46 | : SpriteProperties() |
47 | , m_sprite(sprite) |
48 | , m_userDataView(Preferences::instance().sprite.userDataVisibility) |
49 | { |
50 | userData()->Click.connect([this]{ onToggleUserData(); }); |
51 | |
52 | m_userDataView.configureAndSet(m_sprite->userData(), |
53 | propertiesGrid()); |
54 | |
55 | remapWindow(); |
56 | centerWindow(); |
57 | load_window_pos(this, "SpriteProperties" ); |
58 | manager()->invalidate(); |
59 | } |
60 | |
61 | const UserData& getUserData() const { return m_userDataView.userData(); } |
62 | |
63 | private: |
64 | void onToggleUserData() { |
65 | m_userDataView.toggleVisibility(); |
66 | remapWindow(); |
67 | manager()->invalidate(); |
68 | } |
69 | |
70 | Sprite* m_sprite; |
71 | UserDataView m_userDataView; |
72 | }; |
73 | |
74 | class SpritePropertiesCommand : public Command { |
75 | public: |
76 | SpritePropertiesCommand(); |
77 | |
78 | protected: |
79 | bool onEnabled(Context* context) override; |
80 | void onExecute(Context* context) override; |
81 | }; |
82 | |
83 | SpritePropertiesCommand::SpritePropertiesCommand() |
84 | : Command(CommandId::SpriteProperties(), CmdUIOnlyFlag) |
85 | { |
86 | } |
87 | |
88 | bool SpritePropertiesCommand::onEnabled(Context* context) |
89 | { |
90 | return context->checkFlags(ContextFlags::ActiveDocumentIsWritable | |
91 | ContextFlags::HasActiveSprite); |
92 | } |
93 | |
94 | void SpritePropertiesCommand::onExecute(Context* context) |
95 | { |
96 | std::string imgtype_text; |
97 | ColorButton* color_button = nullptr; |
98 | |
99 | // List of available color profiles |
100 | std::vector<os::ColorSpaceRef> colorSpaces; |
101 | os::instance()->listColorSpaces(colorSpaces); |
102 | |
103 | // Load the window widget |
104 | SpritePropertiesWindow window(context->activeDocument()->sprite()); |
105 | |
106 | int selectedColorProfile = -1; |
107 | |
108 | auto updateButtons = |
109 | [&] { |
110 | bool enabled = (selectedColorProfile != window.colorProfile()->getSelectedItemIndex()); |
111 | window.assignColorProfile()->setEnabled(enabled); |
112 | window.convertColorProfile()->setEnabled(enabled); |
113 | window.ok()->setEnabled(!enabled); |
114 | }; |
115 | |
116 | // Get sprite properties and fill frame fields |
117 | { |
118 | const ContextReader reader(context); |
119 | const Doc* document(reader.document()); |
120 | const Sprite* sprite(reader.sprite()); |
121 | |
122 | // Update widgets values |
123 | switch (sprite->pixelFormat()) { |
124 | case IMAGE_RGB: |
125 | imgtype_text = Strings::sprite_properties_rgb(); |
126 | break; |
127 | case IMAGE_GRAYSCALE: |
128 | imgtype_text = Strings::sprite_properties_grayscale(); |
129 | break; |
130 | case IMAGE_INDEXED: |
131 | imgtype_text = fmt::format(Strings::sprite_properties_indexed_color(), |
132 | sprite->palette(0)->size()); |
133 | break; |
134 | default: |
135 | ASSERT(false); |
136 | imgtype_text = Strings::general_unknown(); |
137 | break; |
138 | } |
139 | |
140 | // Filename |
141 | window.name()->setText(document->filename()); |
142 | |
143 | // Color mode |
144 | window.type()->setText(imgtype_text.c_str()); |
145 | |
146 | // Sprite size (width and height) |
147 | window.size()->setTextf( |
148 | "%dx%d (%s)" , |
149 | sprite->width(), |
150 | sprite->height(), |
151 | base::get_pretty_memory_size(sprite->getMemSize()).c_str()); |
152 | |
153 | // How many frames |
154 | window.frames()->setTextf("%d" , (int)sprite->totalFrames()); |
155 | |
156 | if (sprite->pixelFormat() == IMAGE_INDEXED) { |
157 | color_button = new ColorButton(app::Color::fromIndex(sprite->transparentColor()), |
158 | IMAGE_INDEXED, |
159 | ColorButtonOptions()); |
160 | |
161 | window.transparentColorPlaceholder()->addChild(color_button); |
162 | |
163 | // TODO add a way to get or create an existent TooltipManager |
164 | TooltipManager* tooltipManager = new TooltipManager; |
165 | window.addChild(tooltipManager); |
166 | tooltipManager->addTooltipFor( |
167 | color_button, |
168 | Strings::sprite_properties_transparent_color_tooltip(), |
169 | LEFT); |
170 | } |
171 | else { |
172 | window.transparentColorPlaceholder()->addChild( |
173 | new Label(Strings::sprite_properties_indexed_image_only())); |
174 | } |
175 | |
176 | // Pixel ratio |
177 | window.pixelRatio()->setValue( |
178 | base::convert_to<std::string>(sprite->pixelRatio())); |
179 | |
180 | // Color profile |
181 | selectedColorProfile = -1; |
182 | int i = 0; |
183 | for (auto& cs : colorSpaces) { |
184 | if (cs->gfxColorSpace()->nearlyEqual(*sprite->colorSpace())) { |
185 | selectedColorProfile = i; |
186 | break; |
187 | } |
188 | ++i; |
189 | } |
190 | if (selectedColorProfile < 0) { |
191 | colorSpaces.push_back(os::instance()->makeColorSpace(sprite->colorSpace())); |
192 | selectedColorProfile = colorSpaces.size()-1; |
193 | } |
194 | |
195 | for (auto& cs : colorSpaces) |
196 | window.colorProfile()->addItem(cs->gfxColorSpace()->name()); |
197 | window.colorProfile()->setSelectedItemIndex(selectedColorProfile); |
198 | |
199 | window.assignColorProfile()->setEnabled(false); |
200 | window.convertColorProfile()->setEnabled(false); |
201 | window.colorProfile()->Change.connect(updateButtons); |
202 | |
203 | window.assignColorProfile()->Click.connect( |
204 | [&](Event&){ |
205 | selectedColorProfile = window.colorProfile()->getSelectedItemIndex(); |
206 | |
207 | ContextWriter writer(context); |
208 | Sprite* sprite(writer.sprite()); |
209 | Tx tx(writer.context(), Strings::sprite_properties_assign_color_profile()); |
210 | tx(new cmd::AssignColorProfile( |
211 | sprite, colorSpaces[selectedColorProfile]->gfxColorSpace())); |
212 | tx.commit(); |
213 | |
214 | updateButtons(); |
215 | }); |
216 | window.convertColorProfile()->Click.connect( |
217 | [&](Event&){ |
218 | selectedColorProfile = window.colorProfile()->getSelectedItemIndex(); |
219 | |
220 | ContextWriter writer(context); |
221 | Sprite* sprite(writer.sprite()); |
222 | Tx tx(writer.context(), Strings::sprite_properties_convert_color_profile()); |
223 | tx(new cmd::ConvertColorProfile( |
224 | sprite, colorSpaces[selectedColorProfile]->gfxColorSpace())); |
225 | tx.commit(); |
226 | |
227 | updateButtons(); |
228 | }); |
229 | } |
230 | |
231 | window.remapWindow(); |
232 | window.centerWindow(); |
233 | |
234 | load_window_pos(&window, "SpriteProperties" ); |
235 | window.setVisible(true); |
236 | window.openWindowInForeground(); |
237 | |
238 | if (window.closer() == window.ok()) { |
239 | ContextWriter writer(context); |
240 | Sprite* sprite(writer.sprite()); |
241 | |
242 | color_t index = (color_button ? color_button->getColor().getIndex(): |
243 | sprite->transparentColor()); |
244 | PixelRatio pixelRatio = |
245 | base::convert_to<PixelRatio>(window.pixelRatio()->getValue()); |
246 | |
247 | const UserData newUserData = window.getUserData(); |
248 | |
249 | if (index != sprite->transparentColor() || |
250 | pixelRatio != sprite->pixelRatio() || |
251 | newUserData != sprite->userData()) { |
252 | Tx tx(writer.context(), Strings::sprite_properties_change_sprite_props()); |
253 | DocApi api = writer.document()->getApi(tx); |
254 | |
255 | if (index != sprite->transparentColor()) |
256 | api.setSpriteTransparentColor(sprite, index); |
257 | |
258 | if (pixelRatio != sprite->pixelRatio()) |
259 | tx(new cmd::SetPixelRatio(sprite, pixelRatio)); |
260 | |
261 | if (newUserData != sprite->userData()) |
262 | tx(new cmd::SetUserData(sprite, newUserData, static_cast<Doc*>(sprite->document()))); |
263 | |
264 | tx.commit(); |
265 | |
266 | update_screen_for_document(writer.document()); |
267 | } |
268 | } |
269 | |
270 | save_window_pos(&window, "SpriteProperties" ); |
271 | } |
272 | |
273 | Command* CommandFactory::createSpritePropertiesCommand() |
274 | { |
275 | return new SpritePropertiesCommand; |
276 | } |
277 | |
278 | } // namespace app |
279 | |