Rename get_param_vocab to get_children_vocab and use a wrapper for the pure virtual...
[synfig.git] / synfig-core / src / synfig / palette.cpp
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 <fstream>
38 #include <iostream>
39
40 #endif
41
42 /* === U S I N G =========================================================== */
43
44 using namespace std;
45 using namespace etl;
46 using namespace synfig;
47
48 /* === M A C R O S ========================================================= */
49
50 #define PALETTE_FILE_COOKIE     "SYNFIGPAL1.0"
51
52 /* === G L O B A L S ======================================================= */
53
54 bool weight_less_than(const PaletteItem& lhs,const PaletteItem& rhs)
55 {
56         return lhs.weight<rhs.weight;
57 }
58
59 bool luma_less_than(const PaletteItem& lhs,const PaletteItem& rhs)
60 {
61         return lhs.color.get_y()<rhs.color.get_y();
62 }
63
64 bool luma_less_than(const PaletteItem& lhs,const float& rhs)
65 {
66         return lhs.color.get_y()<rhs;
67 }
68
69 /* === P R O C E D U R E S ================================================= */
70
71 /* === M E T H O D S ======================================================= */
72
73 Palette::Palette():
74         name_(_("Unnamed"))
75 {
76 }
77
78 Palette::Palette(const String& name_):
79         name_(name_)
80 {
81 }
82
83 void
84 PaletteItem::add(const Color& x,int xweight)
85 {
86         color=(color*weight+x*xweight)/(weight+xweight);
87         weight+=xweight;
88 }
89
90 Palette::Palette(const Surface& surface, int max_colors):
91         name_(_("Surface Palette"))
92 {
93         max_colors-=2;
94         for(int i=0;(signed)size()<(max_colors-1) && i<max_colors*16;++i) {
95         int x=rand()%surface.get_w();
96         int y=rand()%surface.get_h();
97
98                         float dist;
99                         Color color(surface[y][x]);
100
101                         if(empty())
102                         {
103                                 push_back(color);
104                                 continue;
105                         }
106
107                         if(color.get_a()==0)
108                         {
109                                 if(front().color.get_a()!=0)
110                                         insert(begin(),Color(1,0,1,0));
111                                 front().weight+=400;
112                                 continue;
113                         }
114
115                         iterator iter(find_closest(color,&dist));
116                         if(sqrt(dist)<0.005)
117                         {
118                                 iter->add(color);
119                                 continue;
120                         }
121
122                         /*if(size()>=max_colors)
123                         {
124                                 iterator iterlight(find_light());
125                                 PaletteItem light(*iterlight);
126                                 erase(iterlight);
127                                 find_closest(light.color)->add(light.color,light.weight);
128                         }
129                         */
130
131                         push_back(color);
132                         continue;
133     }
134
135 /*
136
137         max_colors-=2;
138         for(int y=0;y<surface.get_h();y++)
139                 for(int x=0;x<surface.get_w();x++)
140                 {
141                         float dist;
142                         Color color(surface[y][x]);
143
144                         if(empty())
145                         {
146                                 push_back(color);
147                                 continue;
148                         }
149
150                         if(color.get_a()==0)
151                         {
152                                 if(front().color.get_a()!=0)
153                                         insert(begin(),Color(1,0,1,0));
154                                 front().weight+=400;
155                                 continue;
156                         }
157
158                         iterator iter(find_closest(color,&dist));
159                         if(sqrt(dist)<0.005)
160                         {
161                                 iter->add(color);
162                                 continue;
163                         }
164
165
166                         push_back(color);
167                         continue;
168                 }
169         sort(rbegin(),rend());
170
171         iterator iter;
172
173         iterator best_match(begin());
174         while((signed)size()>max_colors)
175         {
176                 PaletteItem item(back());
177                 pop_back();
178                 find_closest(item.color)->add(item.color,item.weight);
179         }
180 */
181         push_back(Color::black());
182         push_back(Color::white());
183
184 //      sort(begin(),end(),&luma_less_than);
185 }
186
187 Palette::const_iterator
188 Palette::find_closest(const Color& color, float* dist)const
189 {
190         // For the sake of avoiding cut-and-paste
191         // bugs, we'll just use the non-const
192         // find_closest()... It doesn't change anything
193         // anyway.
194         return const_cast<Palette*>(this)->find_closest(color,dist);
195 }
196
197 Palette::iterator
198 Palette::find_closest(const Color& color, float* dist)
199 {
200         iterator iter;
201
202         iterator best_match(begin());
203         float best_dist(1000000);
204
205         const float prep_y(powf(color.get_y(),2.2f)*color.get_a());
206         const float prep_u(color.get_u());
207         const float prep_v(color.get_v());
208
209         for(iter=begin();iter!=end();++iter)
210         {
211                 const float diff_y(prep_y-powf(iter->color.get_y(),2.2f)*iter->color.get_a());
212                 const float diff_u(prep_u-iter->color.get_u());
213                 const float diff_v(prep_v-iter->color.get_v());
214                 const float diff_a(color.get_a()-iter->color.get_a());
215
216
217                 const float dist(
218                         diff_y*diff_y*1.5f+
219                         diff_a*diff_a+
220
221                         diff_u*diff_u+
222                         diff_v*diff_v
223
224                         // cross product
225                         /*abs(
226                                 prep_u*iter->color.get_u()-
227                                 prep_v*iter->color.get_v()
228                         )*/
229                 );
230                 if(dist<best_dist)
231                 {
232                         best_dist=dist;
233                         best_match=iter;
234                 }
235         }
236         if(dist)
237                 *dist=best_dist;
238
239         return best_match;
240 }
241
242
243 Palette::iterator
244 Palette::find_heavy()
245 {
246         iterator iter;
247
248         iterator best_match(begin());
249
250         for(iter=begin();iter!=end();++iter)
251         {
252                 if(iter->weight>best_match->weight)
253                         best_match=iter;
254         }
255
256         return best_match;
257 }
258
259 Palette::iterator
260 Palette::find_light()
261 {
262         iterator iter;
263
264         iterator best_match(begin());
265
266         for(iter=begin();iter!=end();++iter)
267         {
268                 if(iter->weight<best_match->weight)
269                         best_match=iter;
270         }
271
272         return best_match;
273 }
274
275 Palette
276 Palette::grayscale(int steps)
277 {
278         Palette ret;
279         for(int i=0;i<steps;i++)
280         {
281                 float amount(i/(steps-1));
282                 float y(powf(amount,2.2f));
283                 ret.push_back(
284                         PaletteItem(
285                                 Color(y,y,y),
286                                 strprintf(_("%0.2f%% Gray"),amount)
287                         )
288                 );
289         }
290         return ret;
291 }
292
293 void
294 Palette::save_to_file(const synfig::String& filename)const
295 {
296         const_iterator iter;
297
298         std::ofstream file(filename.c_str());
299
300         if(!file)
301                 throw strprintf(_("Unable to open %s for write"),filename.c_str());
302
303         file<<PALETTE_FILE_COOKIE<<endl;
304         file<<name_<<endl;
305         for(iter=begin();iter!=end();++iter)
306         {
307                 file<<iter->name<<endl;
308                 file
309                         <<iter->color.get_r()<<endl
310                         <<iter->color.get_g()<<endl
311                         <<iter->color.get_b()<<endl
312                         <<iter->color.get_a()<<endl;
313
314         }
315 }
316
317 Palette
318 Palette::load_from_file(const synfig::String& filename)
319 {
320         std::ifstream file(filename.c_str());
321
322         if(!file)
323                 throw strprintf(_("Unable to open %s for read"),filename.c_str());
324
325         Palette ret;
326         String line;
327
328         getline(file,line);
329
330         if(line!=PALETTE_FILE_COOKIE)
331                 throw strprintf(_("%s does not appear to be a palette file"),filename.c_str());
332
333         getline(file,ret.name_);
334
335         while(!file.eof())
336         {
337                 PaletteItem item;
338                 String n;
339                 float r, g, b, a;
340                 getline(file,item.name);
341                 file>>r>>g>>b>>a;
342                 item.color.set_r(r);
343                 item.color.set_g(g);
344                 item.color.set_b(b);
345                 item.color.set_a(a);
346                 if(file.eof()) break;
347                 ret.push_back(item);
348         }
349
350         return ret;
351 }