VideoTools
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
grimagetype.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2017 Graeme Walker
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // grimagetype.cpp
19 //
20 
21 #include "gdef.h"
22 #include "grdef.h"
23 #include "grimagetype.h"
24 #include "grpng.h"
25 #include "grjpeg.h"
26 #include "grpnm.h"
27 #include "ghexdump.h"
28 #include "gassert.h"
29 #include <algorithm> // std::reverse
30 #include <cstring>
31 
32 namespace
33 {
34  template <typename T> T append_c( T p , T p_end , char c )
35  {
36  if( p != p_end ) *p++ = c ;
37  return p ;
38  }
39  template <typename T> T append_s( T p , T p_end , const char * s )
40  {
41  for( ; *s ; s++ )
42  p = append_c( p , p_end , *s ) ;
43  return p ;
44  }
45  template <typename T> T append_n( T p_in , T p_end , int n )
46  {
47  n = std::max( 0 , n ) ;
48  T p = p_in ;
49  for( ; p != p_end && n > 0 ; n /= 10 )
50  *p++ = static_cast<char>('0'+(n%10)) ;
51  std::reverse( p_in , p ) ;
52  return p ;
53  }
54  template <typename T>
55  T append_size( T p , T p_end , int dx , int dy , int channels )
56  {
57  p = append_s( p , p_end , ";xsize=" ) ;
58  p = append_n( p , p_end , dx ) ;
59  p = append_c( p , p_end , 'x' ) ;
60  p = append_n( p , p_end , dy ) ;
61  p = append_c( p , p_end , 'x' ) ;
62  p = append_n( p , p_end , channels ) ;
63  append_c( p , p_end , '\0' ) ;
64  return p ;
65  }
66  int atoi( const std::string & s , size_t p , size_t n )
67  {
68  int result = 0 ;
69  for( size_t i = 0U ; i < n ; i++ , p++ )
70  {
71  if( s.at(p) < '0' || s.at(p) > '9' ) break ;
72  result *= 10 ;
73  result += static_cast<int>(s.at(p)-'0') ;
74  }
75  return result ;
76  }
77 }
78 
79 // ==
80 
81 Gr::ImageType::ImageType( ImageType::Type type_ , int dx_ , int dy_ , int channels_ ) :
82  m_type(type_) ,
83  m_dx(dx_) ,
84  m_dy(dy_) ,
85  m_channels(channels_)
86 {
87  if( !valid() )
88  m_type = t_invalid ;
89 }
90 
92  m_type(t_invalid) ,
93  m_dx(0) ,
94  m_dy(0) ,
95  m_channels(0)
96 {
97 }
98 
99 Gr::ImageType::ImageType( std::istream & stream ) :
100  m_type(t_invalid) ,
101  m_dx(0) ,
102  m_dy(0) ,
103  m_channels(0)
104 {
105  init( stream ) ;
106  if( !valid() )
107  m_type = t_invalid ;
108 }
109 
110 Gr::ImageType::ImageType( const std::vector<char> & buffer ) :
111  m_type(t_invalid) ,
112  m_dx(0) ,
113  m_dy(0) ,
114  m_channels(0)
115 {
116  init( reinterpret_cast<const unsigned char *>(&buffer[0]) , buffer.size() ) ;
117  if( !valid() )
118  m_type = t_invalid ;
119 }
120 
122  m_type(t_invalid) ,
123  m_dx(0) ,
124  m_dy(0) ,
125  m_channels(0)
126 {
128  imagebuf inbuf( buffer ) ;
129  std::istream instream( &inbuf ) ;
130  init( instream ) ;
131  if( !valid() )
132  m_type = t_invalid ;
133 }
134 
135 Gr::ImageType::ImageType( const char * p , size_t n ) :
136  m_type(t_invalid) ,
137  m_dx(0) ,
138  m_dy(0) ,
139  m_channels(0)
140 {
141  init( reinterpret_cast<const unsigned char *>(p) , n ) ;
142  if( !valid() )
143  m_type = t_invalid ;
144 }
145 
146 Gr::ImageType::ImageType( const unsigned char * p , size_t n ) :
147  m_type(t_invalid) ,
148  m_dx(0) ,
149  m_dy(0) ,
150  m_channels(0)
151 {
152  init( p , n ) ;
153  if( !valid() )
154  m_type = t_invalid ;
155 }
156 
157 Gr::ImageType::ImageType( const std::string & type_str ) :
158  m_type(t_invalid) ,
159  m_dx(0) ,
160  m_dy(0) ,
161  m_channels(0)
162 {
163  int default_channels = 3 ;
164  if( type_str.find("image/x.raw") == 0U )
165  {
166  m_type = t_raw ;
167  default_channels = 0 ;
168  }
169  else if( type_str.find("image/jpeg") == 0U )
170  {
171  m_type = t_jpeg ;
172  }
173  else if( type_str.find("image/png") == 0U )
174  {
175  m_type = t_png ;
176  }
177  else if( type_str.find("image/x-portable-anymap") == 0U )
178  {
179  m_type = t_pnm ;
180  }
181 
182  if( m_type != t_invalid )
183  {
184  typedef std::string::size_type pos_t ;
185  const pos_t npos = std::string::npos ;
186 
187  // "image/whatever;xsize=<dx>x<dy>x<channels>"
188  pos_t p0 = type_str.find(';') ;
189  pos_t p1 = p0 == npos ? npos : type_str.find("xsize=",p0+1) ;
190  if( p1 != npos ) p1 += 5U ;
191  pos_t p2 = p1 == npos ? npos : type_str.find_first_of(",_x",p1+1U) ;
192  pos_t p3 = p2 == npos ? npos : type_str.find_first_of(",_x",p2+1U) ;
193 
194  m_dx = 0 ;
195  m_dy = 0 ;
196  m_channels = default_channels ;
197 
198  if( p1 != npos && p2 != npos )
199  m_dx = atoi( type_str , p1+1U , p2-p1-1U ) ;
200 
201  if( m_dx && p2 != npos && p3 != npos )
202  m_dy = atoi( type_str , p2+1U , p3-p2-1U ) ;
203 
204  if( m_dy && p3 != npos && p3 < type_str.size() )
205  m_channels = atoi( type_str , p3+1U , type_str.size()-p3-1U ) ;
206 
207  if( !valid() )
208  m_type = t_invalid ;
209  }
210 }
211 
212 std::string Gr::ImageType::str() const
213 {
214  String s ;
215  set( s ) ;
216  return std::string( s.s ) ;
217 }
218 
219 bool Gr::ImageType::matches( const std::string & other ) const
220 {
221  String s ;
222  set( s ) ;
223  return 0 == std::strcmp( s.c_str() , other.c_str() ) ;
224 }
225 
226 void Gr::ImageType::streamOut( std::ostream & stream ) const
227 {
228  String s ;
229  set( s ) ;
230  stream << s.s ;
231 }
232 
234 {
235  setsimple( out ) ;
236  append_size( out.s+std::strlen(out.s) , out.s+sizeof(out.s)-1U , m_dx , m_dy , m_channels ) ;
237  out.s[sizeof(out.s)-1U] = '\0' ;
238  return out ;
239 }
240 
241 void Gr::ImageType::setsimple( String & out ) const
242 {
243  G_ASSERT( sizeof(out.s) > 30U ) ;
244  out.s[0] = '\0' ;
245  if( m_type == t_jpeg ) std::strcpy( out.s , "image/jpeg" ) ; // ignore warnings
246  else if( m_type == t_png ) std::strcpy( out.s , "image/png" ) ; // ignore warnings
247  else if( m_type == t_raw ) std::strcpy( out.s , "image/x.raw" ) ; // ignore warnings
248  else if( m_type == t_pnm ) std::strcpy( out.s , "image/x-portable-anymap" ) ; // ignore warnings
249 }
250 
251 std::string Gr::ImageType::simple() const
252 {
253  String s ;
254  setsimple( s ) ;
255  return std::string( s.s ) ;
256 }
257 
259 {
260  // extra sanity checks and restrictions
261  return m_type != t_invalid && m_dx > 0 && m_dy > 0 && (m_channels==1||m_channels==3) ;
262 }
263 
265 {
266  return m_type == t_raw ;
267 }
268 
270 {
271  return m_type == t_jpeg ;
272 }
273 
275 {
276  return m_type == t_png ;
277 }
278 
280 {
281  return m_type == t_pnm ;
282 }
283 
284 size_t Gr::ImageType::size() const
285 {
286  return sizet( m_dx , m_dy , m_channels ) ;
287 }
288 
290 {
291  G_ASSERT( isRaw() ) ;
292  return sizet( m_dx , 1 , m_channels ) ;
293 }
294 
295 bool Gr::ImageType::operator==( const ImageType & other ) const
296 {
297  return m_type == other.m_type && m_dx == other.m_dx && m_dy == other.m_dy && m_channels == other.m_channels ;
298 }
299 
300 bool Gr::ImageType::operator!=( const ImageType & other ) const
301 {
302  return !( (*this) == other ) ;
303 }
304 
305 bool Gr::ImageType::operator<( const ImageType & other ) const
306 {
307  if( m_type != other.m_type ) return int(m_type) < int(other.m_type) ;
308  if( m_dx != other.m_dx ) return m_dx < other.m_dx ;
309  if( m_dy != other.m_dy ) return m_dy < other.m_dy ;
310  return m_channels < other.m_channels ;
311 }
312 
313 Gr::ImageType Gr::ImageType::jpeg( int dx , int dy , int channels )
314 {
315  return ImageType( t_jpeg , dx , dy , channels ) ;
316 }
317 
318 Gr::ImageType Gr::ImageType::jpeg( ImageType type_in , int scale , bool monochrome )
319 {
320  return jpeg( scaled(type_in.dx(),scale) , scaled(type_in.dy(),scale) , monochrome?1:type_in.channels() ) ;
321 }
322 
323 Gr::ImageType Gr::ImageType::raw( int dx , int dy , int channels )
324 {
325  return ImageType( t_raw , dx , dy , channels ) ;
326 }
327 
328 Gr::ImageType Gr::ImageType::raw( ImageType type_in , int scale , bool monochrome )
329 {
330  return raw( scaled(type_in.dx(),scale) , scaled(type_in.dy(),scale) , monochrome?1:type_in.channels() ) ;
331 }
332 
333 Gr::ImageType Gr::ImageType::png( int dx , int dy , int channels )
334 {
335  return ImageType( t_png , dx , dy , channels ) ;
336 }
337 
338 Gr::ImageType Gr::ImageType::png( ImageType type_in , int scale , bool monochrome )
339 {
340  return png( scaled(type_in.dx(),scale) , scaled(type_in.dy(),scale) , monochrome?1:type_in.channels() ) ;
341 }
342 
343 void Gr::ImageType::init( ImageType::Type t , int dx , int dy , int channels )
344 {
345  m_type = t ;
346  m_dx = dx ;
347  m_dy = dy ;
348  m_channels = channels ;
349 }
350 
351 void Gr::ImageType::init( std::istream & stream )
352 {
353  std::streampos pos = stream.tellg() ;
354 
355  unsigned char buffer[6] = { 0 , 0 , 0 , 0 , 0 , 0 } ;
356  stream.read( reinterpret_cast<char*>(&buffer[0]) , sizeof(buffer) ) ;
357  size_t n = static_cast<size_t>(stream.gcount()) ;
358 
359  stream.seekg( pos , std::ios_base::beg ) ;
360  if( pos == -1 || stream.tellg() != pos )
361  throw std::runtime_error( "stream not seekable" ) ;
362 
363  Type t = typeFromSignature( buffer , n ) ;
364  if( t == t_jpeg )
365  {
366  JpegInfo info( stream ) ;
367  if( info.valid() )
368  init( t_jpeg , info.dx() , info.dy() , info.channels() ) ;
369  }
370  else if( t == t_png )
371  {
372  PngInfo info( stream ) ;
373  if( info.valid() )
374  init( t_png , info.dx() , info.dy() , 3 ) ;
375  }
376  else if( t == t_pnm )
377  {
378  PnmInfo info( stream ) ;
379  if( info.valid() )
380  init( t_pnm , info.dx() , info.dy() , info.channels() ) ;
381  }
382 
383  stream.seekg( pos , std::ios_base::beg ) ;
384  if( pos == -1 || stream.tellg() != pos )
385  throw std::runtime_error( "stream not seekable" ) ;
386 }
387 
388 void Gr::ImageType::init( const unsigned char * p , size_t n )
389 {
390  Type t = typeFromSignature( p , n ) ;
391  if( t == t_jpeg )
392  {
393  JpegInfo info( p , n ) ;
394  if( info.valid() )
395  init( t_jpeg , info.dx() , info.dy() , info.channels() ) ;
396  }
397  else if( t == t_png )
398  {
399  PngInfo info( p , n ) ;
400  if( info.valid() )
401  init( t_png , info.dx() , info.dy() , 3 ) ;
402  }
403  else if( t == t_pnm )
404  {
405  PnmInfo info( p , n ) ;
406  if( info.valid() )
407  init( t_pnm , info.dx() , info.dy() , info.channels() ) ;
408  }
409 }
410 
411 Gr::ImageType::Type Gr::ImageType::typeFromSignature( const unsigned char * p , size_t n )
412 {
413  if( n > 4U &&
414  p[0] == 0xff && p[1] == 0xd8 && // SOI
415  p[2] == 0xff )
416  {
417  return t_jpeg ;
418  }
419  else if( n > 4U &&
420  p[0] == 0x89 && p[1] == 'P' && p[2] == 'N' && p[3] == 'G' )
421  {
422  return t_png ;
423  }
424  else if( n > 4U && p[0] == 'P' &&
425  // 1..3=>ascii, 4..6=>binary
426  ( p[1] == '1' || p[1] == '2' || p[1] == '3' || p[1] == '4' || p[1] == '5' || p[1] == '6' ) &&
427  ( p[2] == ' ' || p[2] == '\n' || p[2] == '\r' ) )
428  {
429  return t_pnm ;
430  }
431  else
432  {
433  return t_invalid ;
434  }
435 }
436 
437 /// \file grimagetype.cpp
bool isRaw() const
Returns true if a raw image type.
A traits class that can be specialised for Gr::ImageBuffer candidates.
Definition: grtraits.h:34
bool isJpeg() const
Returns true if a jpeg image type.
bool operator<(const ImageType &) const
Comparison operator.
int channels() const
Returns the number of channels.
Definition: grimagetype.h:197
static ImageType jpeg(int dx, int dy, int channels=3)
Factory function for a jpeg image type.
Synopsis:
An encapsulation of image type, including width, height and number of channels, with support for a st...
Definition: grimagetype.h:43
Vectors ImageBuffer
An ImageBuffer is used to hold raw image data, typically in more than one chunk.
Definition: grimagebuffer.h:47
String & set(String &out) const
Returns str() by reference.
static ImageType raw(int dx, int dy, int channels)
Factory function for a raw image type.
std::string str() const
Returns the image type string (including the size parameter).
size_t size() const
Returns the product of dx, dy and channels.
bool valid() const
Returns true if valid.
bool isPng() const
Returns true if a png image type.
int dy() const
Returns the image height.
Definition: grimagetype.h:191
void streamOut(std::ostream &) const
Used by op<<().
size_t rowsize() const
Returns the product of dx and channels.
bool matches(const std::string &str) const
Returns true if this type matches the given type (including size decorations).
int dx() const
Returns the image width.
Definition: grimagetype.h:185
std::string simple() const
Returns the basic image type string, excluding the size parameter.
bool operator!=(const ImageType &) const
Comparison operator.
ImageType()
Default constructor for an in-valid() image type with dimensions of zero.
Definition: grimagetype.cpp:91
A small-string class used for stringised Gr::ImageType instances.
Definition: grimagetype.h:46
bool operator==(const ImageType &) const
Comparison operator.
bool isPnm() const
Returns true if a pnm image type.
static ImageType png(int dx, int dy, int channels=3)
Factory function for a png image type.