1 /* === S Y N F I G ========================================================= */
3 ** \brief BMP Target Module
6 ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
8 ** This package is free software; you can redistribute it and/or
9 ** modify it under the terms of the GNU General Public License as
10 ** published by the Free Software Foundation; either version 2 of
11 ** the License, or (at your option) any later version.
13 ** This package is distributed in the hope that it will be useful,
14 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 ** General Public License for more details.
19 ** === N O T E S ===========================================================
21 ** ========================================================================= */
23 /* === H E A D E R S ======================================================= */
34 #include <ETL/stringf>
39 /* === M A C R O S ========================================================= */
41 using namespace synfig;
45 #define MAX_FRAME_RATE (20.0)
47 /* === G L O B A L S ======================================================= */
49 SYNFIG_TARGET_INIT(gif);
50 SYNFIG_TARGET_SET_NAME(gif,"gif");
51 SYNFIG_TARGET_SET_EXT(gif,"gif");
52 SYNFIG_TARGET_SET_VERSION(gif,"0.1");
53 SYNFIG_TARGET_SET_CVS_ID(gif,"$Id: trgt_gif.cpp,v 1.1.1.1 2005/01/04 01:23:10 darco Exp $");
55 /* === M E T H O D S ======================================================= */
57 gif::gif(const char *filename_):
59 file( (filename=="-")?stdout:fopen(filename_,"wb") ),
75 fputc(';',file.get()); // Image terminator
79 gif::set_rend_desc(RendDesc *given_desc)
81 if(given_desc->get_frame_rate()>MAX_FRAME_RATE)
82 given_desc->set_frame_rate(MAX_FRAME_RATE);
86 if(desc.get_frame_end()-desc.get_frame_start()>0)
90 imagecount=desc.get_frame_end()-desc.get_frame_start();
100 int w=desc.get_w(),h=desc.get_h();
104 synfig::error(strprintf(_("Unable to open \"%s\" for write access!"),filename.c_str()));
108 rootsize=color_bits; // Size of pixel bits
110 curr_frame.set_wh(w,h);
111 prev_frame.set_wh(w,h);
112 curr_surface.set_wh(w,h);
115 curr_surface.clear();
123 fprintf(file.get(),"GIF89a");
124 fputc(w&0x000000ff,file.get());
125 fputc((w&0x0000ff00)>>8,file.get());
126 fputc(h&0x000000ff,file.get());
127 fputc((h&0x0000ff00)>>8,file.get());
129 fputc(0xF0+(rootsize-1),file.get()); // flags
131 fputc((0xF0+(rootsize-1))&~(1<<7),file.get()); // flags
133 fputc(0,file.get()); // backgound color
134 fputc(0,file.get()); // Pixel Aspect Ratio
141 curr_palette=Palette::grayscale(256/(1<<(8-rootsize))-1);
142 output_curr_palette();
145 if(loop_count && multi_image)
148 fputc(33,file.get()); // 33 (hex 0x21) GIF Extension code
149 fputc(255,file.get()); // 255 (hex 0xFF) Application Extension Label
150 fputc(11,file.get()); // 11 (hex (0x0B) Length of Application Block
151 fprintf(file.get(),"NETSCAPE2.0");
152 fputc(3,file.get()); // 3 (hex 0x03) Length of Data Sub-Block
153 fputc(1,file.get()); // 1 (hex 0x01)
154 fputc(loop_count&0x000000ff,file.get());
155 fputc((loop_count&0x0000ff00)>>8,file.get());
156 fputc(0,file.get()); // 0 (hex 0x00) a Data Sub-block Terminator.
164 gif::output_curr_palette()
166 // Output the color table
167 for(i=0;i<256/(1<<(8-rootsize));i++)
169 if(i<(signed)curr_palette.size())
171 Color color(curr_palette[i].color.clamped());
172 //fputc(i*(1<<(8-rootsize)),file.get());
173 //fputc(i*(1<<(8-rootsize)),file.get());
174 //fputc(i*(1<<(8-rootsize)),file.get());
175 fputc(gamma().r_F32_to_U8(color.get_r()),file.get());
176 fputc(gamma().g_F32_to_U8(color.get_g()),file.get());
177 fputc(gamma().b_F32_to_U8(color.get_b()),file.get());
181 fputc(255,file.get());
183 fputc(255,file.get());
189 gif::start_frame(synfig::ProgressCallback *callback)
197 if(callback)callback->error(string("BUG:")+_("Description not set!"));
201 if(callback)callback->task(filename+strprintf(" %d",imagecount));
211 int w=desc.get_w(),h=desc.get_h(),i;
214 delaytime=round_to_int(100.0/desc.get_frame_rate());
216 bool build_off_previous(multi_image);
218 Palette prev_palette(curr_palette);
220 // Fill in the background color
221 if(!get_remove_alpha())
223 Surface::alpha_pen pen(curr_surface.begin(),1.0,Color::BLEND_BEHIND);
224 pen.set_value(get_canvas()->rend_desc().get_bg_color());
225 for(int y=0;y<curr_surface.get_h();y++,pen.inc_y())
228 for(x=0;x<curr_surface.get_w();x++,pen.inc_x())
230 if(pen.get_value().get_a()>0.1)
233 pen[0][0]=Color::alpha();
241 curr_palette=Palette(curr_surface,256/(1<<(8-rootsize))-build_off_previous-1);
242 synfig::info("curr_palette.size()=%d",curr_palette.size());
245 int transparent_index(curr_palette.find_closest(Color(1,0,1,0))-curr_palette.begin());
246 bool has_transparency(curr_palette[transparent_index].color.get_a()<=0.00001);
249 build_off_previous=false;
251 if(build_off_previous)
254 has_transparency=true;
257 #define DISPOSE_UNDEFINED (0)
258 #define DISPOSE_NONE (1<<2)
259 #define DISPOSE_RESTORE_BGCOLOR (2<<2)
260 #define DISPOSE_RESTORE_PREVIOUS (3<<2)
262 if(build_off_previous)
263 gec_flags|=DISPOSE_NONE;
265 gec_flags|=DISPOSE_RESTORE_PREVIOUS;
269 // output the Graphic Control Extension
270 fputc(0x21,file.get()); // Extension introducer
271 fputc(0xF9,file.get()); // Graphic Control Label
272 fputc(4,file.get()); // Block Size
273 fputc(gec_flags,file.get()); // Flags (Packed Fields)
274 fputc(delaytime&0x000000ff,file.get()); // Delay Time (MSB)
275 fputc((delaytime&0x0000ff00)>>8,file.get()); // Delay Time (LSB)
276 fputc(transparent_index,file.get()); // Transparent Color Index
277 fputc(0,file.get()); // Block Terminator
279 // output the image header
280 fputc(',',file.get());
281 fputc(0,file.get()); // image left
282 fputc(0,file.get()); // image left
283 fputc(0,file.get()); // image top
284 fputc(0,file.get()); // image top
285 fputc(w&0x000000ff,file.get());
286 fputc((w&0x0000ff00)>>8,file.get());
287 fputc(h&0x000000ff,file.get());
288 fputc((h&0x0000ff00)>>8,file.get());
290 fputc(0x80|(rootsize-1),file.get()); // flags
292 fputc(0x00+ rootsize-1,file.get()); // flags
297 Palette out(curr_palette);
299 if(build_off_previous)
300 curr_palette.insert(curr_palette.begin(),Color(1,0,1,0));
301 output_curr_palette();
307 // Prepare ourselves for LZW compression
309 nextcode=(1<<rootsize)+2;
310 table=lzwcode::NewTable((1<<rootsize));
313 // Output the rootsize
314 fputc(rootsize,file.get()); // rootsize;
316 // Push a table reset into the bitstream
317 bs.push_value(1<<rootsize,codesize);
319 for(int cur_scanline=0;cur_scanline<desc.get_h();cur_scanline++)
321 //convert_color_format(curr_frame[cur_scanline], curr_surface[cur_scanline], desc.get_w(), PF_GRAY, gamma());
323 // Now we compress it!
326 Color color(curr_surface[cur_scanline][i].clamped());
327 Palette::iterator iter(curr_palette.find_closest(color));
331 Color error(color-iter->color);
333 if(curr_surface.get_h()>cur_scanline+1)
335 curr_surface[cur_scanline+1][i-1] += error * ((float)3/(float)16);
336 curr_surface[cur_scanline+1][i] += error * ((float)5/(float)16);
337 if(curr_surface.get_w()>i+1)
338 curr_surface[cur_scanline+1][i+1] += error * ((float)1/(float)16);
340 if(curr_surface.get_w()>i+1)
341 curr_surface[cur_scanline][i+1] += error * ((float)7/(float)16);
344 curr_frame[cur_scanline][i]=iter-curr_palette.begin();
346 value=curr_frame[cur_scanline][i];
347 if(build_off_previous)
349 if(value>(unsigned)(1<<rootsize)-1)
350 value=(1<<rootsize)-1;
352 // If the pixel is the same as the one that
353 // is already there, then we should make it
355 if(build_off_previous)
362 abs( ( iter->color-prev_palette[prev_frame[cur_scanline][i]-1].color ).get_y() ) > (1.0/16.0) ||
363 // abs((int)value-(int)prev_frame[cur_scanline][i])>2||
364 // (value<=2 && value!=prev_frame[cur_scanline][i]) ||
365 (imagecount%iframe_density)==0 || imagecount==desc.get_frame_end()-1 ) // lossy version
366 prev_frame[cur_scanline][i]=value;
369 prev_frame[cur_scanline][i]=value;
376 if(value!=prev_frame[cur_scanline][i])
377 prev_frame[cur_scanline][i]=value;
383 prev_frame[cur_scanline][i]=value;
385 next=node->FindCode(value);
390 node->AddNode(nextcode, value);
391 bs.push_value(node->code, codesize);
392 node = table->FindCode(value);
394 // Check to see if we need to increase the codesize
395 if (nextcode == ( 1 << codesize))
400 // check to see if we have filled up the table
401 if (nextcode == 4096)
403 // output the clear code: make sure to use the current
405 bs.push_value((unsigned) 1 << rootsize, codesize);
408 table = lzwcode::NewTable((1<<rootsize));
409 codesize = rootsize + 1;
410 nextcode = (1 << rootsize) + 2;
412 // since we have a new table, need the correct prefix
413 node = table->FindCode(value);
423 // Push the last code onto the bitstream
424 bs.push_value(node->code,codesize);
426 // Push a end-of-stream code onto the bitstream
427 bs.push_value((1<<rootsize)+1,codesize);
429 // Make sure everything is dumped out
434 fputc(0,file.get()); // Block terminator
441 gif::start_scanline(int scanline)
443 cur_scanline=scanline;
444 return curr_surface[scanline];
453 // int w=desc.get_w(),i;
454 // unsigned int value;