e576bb2002-10-11Martin Nilsson /* || This file is part of Pike. For copyright information see COPYRIGHT. || Pike is distributed under GPL, LGPL and MPL. See the file COPYING || for more information. */
2588311999-10-22Per Hedbor #include "global.h" #include <math.h> #include <ctype.h> #include "stralloc.h" #include "object.h" #include "mapping.h" #include "interpret.h" #include "operators.h" #include "svalue.h"
b2d3e42000-12-01Fredrik Hübinette (Hubbe) #include "pike_error.h"
2588311999-10-22Per Hedbor #include "builtin_functions.h"
9de2c01999-11-19Henrik Grubbström (Grubba) #include "program.h"
2588311999-10-22Per Hedbor  #include "image.h" #include "builtin_functions.h" #include "module_support.h"
6dc2772000-07-28Fredrik Hübinette (Hubbe) 
6ad2372002-05-11Martin Nilsson #define sp Pike_sp
6dc2772000-07-28Fredrik Hübinette (Hubbe) 
2588311999-10-22Per Hedbor extern struct program *image_program;
eab08e2001-11-07Martin Nilsson /*! @module Image */ /*! @module WBMP
378e9c2014-07-31Per Hedbor  *! Wireless Application Protocol Bitmap Format - WAP bitmap format - WBMP. *! *! Wireless Application Protocol Bitmap Format (shortened to Wireless *! Bitmap and with file extension .wbmp) is a monochrome graphics *! file format that was optimized for the extremely low-end mobile *! computing devices that were common in the early days of the mobile web.
eab08e2001-11-07Martin Nilsson  */
2588311999-10-22Per Hedbor  struct buffer {
3bd87a2000-08-03Henrik Grubbström (Grubba)  size_t len;
2588311999-10-22Per Hedbor  unsigned char *str; }; struct ext_header { struct ext_header *next; char name[8]; char value[16]; char name_len; char value_len; }; struct wbf_header { unsigned int width; unsigned int height; int type; int header; int fix_header_field; int ext_header_field; struct ext_header *first_ext_header; }; static void read_string( struct buffer *from, unsigned int len, char *to ) { if( from->len < len )
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Invalid data format\n");
59fc9e2014-09-03Martin Nilsson  memcpy( from->str, to, len );
2588311999-10-22Per Hedbor  from->str += len; from->len -= len; } static unsigned char read_uchar( struct buffer *from ) { unsigned char res = 0; if(from->len) { res = from->str[0]; from->str++; from->len--; } else
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Invalid data format\n");
2588311999-10-22Per Hedbor  return res; }
6dc2772000-07-28Fredrik Hübinette (Hubbe) static int wbf_read_int( struct buffer *from )
2588311999-10-22Per Hedbor { int res = 0; while( 1 ) { int i = read_uchar( from );
b5f3b21999-10-22Per Hedbor  res <<= 7;
2588311999-10-22Per Hedbor  res |= i&0x7f;
b5f3b21999-10-22Per Hedbor  if( !(i & 0x80 ) )
2588311999-10-22Per Hedbor  break; } return res; } static void push_ext_header( struct ext_header *eh ) {
5e9fc02015-08-18Per Hedbor  push_static_text( "identifier" );
2588311999-10-22Per Hedbor  push_string( make_shared_binary_string( eh->name, eh->name_len ) );
5e9fc02015-08-18Per Hedbor  push_static_text( "value" );
2588311999-10-22Per Hedbor  push_string( make_shared_binary_string( eh->value, eh->value_len ) ); f_aggregate_mapping( 4 ); }
fad12d2009-08-10Henrik Grubbström (Grubba) static void free_wbf_header_contents( struct wbf_header *wh ) { while( wh->first_ext_header ) { struct ext_header *eh = wh->first_ext_header; wh->first_ext_header = eh->next; free( eh ); } }
2588311999-10-22Per Hedbor static struct wbf_header decode_header( struct buffer *data ) { struct wbf_header res;
fad12d2009-08-10Henrik Grubbström (Grubba)  ONERROR err;
21b12a2014-09-03Martin Nilsson  memset( &res, 0, sizeof(res) );
6dc2772000-07-28Fredrik Hübinette (Hubbe)  res.type = wbf_read_int( data );
2588311999-10-22Per Hedbor  res.fix_header_field = read_uchar( data );
fad12d2009-08-10Henrik Grubbström (Grubba)  SET_ONERROR(err, free_wbf_header_contents, &res);
2588311999-10-22Per Hedbor  if( res.fix_header_field & 0x80 ) { switch( (res.fix_header_field>>5) & 0x3 ) { case 0: /* Single varint extra header */
6dc2772000-07-28Fredrik Hübinette (Hubbe)  res.ext_header_field = wbf_read_int( data );
2588311999-10-22Per Hedbor  break; case 1: /* reserved */ case 2: /* reserved */ case 3: /* Array of parameter/value */ { int q = 0x80;
13670c2015-05-25Martin Nilsson 
2588311999-10-22Per Hedbor  while( q & 0x80 ) { struct ext_header *eh; q = read_uchar( data );
9c14f32014-04-27Martin Nilsson  eh = xcalloc( 1, sizeof( struct ext_header ) );
fad12d2009-08-10Henrik Grubbström (Grubba)  eh->next = res.first_ext_header; res.first_ext_header = eh;
2588311999-10-22Per Hedbor  eh->name_len = ((q>>4) & 0x7) + 1; eh->value_len = (q & 0xf) + 1; read_string( data, eh->name_len, eh->name ); read_string( data, eh->value_len, eh->value ); } } } }
6dc2772000-07-28Fredrik Hübinette (Hubbe)  res.width = wbf_read_int( data ); res.height = wbf_read_int( data );
fad12d2009-08-10Henrik Grubbström (Grubba)  UNSET_ONERROR(err);
2588311999-10-22Per Hedbor  return res; } static void low_image_f_wbf_decode_type0( struct wbf_header *wh, struct buffer *buff ) { unsigned int x, y; struct image *i; struct object *io; unsigned int rowsize = (wh->width+7) / 8; rgb_group white; rgb_group *id; push_int( wh->width ); push_int( wh->height ); io = clone_object( image_program, 2 );
13b5ed2014-05-26Per Hedbor  i = get_storage(io,image_program);
2588311999-10-22Per Hedbor  id = i->img; white.r = 255; white.g = 255; white.b = 255;
13670c2015-05-25Martin Nilsson 
2588311999-10-22Per Hedbor  for( y = 0; y<wh->height; y++ ) { unsigned char q = 0; /* avoid warning */ unsigned char *data = buff->str + y * rowsize; if( buff->len < (rowsize+1)*y ) break; for( x = 0; x<wh->width; x++ ) { if( !(x % 8) ) q = data[x/8]; else
b5f3b21999-10-22Per Hedbor  q <<= 1;
2588311999-10-22Per Hedbor  if( q & 128 ) *id = white; id++; } } push_object( io ); } static void low_image_f_wbf_decode( int args, int mode ) { struct pike_string *s; struct wbf_header wh; int map_num_elems = 0; struct buffer buff; get_all_args( "decode", args, "%S", &s ); buff.len = s->len;
01a9572000-02-03Henrik Grubbström (Grubba)  buff.str = (unsigned char *)s->str;
2588311999-10-22Per Hedbor  sp--; /* Evil me */ wh = decode_header( &buff ); switch( wh.type ) {
13670c2015-05-25Martin Nilsson  case 0: /* The only supported format. B/W uncompressed bitmap
2588311999-10-22Per Hedbor  No compresson. Colour: 1 = white, 0 = black. Depth: 1 bit. high bit = rightmost. first byte is upper left corner of image. each row is padded to one byte */ switch( mode ) { case 2: /* Image only */ low_image_f_wbf_decode_type0( &wh, &buff ); return; case 1: /* Image and header */
5e9fc02015-08-18Per Hedbor  push_static_text( "image" );
2588311999-10-22Per Hedbor  low_image_f_wbf_decode_type0( &wh, &buff ); map_num_elems++;
3595ea2018-02-12Marcus Comstedt  /* FALLTHRU */
13670c2015-05-25Martin Nilsson 
2588311999-10-22Per Hedbor  case 0: /* Header only */
5e9fc02015-08-18Per Hedbor  push_static_text( "format" ); push_static_text( "image/x-wap.wbmp; type=0" );
2588311999-10-22Per Hedbor  map_num_elems++;
13670c2015-05-25Martin Nilsson 
5e9fc02015-08-18Per Hedbor  push_static_text( "xsize" );
2588311999-10-22Per Hedbor  push_int( wh.width ); map_num_elems++;
5e9fc02015-08-18Per Hedbor  push_static_text( "ysize" );
2588311999-10-22Per Hedbor  push_int( wh.height ); map_num_elems++; if( wh.fix_header_field ) {
5e9fc02015-08-18Per Hedbor  push_static_text( "fix_header_field" );
2588311999-10-22Per Hedbor  push_int( wh.fix_header_field ); map_num_elems++; } if( wh.ext_header_field ) {
5e9fc02015-08-18Per Hedbor  push_static_text( "ext_header_field" );
2588311999-10-22Per Hedbor  push_int( wh.ext_header_field ); map_num_elems++; }
13670c2015-05-25Martin Nilsson 
2588311999-10-22Per Hedbor  if( wh.first_ext_header ) { int num_headers = 0; struct ext_header *eh = wh.first_ext_header; while( eh ) { push_ext_header( eh ); eh = eh->next; num_headers++; } f_aggregate( num_headers ); f_reverse( 1 ); map_num_elems++; } f_aggregate_mapping( map_num_elems * 2 ); } free_string( s ); free_wbf_header_contents( &wh ); break; default: free_string( s ); free_wbf_header_contents( &wh );
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Unsupported wbf image type.\n");
2588311999-10-22Per Hedbor  } }
378e9c2014-07-31Per Hedbor /*! @decl Image.Image decode(string image)
eab08e2001-11-07Martin Nilsson  *!
378e9c2014-07-31Per Hedbor  *! Decode the given image
eab08e2001-11-07Martin Nilsson  */
2588311999-10-22Per Hedbor static void image_f_wbf_decode( int args ) { low_image_f_wbf_decode( args, 2 ); }
eab08e2001-11-07Martin Nilsson /*! @decl mapping _decode(string image) *!
378e9c2014-07-31Per Hedbor  *! Decode the given image *! @returns *! @mapping *! @member Image.Image "img" *! @member string "format" *! The MIME type and encoding for the image, e.g. "image/x-wap.wbmp; type=0". *! @member int "xsize" *! @member int "ysize" *! @member int "fix_header_field" *! @member int "ext_header_field" *! @endmapping
eab08e2001-11-07Martin Nilsson  */
2588311999-10-22Per Hedbor static void image_f_wbf__decode( int args ) { low_image_f_wbf_decode( args, 1 ); }
eab08e2001-11-07Martin Nilsson /*! @decl mapping decode_header(string image) *! *! @returns *! @mapping *! @member string "format" *! The MIME type and encoding for the image, e.g. "image/x-wap.wbmp; type=0". *! @member int "xsize" *! @member int "ysize" *! @member int "fix_header_field" *! @member int "ext_header_field" *! @endmapping */
2588311999-10-22Per Hedbor static void image_f_wbf_decode_header( int args ) { low_image_f_wbf_decode( args, 0 ); }
b5f3b21999-10-22Per Hedbor void push_wap_integer( unsigned int i )
2588311999-10-22Per Hedbor {
b5f3b21999-10-22Per Hedbor  char data[10]; /* More than big enough... */
2588311999-10-22Per Hedbor  int pos = 0;
b5f3b21999-10-22Per Hedbor  if( !i ) { data[0] = 0; pos=1; } while( i )
2588311999-10-22Per Hedbor  {
b5f3b21999-10-22Per Hedbor  data[pos] = (i&0x7f) | 0x80;
13670c2015-05-25Martin Nilsson  i>>=7;
b5f3b21999-10-22Per Hedbor  pos++;
2588311999-10-22Per Hedbor  }
b5f3b21999-10-22Per Hedbor  data[0] &= 0x7f; push_string( make_shared_binary_string( data, pos ) ); f_reverse(1);
2588311999-10-22Per Hedbor } static void push_wap_type0_image_data( struct image *i ) { int x, y; unsigned char *data, *p; rgb_group *is;
9c14f32014-04-27Martin Nilsson  data = xcalloc( i->ysize, (i->xsize+7)/8 );
2588311999-10-22Per Hedbor  is = i->img; for( y = 0; y<i->ysize; y++ ) { p = data + (i->xsize+7)/8*y; for( x = 0; x<i->xsize; x++ ) { if( is->r || is->g || is->b ) p[x/8] |= 128 >> (x%8); is++; } }
01a9572000-02-03Henrik Grubbström (Grubba)  push_string( make_shared_binary_string( (char *)data, i->ysize * (i->xsize+7)/8 ) );
2588311999-10-22Per Hedbor }
eab08e2001-11-07Martin Nilsson /*! @decl string encode(object image, void|mapping args) *! @decl string _encode(object image, void|mapping args) *!
378e9c2014-07-31Per Hedbor  *! Encode the given image as a WBMP image. All pixels that are not *! black will be encoded as white.
eab08e2001-11-07Martin Nilsson  */
2588311999-10-22Per Hedbor static void image_f_wbf_encode( int args ) { struct object *o; struct image *i; struct mapping *options = NULL; int num_strings = 0; if( !args )
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("No image given to encode.\n");
2588311999-10-22Per Hedbor  if( args > 2 )
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Too many arguments to encode.\n");
017b572011-10-28Henrik Grubbström (Grubba)  if( TYPEOF(sp[-args]) != T_OBJECT )
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("No image given to encode.\n");
2588311999-10-22Per Hedbor  o = sp[-args].u.object;
13b5ed2014-05-26Per Hedbor  i = get_storage(o,image_program);
2588311999-10-22Per Hedbor  if(!i)
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Wrong type object argument\n");
2588311999-10-22Per Hedbor  if( args == 2 ) {
017b572011-10-28Henrik Grubbström (Grubba)  if( TYPEOF(sp[-args+1]) != T_MAPPING )
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Wrong type for argument 2.\n");
2588311999-10-22Per Hedbor  options = sp[-args+1].u.mapping; } sp-=args; num_strings=0; push_wap_integer( 0 ); num_strings++; /* type */ push_wap_integer( 0 ); num_strings++; /* extra header */
13670c2015-05-25Martin Nilsson  push_wap_integer( i->xsize ); num_strings++; push_wap_integer( i->ysize ); num_strings++;
2588311999-10-22Per Hedbor  push_wap_type0_image_data( i ); num_strings++; f_add( num_strings ); if( options ) free_mapping( options ); free_object( o ); }
eab08e2001-11-07Martin Nilsson /*! @endmodule */ /*! @endmodule */
c9eefb2014-08-21Martin Nilsson void init_image_wbf(void)
2588311999-10-22Per Hedbor {
226ec82005-01-23Martin Nilsson  ADD_FUNCTION( "encode", image_f_wbf_encode, tFunc(tObj tOr(tVoid,tMapping),tStr), 0); ADD_FUNCTION( "_encode", image_f_wbf_encode, tFunc(tObj tOr(tVoid,tMapping),tStr), 0); ADD_FUNCTION( "decode", image_f_wbf_decode, tFunc(tStr,tObj), 0); ADD_FUNCTION( "_decode", image_f_wbf__decode, tFunc(tStr,tMapping), 0); ADD_FUNCTION( "decode_header", image_f_wbf_decode_header, tFunc(tStr,tMapping), 0);
2588311999-10-22Per Hedbor }
c9eefb2014-08-21Martin Nilsson void exit_image_wbf(void)
2588311999-10-22Per Hedbor { }