Claw 1.7.3
gif_reader.cpp
Go to the documentation of this file.
1/*
2 CLAW - a C++ Library Absolutely Wonderful
3
4 CLAW is a free library without any particular aim but being useful to
5 anyone.
6
7 Copyright (C) 2005-2011 Julien Jorge
8
9 This library is free software; you can redistribute it and/or
10 modify it under the terms of the GNU Lesser General Public
11 License as published by the Free Software Foundation; either
12 version 2.1 of the License, or (at your option) any later version.
13
14 This library is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public
20 License along with this library; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22
23 contact: julien.jorge@gamned.org
24*/
30#include <claw/gif.hpp>
31
32#include <algorithm>
33#include <climits>
34#include <limits>
35#include <claw/functional.hpp>
36#include <claw/exception.hpp>
37#include <claw/it_index.hpp>
38
39/*----------------------------------------------------------------------------*/
45claw::graphic::gif::reader::input_buffer::input_buffer
46( std::istream& is, u_int_8 code_size )
47 : m_val(0), m_input(is), m_pending(0), m_pending_bits(0), m_pending_end(0),
48 m_initial_code_size(code_size), m_code_size(m_initial_code_size+1),
49 m_code_limit(1 << m_code_size)
50{
51 m_input.read
52 ( reinterpret_cast<char*>(&m_next_data_length),
53 sizeof(m_next_data_length) );
54} // gif::reader::input_buffer::input_buffer()
55
56/*----------------------------------------------------------------------------*/
61bool claw::graphic::gif::reader::input_buffer::end_of_data() const
62{
63 return (m_val == (unsigned int)(1 << m_initial_code_size))
64 || end_of_information();
65} // gif::reader::input_buffer::end_of_data()
66
67/*----------------------------------------------------------------------------*/
71bool claw::graphic::gif::reader::input_buffer::end_of_information() const
72{
73 return !m_input || (m_val == (unsigned int)(1 << m_initial_code_size)+1)
74 || ( (m_next_data_length == 0) // no more data in the stream
75 && (m_pending == m_pending_end) // no more data in the buffer
76 && (m_pending_bits < m_code_size) );
77} // gif::reader::input_buffer::end_of_information()
78
79/*----------------------------------------------------------------------------*/
83unsigned int claw::graphic::gif::reader::input_buffer::symbols_count() const
84{
85 return (1 << m_initial_code_size) + 2;
86} // gif::reader::input_buffer::symbols_count()
87
88/*----------------------------------------------------------------------------*/
92unsigned int claw::graphic::gif::reader::input_buffer::get_next()
93{
94 if ( m_pending == m_pending_end )
95 fill_buffer();
96 else if ( m_pending_bits + (m_pending_end - m_pending - 1) * CHAR_BIT
97 < m_code_size )
98 fill_buffer();
99
100 m_val = 0;
101
102 std::size_t n(m_code_size);
103 unsigned int cur_size = 0;
104 char* buf = reinterpret_cast<char*>(&m_val);
105
106 while ( (n != 0) && m_input )
107 {
108 while( (m_pending_bits != 0) && (n!=0) && m_input )
109 {
110 unsigned int bits = std::min((std::size_t)m_pending_bits, n);
111
112 if ( CHAR_BIT - cur_size < bits )
113 bits = CHAR_BIT - cur_size;
114
115 unsigned int mask = (1 << bits) - 1;
116
117 *buf |= (m_buffer[m_pending] & mask) << cur_size;
118 cur_size += bits;
119 m_pending_bits -= bits;
120 m_buffer[m_pending] >>= bits;
121 n -= bits;
122
123 if ( cur_size == CHAR_BIT )
124 {
125 ++buf;
126 cur_size = 0;
127 }
128 }
129
130 if ( m_pending_bits == 0 )
131 {
132 ++m_pending;
133
134 if ( (m_pending == m_pending_end) && (n!=0) )
135 fill_buffer();
136
137 if ( m_pending == m_pending_end )
138 n = 0;
139 else
140 m_pending_bits = CHAR_BIT;
141 }
142 }
143
144 return m_val;
145} // gif::reader::input_buffer::get_next()
146
147/*----------------------------------------------------------------------------*/
151void claw::graphic::gif::reader::input_buffer::reset()
152{
153 m_val = 0;
154 m_code_size = m_initial_code_size+1;
155 m_code_limit = 1 << m_code_size;
156} // gif::reader::input_buffer::reset()
157
158/*----------------------------------------------------------------------------*/
164void claw::graphic::gif::reader::input_buffer::new_code( unsigned int code )
165{
166 if ( (code == m_code_limit) && (m_code_size != 12) )
167 {
168 ++m_code_size;
169 m_code_limit = 1 << m_code_size;
170 }
171} // gif::reader::input_buffer::new_code()
172
173/*----------------------------------------------------------------------------*/
177void claw::graphic::gif::reader::input_buffer::fill_buffer()
178{
179 // move available data at the begining of the buffer
180 std::copy( m_buffer + m_pending, m_buffer + m_pending_end, m_buffer );
181 m_pending_end = m_pending_end - m_pending;
182 m_pending = 0;
183
184 if (m_next_data_length != 0)
185 {
186 assert( m_pending_end + m_next_data_length <= sizeof(m_buffer) );
187
188 m_input.read( m_buffer + m_pending_end, m_next_data_length );
189 m_pending_end += m_next_data_length;
190
191 if ( (m_pending_bits == 0) && (m_pending != m_pending_end) )
192 m_pending_bits = CHAR_BIT;
193
194 m_input.read
195 ( reinterpret_cast<char*>(&m_next_data_length),
196 sizeof(m_next_data_length) );
197 }
198} // gif::reader::input_buffer::fill_buffer()
199
200
201
202
203/*----------------------------------------------------------------------------*/
211claw::graphic::gif::reader::output_buffer::output_buffer
212( const palette_type& p, const image_descriptor& id,
213 int transparent_color_index, image& output )
214 : m_palette(p), m_id(id), m_transparent_color_index(transparent_color_index),
215 m_output(output), m_x(0), m_y(0), m_interlace_pass(0),
216 m_interlace_step(8)
217{
218
219} // gif::reader::output_buffer::output_buffer()
220
221/*----------------------------------------------------------------------------*/
226void claw::graphic::gif::reader::output_buffer::write( unsigned int code )
227{
228 assert(code < m_palette.size());
229 assert(m_x < m_id.width);
230 assert(m_y < m_id.height);
231
232 m_output[m_y + m_id.top][m_x + m_id.left] = m_palette[code];
233
234 if ( m_transparent_color_index != -1 )
235 if ( code == (unsigned int)m_transparent_color_index )
236 m_output[m_y + m_id.top][m_x + m_id.left].components.alpha = 0;
237
238 ++m_x;
239
240 if (m_x == m_id.width)
241 {
242 m_x = 0;
243
244 if ( !m_id.is_interlaced() )
245 ++m_y;
246 else
247 {
248 m_y += m_interlace_step;
249
250 while ( (m_y >= m_id.height) && (m_interlace_pass!=3) )
251 {
252 ++m_interlace_pass;
253 switch (m_interlace_pass)
254 {
255 case 1: m_y = 4; m_interlace_step = 8; break;
256 case 2: m_y = 2; m_interlace_step = 4; break;
257 case 3: m_y = 1; m_interlace_step = 2; break;
258 }
259 }
260 }
261 }
262} // gif::reader::output_buffer::write()
263
264
265
266
267/*----------------------------------------------------------------------------*/
273 : m_image(&img)
274{
275
276} // gif::reader::reader()
277
278/*----------------------------------------------------------------------------*/
288 : m_image(&img)
289{
290 load( f );
291} // gif::reader::reader()
292
293/*----------------------------------------------------------------------------*/
300( frame_list& frames, std::istream& f )
301 : m_image(NULL)
302{
303 load( f );
304 frames = m_frame;
305 m_frame.clear();
306} // gif::reader::reader()
307
308/*----------------------------------------------------------------------------*/
319( image& img, frame_list& frames, std::istream& f )
320 : m_image(&img)
321{
322 load( f );
323 frames = m_frame;
324 m_frame.clear();
325} // gif::reader::reader()
326
327/*----------------------------------------------------------------------------*/
332{
333 clear();
334} // gif::reader::~reader()
335
336/*----------------------------------------------------------------------------*/
341void claw::graphic::gif::reader::load( std::istream& f )
342{
343 clear();
344
345 inside_load(f);
346
347 if ( !m_frame.empty() && (m_image!=NULL) )
348 *m_image = *m_frame.front();
349} // gif::reader::load()
350
351/*----------------------------------------------------------------------------*/
355void claw::graphic::gif::reader::clear()
356{
357 std::for_each
358 ( m_frame.begin(), m_frame.end(), claw::delete_function<frame*>() );
359 m_frame.clear();
360} // gif::reader::clear()
361
362/*----------------------------------------------------------------------------*/
367void claw::graphic::gif::reader::inside_load( std::istream& f )
368{
369 std::istream::pos_type init_pos = f.tellg();
370 reader_info info;
371 info.palette = NULL;
372
373 try
374 {
375 check_if_gif(f);
376
377 read_screen_descriptor(f, info);
378 read_data(f, info);
379 make_frames(info);
380
381 delete info.palette;
382 }
383 catch(...)
384 {
385 delete info.palette;
386
387 f.seekg( init_pos, std::ios_base::beg );
388 throw;
389 }
390} // gif::reader::inside_load()
391
392/*----------------------------------------------------------------------------*/
397void claw::graphic::gif::reader::make_frames( const reader_info& info )
398{
399 it_index<frame_list::const_iterator> it(m_frame.begin());
400
401 frame_list result;
402 std::size_t cumul_count(0);
403 frame cumul(info.sd.screen_width, info.sd.screen_height);
404 frame prev;
405
406 if ( !info.disposal_method.empty() )
407 {
408 if ( info.disposal_method[0]
409 == graphic_control_extension::dispose_background )
410 fill_background(cumul, info);
411 else
412 std::fill(cumul.begin(), cumul.end(), transparent_pixel);
413 }
414
415 for ( ; it!=m_frame.end(); ++it )
416 {
417 if ( info.disposal_method[it]
418 == graphic_control_extension::dispose_previous )
419 prev = cumul;
420
421 cumul.merge(**it);
422 cumul.set_delay( (*it)->get_delay() );
423 ++cumul_count;
424
425 if ( cumul.get_delay() > 0 )
426 {
427 result.push_back( new frame(cumul) );
428 cumul_count = 0;
429 }
430
431 switch( info.disposal_method[it] )
432 {
433 case graphic_control_extension::dispose_background:
434 fill_background(cumul, info);
435 break;
436 case graphic_control_extension::dispose_previous:
437 cumul = prev;
438 break;
439 default:
440 { /* nothing to do */ }
441 }
442 }
443
444 if ( cumul_count != 0 )
445 result.push_back( new frame(cumul) );
446
447 clear();
448 std::swap( m_frame, result );
449} // gif::reader::make_frames()
450
451/*----------------------------------------------------------------------------*/
457void claw::graphic::gif::reader::fill_background
458( image& img, const reader_info& info ) const
459{
460 rgba_pixel clr(transparent_pixel);
461
462 if ( info.sd.has_global_color_table() && (info.palette != NULL) )
463 if (info.sd.background_color < info.palette->size() )
464 clr = (*info.palette)[info.sd.background_color];
465
466 std::fill( img.begin(), img.end(), clr );
467} // gif::reader::fill_background()
468
469/*----------------------------------------------------------------------------*/
474void claw::graphic::gif::reader::check_if_gif( std::istream& f ) const
475{
476 CLAW_PRECOND( !!f );
477
478 header h;
479 f.read( reinterpret_cast<char*>(&h), sizeof(header) );
480
481 bool valid = false;
482
483 if ( f.rdstate() == std::ios_base::goodbit )
484 if ( (h.signature[0] == 'G')
485 && (h.signature[1] == 'I')
486 && (h.signature[2] == 'F')
487 && (h.version[0] == '8')
488 && ( (h.version[1] == '7') || (h.version[1] == '9') )
489 && (h.version[2] == 'a') )
490 valid = true;
491
492 if ( !valid )
493 throw claw::bad_format( "Not a GIF file." );
494} // gif::reader::check_if_gif()
495
496/*----------------------------------------------------------------------------*/
502void claw::graphic::gif::reader::read_screen_descriptor
503( std::istream& f, reader_info& info )
504{
505 f.read( reinterpret_cast<char*>(&info.sd), sizeof(screen_descriptor) );
506
507 if ( info.sd.has_global_color_table() )
508 {
509 info.palette = new palette_type(info.sd.color_palette_size());
510 read_palette(f, *info.palette);
511 }
512} // gif::reader::read_screen_descriptor()
513
514/*----------------------------------------------------------------------------*/
520void claw::graphic::gif::reader::read_palette
521( std::istream& f, palette_type& p ) const
522{
523 u_int_8 red, green, blue;
524
525 for (std::size_t i=0; i!=p.size(); ++i)
526 {
527 f.read( reinterpret_cast<char*>(&red), sizeof(u_int_8) );
528 f.read( reinterpret_cast<char*>(&green), sizeof(u_int_8) );
529 f.read( reinterpret_cast<char*>(&blue), sizeof(u_int_8) );
530
531 p[i].components.red = red;
532 p[i].components.green = green;
533 p[i].components.blue = blue;
534 }
535} // gif::reader::read_palette()
536
537/*----------------------------------------------------------------------------*/
543void claw::graphic::gif::reader::read_data
544( std::istream& f, reader_info& info )
545{
546 u_int_8 code;
547
548 do
549 {
550 code = 0;
551 f.read( reinterpret_cast<char*>(&code), sizeof(code) );
552
553 if (f)
554 switch(code)
555 {
556 case extension::block_id:
557 f.read( reinterpret_cast<char*>(&code), sizeof(code) );
558
559 if (code == graphic_control_extension::block_label)
560 read_frame_with_gce(f, info);
561 else
562 skip_extension(f);
563
564 break;
565 case image_descriptor::block_id:
566 read_frame(f, info);
567 break;
568 case trailer::block_id:
569 break;
570 default:
571 throw claw::bad_format( "gif::reader: invalid code" );
572 }
573 }
574 while ( f && (code != trailer::block_id) );
575} // gif::reader::read_data()
576
577/*----------------------------------------------------------------------------*/
583void claw::graphic::gif::reader::read_frame
584( std::istream& f, reader_info& info )
585{
586 frame* new_frame(NULL);
587
588 try
589 {
590 new_frame = new frame;
591 read_frame_data(f, info, *new_frame);
592
593 info.disposal_method.push_back(graphic_control_extension::dispose_none);
594 m_frame.push_back(new_frame);
595 }
596 catch(...)
597 {
598 delete new_frame;
599 throw;
600 }
601} // gif::reader::read_frame()
602
603/*----------------------------------------------------------------------------*/
609void claw::graphic::gif::reader::read_frame_with_gce
610( std::istream& f, reader_info& info )
611{
612 graphic_control_extension gce;
613 u_int_8 code;
614
615 f.read( reinterpret_cast<char*>(&gce), sizeof(gce) );
616 f.read( reinterpret_cast<char*>(&code), sizeof(code) );
617
618 while ( (code == extension::block_id) && f )
619 {
620 f.read( reinterpret_cast<char*>(&code), sizeof(code) );
621
622 if (code == graphic_control_extension::block_label)
623 f.read( reinterpret_cast<char*>(&gce), sizeof(gce) );
624 else // unknown extension
625 skip_extension(f);
626
627 // read the code of the following block
628 f.read( reinterpret_cast<char*>(&code), sizeof(code) );
629 }
630
631 if (code == image_descriptor::block_id)
632 {
633 frame* new_frame = new frame;
634 new_frame->set_delay(gce.delay);
635
636 info.disposal_method.push_back(gce.get_disposal_method());
637
638 if ( gce.has_transparent_color() )
639 info.transparent_color_index = gce.transparent_color;
640 else
641 info.transparent_color_index = -1;
642
643 read_frame_data(f, info, *new_frame);
644 m_frame.push_back(new_frame);
645 }
646} // gif::reader::read_frame_with_gce()
647
648/*----------------------------------------------------------------------------*/
654void claw::graphic::gif::reader::skip_extension( std::istream& f ) const
655{
656 u_int_8 block_size(0);
657
658 f.read( reinterpret_cast<char*>(&block_size), sizeof(block_size) );
659
660 while ( f && (block_size!=0) )
661 {
662 f.seekg( block_size, std::ios_base::cur );
663 f.read( reinterpret_cast<char*>(&block_size), sizeof(block_size) );
664 }
665} // gif::reader::skip_extension()
666
667/*----------------------------------------------------------------------------*/
676void claw::graphic::gif::reader::read_frame_data
677( std::istream& f, const reader_info& info, frame& the_frame ) const
678{
679 image_descriptor id;
680
681 f.read( reinterpret_cast<char*>(&id), sizeof(id) );
682
683 the_frame.set_size(info.sd.screen_width, info.sd.screen_height);
684
685 std::fill( the_frame.begin(), the_frame.end(), transparent_pixel );
686
687 palette_type* palette(info.palette);
688
689 if ( id.has_color_table() )
690 {
691 palette = new palette_type(id.color_palette_size());
692 read_palette(f, *palette);
693 }
694
695 decode_data(f, *palette, id, info.transparent_color_index, the_frame);
696
697 if ( id.has_color_table() )
698 delete palette;
699} // gif::reader::read_frame_data()
700
701/*----------------------------------------------------------------------------*/
712void claw::graphic::gif::reader::decode_data
713( std::istream& f, const palette_type& palette, const image_descriptor& id,
714 int transparent_color_index, frame& the_frame ) const
715{
716 u_int_8 code_size;
717
718 f.read( reinterpret_cast<char*>(&code_size), sizeof(code_size) );
719 input_buffer input(f, code_size);
720 output_buffer output(palette, id, transparent_color_index, the_frame);
721
722 do
723 {
724 gif_lzw_decoder decoder;
725 input.reset();
726 decoder.decode(input, output);
727 }
728 while ( !input.end_of_information() );
729} // gif::reader::decode_data()
#define CLAW_PRECOND(b)
Abort the program if a precondition is not true.
Definition assert.hpp:98
Exception thrown when accessing bad formated data.
Definition exception.hpp:72
Function object that deletes a pointer.
reader(image &img)
Constructor.
void load(std::istream &f)
Load the image data from a stream.
A class to deal with images.
Definition image.hpp:50
image()
Constructor. Creates an image without datas.
Definition image.cpp:106
A simple class to use as exception with string message.
Some function object classes.
Image class for gif files.
rgba_pixel transparent_pixel(0, 0, 0, 0)
A transparent color.
Definition pixel.hpp:133
A class to manage an index and an iterator easily.
unsigned_integer_of_size< 8 >::type u_int_8
An unsigned integer on 8 bits.
Definition types.hpp:135