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