X-Git-Url: https://git.pterodactylus.net/?a=blobdiff_plain;f=synfig-core%2Fsrc%2Fmodules%2Fmod_gif%2Ftrgt_gif.cpp;fp=synfig-core%2Fsrc%2Fmodules%2Fmod_gif%2Ftrgt_gif.cpp;h=20689955c714b954fddbe2ed01d3e5ca0d9d8bbb;hb=a095981e18cc37a8ecc7cd237cc22b9c10329264;hp=0000000000000000000000000000000000000000;hpb=9459638ad6797b8139f1e9f0715c96076dbf0890;p=synfig.git diff --git a/synfig-core/src/modules/mod_gif/trgt_gif.cpp b/synfig-core/src/modules/mod_gif/trgt_gif.cpp new file mode 100644 index 0000000..2068995 --- /dev/null +++ b/synfig-core/src/modules/mod_gif/trgt_gif.cpp @@ -0,0 +1,456 @@ +/* === 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 +** Copyright (c) 2007 Chris Moore +** +** 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 +#endif + +#include +#include "trgt_gif.h" +#include +#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_,POPEN_BINARY_WRITE_TYPE) ), + 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()); // background color + fputc(0,file.get()); // Pixel Aspect Ratio + + if(!local_palette) + { + curr_palette=Palette::grayscale(256/(1<<(8-rootsize))-1); + output_curr_palette(); + } + + if(loop_count && multi_image) + { + 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. + } + + 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;y0.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<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<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<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<