gif_reader.cpp
Go to the documentation of this file.
/*
CLAW - a C++ Library Absolutely Wonderful
CLAW is a free library without any particular aim but being useful to
anyone.
Copyright (C) 2005-2011 Julien Jorge
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
contact: julien.jorge@gamned.org
*/
#include <claw/gif.hpp>
#include <algorithm>
#include <climits>
#include <limits>
#include <claw/functional.hpp>
#include <claw/exception.hpp>
#include <claw/it_index.hpp>
/*----------------------------------------------------------------------------*/
claw::graphic::gif::reader::input_buffer::input_buffer
( std::istream& is, u_int_8 code_size )
: m_val(0), m_input(is), m_pending(0), m_pending_bits(0), m_pending_end(0),
m_initial_code_size(code_size), m_code_size(m_initial_code_size+1),
m_code_limit(1 << m_code_size)
{
m_input.read
( reinterpret_cast<char*>(&m_next_data_length),
sizeof(m_next_data_length) );
} // gif::reader::input_buffer::input_buffer()
/*----------------------------------------------------------------------------*/
bool claw::graphic::gif::reader::input_buffer::end_of_data() const
{
return (m_val == (unsigned int)(1 << m_initial_code_size))
|| end_of_information();
} // gif::reader::input_buffer::end_of_data()
/*----------------------------------------------------------------------------*/
bool claw::graphic::gif::reader::input_buffer::end_of_information() const
{
return !m_input || (m_val == (unsigned int)(1 << m_initial_code_size)+1)
|| ( (m_next_data_length == 0) // no more data in the stream
&& (m_pending == m_pending_end) // no more data in the buffer
&& (m_pending_bits < m_code_size) );
} // gif::reader::input_buffer::end_of_information()
/*----------------------------------------------------------------------------*/
unsigned int claw::graphic::gif::reader::input_buffer::symbols_count() const
{
return (1 << m_initial_code_size) + 2;
} // gif::reader::input_buffer::symbols_count()
/*----------------------------------------------------------------------------*/
unsigned int claw::graphic::gif::reader::input_buffer::get_next()
{
if ( m_pending == m_pending_end )
fill_buffer();
else if ( m_pending_bits + (m_pending_end - m_pending - 1) * CHAR_BIT
< m_code_size )
fill_buffer();
m_val = 0;
std::size_t n(m_code_size);
unsigned int cur_size = 0;
char* buf = reinterpret_cast<char*>(&m_val);
while ( (n != 0) && m_input )
{
while( (m_pending_bits != 0) && (n!=0) && m_input )
{
unsigned int bits = std::min((std::size_t)m_pending_bits, n);
if ( CHAR_BIT - cur_size < bits )
bits = CHAR_BIT - cur_size;
unsigned int mask = (1 << bits) - 1;
*buf |= (m_buffer[m_pending] & mask) << cur_size;
cur_size += bits;
m_pending_bits -= bits;
m_buffer[m_pending] >>= bits;
n -= bits;
if ( cur_size == CHAR_BIT )
{
++buf;
cur_size = 0;
}
}
if ( m_pending_bits == 0 )
{
++m_pending;
if ( (m_pending == m_pending_end) && (n!=0) )
fill_buffer();
if ( m_pending == m_pending_end )
n = 0;
else
m_pending_bits = CHAR_BIT;
}
}
return m_val;
} // gif::reader::input_buffer::get_next()
/*----------------------------------------------------------------------------*/
void claw::graphic::gif::reader::input_buffer::reset()
{
m_val = 0;
m_code_size = m_initial_code_size+1;
m_code_limit = 1 << m_code_size;
} // gif::reader::input_buffer::reset()
/*----------------------------------------------------------------------------*/
void claw::graphic::gif::reader::input_buffer::new_code( unsigned int code )
{
if ( (code == m_code_limit) && (m_code_size != 12) )
{
++m_code_size;
m_code_limit = 1 << m_code_size;
}
} // gif::reader::input_buffer::new_code()
/*----------------------------------------------------------------------------*/
void claw::graphic::gif::reader::input_buffer::fill_buffer()
{
// move available data at the begining of the buffer
std::copy( m_buffer + m_pending, m_buffer + m_pending_end, m_buffer );
m_pending_end = m_pending_end - m_pending;
m_pending = 0;
if (m_next_data_length != 0)
{
assert( m_pending_end + m_next_data_length <= sizeof(m_buffer) );
m_input.read( m_buffer + m_pending_end, m_next_data_length );
m_pending_end += m_next_data_length;
if ( (m_pending_bits == 0) && (m_pending != m_pending_end) )
m_pending_bits = CHAR_BIT;
m_input.read
( reinterpret_cast<char*>(&m_next_data_length),
sizeof(m_next_data_length) );
}
} // gif::reader::input_buffer::fill_buffer()
/*----------------------------------------------------------------------------*/
claw::graphic::gif::reader::output_buffer::output_buffer
( const palette_type& p, const image_descriptor& id,
int transparent_color_index, image& output )
: m_palette(p), m_id(id), m_transparent_color_index(transparent_color_index),
m_output(output), m_x(0), m_y(0), m_interlace_pass(0),
m_interlace_step(8)
{
} // gif::reader::output_buffer::output_buffer()
/*----------------------------------------------------------------------------*/
void claw::graphic::gif::reader::output_buffer::write( unsigned int code )
{
assert(code < m_palette.size());
assert(m_x < m_id.width);
assert(m_y < m_id.height);
m_output[m_y + m_id.top][m_x + m_id.left] = m_palette[code];
if ( m_transparent_color_index != -1 )
if ( code == (unsigned int)m_transparent_color_index )
m_output[m_y + m_id.top][m_x + m_id.left].components.alpha = 0;
++m_x;
if (m_x == m_id.width)
{
m_x = 0;
if ( !m_id.is_interlaced() )
++m_y;
else
{
m_y += m_interlace_step;
while ( (m_y >= m_id.height) && (m_interlace_pass!=3) )
{
++m_interlace_pass;
switch (m_interlace_pass)
{
case 1: m_y = 4; m_interlace_step = 8; break;
case 2: m_y = 2; m_interlace_step = 4; break;
case 3: m_y = 1; m_interlace_step = 2; break;
}
}
}
}
} // gif::reader::output_buffer::write()
/*----------------------------------------------------------------------------*/
claw::graphic::gif::reader::reader( image& img )
: m_image(&img)
{
} // gif::reader::reader()
/*----------------------------------------------------------------------------*/
claw::graphic::gif::reader::reader( image& img, std::istream& f )
: m_image(&img)
{
load( f );
} // gif::reader::reader()
/*----------------------------------------------------------------------------*/
claw::graphic::gif::reader::reader
( frame_list& frames, std::istream& f )
: m_image(NULL)
{
load( f );
frames = m_frame;
m_frame.clear();
} // gif::reader::reader()
/*----------------------------------------------------------------------------*/
claw::graphic::gif::reader::reader
( image& img, frame_list& frames, std::istream& f )
: m_image(&img)
{
load( f );
frames = m_frame;
m_frame.clear();
} // gif::reader::reader()
/*----------------------------------------------------------------------------*/
claw::graphic::gif::reader::~reader()
{
clear();
} // gif::reader::~reader()
/*----------------------------------------------------------------------------*/
void claw::graphic::gif::reader::load( std::istream& f )
{
clear();
inside_load(f);
if ( !m_frame.empty() && (m_image!=NULL) )
*m_image = *m_frame.front();
} // gif::reader::load()
/*----------------------------------------------------------------------------*/
void claw::graphic::gif::reader::clear()
{
std::for_each
( m_frame.begin(), m_frame.end(), claw::delete_function<frame*>() );
m_frame.clear();
} // gif::reader::clear()
/*----------------------------------------------------------------------------*/
void claw::graphic::gif::reader::inside_load( std::istream& f )
{
std::istream::pos_type init_pos = f.tellg();
reader_info info;
info.palette = NULL;
try
{
check_if_gif(f);
read_screen_descriptor(f, info);
read_data(f, info);
make_frames(info);
delete info.palette;
}
catch(...)
{
delete info.palette;
f.seekg( init_pos, std::ios_base::beg );
throw;
}
} // gif::reader::inside_load()
/*----------------------------------------------------------------------------*/
void claw::graphic::gif::reader::make_frames( const reader_info& info )
{
it_index<frame_list::const_iterator> it(m_frame.begin());
frame_list result;
std::size_t cumul_count(0);
frame cumul(info.sd.screen_width, info.sd.screen_height);
frame prev;
if ( !info.disposal_method.empty() )
{
if ( info.disposal_method[0]
== graphic_control_extension::dispose_background )
fill_background(cumul, info);
else
std::fill(cumul.begin(), cumul.end(), transparent_pixel);
}
for ( ; it!=m_frame.end(); ++it )
{
if ( info.disposal_method[it]
== graphic_control_extension::dispose_previous )
prev = cumul;
cumul.merge(**it);
cumul.set_delay( (*it)->get_delay() );
++cumul_count;
if ( cumul.get_delay() > 0 )
{
result.push_back( new frame(cumul) );
cumul_count = 0;
}
switch( info.disposal_method[it] )
{
case graphic_control_extension::dispose_background:
fill_background(cumul, info);
break;
case graphic_control_extension::dispose_previous:
cumul = prev;
break;
default:
{ /* nothing to do */ }
}
}
if ( cumul_count != 0 )
result.push_back( new frame(cumul) );
clear();
std::swap( m_frame, result );
} // gif::reader::make_frames()
/*----------------------------------------------------------------------------*/
void claw::graphic::gif::reader::fill_background
( image& img, const reader_info& info ) const
{
rgba_pixel clr(transparent_pixel);
if ( info.sd.has_global_color_table() && (info.palette != NULL) )
if (info.sd.background_color < info.palette->size() )
clr = (*info.palette)[info.sd.background_color];
std::fill( img.begin(), img.end(), clr );
} // gif::reader::fill_background()
/*----------------------------------------------------------------------------*/
void claw::graphic::gif::reader::check_if_gif( std::istream& f ) const
{
CLAW_PRECOND( !!f );
header h;
f.read( reinterpret_cast<char*>(&h), sizeof(header) );
bool valid = false;
if ( f.rdstate() == std::ios_base::goodbit )
if ( (h.signature[0] == 'G')
&& (h.signature[1] == 'I')
&& (h.signature[2] == 'F')
&& (h.version[0] == '8')
&& ( (h.version[1] == '7') || (h.version[1] == '9') )
&& (h.version[2] == 'a') )
valid = true;
if ( !valid )
throw claw::bad_format( "Not a GIF file." );
} // gif::reader::check_if_gif()
/*----------------------------------------------------------------------------*/
void claw::graphic::gif::reader::read_screen_descriptor
( std::istream& f, reader_info& info )
{
f.read( reinterpret_cast<char*>(&info.sd), sizeof(screen_descriptor) );
if ( info.sd.has_global_color_table() )
{
info.palette = new palette_type(info.sd.color_palette_size());
read_palette(f, *info.palette);
}
} // gif::reader::read_screen_descriptor()
/*----------------------------------------------------------------------------*/
void claw::graphic::gif::reader::read_palette
( std::istream& f, palette_type& p ) const
{
u_int_8 red, green, blue;
for (std::size_t i=0; i!=p.size(); ++i)
{
f.read( reinterpret_cast<char*>(&red), sizeof(u_int_8) );
f.read( reinterpret_cast<char*>(&green), sizeof(u_int_8) );
f.read( reinterpret_cast<char*>(&blue), sizeof(u_int_8) );
p[i].components.red = red;
p[i].components.green = green;
p[i].components.blue = blue;
}
} // gif::reader::read_palette()
/*----------------------------------------------------------------------------*/
void claw::graphic::gif::reader::read_data
( std::istream& f, reader_info& info )
{
u_int_8 code;
do
{
code = 0;
f.read( reinterpret_cast<char*>(&code), sizeof(code) );
if (f)
switch(code)
{
case extension::block_id:
f.read( reinterpret_cast<char*>(&code), sizeof(code) );
if (code == graphic_control_extension::block_label)
read_frame_with_gce(f, info);
else
skip_extension(f);
break;
case image_descriptor::block_id:
read_frame(f, info);
break;
case trailer::block_id:
break;
default:
throw claw::bad_format( "gif::reader: invalid code" );
}
}
while ( f && (code != trailer::block_id) );
} // gif::reader::read_data()
/*----------------------------------------------------------------------------*/
void claw::graphic::gif::reader::read_frame
( std::istream& f, reader_info& info )
{
frame* new_frame(NULL);
try
{
new_frame = new frame;
read_frame_data(f, info, *new_frame);
info.disposal_method.push_back(graphic_control_extension::dispose_none);
m_frame.push_back(new_frame);
}
catch(...)
{
delete new_frame;
throw;
}
} // gif::reader::read_frame()
/*----------------------------------------------------------------------------*/
void claw::graphic::gif::reader::read_frame_with_gce
( std::istream& f, reader_info& info )
{
graphic_control_extension gce;
u_int_8 code;
f.read( reinterpret_cast<char*>(&gce), sizeof(gce) );
f.read( reinterpret_cast<char*>(&code), sizeof(code) );
while ( (code == extension::block_id) && f )
{
f.read( reinterpret_cast<char*>(&code), sizeof(code) );
if (code == graphic_control_extension::block_label)
f.read( reinterpret_cast<char*>(&gce), sizeof(gce) );
else // unknown extension
skip_extension(f);
// read the code of the following block
f.read( reinterpret_cast<char*>(&code), sizeof(code) );
}
if (code == image_descriptor::block_id)
{
frame* new_frame = new frame;
new_frame->set_delay(gce.delay);
info.disposal_method.push_back(gce.get_disposal_method());
if ( gce.has_transparent_color() )
info.transparent_color_index = gce.transparent_color;
else
info.transparent_color_index = -1;
read_frame_data(f, info, *new_frame);
m_frame.push_back(new_frame);
}
} // gif::reader::read_frame_with_gce()
/*----------------------------------------------------------------------------*/
void claw::graphic::gif::reader::skip_extension( std::istream& f ) const
{
u_int_8 block_size(0);
f.read( reinterpret_cast<char*>(&block_size), sizeof(block_size) );
while ( f && (block_size!=0) )
{
f.seekg( block_size, std::ios_base::cur );
f.read( reinterpret_cast<char*>(&block_size), sizeof(block_size) );
}
} // gif::reader::skip_extension()
/*----------------------------------------------------------------------------*/
void claw::graphic::gif::reader::read_frame_data
( std::istream& f, const reader_info& info, frame& the_frame ) const
{
image_descriptor id;
f.read( reinterpret_cast<char*>(&id), sizeof(id) );
the_frame.set_size(info.sd.screen_width, info.sd.screen_height);
std::fill( the_frame.begin(), the_frame.end(), transparent_pixel );
palette_type* palette(info.palette);
if ( id.has_color_table() )
{
palette = new palette_type(id.color_palette_size());
read_palette(f, *palette);
}
decode_data(f, *palette, id, info.transparent_color_index, the_frame);
if ( id.has_color_table() )
delete palette;
} // gif::reader::read_frame_data()
/*----------------------------------------------------------------------------*/
void claw::graphic::gif::reader::decode_data
( std::istream& f, const palette_type& palette, const image_descriptor& id,
int transparent_color_index, frame& the_frame ) const
{
u_int_8 code_size;
f.read( reinterpret_cast<char*>(&code_size), sizeof(code_size) );
input_buffer input(f, code_size);
output_buffer output(palette, id, transparent_color_index, the_frame);
do
{
gif_lzw_decoder decoder;
input.reset();
decoder.decode(input, output);
}
while ( !input.end_of_information() );
} // gif::reader::decode_data()
CLAW_PRECOND
#define CLAW_PRECOND(b)
Abort the program if a precondition is not true.
Definition assert.hpp:98
claw::delete_function
Function object that deletes a pointer.
Definition functional.hpp:280
claw::graphic::gif::frame
One frame in the animation.
Definition gif.hpp:57
claw::graphic::gif::frame::set_delay
void set_delay(unsigned int d)
Set the time duration of this frame.
Definition gif_frame.cpp:59
claw::graphic::gif::reader::reader
reader(image &img)
Constructor.
Definition gif_reader.cpp:272
claw::graphic::gif::reader::~reader
~reader()
Destructor.
Definition gif_reader.cpp:331
claw::graphic::gif::reader::load
void load(std::istream &f)
Load the image data from a stream.
Definition gif_reader.cpp:341
claw::graphic::image::image
image()
Constructor. Creates an image without datas.
Definition image.cpp:106
exception.hpp
A simple class to use as exception with string message.
functional.hpp
Some function object classes.
gif.hpp
Image class for gif files.
std::swap
void swap(claw::graphic::gif &a, claw::graphic::gif &b)
Swap the content of two gifs.
Definition gif.cpp:235
claw::graphic::transparent_pixel
rgba_pixel transparent_pixel(0, 0, 0, 0)
A transparent color.
Definition pixel.hpp:133
it_index.hpp
A class to manage an index and an iterator easily.
claw::u_int_8
unsigned_integer_of_size< 8 >::type u_int_8
An unsigned integer on 8 bits.
Definition types.hpp:135