Release synfig_0_61_07_rc2
[synfig.git] / synfig-core / tags / synfig_0_61_07_rc2 / src / modules / mod_gif / trgt_gif.cpp
diff --git a/synfig-core/tags/synfig_0_61_07_rc2/src/modules/mod_gif/trgt_gif.cpp b/synfig-core/tags/synfig_0_61_07_rc2/src/modules/mod_gif/trgt_gif.cpp
new file mode 100644 (file)
index 0000000..0801310
--- /dev/null
@@ -0,0 +1,460 @@
+/* === S Y N F I G ========================================================= */
+/*!    \file trgt_gif.cpp
+**     \brief BMP Target Module
+**
+**     $Id$
+**
+**     \legal
+**     Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
+**
+**     This package is free software; you can redistribute it and/or
+**     modify it under the terms of the GNU General Public License as
+**     published by the Free Software Foundation; either version 2 of
+**     the License, or (at your option) any later version.
+**
+**     This package is distributed in the hope that it will be useful,
+**     but WITHOUT ANY WARRANTY; without even the implied warranty of
+**     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+**     General Public License for more details.
+**     \endlegal
+**
+** === N O T E S ===========================================================
+**
+** ========================================================================= */
+
+/* === H E A D E R S ======================================================= */
+
+#define SYNFIG_TARGET
+
+#ifdef USING_PCH
+#      include "pch.h"
+#else
+#ifdef HAVE_CONFIG_H
+#      include <config.h>
+#endif
+
+#include <ETL/stringf>
+#include "trgt_gif.h"
+#include <stdio.h>
+#endif
+
+/* === M A C R O S ========================================================= */
+
+using namespace synfig;
+using namespace std;
+using namespace etl;
+
+#define MAX_FRAME_RATE (20.0)
+
+/* === G L O B A L S ======================================================= */
+
+SYNFIG_TARGET_INIT(gif);
+SYNFIG_TARGET_SET_NAME(gif,"gif");
+SYNFIG_TARGET_SET_EXT(gif,"gif");
+SYNFIG_TARGET_SET_VERSION(gif,"0.1");
+SYNFIG_TARGET_SET_CVS_ID(gif,"$Id$");
+
+/* === M E T H O D S ======================================================= */
+
+gif::gif(const char *filename_):
+       filename(filename_),
+       file( (filename=="-")?stdout:fopen(filename_,"wb") ),
+       imagecount(0),
+
+       lossy(true),
+       multi_image(false),
+       dithering(true),
+       color_bits(8),
+       iframe_density(30),
+       loop_count(0x7fff),
+       local_palette(true)
+{
+}
+
+gif::~gif()
+{
+       if(file)
+               fputc(';',file.get());  // Image terminator
+}
+
+bool
+gif::set_rend_desc(RendDesc *given_desc)
+{
+       if(given_desc->get_frame_rate()>MAX_FRAME_RATE)
+               given_desc->set_frame_rate(MAX_FRAME_RATE);
+
+       desc=*given_desc;
+
+       if(desc.get_frame_end()-desc.get_frame_start()>0)
+       {
+               multi_image=true;
+               //set_remove_alpha();
+               imagecount=desc.get_frame_end()-desc.get_frame_start();
+       }
+       else
+               multi_image=false;
+       return true;
+}
+
+bool
+gif::init()
+{
+       int w=desc.get_w(),h=desc.get_h();
+
+       if(!file)
+       {
+               synfig::error(strprintf(_("Unable to open \"%s\" for write access!"),filename.c_str()));
+               return false;
+       }
+
+       rootsize=color_bits;    // Size of pixel bits
+
+       curr_frame.set_wh(w,h);
+       prev_frame.set_wh(w,h);
+       curr_surface.set_wh(w,h);
+       curr_frame.clear();
+       prev_frame.clear();
+       curr_surface.clear();
+
+       if(get_quality()>5)
+               lossy=true;
+       else
+               lossy=false;
+
+       // Output the header
+       fprintf(file.get(),"GIF89a");
+       fputc(w&0x000000ff,file.get());
+       fputc((w&0x0000ff00)>>8,file.get());
+       fputc(h&0x000000ff,file.get());
+       fputc((h&0x0000ff00)>>8,file.get());
+       if(!local_palette)
+               fputc(0xF0+(rootsize-1),file.get());    // flags
+       else
+               fputc((0xF0+(rootsize-1))&~(1<<7),file.get());  // flags
+
+       fputc(0,file.get());            // backgound color
+       fputc(0,file.get());            // Pixel Aspect Ratio
+
+       DEBUGPOINT();
+
+       if(!local_palette)
+       {
+       DEBUGPOINT();
+               curr_palette=Palette::grayscale(256/(1<<(8-rootsize))-1);
+               output_curr_palette();
+       }
+
+       if(loop_count && multi_image)
+       {
+       DEBUGPOINT();
+               fputc(33,file.get()); // 33 (hex 0x21) GIF Extension code
+               fputc(255,file.get()); // 255 (hex 0xFF) Application Extension Label
+               fputc(11,file.get()); // 11 (hex (0x0B) Length of Application Block
+               fprintf(file.get(),"NETSCAPE2.0");
+               fputc(3,file.get()); // 3 (hex 0x03) Length of Data Sub-Block
+               fputc(1,file.get()); // 1 (hex 0x01)
+               fputc(loop_count&0x000000ff,file.get());
+               fputc((loop_count&0x0000ff00)>>8,file.get());
+               fputc(0,file.get()); // 0 (hex 0x00) a Data Sub-block Terminator.
+       }
+       DEBUGPOINT();
+
+       return true;
+}
+
+void
+gif::output_curr_palette()
+{
+       // Output the color table
+       for(i=0;i<256/(1<<(8-rootsize));i++)
+       {
+               if(i<(signed)curr_palette.size())
+               {
+                       Color color(curr_palette[i].color.clamped());
+                       //fputc(i*(1<<(8-rootsize)),file.get());
+                       //fputc(i*(1<<(8-rootsize)),file.get());
+                       //fputc(i*(1<<(8-rootsize)),file.get());
+                       fputc(gamma().r_F32_to_U8(color.get_r()),file.get());
+                       fputc(gamma().g_F32_to_U8(color.get_g()),file.get());
+                       fputc(gamma().b_F32_to_U8(color.get_b()),file.get());
+               }
+               else
+               {
+                       fputc(255,file.get());
+                       fputc(0,file.get());
+                       fputc(255,file.get());
+               }
+       }
+}
+
+bool
+gif::start_frame(synfig::ProgressCallback *callback)
+{
+//     int
+//             w=desc.get_w(),
+//             h=desc.get_h();
+
+       if(!file)
+       {
+               if(callback)callback->error(string("BUG:")+_("Description not set!"));
+               return false;
+       }
+
+       if(callback)callback->task(filename+strprintf(" %d",imagecount));
+
+
+
+       return true;
+}
+
+void
+gif::end_frame()
+{
+       int w=desc.get_w(),h=desc.get_h(),i;
+       unsigned int value;
+       int
+               delaytime=round_to_int(100.0/desc.get_frame_rate());
+
+       bool build_off_previous(multi_image);
+
+       Palette prev_palette(curr_palette);
+
+       // Fill in the background color
+       if(!get_remove_alpha())
+       {
+               Surface::alpha_pen pen(curr_surface.begin(),1.0,Color::BLEND_BEHIND);
+               pen.set_value(get_canvas()->rend_desc().get_bg_color());
+               for(int y=0;y<curr_surface.get_h();y++,pen.inc_y())
+               {
+                       int x;
+                       for(x=0;x<curr_surface.get_w();x++,pen.inc_x())
+                       {
+                               if(pen.get_value().get_a()>0.1)
+                                       pen.put_value();
+                               else
+                                       pen[0][0]=Color::alpha();
+                       }
+                       pen.dec_x(x);
+               }
+       }
+
+       if(local_palette)
+       {
+               curr_palette=Palette(curr_surface,256/(1<<(8-rootsize))-build_off_previous-1);
+               synfig::info("curr_palette.size()=%d",curr_palette.size());
+       }
+
+       int transparent_index(curr_palette.find_closest(Color(1,0,1,0))-curr_palette.begin());
+       bool has_transparency(curr_palette[transparent_index].color.get_a()<=0.00001);
+
+       if(has_transparency)
+               build_off_previous=false;
+
+       if(build_off_previous)
+       {
+               transparent_index=0;
+               has_transparency=true;
+       }
+
+#define DISPOSE_UNDEFINED                      (0)
+#define DISPOSE_NONE                           (1<<2)
+#define DISPOSE_RESTORE_BGCOLOR                (2<<2)
+#define DISPOSE_RESTORE_PREVIOUS       (3<<2)
+       int gec_flags(0);
+       if(build_off_previous)
+               gec_flags|=DISPOSE_NONE;
+       else
+               gec_flags|=DISPOSE_RESTORE_PREVIOUS;
+       if(has_transparency)
+               gec_flags|=1;
+
+       // output the Graphic Control Extension
+       fputc(0x21,file.get()); // Extension introducer
+       fputc(0xF9,file.get()); // Graphic Control Label
+       fputc(4,file.get()); // Block Size
+       fputc(gec_flags,file.get()); // Flags (Packed Fields)
+       fputc(delaytime&0x000000ff,file.get()); // Delay Time (MSB)
+       fputc((delaytime&0x0000ff00)>>8,file.get()); // Delay Time (LSB)
+       fputc(transparent_index,file.get()); // Transparent Color Index
+       fputc(0,file.get()); // Block Terminator
+
+       // output the image header
+       fputc(',',file.get());
+       fputc(0,file.get());    // image left
+       fputc(0,file.get());    // image left
+       fputc(0,file.get());    // image top
+       fputc(0,file.get());    // image top
+       fputc(w&0x000000ff,file.get());
+       fputc((w&0x0000ff00)>>8,file.get());
+       fputc(h&0x000000ff,file.get());
+       fputc((h&0x0000ff00)>>8,file.get());
+       if(local_palette)
+               fputc(0x80|(rootsize-1),file.get());    // flags
+       else
+               fputc(0x00+ rootsize-1,file.get());     // flags
+
+
+       if(local_palette)
+       {
+               Palette out(curr_palette);
+
+               if(build_off_previous)
+                       curr_palette.insert(curr_palette.begin(),Color(1,0,1,0));
+               output_curr_palette();
+               curr_palette=out;
+       }
+
+       bs=bitstream(file);
+
+       // Prepare ourselves for LZW compression
+       codesize=rootsize+1;
+       nextcode=(1<<rootsize)+2;
+       table=lzwcode::NewTable((1<<rootsize));
+       node=table;
+
+       // Output the rootsize
+       fputc(rootsize,file.get());     // rootsize;
+
+       // Push a table reset into the bitstream
+       bs.push_value(1<<rootsize,codesize);
+
+       for(int cur_scanline=0;cur_scanline<desc.get_h();cur_scanline++)
+       {
+               //convert_color_format(curr_frame[cur_scanline], curr_surface[cur_scanline], desc.get_w(), PF_GRAY, gamma());
+
+               // Now we compress it!
+               for(i=0;i<w;i++)
+               {
+                       Color color(curr_surface[cur_scanline][i].clamped());
+                       Palette::iterator iter(curr_palette.find_closest(color));
+
+                       if(dithering)
+                       {
+                               Color error(color-iter->color);
+                               //error*=0.25;
+                               if(curr_surface.get_h()>cur_scanline+1)
+                               {
+                                       curr_surface[cur_scanline+1][i-1]  += error * ((float)3/(float)16);
+                                       curr_surface[cur_scanline+1][i]    += error * ((float)5/(float)16);
+                                       if(curr_surface.get_w()>i+1)
+                                               curr_surface[cur_scanline+1][i+1]  += error * ((float)1/(float)16);
+                               }
+                               if(curr_surface.get_w()>i+1)
+                                       curr_surface[cur_scanline][i+1]    += error * ((float)7/(float)16);
+                       }
+
+                       curr_frame[cur_scanline][i]=iter-curr_palette.begin();
+
+                       value=curr_frame[cur_scanline][i];
+                       if(build_off_previous)
+                               value++;
+                       if(value>(unsigned)(1<<rootsize)-1)
+                               value=(1<<rootsize)-1;
+
+                       // If the pixel is the same as the one that
+                       // is already there, then we should make it
+                       // transparent
+                       if(build_off_previous)
+                       {
+                               if(lossy)
+                               {
+
+                                       // Lossy
+                                       if(
+                                               abs( ( iter->color-prev_palette[prev_frame[cur_scanline][i]-1].color ).get_y() ) > (1.0/16.0) ||
+//                                             abs((int)value-(int)prev_frame[cur_scanline][i])>2||
+//                                             (value<=2 && value!=prev_frame[cur_scanline][i]) ||
+                                               (imagecount%iframe_density)==0 || imagecount==desc.get_frame_end()-1 ) // lossy version
+                                               prev_frame[cur_scanline][i]=value;
+                                       else
+                                       {
+                                               prev_frame[cur_scanline][i]=value;
+                                               value=0;
+                                       }
+                               }
+                               else
+                               {
+                                       // lossless version
+                                       if(value!=prev_frame[cur_scanline][i])
+                                               prev_frame[cur_scanline][i]=value;
+                                       else
+                                               value=0;
+                               }
+                       }
+                       else
+                       prev_frame[cur_scanline][i]=value;
+
+                       next=node->FindCode(value);
+                       if(next)
+                               node=next;
+                       else
+                       {
+                               node->AddNode(nextcode, value);
+                               bs.push_value(node->code, codesize);
+                               node = table->FindCode(value);
+
+                               // Check to see if we need to increase the codesize
+                               if (nextcode == ( 1 << codesize))
+                                       codesize += 1;
+
+                               nextcode += 1;
+
+                               // check to see if we have filled up the table
+                               if (nextcode == 4096)
+                               {
+                                       // output the clear code: make sure to use the current
+                                       // codesize
+                                       bs.push_value((unsigned) 1 << rootsize, codesize);
+
+                                       delete table;
+                                       table = lzwcode::NewTable((1<<rootsize));
+                                       codesize = rootsize + 1;
+                                       nextcode = (1 << rootsize) + 2;
+
+                                       // since we have a new table, need the correct prefix
+                                       node = table->FindCode(value);
+                               }
+                       }
+               }
+       }
+
+
+
+
+
+       // Push the last code onto the bitstream
+       bs.push_value(node->code,codesize);
+
+       // Push a end-of-stream code onto the bitstream
+       bs.push_value((1<<rootsize)+1,codesize);
+
+       // Make sure everything is dumped out
+       bs.dump();
+
+       delete table;
+
+       fputc(0,file.get());            // Block terminator
+
+       fflush(file.get());
+       imagecount++;
+}
+
+synfig::Color*
+gif::start_scanline(int scanline)
+{
+       cur_scanline=scanline;
+       return curr_surface[scanline];
+}
+
+bool
+gif::end_scanline()
+{
+       if(!file)
+               return false;
+
+//     int w=desc.get_w(),i;
+//     unsigned int value;
+
+
+       return true;
+}