Remove unused define no longer used
[synfig.git] / synfig-core / src / modules / mod_gif / trgt_gif.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file trgt_gif.cpp
3 **      \brief BMP Target Module
4 **
5 **      $Id$
6 **
7 **      \legal
8 **      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 **      Copyright (c) 2007 Chris Moore
10 **
11 **      This package is free software; you can redistribute it and/or
12 **      modify it under the terms of the GNU General Public License as
13 **      published by the Free Software Foundation; either version 2 of
14 **      the License, or (at your option) any later version.
15 **
16 **      This package is distributed in the hope that it will be useful,
17 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
18 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 **      General Public License for more details.
20 **      \endlegal
21 **
22 ** === N O T E S ===========================================================
23 **
24 ** ========================================================================= */
25
26 /* === H E A D E R S ======================================================= */
27
28 #ifdef USING_PCH
29 #       include "pch.h"
30 #else
31 #ifdef HAVE_CONFIG_H
32 #       include <config.h>
33 #endif
34
35 #include <ETL/stringf>
36 #include "trgt_gif.h"
37 #include <stdio.h>
38 #endif
39
40 /* === M A C R O S ========================================================= */
41
42 using namespace synfig;
43 using namespace std;
44 using namespace etl;
45
46 #define MAX_FRAME_RATE  (20.0)
47
48 /* === G L O B A L S ======================================================= */
49
50 SYNFIG_TARGET_INIT(gif);
51 SYNFIG_TARGET_SET_NAME(gif,"gif");
52 SYNFIG_TARGET_SET_EXT(gif,"gif");
53 SYNFIG_TARGET_SET_VERSION(gif,"0.1");
54 SYNFIG_TARGET_SET_CVS_ID(gif,"$Id$");
55
56 /* === M E T H O D S ======================================================= */
57
58 gif::gif(const char *filename_, const synfig::TargetParam& /* params */):
59         filename(filename_),
60         file( (filename=="-")?stdout:fopen(filename_,POPEN_BINARY_WRITE_TYPE) ),
61         imagecount(0),
62
63         lossy(true),
64         multi_image(false),
65         dithering(true),
66         color_bits(8),
67         iframe_density(30),
68         loop_count(0x7fff),
69         local_palette(true)
70 {
71 }
72
73 gif::~gif()
74 {
75         if(file)
76                 fputc(';',file.get());  // Image terminator
77 }
78
79 bool
80 gif::set_rend_desc(RendDesc *given_desc)
81 {
82         if(given_desc->get_frame_rate()>MAX_FRAME_RATE)
83                 given_desc->set_frame_rate(MAX_FRAME_RATE);
84
85         desc=*given_desc;
86
87         if(desc.get_frame_end()-desc.get_frame_start()>0)
88         {
89                 multi_image=true;
90                 //set_remove_alpha();
91                 imagecount=desc.get_frame_end()-desc.get_frame_start();
92         }
93         else
94                 multi_image=false;
95         return true;
96 }
97
98 bool
99 gif::init()
100 {
101         int w=desc.get_w(),h=desc.get_h();
102
103         if(!file)
104         {
105                 synfig::error(strprintf(_("Unable to open \"%s\" for write access!"),filename.c_str()));
106                 return false;
107         }
108
109         rootsize=color_bits;    // Size of pixel bits
110
111         curr_frame.set_wh(w,h);
112         prev_frame.set_wh(w,h);
113         curr_surface.set_wh(w,h);
114         curr_frame.clear();
115         prev_frame.clear();
116         curr_surface.clear();
117
118         if(get_quality()>5)
119                 lossy=true;
120         else
121                 lossy=false;
122
123         // Output the header
124         fprintf(file.get(),"GIF89a");
125         fputc(w&0x000000ff,file.get());
126         fputc((w&0x0000ff00)>>8,file.get());
127         fputc(h&0x000000ff,file.get());
128         fputc((h&0x0000ff00)>>8,file.get());
129         if(!local_palette)
130                 fputc(0xF0+(rootsize-1),file.get());    // flags
131         else
132                 fputc((0xF0+(rootsize-1))&~(1<<7),file.get());  // flags
133
134         fputc(0,file.get());            // background color
135         fputc(0,file.get());            // Pixel Aspect Ratio
136
137         if(!local_palette)
138         {
139                 curr_palette=Palette::grayscale(256/(1<<(8-rootsize))-1);
140                 output_curr_palette();
141         }
142
143         if(loop_count && multi_image)
144         {
145                 fputc(33,file.get()); // 33 (hex 0x21) GIF Extension code
146                 fputc(255,file.get()); // 255 (hex 0xFF) Application Extension Label
147                 fputc(11,file.get()); // 11 (hex (0x0B) Length of Application Block
148                 fprintf(file.get(),"NETSCAPE2.0");
149                 fputc(3,file.get()); // 3 (hex 0x03) Length of Data Sub-Block
150                 fputc(1,file.get()); // 1 (hex 0x01)
151                 fputc(loop_count&0x000000ff,file.get());
152                 fputc((loop_count&0x0000ff00)>>8,file.get());
153                 fputc(0,file.get()); // 0 (hex 0x00) a Data Sub-block Terminator.
154         }
155
156         return true;
157 }
158
159 void
160 gif::output_curr_palette()
161 {
162         // Output the color table
163         for(i=0;i<256/(1<<(8-rootsize));i++)
164         {
165                 if(i<(signed)curr_palette.size())
166                 {
167                         Color color(curr_palette[i].color.clamped());
168                         //fputc(i*(1<<(8-rootsize)),file.get());
169                         //fputc(i*(1<<(8-rootsize)),file.get());
170                         //fputc(i*(1<<(8-rootsize)),file.get());
171                         fputc(gamma().r_F32_to_U8(color.get_r()),file.get());
172                         fputc(gamma().g_F32_to_U8(color.get_g()),file.get());
173                         fputc(gamma().b_F32_to_U8(color.get_b()),file.get());
174                 }
175                 else
176                 {
177                         fputc(255,file.get());
178                         fputc(0,file.get());
179                         fputc(255,file.get());
180                 }
181         }
182 }
183
184 bool
185 gif::start_frame(synfig::ProgressCallback *callback)
186 {
187 //      int
188 //              w=desc.get_w(),
189 //              h=desc.get_h();
190
191         if(!file)
192         {
193                 if(callback)callback->error(string("BUG:")+_("Description not set!"));
194                 return false;
195         }
196
197         if(callback)callback->task(filename+strprintf(" %d",imagecount));
198
199
200
201         return true;
202 }
203
204 void
205 gif::end_frame()
206 {
207         int w=desc.get_w(),h=desc.get_h(),i;
208         unsigned int value;
209         int
210                 delaytime=round_to_int(100.0/desc.get_frame_rate());
211
212         bool build_off_previous(multi_image);
213
214         Palette prev_palette(curr_palette);
215
216         // Fill in the background color
217         if(!get_remove_alpha())
218         {
219                 Surface::alpha_pen pen(curr_surface.begin(),1.0,Color::BLEND_BEHIND);
220                 pen.set_value(get_canvas()->rend_desc().get_bg_color());
221                 for(int y=0;y<curr_surface.get_h();y++,pen.inc_y())
222                 {
223                         int x;
224                         for(x=0;x<curr_surface.get_w();x++,pen.inc_x())
225                         {
226                                 if(pen.get_value().get_a()>0.1)
227                                         pen.put_value();
228                                 else
229                                         pen[0][0]=Color::alpha();
230                         }
231                         pen.dec_x(x);
232                 }
233         }
234
235         if(local_palette)
236         {
237                 curr_palette=Palette(curr_surface,256/(1<<(8-rootsize))-build_off_previous-1);
238                 synfig::info("curr_palette.size()=%d",curr_palette.size());
239         }
240
241         int transparent_index(curr_palette.find_closest(Color(1,0,1,0))-curr_palette.begin());
242         bool has_transparency(curr_palette[transparent_index].color.get_a()<=0.00001);
243
244         if(has_transparency)
245                 build_off_previous=false;
246
247         if(build_off_previous)
248         {
249                 transparent_index=0;
250                 has_transparency=true;
251         }
252
253 #define DISPOSE_UNDEFINED                       (0)
254 #define DISPOSE_NONE                            (1<<2)
255 #define DISPOSE_RESTORE_BGCOLOR         (2<<2)
256 #define DISPOSE_RESTORE_PREVIOUS        (3<<2)
257         int gec_flags(0);
258         if(build_off_previous)
259                 gec_flags|=DISPOSE_NONE;
260         else
261                 gec_flags|=DISPOSE_RESTORE_PREVIOUS;
262         if(has_transparency)
263                 gec_flags|=1;
264
265         // output the Graphic Control Extension
266         fputc(0x21,file.get()); // Extension introducer
267         fputc(0xF9,file.get()); // Graphic Control Label
268         fputc(4,file.get()); // Block Size
269         fputc(gec_flags,file.get()); // Flags (Packed Fields)
270         fputc(delaytime&0x000000ff,file.get()); // Delay Time (MSB)
271         fputc((delaytime&0x0000ff00)>>8,file.get()); // Delay Time (LSB)
272         fputc(transparent_index,file.get()); // Transparent Color Index
273         fputc(0,file.get()); // Block Terminator
274
275         // output the image header
276         fputc(',',file.get());
277         fputc(0,file.get());    // image left
278         fputc(0,file.get());    // image left
279         fputc(0,file.get());    // image top
280         fputc(0,file.get());    // image top
281         fputc(w&0x000000ff,file.get());
282         fputc((w&0x0000ff00)>>8,file.get());
283         fputc(h&0x000000ff,file.get());
284         fputc((h&0x0000ff00)>>8,file.get());
285         if(local_palette)
286                 fputc(0x80|(rootsize-1),file.get());    // flags
287         else
288                 fputc(0x00+ rootsize-1,file.get());     // flags
289
290
291         if(local_palette)
292         {
293                 Palette out(curr_palette);
294
295                 if(build_off_previous)
296                         curr_palette.insert(curr_palette.begin(),Color(1,0,1,0));
297                 output_curr_palette();
298                 curr_palette=out;
299         }
300
301         bs=bitstream(file);
302
303         // Prepare ourselves for LZW compression
304         codesize=rootsize+1;
305         nextcode=(1<<rootsize)+2;
306         table=lzwcode::NewTable((1<<rootsize));
307         node=table;
308
309         // Output the rootsize
310         fputc(rootsize,file.get());     // rootsize;
311
312         // Push a table reset into the bitstream
313         bs.push_value(1<<rootsize,codesize);
314
315         for(int cur_scanline=0;cur_scanline<desc.get_h();cur_scanline++)
316         {
317                 //convert_color_format(curr_frame[cur_scanline], curr_surface[cur_scanline], desc.get_w(), PF_GRAY, gamma());
318
319                 // Now we compress it!
320                 for(i=0;i<w;i++)
321                 {
322                         Color color(curr_surface[cur_scanline][i].clamped());
323                         Palette::iterator iter(curr_palette.find_closest(color));
324
325                         if(dithering)
326                         {
327                                 Color error(color-iter->color);
328                                 //error*=0.25;
329                                 if(curr_surface.get_h()>cur_scanline+1)
330                                 {
331                                         curr_surface[cur_scanline+1][i-1]  += error * ((float)3/(float)16);
332                                         curr_surface[cur_scanline+1][i]    += error * ((float)5/(float)16);
333                                         if(curr_surface.get_w()>i+1)
334                                                 curr_surface[cur_scanline+1][i+1]  += error * ((float)1/(float)16);
335                                 }
336                                 if(curr_surface.get_w()>i+1)
337                                         curr_surface[cur_scanline][i+1]    += error * ((float)7/(float)16);
338                         }
339
340                         curr_frame[cur_scanline][i]=iter-curr_palette.begin();
341
342                         value=curr_frame[cur_scanline][i];
343                         if(build_off_previous)
344                                 value++;
345                         if(value>(unsigned)(1<<rootsize)-1)
346                                 value=(1<<rootsize)-1;
347
348                         // If the pixel is the same as the one that
349                         // is already there, then we should make it
350                         // transparent
351                         if(build_off_previous)
352                         {
353                                 if(lossy)
354                                 {
355
356                                         // Lossy
357                                         if(
358                                                 abs( ( iter->color-prev_palette[prev_frame[cur_scanline][i]-1].color ).get_y() ) > (1.0/16.0) ||
359 //                                              abs((int)value-(int)prev_frame[cur_scanline][i])>2||
360 //                                              (value<=2 && value!=prev_frame[cur_scanline][i]) ||
361                                                 (imagecount%iframe_density)==0 || imagecount==desc.get_frame_end()-1 ) // lossy version
362                                                 prev_frame[cur_scanline][i]=value;
363                                         else
364                                         {
365                                                 prev_frame[cur_scanline][i]=value;
366                                                 value=0;
367                                         }
368                                 }
369                                 else
370                                 {
371                                         // lossless version
372                                         if(value!=prev_frame[cur_scanline][i])
373                                                 prev_frame[cur_scanline][i]=value;
374                                         else
375                                                 value=0;
376                                 }
377                         }
378                         else
379                         prev_frame[cur_scanline][i]=value;
380
381                         next=node->FindCode(value);
382                         if(next)
383                                 node=next;
384                         else
385                         {
386                                 node->AddNode(nextcode, value);
387                                 bs.push_value(node->code, codesize);
388                                 node = table->FindCode(value);
389
390                                 // Check to see if we need to increase the codesize
391                                 if (nextcode == ( 1 << codesize))
392                                         codesize += 1;
393
394                                 nextcode += 1;
395
396                                 // check to see if we have filled up the table
397                                 if (nextcode == 4096)
398                                 {
399                                         // output the clear code: make sure to use the current
400                                         // codesize
401                                         bs.push_value((unsigned) 1 << rootsize, codesize);
402
403                                         delete table;
404                                         table = lzwcode::NewTable((1<<rootsize));
405                                         codesize = rootsize + 1;
406                                         nextcode = (1 << rootsize) + 2;
407
408                                         // since we have a new table, need the correct prefix
409                                         node = table->FindCode(value);
410                                 }
411                         }
412                 }
413         }
414
415
416
417
418
419         // Push the last code onto the bitstream
420         bs.push_value(node->code,codesize);
421
422         // Push a end-of-stream code onto the bitstream
423         bs.push_value((1<<rootsize)+1,codesize);
424
425         // Make sure everything is dumped out
426         bs.dump();
427
428         delete table;
429
430         fputc(0,file.get());            // Block terminator
431
432         fflush(file.get());
433         imagecount++;
434 }
435
436 synfig::Color*
437 gif::start_scanline(int scanline)
438 {
439         cur_scanline=scanline;
440         return curr_surface[scanline];
441 }
442
443 bool
444 gif::end_scanline()
445 {
446         if(!file)
447                 return false;
448
449 //      int w=desc.get_w(),i;
450 //      unsigned int value;
451
452
453         return true;
454 }