jpeg_size.anubis 4.37 KB

 *Project*                             The Anubis Project
   
 *Title*                       Getting the size of a 'jpeg' image.
   
 *Copyright*                     Copyright (c) Alain Prouté 2002. 

 *Released*

 *Author*       Alain Prouté
   
   
 
 *Overview*
   The function 'jpeg_size', taking the name of a 'jpeg' (.jpeg or .jpg) image file as its
   argument, returns (maybe) a pair of Word32:
   
                                               (w,h)
   
   where w is the width and h the height of the image in pixels. 
   
   
public define Maybe((Word32,Word32))
  jpeg_size
    (
      String file_name
    ). 
   
   If the result is 'failure',  the file may not exist, or may not be  a jpeg file or some
   other problem arose.
   
   
   
   
   --- That's all for the public part. ---------------------------------------------------
   
   
   
   The  'jpeg'  format   is  rather  complicated  (documentation  may   be  obtained  from
   http://www.w3.org/Graphics/...).   Nevertheless,  size  of  the  image may  be  got  as
   follows:
   
   Any 'jpeg' file contains 'markers'. A marker is a sequence of two bytes, such that:
   
     (1) the first byte is 255 (ff in hexadecimal),
     (2) the second byte is neither 0 nor 255.
   
   The following markers are of interest to us, because they begin so-called 'frames':
   
      ff c0         
      ff c1
      ff c2
      ff c3
      ff c9
      ff ca
      ff cb
   
   The marker  is followed by 3 bytes  which are of no  interest to us, and  4 bytes which
   give the size of the frame. For example:
   
                               ff c0 ?? ?? ?? hh hh ww ww
   
   As  suggested, the  height  is comming  before  the width,  and both  are  coded on  16
   bits. Furthermore, within 'hh  hh' the first byte is the most  significant (the same is
   true for the width).
   
   We do not consider images with several frames, but maybe we should....
   

   
define Word32   
   to_Word32
     (
       Word8 x
     ) =
   word32(word16(x,0),0). 

   Read a dimension (coded on two bytes):
   
define Maybe(Word32)
  read_dimension
    (
      RStream fp
    ) =
  if *fp is 
    {
      failure then failure, 
      success(high) then 
        if *fp is
          {
            failure then failure, 
            success(low) then success((255*to_Word32(high))+to_Word32(low))
          }
    }.
   
   
   Read both dimensions, after the marker has been read:
   
define Maybe((Word32,Word32))
  marker_seen
    (
      RStream fp
    ) =
  // marker has just been read, ignore 3 bytes
  forget(*fp); 
  forget(*fp); 
  forget(*fp); 
  if read_dimension(fp) is 
    {
      failure then failure, 
      success(h) then 
        if read_dimension(fp) is 
          {
            failure then failure, 
            success(w) then success((w,h))
          }
    }. 
   
   

   Find the marker and read the size:
   
define Maybe((Word32,Word32))   
  jpeg_size
    (
      RStream fp
    ) = 
  if *fp is 
    {
      failure then failure,    // end of file
      success(c) then 
        if c = 255 then 
          (
            if *fp is 
              {
                failure then failure, 
                success(d) then
                  if d = 192 /* c0 */ then marker_seen(fp) else
                  if d = 193 /* c1 */ then marker_seen(fp) else
                  if d = 194 /* c2 */ then marker_seen(fp) else
                  if d = 195 /* c3 */ then marker_seen(fp) else
                  if d = 201 /* c9 */ then marker_seen(fp) else
                  if d = 202 /* ca */ then marker_seen(fp) else
                  if d = 203 /* cb */ then marker_seen(fp) else                 
                  jpeg_size(fp)
              }
          )
        else jpeg_size(fp)
    }. 
    
   
   
   Our public tool:
   
public define Maybe((Word32,Word32))
  jpeg_size
    (
      String filename
    ) =
  if (Maybe(RStream)) file(filename,read) is 
    {
      failure then failure,
      success(fp) then jpeg_size(fp)
    }. 
   
   
   
   
   Try this out:
   
global define One
  jpeg_size
    (
      List(String) args
    ) =
  if args is 
    {
      [ ] then print("Usage: jpeg_size <filename>\n"), 
      [h . t] then 
        if jpeg_size(h) is 
          {
            failure then print(h+": error\n"), 
            success(p) then if p is (w,hi) then 
              print(h+": width: "+to_decimal(w)+" height: "+to_decimal(hi)+"\n")
          }
    }.