more updates
[synfig.git] / synfig-core / trunk / src / synfig / palette.cpp
1 /* === S I N F G =========================================================== */
2 /*!     \file template.cpp
3 **      \brief Template File
4 **
5 **      $Id: palette.cpp,v 1.1.1.1 2005/01/04 01:23:14 darco Exp $
6 **
7 **      \legal
8 **      Copyright (c) 2002 Robert B. Quattlebaum Jr.
9 **
10 **      This software and associated documentation
11 **      are CONFIDENTIAL and PROPRIETARY property of
12 **      the above-mentioned copyright holder.
13 **
14 **      You may not copy, print, publish, or in any
15 **      other way distribute this software without
16 **      a prior written agreement with
17 **      the copyright holder.
18 **      \endlegal
19 */
20 /* ========================================================================= */
21
22 /* === H E A D E R S ======================================================= */
23
24 #ifdef USING_PCH
25 #       include "pch.h"
26 #else
27 #ifdef HAVE_CONFIG_H
28 #       include <config.h>
29 #endif
30
31 #include "palette.h"
32 #include "surface.h"
33 #include "general.h"
34 #include <fstream>
35 #include <iostream>
36
37 #endif
38
39 /* === U S I N G =========================================================== */
40
41 using namespace std;
42 using namespace etl;
43 using namespace sinfg;
44
45 /* === M A C R O S ========================================================= */
46
47 #define PALETTE_FILE_COOKIE     "SINFGPAL1.0"
48
49 /* === G L O B A L S ======================================================= */
50
51 bool weight_less_than(const PaletteItem& lhs,const PaletteItem& rhs)
52 {
53         return lhs.weight<rhs.weight;
54 }
55
56 bool luma_less_than(const PaletteItem& lhs,const PaletteItem& rhs)
57 {
58         return lhs.color.get_y()<rhs.color.get_y();
59 }
60
61 bool luma_less_than(const PaletteItem& lhs,const float& rhs)
62 {
63         return lhs.color.get_y()<rhs;
64 }
65
66 /* === P R O C E D U R E S ================================================= */
67
68 /* === M E T H O D S ======================================================= */
69
70 Palette::Palette():
71         name_(_("Unnamed"))
72 {
73 }
74
75 Palette::Palette(const String& name_):
76         name_(name_)
77 {
78 }
79
80 void
81 PaletteItem::add(const Color& x,int xweight)
82 {
83         color=(color*weight+x*xweight)/(weight+xweight);
84         weight+=xweight;
85 }
86
87 Palette::Palette(const Surface& surface, int max_colors):
88         name_(_("Surface Palette"))
89 {       
90         max_colors-=2;
91         for(int y=0;y<surface.get_h();y++)
92                 for(int x=0;x<surface.get_w();x++)
93                 {
94                         float dist;
95                         Color color(surface[y][x]);
96                                                 
97                         if(empty())
98                         {
99                                 push_back(color);
100                                 continue;
101                         }
102
103                         if(color.get_a()==0)
104                         {
105                                 if(front().color.get_a()!=0)
106                                         insert(begin(),Color(1,0,1,0));
107                                 front().weight+=400;
108                                 continue;
109                         }
110
111                         iterator iter(find_closest(color,&dist));
112                         if(sqrt(dist)<0.005)
113                         {
114                                 iter->add(color);
115                                 continue;
116                         }
117                         
118                         /*if(size()>=max_colors)
119                         {
120                                 iterator iterlight(find_light());
121                                 PaletteItem light(*iterlight);
122                                 erase(iterlight);
123                                 find_closest(light.color)->add(light.color,light.weight);
124                         }
125                         */
126                         
127                         push_back(color);
128                         continue;                       
129                 }
130         sort(rbegin(),rend());
131
132         iterator iter;
133
134         iterator best_match(begin());
135         while((signed)size()>max_colors)
136         {
137                 PaletteItem item(back());
138                 pop_back();
139                 find_closest(item.color)->add(item.color,item.weight);
140         }
141         push_back(Color::black());
142         push_back(Color::white());
143
144 //      sort(begin(),end(),&luma_less_than);
145 }
146
147 Palette::const_iterator
148 Palette::find_closest(const Color& color, float* dist)const
149 {
150         // For the sake of avoiding cut-and-paste
151         // bugs, we'll just use the non-const
152         // find_closest()... It doesn't change anything
153         // anyway.
154         return const_cast<Palette*>(this)->find_closest(color,dist);
155 }
156
157 Palette::iterator
158 Palette::find_closest(const Color& color, float* dist)
159 {
160         iterator iter;
161
162         iterator best_match(begin());
163         float best_dist(1000000);
164
165         const float prep_y(powf(color.get_y(),2.2f)*color.get_a());
166         const float prep_u(color.get_u());
167         const float prep_v(color.get_v());
168         
169         for(iter=begin();iter!=end();++iter)
170         {
171                 const float diff_y(prep_y-powf(iter->color.get_y(),2.2f)*iter->color.get_a());
172                 const float diff_u(prep_u-iter->color.get_u());
173                 const float diff_v(prep_v-iter->color.get_v());
174                 const float diff_a(color.get_a()-iter->color.get_a());
175
176
177                 const float dist(
178                         diff_y*diff_y*1.5f+
179                         diff_a*diff_a+
180
181                         diff_u*diff_u+  
182                         diff_v*diff_v
183                         
184                         // cross product
185                         /*abs(
186                                 prep_u*iter->color.get_u()-     
187                                 prep_v*iter->color.get_v()
188                         )*/
189                 );
190                 if(dist<best_dist)
191                 {
192                         best_dist=dist;
193                         best_match=iter;
194                 }
195         }
196         if(dist)
197                 *dist=best_dist;
198         
199         return best_match;
200 }
201
202
203 Palette::iterator
204 Palette::find_heavy()
205 {
206         iterator iter;
207
208         iterator best_match(begin());
209         
210         for(iter=begin();iter!=end();++iter)
211         {
212                 if(iter->weight>best_match->weight)
213                         best_match=iter;
214         }
215         
216         return best_match;
217 }
218
219 Palette::iterator
220 Palette::find_light()
221 {
222         iterator iter;
223
224         iterator best_match(begin());
225         
226         for(iter=begin();iter!=end();++iter)
227         {
228                 if(iter->weight<best_match->weight)
229                         best_match=iter;
230         }
231         
232         return best_match;
233 }
234
235 Palette
236 Palette::grayscale(int steps)
237 {
238         Palette ret;
239         for(int i=0;i<steps;i++)
240         {
241                 float amount(i/(steps-1));
242                 float y(powf(amount,2.2f));
243                 ret.push_back(
244                         PaletteItem(
245                                 Color(y,y,y),
246                                 strprintf(_("%0.2f%% Gray"),amount)
247                         )
248                 );
249         }
250         return ret;
251 }
252
253 void
254 Palette::save_to_file(const sinfg::String& filename)const
255 {
256         const_iterator iter;
257
258         std::ofstream file(filename.c_str());
259         
260         if(!file)
261                 throw strprintf(_("Unable to open %s for write"),filename.c_str());
262         
263         file<<PALETTE_FILE_COOKIE<<endl;
264         file<<name_<<endl;      
265         for(iter=begin();iter!=end();++iter)
266         {
267                 file<<iter->name<<endl;
268                 file
269                         <<iter->color.get_r()<<endl
270                         <<iter->color.get_g()<<endl
271                         <<iter->color.get_b()<<endl
272                         <<iter->color.get_a()<<endl;
273                 
274         }
275 }
276
277 Palette
278 Palette::load_from_file(const sinfg::String& filename)
279 {
280         std::ifstream file(filename.c_str());
281         
282         if(!file)
283                 throw strprintf(_("Unable to open %s for read"),filename.c_str());
284         
285         Palette ret;
286         String line;
287         
288         getline(file,line);
289
290         if(line!=PALETTE_FILE_COOKIE)
291                 throw strprintf(_("%s does not appear to be a palette file"),filename.c_str());
292
293         getline(file,ret.name_);
294         
295         while(!file.eof())
296         {
297                 PaletteItem item;
298                 getline(file,item.name);
299                 if(!file.eof())break;
300
301                 getline(file,line);
302                 if(!file.eof())break;
303                 item.color.set_r(atof(line.c_str()));
304
305                 getline(file,line);
306                 if(!file.eof())break;
307                 item.color.set_g(atof(line.c_str()));
308
309                 getline(file,line);
310                 if(!file.eof())break;
311                 item.color.set_b(atof(line.c_str()));
312
313                 getline(file,line);
314                 if(!file.eof())break;
315                 item.color.set_a(atof(line.c_str()));
316                 
317                 ret.push_back(item);
318         }
319         
320         return ret;
321 }