claw 1.9.0
 
Loading...
Searching...
No Matches
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@stuff-o-matic.com
24*/
30#include <claw/graphic/gif.hpp>
31
32#include <claw/exception.hpp>
33#include <claw/functional.hpp>
34#include <claw/it_index.hpp>
35
36#include <algorithm>
37#include <climits>
38#include <limits>
39
45claw::graphic::gif::reader::input_buffer::input_buffer(std::istream& is,
46 u_int_8 code_size)
47 : m_val(0)
48 , m_input(is)
49 , m_pending(0)
50 , m_pending_bits(0)
51 , m_pending_end(0)
52 , m_initial_code_size(code_size)
53 , m_code_size(m_initial_code_size + 1)
54 , m_code_limit(1 << m_code_size)
55{
56 m_input.read(reinterpret_cast<char*>(&m_next_data_length),
57 sizeof(m_next_data_length));
58}
59
64bool claw::graphic::gif::reader::input_buffer::end_of_data() const
65{
66 return (m_val == (unsigned int)(1 << m_initial_code_size))
67 || end_of_information();
68}
69
73bool claw::graphic::gif::reader::input_buffer::end_of_information() const
74{
75 return !m_input || (m_val == (unsigned int)(1 << m_initial_code_size) + 1)
76 || ((m_next_data_length == 0) // no more data in the stream
77 && (m_pending == m_pending_end) // no more data in the buffer
78 && (m_pending_bits < m_code_size));
79}
80
84unsigned int claw::graphic::gif::reader::input_buffer::symbols_count() const
85{
86 return (1 << m_initial_code_size) + 2;
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}
146
150void claw::graphic::gif::reader::input_buffer::reset()
151{
152 m_val = 0;
153 m_code_size = m_initial_code_size + 1;
154 m_code_limit = 1 << m_code_size;
155}
156
162void claw::graphic::gif::reader::input_buffer::new_code(unsigned int code)
163{
164 if((code == m_code_limit) && (m_code_size != 12))
165 {
166 ++m_code_size;
167 m_code_limit = 1 << m_code_size;
168 }
169}
170
174void claw::graphic::gif::reader::input_buffer::fill_buffer()
175{
176 // move available data at the begining of the buffer
177 std::copy(m_buffer + m_pending, m_buffer + m_pending_end, m_buffer);
178 m_pending_end = m_pending_end - m_pending;
179 m_pending = 0;
180
181 if(m_next_data_length != 0)
182 {
183 assert(m_pending_end + m_next_data_length <= sizeof(m_buffer));
184
185 m_input.read(m_buffer + m_pending_end, m_next_data_length);
186 m_pending_end += m_next_data_length;
187
188 if((m_pending_bits == 0) && (m_pending != m_pending_end))
189 m_pending_bits = CHAR_BIT;
190
191 m_input.read(reinterpret_cast<char*>(&m_next_data_length),
192 sizeof(m_next_data_length));
193 }
194}
195
203claw::graphic::gif::reader::output_buffer::output_buffer(
204 const palette_type& p, const image_descriptor& id,
205 int transparent_color_index, image& output)
206 : m_palette(p)
207 , m_id(id)
208 , m_transparent_color_index(transparent_color_index)
209 , m_output(output)
210 , m_x(0)
211 , m_y(0)
212 , m_interlace_pass(0)
213 , m_interlace_step(8)
214{}
215
220void claw::graphic::gif::reader::output_buffer::write(unsigned int code)
221{
222 assert(code < m_palette.size());
223 assert(m_x < m_id.width);
224 assert(m_y < m_id.height);
225
226 m_output[m_y + m_id.top][m_x + m_id.left] = m_palette[code];
227
228 if(m_transparent_color_index != -1)
229 if(code == (unsigned int)m_transparent_color_index)
230 m_output[m_y + m_id.top][m_x + m_id.left].components.alpha = 0;
231
232 ++m_x;
233
234 if(m_x == m_id.width)
235 {
236 m_x = 0;
237
238 if(!m_id.is_interlaced())
239 ++m_y;
240 else
241 {
242 m_y += m_interlace_step;
243
244 while((m_y >= m_id.height) && (m_interlace_pass != 3))
245 {
246 ++m_interlace_pass;
247 switch(m_interlace_pass)
248 {
249 case 1:
250 m_y = 4;
251 m_interlace_step = 8;
252 break;
253 case 2:
254 m_y = 2;
255 m_interlace_step = 4;
256 break;
257 case 3:
258 m_y = 1;
259 m_interlace_step = 2;
260 break;
261 }
262 }
263 }
264 }
265}
266
272 : m_image(&img)
273{}
274
284 : m_image(&img)
285{
286 load(f);
287}
288
294claw::graphic::gif::reader::reader(frame_list& frames, std::istream& f)
295 : m_image(NULL)
296{
297 load(f);
298 frames = m_frame;
299 m_frame.clear();
300}
301
312 std::istream& f)
313 : m_image(&img)
314{
315 load(f);
316 frames = m_frame;
317 m_frame.clear();
318}
319
324{
325 clear();
326}
327
333{
334 clear();
335
336 inside_load(f);
337
338 if(!m_frame.empty() && (m_image != NULL))
339 *m_image = *m_frame.front();
340}
341
345void claw::graphic::gif::reader::clear()
346{
347 std::for_each(m_frame.begin(), m_frame.end(),
349 m_frame.clear();
350}
351
356void claw::graphic::gif::reader::inside_load(std::istream& f)
357{
358 std::istream::pos_type init_pos = f.tellg();
359 reader_info info;
360 info.palette = NULL;
361
362 try
363 {
364 check_if_gif(f);
365
366 read_screen_descriptor(f, info);
367 read_data(f, info);
368 make_frames(info);
369
370 delete info.palette;
371 }
372 catch(...)
373 {
374 delete info.palette;
375
376 f.seekg(init_pos, std::ios_base::beg);
377 throw;
378 }
379}
380
385void claw::graphic::gif::reader::make_frames(const reader_info& info)
386{
387 it_index<frame_list::const_iterator> it(m_frame.begin());
388
389 frame_list result;
390 std::size_t cumul_count(0);
391 frame cumul(info.sd.screen_width, info.sd.screen_height);
392 frame prev;
393
394 if(!info.disposal_method.empty())
395 {
396 if(info.disposal_method[0]
397 == graphic_control_extension::dispose_background)
398 fill_background(cumul, info);
399 else
400 std::fill(cumul.begin(), cumul.end(), transparent_pixel);
401 }
402
403 for(; it != m_frame.end(); ++it)
404 {
405 if(info.disposal_method[it]
406 == graphic_control_extension::dispose_previous)
407 prev = cumul;
408
409 cumul.merge(**it);
410 cumul.set_delay((*it)->get_delay());
411 ++cumul_count;
412
413 if(cumul.get_delay() > 0)
414 {
415 result.push_back(new frame(cumul));
416 cumul_count = 0;
417 }
418
419 switch(info.disposal_method[it])
420 {
421 case graphic_control_extension::dispose_background:
422 fill_background(cumul, info);
423 break;
424 case graphic_control_extension::dispose_previous:
425 cumul = prev;
426 break;
427 default:
428 { /* nothing to do */
429 }
430 }
431 }
432
433 if(cumul_count != 0)
434 result.push_back(new frame(cumul));
435
436 clear();
437 std::swap(m_frame, result);
438}
439
445void claw::graphic::gif::reader::fill_background(image& img,
446 const reader_info& info) const
447{
448 rgba_pixel clr(transparent_pixel);
449
450 if(info.sd.has_global_color_table() && (info.palette != NULL))
451 if(info.sd.background_color < info.palette->size())
452 clr = (*info.palette)[info.sd.background_color];
453
454 std::fill(img.begin(), img.end(), clr);
455}
456
461void claw::graphic::gif::reader::check_if_gif(std::istream& f) const
462{
463 CLAW_PRECOND(!!f);
464
465 header h;
466 f.read(reinterpret_cast<char*>(&h), sizeof(header));
467
468 bool valid = false;
469
470 if(f.rdstate() == std::ios_base::goodbit)
471 if((h.signature[0] == 'G') && (h.signature[1] == 'I')
472 && (h.signature[2] == 'F') && (h.version[0] == '8')
473 && ((h.version[1] == '7') || (h.version[1] == '9'))
474 && (h.version[2] == 'a'))
475 valid = true;
476
477 if(!valid)
478 throw claw::bad_format("Not a GIF file.");
479}
480
486void claw::graphic::gif::reader::read_screen_descriptor(std::istream& f,
487 reader_info& info)
488{
489 f.read(reinterpret_cast<char*>(&info.sd), sizeof(screen_descriptor));
490
491 if(info.sd.has_global_color_table())
492 {
493 info.palette = new palette_type(info.sd.color_palette_size());
494 read_palette(f, *info.palette);
495 }
496}
497
503void claw::graphic::gif::reader::read_palette(std::istream& f,
504 palette_type& p) const
505{
506 u_int_8 red, green, blue;
507
508 for(std::size_t i = 0; i != p.size(); ++i)
509 {
510 f.read(reinterpret_cast<char*>(&red), sizeof(u_int_8));
511 f.read(reinterpret_cast<char*>(&green), sizeof(u_int_8));
512 f.read(reinterpret_cast<char*>(&blue), sizeof(u_int_8));
513
514 p[i].components.red = red;
515 p[i].components.green = green;
516 p[i].components.blue = blue;
517 }
518}
519
525void claw::graphic::gif::reader::read_data(std::istream& f, reader_info& info)
526{
527 u_int_8 code;
528
529 do
530 {
531 code = 0;
532 f.read(reinterpret_cast<char*>(&code), sizeof(code));
533
534 if(f)
535 switch(code)
536 {
537 case extension::block_id:
538 f.read(reinterpret_cast<char*>(&code), sizeof(code));
539
540 if(code == graphic_control_extension::block_label)
541 read_frame_with_gce(f, info);
542 else
543 skip_extension(f);
544
545 break;
546 case image_descriptor::block_id:
547 read_frame(f, info);
548 break;
549 case trailer::block_id:
550 break;
551 default:
552 throw claw::bad_format("gif::reader: invalid code");
553 }
554 }
555 while(f && (code != trailer::block_id));
556}
557
563void claw::graphic::gif::reader::read_frame(std::istream& f, reader_info& info)
564{
565 frame* new_frame(NULL);
566
567 try
568 {
569 new_frame = new frame;
570 read_frame_data(f, info, *new_frame);
571
572 info.disposal_method.push_back(graphic_control_extension::dispose_none);
573 m_frame.push_back(new_frame);
574 }
575 catch(...)
576 {
577 delete new_frame;
578 throw;
579 }
580}
581
587void claw::graphic::gif::reader::read_frame_with_gce(std::istream& f,
588 reader_info& info)
589{
590 graphic_control_extension gce;
591 u_int_8 code;
592
593 f.read(reinterpret_cast<char*>(&gce), sizeof(gce));
594 f.read(reinterpret_cast<char*>(&code), sizeof(code));
595
596 while((code == extension::block_id) && f)
597 {
598 f.read(reinterpret_cast<char*>(&code), sizeof(code));
599
600 if(code == graphic_control_extension::block_label)
601 f.read(reinterpret_cast<char*>(&gce), sizeof(gce));
602 else // unknown extension
603 skip_extension(f);
604
605 // read the code of the following block
606 f.read(reinterpret_cast<char*>(&code), sizeof(code));
607 }
608
609 if(code == image_descriptor::block_id)
610 {
611 frame* new_frame = new frame;
612 new_frame->set_delay(gce.delay);
613
614 info.disposal_method.push_back(gce.get_disposal_method());
615
616 if(gce.has_transparent_color())
617 info.transparent_color_index = gce.transparent_color;
618 else
619 info.transparent_color_index = -1;
620
621 read_frame_data(f, info, *new_frame);
622 m_frame.push_back(new_frame);
623 }
624}
625
631void claw::graphic::gif::reader::skip_extension(std::istream& f) const
632{
633 u_int_8 block_size(0);
634
635 f.read(reinterpret_cast<char*>(&block_size), sizeof(block_size));
636
637 while(f && (block_size != 0))
638 {
639 f.seekg(block_size, std::ios_base::cur);
640 f.read(reinterpret_cast<char*>(&block_size), sizeof(block_size));
641 }
642}
643
652void claw::graphic::gif::reader::read_frame_data(std::istream& f,
653 const reader_info& info,
654 frame& the_frame) const
655{
656 image_descriptor id;
657
658 f.read(reinterpret_cast<char*>(&id), sizeof(id));
659
660 the_frame.set_size(info.sd.screen_width, info.sd.screen_height);
661
662 std::fill(the_frame.begin(), the_frame.end(), transparent_pixel);
663
664 palette_type* palette(info.palette);
665
666 if(id.has_color_table())
667 {
668 palette = new palette_type(id.color_palette_size());
669 read_palette(f, *palette);
670 }
671
672 decode_data(f, *palette, id, info.transparent_color_index, the_frame);
673
674 if(id.has_color_table())
675 delete palette;
676}
677
688void claw::graphic::gif::reader::decode_data(std::istream& f,
689 const palette_type& palette,
690 const image_descriptor& id,
691 int transparent_color_index,
692 frame& the_frame) const
693{
694 u_int_8 code_size;
695
696 f.read(reinterpret_cast<char*>(&code_size), sizeof(code_size));
697 input_buffer input(f, code_size);
698 output_buffer output(palette, id, transparent_color_index, the_frame);
699
700 do
701 {
702 gif_lzw_decoder decoder;
703 input.reset();
704 decoder.decode(input, output);
705 }
706 while(!input.end_of_information());
707}
#define CLAW_PRECOND(b)
Abort the program if a precondition is not true.
Definition assert.hpp:94
Function object that deletes a pointer.
One frame in the animation.
Definition gif.hpp:56
void set_delay(unsigned int d)
Set the time duration of this frame.
Definition gif_frame.cpp:53
reader(image &img)
Constructor.
void load(std::istream &f)
Load the image data from a stream.
image()
Constructor. Creates an image without datas.
Definition image.cpp:95
A simple class to use as exception with string message.
Some function object classes.
Image class for gif files.
void swap(claw::graphic::gif &a, claw::graphic::gif &b)
Swap the content of two gifs.
Definition gif.cpp:209
rgba_pixel transparent_pixel
A transparent color.
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:132