claw  1.9.0
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 
45 claw::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 
64 bool 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 
73 bool 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 
84 unsigned int claw::graphic::gif::reader::input_buffer::symbols_count() const
85 {
86  return (1 << m_initial_code_size) + 2;
87 }
88 
92 unsigned 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 
150 void 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 
162 void 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 
174 void 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 
203 claw::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 
220 void 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 
294 claw::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 
311 claw::graphic::gif::reader::reader(image& img, frame_list& frames,
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 
332 void claw::graphic::gif::reader::load(std::istream& f)
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 
345 void claw::graphic::gif::reader::clear()
346 {
347  std::for_each(m_frame.begin(), m_frame.end(),
349  m_frame.clear();
350 }
351 
356 void 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 
385 void 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 
445 void 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 
461 void 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 
486 void 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 
503 void 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 
525 void 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 
563 void 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 
587 void 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 
631 void 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 
652 void 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 
688 void 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 }
Function object that deletes a pointer.
Definition: functional.hpp:273
void load(std::istream &f)
Load the image data from a stream.
Definition: gif_reader.cpp:332
unsigned_integer_of_size< 8 >::type u_int_8
An unsigned integer on 8 bits.
Definition: types.hpp:132
A class to manage an index and an iterator easily.
Image class for gif files.
image()
Constructor. Creates an image without datas.
Definition: image.cpp:95
void swap(claw::tween::tweener &a, claw::tween::tweener &b)
Swap two tweeners.
Definition: tweener.cpp:122
reader(image &img)
Constructor.
Definition: gif_reader.cpp:271
A simple class to use as exception with string message.
rgba_pixel transparent_pixel
A transparent color.
A class to deal with images.
Definition: image.hpp:50
#define CLAW_PRECOND(b)
Abort the program if a precondition is not true.
Definition: assert.hpp:94
Exception thrown when accessing bad formated data.
Definition: exception.hpp:73
Some function object classes.