1 /* === S Y N F I G ========================================================= */
2 /*! \file palette.cpp
3 ** \brief Template File
4 **
5 ** $Id$
6 **
7 ** \legal
8 ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 ** Copyright (c) 2010 Nikita Kitaev
10 ** Copyright (c) 2010 Carlos López
11 **
12 ** This package is free software; you can redistribute it and/or
13 ** modify it under the terms of the GNU General Public License as
14 ** published by the Free Software Foundation; either version 2 of
15 ** the License, or (at your option) any later version.
16 **
17 ** This package is distributed in the hope that it will be useful,
18 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 ** General Public License for more details.
21 ** \endlegal
22 */
23 /* ========================================================================= */
24
25 /* === H E A D E R S ======================================================= */
26
27 #ifdef USING_PCH
28 # include "pch.h"
29 #else
30 #ifdef HAVE_CONFIG_H
31 # include <config.h>
32 #endif
33
34 #include "palette.h"
35 #include "surface.h"
36 #include "general.h"
37 #include <synfig/localization.h>
38 #include "gamma.h"
39 #include <fstream>
40 #include <iostream>
41 #include <sstream>
42
43 #endif
44
45 /* === U S I N G =========================================================== */
46
47 using namespace std;
48 using namespace etl;
49 using namespace synfig;
50
51 /* === M A C R O S ========================================================= */
52
53 #define PALETTE_SYNFIG_FILE_COOKIE "SYNFIGPAL1.0"
54 #define PALETTE_SYNFIG_EXT ".spal"
55 #define PALETTE_GIMP_FILE_COOKIE "GIMP Palette"
56 #define PALETTE_GIMP_EXT ".gpl"
57
58 /* === G L O B A L S ======================================================= */
59
weight_less_than(const PaletteItem & lhs,const PaletteItem & rhs)60 bool weight_less_than(const PaletteItem& lhs,const PaletteItem& rhs)
61 {
62 return lhs.weight<rhs.weight;
63 }
64
luma_less_than(const PaletteItem & lhs,const PaletteItem & rhs)65 bool luma_less_than(const PaletteItem& lhs,const PaletteItem& rhs)
66 {
67 return lhs.color.get_y()<rhs.color.get_y();
68 }
69
luma_less_than(const PaletteItem & lhs,const float & rhs)70 bool luma_less_than(const PaletteItem& lhs,const float& rhs)
71 {
72 return lhs.color.get_y()<rhs;
73 }
74
75 /* === P R O C E D U R E S ================================================= */
76
77 /* === M E T H O D S ======================================================= */
78
Palette()79 Palette::Palette():
80 name_(_("Unnamed"))
81 {
82 }
83
Palette(const String & name_)84 Palette::Palette(const String& name_):
85 name_(name_)
86 {
87 }
88
89 void
add(const Color & x,int xweight)90 PaletteItem::add(const Color& x,int xweight)
91 {
92 color=(color*weight+x*xweight)/(weight+xweight);
93 weight+=xweight;
94 }
95
Palette(const Surface & surface,int max_colors)96 Palette::Palette(const Surface& surface, int max_colors):
97 name_(_("Surface Palette"))
98 {
99 max_colors-=2;
100 for(int i=0;(signed)size()<(max_colors-1) && i<max_colors*16;++i) {
101 int x=rand()%surface.get_w();
102 int y=rand()%surface.get_h();
103
104 float dist;
105 Color color(surface[y][x]);
106
107 if(empty())
108 {
109 push_back(color);
110 continue;
111 }
112
113 if(color.get_a()==0)
114 {
115 if(front().color.get_a()!=0)
116 insert(begin(),Color(1,0,1,0));
117 front().weight+=400;
118 continue;
119 }
120
121 iterator iter(find_closest(color,&dist));
122 if(sqrt(dist)<0.005)
123 {
124 iter->add(color);
125 continue;
126 }
127
128 /*if(size()>=max_colors)
129 {
130 iterator iterlight(find_light());
131 PaletteItem light(*iterlight);
132 erase(iterlight);
133 find_closest(light.color)->add(light.color,light.weight);
134 }
135 */
136
137 push_back(color);
138 continue;
139 }
140
141 /*
142
143 max_colors-=2;
144 for(int y=0;y<surface.get_h();y++)
145 for(int x=0;x<surface.get_w();x++)
146 {
147 float dist;
148 Color color(surface[y][x]);
149
150 if(empty())
151 {
152 push_back(color);
153 continue;
154 }
155
156 if(color.get_a()==0)
157 {
158 if(front().color.get_a()!=0)
159 insert(begin(),Color(1,0,1,0));
160 front().weight+=400;
161 continue;
162 }
163
164 iterator iter(find_closest(color,&dist));
165 if(sqrt(dist)<0.005)
166 {
167 iter->add(color);
168 continue;
169 }
170
171
172 push_back(color);
173 continue;
174 }
175 sort(rbegin(),rend());
176
177 iterator iter;
178
179 iterator best_match(begin());
180 while((signed)size()>max_colors)
181 {
182 PaletteItem item(back());
183 pop_back();
184 find_closest(item.color)->add(item.color,item.weight);
185 }
186 */
187 push_back(Color::black());
188 push_back(Color::white());
189
190 // sort(begin(),end(),&luma_less_than);
191 }
192
193 Palette::const_iterator
find_closest(const Color & color,float * dist) const194 Palette::find_closest(const Color& color, float* dist)const
195 {
196 // For the sake of avoiding cut-and-paste
197 // bugs, we'll just use the non-const
198 // find_closest()... It doesn't change anything
199 // anyway.
200 return const_cast<Palette*>(this)->find_closest(color,dist);
201 }
202
203 Palette::iterator
find_closest(const Color & color,float * dist)204 Palette::find_closest(const Color& color, float* dist)
205 {
206 iterator iter;
207
208 iterator best_match(begin());
209 float best_dist(1000000);
210
211 const float prep_y(powf(color.get_y(),2.2f)*color.get_a());
212 const float prep_u(color.get_u());
213 const float prep_v(color.get_v());
214
215 for(iter=begin();iter!=end();++iter)
216 {
217 const float diff_y(prep_y-powf(iter->color.get_y(),2.2f)*iter->color.get_a());
218 const float diff_u(prep_u-iter->color.get_u());
219 const float diff_v(prep_v-iter->color.get_v());
220 const float diff_a(color.get_a()-iter->color.get_a());
221
222
223 const float dist(
224 diff_y*diff_y*1.5f+
225 diff_a*diff_a+
226
227 diff_u*diff_u+
228 diff_v*diff_v
229
230 // cross product
231 /*abs(
232 prep_u*iter->color.get_u()-
233 prep_v*iter->color.get_v()
234 )*/
235 );
236 if(dist<best_dist)
237 {
238 best_dist=dist;
239 best_match=iter;
240 }
241 }
242 if(dist)
243 *dist=best_dist;
244
245 return best_match;
246 }
247
248
249 Palette::iterator
find_heavy()250 Palette::find_heavy()
251 {
252 iterator iter;
253
254 iterator best_match(begin());
255
256 for(iter=begin();iter!=end();++iter)
257 {
258 if(iter->weight>best_match->weight)
259 best_match=iter;
260 }
261
262 return best_match;
263 }
264
265 Palette::iterator
find_light()266 Palette::find_light()
267 {
268 iterator iter;
269
270 iterator best_match(begin());
271
272 for(iter=begin();iter!=end();++iter)
273 {
274 if(iter->weight<best_match->weight)
275 best_match=iter;
276 }
277
278 return best_match;
279 }
280
281 Palette
grayscale(int steps)282 Palette::grayscale(int steps)
283 {
284 Palette ret;
285 for(int i=0;i<steps;i++)
286 {
287 float amount(i/(steps-1));
288 float y(powf(amount,2.2f));
289 ret.push_back(
290 PaletteItem(
291 Color(y,y,y),
292 strprintf(_("%0.2f%% Gray"),amount)
293 )
294 );
295 }
296 return ret;
297 }
298
299 void
save_to_file(const synfig::String & filename) const300 Palette::save_to_file(const synfig::String& filename)const
301 {
302 const_iterator iter;
303
304 std::ofstream file(filename.c_str());
305
306 if(!file)
307 throw strprintf(_("Unable to open %s for write"),filename.c_str());
308
309 file<<PALETTE_SYNFIG_FILE_COOKIE<<endl;
310 file<<name_.c_str()<<endl;
311 for(iter=begin();iter!=end();++iter)
312 {
313 file<<iter->name.c_str()<<endl;
314 file
315 <<iter->color.get_r()<<endl
316 <<iter->color.get_g()<<endl
317 <<iter->color.get_b()<<endl
318 <<iter->color.get_a()<<endl;
319
320 }
321 }
322
323 Palette
load_from_file(const synfig::String & filename)324 Palette::load_from_file(const synfig::String& filename)
325 {
326 std::ifstream file(filename.c_str());
327
328 if(!file)
329 throw strprintf(_("Unable to open %s for read"),filename.c_str());
330
331 Palette ret;
332 String line("");
333 String ext(filename_extension(filename));
334
335
336 if (ext==PALETTE_SYNFIG_EXT)
337 {
338 getline(file,line);
339
340 if(line!=PALETTE_SYNFIG_FILE_COOKIE)
341 throw strprintf(_("%s does not appear to be a valid %s palette file"),filename.c_str(),"Synfig");
342
343 getline(file,ret.name_);
344
345 while(!file.eof())
346 {
347 PaletteItem item;
348 String n;
349 float r, g, b, a;
350 getline(file,item.name);
351 file >> r >> g >> b >> a;
352 item.color.set_r(r);
353 item.color.set_g(g);
354 item.color.set_b(b);
355 item.color.set_a(a);
356
357 // file ends in new line
358 if (!file.eof())
359 ret.push_back(item);
360 }
361 }
362 else if (ext==PALETTE_GIMP_EXT)
363 {
364 /*
365 file format: GPL (GIMP Palette) file should have the following layout:
366 GIMP Palette
367 Name: <palette name>
368 [Columns: <number>]
369 [#]
370 [# Optional comments]
371 [#]
372 <value R> <value G> <value B> <swatch name>
373 <value R> <value G> <value B> <swatch name>
374 ... ...
375 [<new line>]
376 */
377
378 do {
379 getline(file,line);
380 } while (!file.eof() && line != PALETTE_GIMP_FILE_COOKIE);
381
382 if (line != PALETTE_GIMP_FILE_COOKIE)
383 throw strprintf(_("%s does not appear to be a valid %s palette file"),filename.c_str(),"GIMP");
384
385
386 bool has_color = false;
387
388 do
389 {
390 getline(file, line);
391
392 if (!line.empty() && line.substr(0,5) == "Name:")
393 ret.name_ = String(line.substr(6));
394 else if (!line.empty() && line.substr(0,8) == "Columns:")
395 ; // Ignore columns
396 else if (!line.empty() && line.substr(0,1) == "#")
397 ; // Ignore comments
398 else if (!line.empty())
399 {
400 // not empty line not part of the header => color
401 has_color = true;
402 // line contains the first color so we put it back in (including \n)
403 for (int i = line.length()+1; i; i--)
404 file.unget();
405 }
406 } while (!file.eof() && !has_color);
407
408 // Gamma color conversion.
409 // In the importing case, gamma factor is 1, as default
410 Gamma gamma;
411
412 while(!file.eof() && has_color)
413 {
414 PaletteItem item;
415 float r, g, b;
416
417 stringstream ss;
418 getline (file, line);
419
420 if (!line.empty())
421 {
422 ss << line.c_str();
423
424 ss >> r >> g >> b;
425 getline(ss, item.name);
426
427 item.color.set_r(gamma.r_F32_to_F32(r/255));
428 item.color.set_g(gamma.g_F32_to_F32(g/255));
429 item.color.set_b(gamma.b_F32_to_F32(b/255));
430 // Alpha is 1 by default
431 item.color.set_a(1);
432
433 ret.push_back(item);
434 }
435 }
436 }
437 else
438 throw strprintf(_("%s does not appear to be a supported palette file"),filename.c_str());
439
440 return ret;
441 }
442