1// Aseprite
2// Copyright (C) 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/modules/palettes.h"
13
14#include "app/app.h"
15#include "app/extensions.h"
16#include "app/file/palette_file.h"
17#include "app/resource_finder.h"
18#include "base/fs.h"
19#include "doc/image.h"
20#include "doc/palette.h"
21#include "doc/sprite.h"
22
23#include <cstring>
24
25namespace app {
26
27// The default color palette.
28static Palette* ase_default_palette = NULL;
29
30// Palette in current sprite frame.
31static Palette* ase_current_palette = NULL;
32
33int init_module_palette()
34{
35 ase_default_palette = new Palette(frame_t(0), 256);
36 ase_current_palette = new Palette(frame_t(0), 256);
37 return 0;
38}
39
40void exit_module_palette()
41{
42 delete ase_default_palette;
43 delete ase_current_palette;
44}
45
46void load_default_palette()
47{
48 std::unique_ptr<Palette> pal;
49 std::string defaultPalName = get_preset_palette_filename(
50 get_default_palette_preset_name(), ".ase");
51
52 // If there is no palette in command line, we use the default one.
53 std::string palFile = defaultPalName;
54 if (base::is_file(palFile)) {
55 pal = load_palette(palFile.c_str());
56 }
57 else {
58 // Migrate old default.gpl to default.ase format
59 palFile = get_preset_palette_filename(
60 get_default_palette_preset_name(), ".gpl");
61
62 if (base::is_file(palFile)) {
63 pal = load_palette(palFile.c_str());
64
65 // Remove duplicate black entries at the end (as old palettes
66 // contains 256 colors)
67 if (pal && pal->size() == 256) {
68 doc::color_t black = rgba(0, 0, 0, 255);
69
70 // Get the last non-black entry
71 int i = 0;
72 for (i=pal->size()-1; i>0; --i) {
73 if (pal->getEntry(i) != black)
74 break;
75 }
76
77 if (i < pal->size()-1) {
78 // Check if there is a black entry in the first entries.
79 bool hasBlack = false;
80 for (int j=0; j<i; ++j) {
81 if (pal->getEntry(j) == black) {
82 hasBlack = true;
83 break;
84 }
85 }
86 if (!hasBlack)
87 ++i; // Leave one black entry
88
89 // Resize the palette
90 if (i < pal->size()-1)
91 pal->resize(i+1);
92 }
93 }
94
95 // We could remove the old .gpl file (palFile), but as the
96 // user could be using multiple versions of Aseprite, it's a
97 // good idea to keep both formats (.gpl for Aseprite <=
98 // v1.1-beta5, and .ase for future versions).
99 }
100 // If the default palette file doesn't exist, we copy db32.gpl
101 // as the default one (default.ase).
102 else {
103 std::string path = App::instance()->extensions().palettePath("DB32");
104 if (path.empty())
105 path = App::instance()->extensions().palettePath("VGA 13h");
106 if (!path.empty())
107 pal = load_palette(path.c_str());
108 }
109
110 // Save default.ase file
111 if (pal) {
112 palFile = defaultPalName;
113 save_palette(palFile.c_str(), pal.get(), 0, nullptr);
114 }
115 }
116
117 if (pal)
118 set_default_palette(pal.get());
119
120 set_current_palette(nullptr, true);
121}
122
123// TODO This palette isn't synced with the current sprite palette when
124// ENABLE_UI=0 and we are running scripts, we should remove this
125// function and use the active Site palette.
126Palette* get_current_palette()
127{
128 return ase_current_palette;
129}
130
131Palette* get_default_palette()
132{
133 return ase_default_palette;
134}
135
136void set_default_palette(const Palette* palette)
137{
138 palette->copyColorsTo(ase_default_palette);
139}
140
141// Changes the current system palette and triggers the
142// App::PaletteChange signal.
143//
144// If "_palette" is nullptr the default palette is set.
145bool set_current_palette(const Palette *_palette, bool forced)
146{
147 const Palette* palette = (_palette ? _palette: ase_default_palette);
148 bool ret = false;
149
150 // Have changes
151 if (forced ||
152 palette->countDiff(ase_current_palette, NULL, NULL) > 0) {
153 // Copy current palette
154 palette->copyColorsTo(ase_current_palette);
155
156 // Call slots in signals
157 App::instance()->PaletteChange();
158
159 ret = true;
160 }
161
162 return ret;
163}
164
165std::string get_preset_palette_filename(const std::string& preset,
166 const std::string& dot_extension)
167{
168 std::string palettesDir = get_preset_palettes_dir();
169
170 return base::join_path(palettesDir, preset + dot_extension);
171}
172
173std::string get_default_palette_preset_name()
174{
175 return "default";
176}
177
178std::string get_preset_palettes_dir()
179{
180 ResourceFinder rf;
181 rf.includeUserDir(base::join_path("palettes", ".").c_str());
182 std::string palettesDir = rf.getFirstOrCreateDefault();
183
184 if (!base::is_directory(palettesDir))
185 base::make_directory(palettesDir);
186
187 return palettesDir;
188}
189
190} // namespace app
191