Image Files with Dyalog

lena_colour.jpeg

lena_neg.gif

As presented in Vector 23:1 here's a Dyalog workspace that offers easy access to an image toolbox. It hooks into the open source image library OpenIL and exposes some of the functionality offered.

In order to use the code in the workspace you will also need to download OpenIL itself, which comes in the shape of 3 DLLs.

The code as presented here is in an immediately deployable state. It does however not cover all that OpenIL has to offer but more "flowers from the OpenIL bouquet" can easily be added and I expect to do so irregularly.

The workspace contains only one namespace, which holds all code required. To get started have a look at the variable 'readme' in that namespace.

Zipped Dyalog v9 workspace: OpenIL.zip

OpenIL downloads: OpenIL

Latest changes

Recently the originator of OpenIL, Denton, has come back in the driver's seat. New versions of DevIL have been released, which are compiled with newer C compilers. They cause me some trouble loading, apparently some run-time modules are missing. I am trying to figure out what those are. Versions before 1.7 still work without problems.


Opening the Developer's Image Library

Encouraged by Vector's editor I here present the code in the Dyalog v9 workspace that went with the original article in Vector.

Interface code

Originally inspired by an article by Morten Kromberg I structured the interface to DevIL to an OO object look-alike. The following functions (until the next headline) all come from the namespace, which in my workspace is called 'DevIL'. The first function listed, the "constructor" New, creates another namespace that holds all the name associations and other "methods" that the object offers.

 REF←New;dummy
⍝ Creates new DevIL-object (basically a link to an instance of DevIL)
⍝ Returns a reference to a namespace
 REF←⎕NS'Load' 'Resize' 'GetFormat' 'GetType' 'GetSize' 'GetBits' 'GetCMap' 'GetCBits' 'DecodeRGB' 'EncodeRGB' 'Set' 'Save' 'ErrorHandler'

 REF.⎕NA'DevIL|ilInit'  ⍝ DLL with basic functionality
 REF.⎕NA'ILU|iluInit'   ⍝ DLL with additional stuff, e.g. resizing

⍝ ILvoid ilGenImages(ILsizei Num, ILuint *Images);
⍝ Use ilGenImages to generate image "names"
 REF.⎕NA'DevIL|ilGenImages I4 >U4[]'
⍝ ILvoid ilBindImage(ILuint Image);
⍝ ilBindImage binds the current image to the image described by the image "name" in Image.
 REF.⎕NA'DevIL|ilBindImage U4'
⍝ ILvoid ilDeleteImages(ILsizei Num, ILuint *Images);
⍝ ilDeleteImages deletes image "names"
 REF.⎕NA'DevIL|ilDeleteImages I4 <U4[]'

⍝ ILboolean ilLoadImage(const char *FileName);
 REF.⎕NA'I DevIL|ilLoadImage <0T[]'
⍝ ILboolean ilLoad(ILenum Type, const char *FileName);
⍝ If IL_TYPE_UNKNOWN is specified for Type, ilLoad behaves exactly like ilLoadImage.
 REF.⎕NA'I DevIL|ilLoad U4 <0T[]'

⍝ ILboolean ilSaveImage(const char *FileName);
 REF.⎕NA'I DevIL|ilSaveImage <0T[]'
⍝ ILboolean ilSave(ILenum Type, const char *FileName);
 REF.⎕NA'I DevIL|ilSave U4 <0T[]'

⍝ ILuint ilCopyPixels(
⍝  ILuint XOff, ILuint YOff, ILuint ZOff,
⍝  ILuint Width, ILuint Height, ILuint Depth,
⍝  ILenum Format, ILenum Type, ILvoid *Data);
 REF.⎕NA'I DevIL|ilCopyPixels U U U U U U U4 U4 >U1[]'

⍝ ILboolean ilTexImage(ILuint Width, ILuint Height, ILuint Depth,
⍝                      ILubyte Bpp, ILenum Format, ILenum Type, ILvoid *Data);
 REF.⎕NA'I DevIL|ilTexImage U4 U4 U4 U1 U4 U4 <U1[]'

⍝ ILuintY ilGetInteger(ILenum Mode);
 REF.⎕NA'I DevIL|ilGetInteger U4'
⍝ ILvoid  ilSetInteger(ILenum Mode, ILint Param);
 REF.⎕NA'DevIL|ilSetInteger U4 U4'

⍝ File format-specific constants
⍝#define IL_TGA_CREATE_STAMP        0x0710
⍝#define IL_JPG_QUALITY             0x0711 = 1809
⍝#define IL_PNG_INTERLACE           0x0712
⍝#define IL_TGA_RLE                 0x0713
⍝#define IL_BMP_RLE                 0x0714
⍝#define IL_SGI_RLE                 0x0715
⍝#define IL_TGA_ID_STRING           0x0717
⍝#define IL_TGA_AUTHNAME_STRING     0x0718
⍝#define IL_TGA_AUTHCOMMENT_STRING  0x0719
⍝#define IL_PNG_AUTHNAME_STRING     0x071A
⍝#define IL_PNG_TITLE_STRING        0x071B
⍝#define IL_PNG_DESCRIPTION_STRING  0x071C
⍝#define IL_TIF_DESCRIPTION_STRING  0x071D
⍝#define IL_TIF_HOSTCOMPUTER_STRING 0x071E
⍝#define IL_TIF_DOCUMENTNAME_STRING 0x071F
⍝#define IL_TIF_AUTHNAME_STRING     0x0720
⍝#define IL_JPG_SAVE_FORMAT         0x0721
⍝#define IL_CHEAD_HEADER_STRING     0x0722
⍝#define IL_PCD_PICNUM              0x0723

⍝ Image-specific constants
⍝#define IL_IMAGE_WIDTH           0x0DE4   = 3556
⍝#define IL_IMAGE_HEIGHT          0x0DE5   = 3557
⍝#define IL_IMAGE_DEPTH           0x0DE6   = 3558
⍝#define IL_IMAGE_SIZE_OF_DATA    0x0DE7   = 3559
⍝#define IL_IMAGE_BPP             0x0DE8   = 3560
⍝#define IL_IMAGE_BYTES_PER_PIXEL 0x0DE8
⍝#define IL_IMAGE_BITS_PER_PIXEL  0x0DE9   = 3561
⍝#define IL_IMAGE_FORMAT          0x0DEA   = 3562
⍝#define IL_IMAGE_TYPE            0x0DEB   = 3563

⍝#define IL_PALETTE_TYPE          0x0DEC   = 3564
⍝#define IL_PALETTE_SIZE          0x0DED   = 3565
⍝#define IL_PALETTE_BPP           0x0DEE   = 3566
⍝#define IL_PALETTE_NUM_COLS      0x0DEF   = 3567
⍝#define IL_PALETTE_BASE_TYPE     0x0DF0   = 3568

⍝ Image format constants
⍝#define IL_COLOUR_INDEX    0x1900  = 6400
⍝#define IL_RGB             0x1907  = 6407
⍝#define IL_RGBA            0x1908  = 6408
⍝#define IL_BGR             0x80E0  = 32992
⍝#define IL_BGRA            0x80E1  = 32993
⍝#define IL_LUMINANCE       0x1909  = 6409 (default/reported when error)
⍝#define IL_LUMINANCE_ALPHA 0x190A  = 6410

⍝ Image types/pixel component size constants
⍝#define IL_BYTE            0x1400  = 5120
⍝#define IL_UNSIGNED_BYTE   0x1401  = 5121
⍝#define IL_SHORT           0x1402  = 5122
⍝#define IL_UNSIGNED_SHORT  0x1403  = 5123
⍝#define IL_INT             0x1404  = 5124
⍝#define IL_UNSIGNED_INT    0x1405  = 5125
⍝#define IL_FLOAT           0x1406  = 5126
⍝#define IL_DOUBLE          0x140A  = 5130

⍝ Palette type contants
⍝#define IL_PAL_NONE        0x0400  = 1024
⍝#define IL_PAL_RGB24       0x0401  = 1025
⍝#define IL_PAL_RGB32       0x0402  = 1026
⍝#define IL_PAL_RGBA32      0x0403  = 1027
⍝#define IL_PAL_BGR24       0x0404  = 1028
⍝#define IL_PAL_BGR32       0x0405  = 1029
⍝#define IL_PAL_BGRA32      0x0406  = 1030

⍝ Animation constants
⍝#define IL_NUM_IMAGES      0x0DF1  = 3569
⍝#define IL_ACTIVE_IMAGE    0x0DF4  = 3572
⍝#define IL_IMAGE_DURATION  0x0DF8  = 3576
⍝ ILboolean ilActiveImage(ILuint ImageNum);
⍝ ActiveImage is functionally equivalent to ActiveMipmap, except that it deals with animations and not mipmaps
⍝ ActiveImage can ONLY step forwards, the image specified always relative to the current
 REF.⎕NA'I DevIL|ilActiveImage U4'

⍝ ILubyte* ilGetPalette(ILvoid);
 REF.⎕NA'I DevIL|ilGetPalette' ⍝ All DevIL returns is a measly pointer! MEMCPY the only way to go, *sigh*
 'CopyPalette'REF.⎕NA'dyalog32|MEMCPY >U1[] I4 U4' ⍝ So, MEMCPY the palette into the session

⍝ ILboolean ilConvertImage(ILenum DestFormat, ILenum DestType);
 REF.⎕NA'I DevIL|ilConvertImage U4 U4'  ⍝ See above for enums

⍝ ILboolean ilConvertPal(ILenum DestFormat);
 REF.⎕NA'I DevIL|ilConvertPal U4'       ⍝ See above for palette type constants

⍝ ILboolean iluNegative(ILvoid);
 REF.⎕NA'I ILU|iluNegative'

⍝ ILboolean iluGammaCorrect(ILfloat Gamma);
 REF.⎕NA'I ILU|iluGammaCorrect F4'

⍝ ILvoid iluGetImageInfo(ILinfo *Info);
 REF.⎕NA'ILU|iluGetImageInfo >0T[]'

⍝ ILvoid iluImageParameter(ILenum PName, ILenum Param);
 REF.⎕NA'ILU|iluImageParameter U4 U4'
⍝ Parameters:
⍝#define ILU_FILTER         0x2600  = 9728
⍝ Filters/methods for resizing:
⍝#define ILU_NEAREST        0x2601  = 9729 \
⍝#define ILU_LINEAR         0x2602  = 9730  | OK for all images, also indexed
⍝#define ILU_BILINEAR       0x2603  = 9731 /
⍝#define ILU_SCALE_BOX      0x2604  = 9732 \
⍝#define ILU_SCALE_TRIANGLE 0x2605  = 9733  |
⍝#define ILU_SCALE_BELL     0x2606  = 9734  | Works only for RGB images
⍝#define ILU_SCALE_BSPLINE  0x2607  = 9735  |
⍝#define ILU_SCALE_LANCZOS3 0x2608  = 9736  |
⍝#define ILU_SCALE_MITCHELL 0x2609  = 9737 /

⍝ ILboolean iluScale(ILuint Width, ILuint Height, ILuint Depth);
 REF.⎕NA'I ILU|iluScale U4 U4 U4'

⍝ ILboolean iluRotate(ILfloat Angle);
 REF.⎕NA'I ILU|iluRotate F4'            ⍝ Rotate degrees anti-clockwise

⍝ ILenum ilGetError(ILvoid);
 REF.⎕NA'U4 DevIL|ilGetError'

⍝ const char* iluErrorString(ILenum Error);
 REF.⎕NA'I ILU|iluErrorString U4'
 'CopyErrorMessage'REF.⎕NA'dyalog32|STRNCPY >0T[] I4 U4'

 dummy←REF.ilInit
 dummy←REF.iluInit
 REF.jpegQuality←REF.ilGetInteger 1809  ⍝ Used when saving image in JPEG format

 pxl←EncodeRGB rgb
⍝ Encodes a 3D matrix with RGB pixel data for use by Cbits
 pxl←0 0⍴0
 →(~3∊⍴rgb)/0 ⍝ Make sure that at least one axis has length 3
 rgb←2 3 1⍉rgb
 pxl←256⊥rgb

 rgb←DecodeRGB pxl
⍝ Decodes a 2D Cbits matrix to 3D RGB pixel component data
 rgb←3 1 2⍉256 256 256⊤pxl ⍝ This gives the proper ravel order

 ErrorHandler ok;err;err_msg
⍝ Checks for errors returned from calls to DevIL
⍝ Possible error codes are in the range 0x0501-0x05FF
⍝ whereas for Dyalog user-defined errors are 500-999
 →(0≠ok)/0
 err←ilGetError
 :If 0≠err
     err_msg←CopyErrorMessage 100(iluErrorString err)100
     err_msg ⎕SIGNAL err-780 ⍝ "Lower" the error code into the range of Dyalog user-defined errors
 :EndIf

 size←GetSize
⍝ Returns size of image in DevIL

 size←ilGetInteger¨3557 3556 ⍝ Image height and width (pixels)
⍝ #define IL_IMAGE_WIDTH           0x0DE4  = 3556 (pixels)
⍝ #define IL_IMAGE_HEIGHT          0x0DE5  = 3557 (pixels)
⍝ #define IL_IMAGE_DEPTH           0x0DE6  = 3558 (pixels)
⍝ #define IL_IMAGE_SIZE_OF_DATA    0x0DE7  = 3559 (bytes)
⍝ #define IL_IMAGE_BPP             0x0DE8  = 3560
⍝ #define IL_IMAGE_BYTES_PER_PIXEL 0x0DE8
⍝ #define IL_IMAGE_BITS_PER_PIXEL  0x0DE9  = 3561

 type←GetType;stype
⍝ Returns type (pixel component size) of image ALREADY LOADED in DevIL

⍝#define IL_IMAGE_TYPE         0x0DEB  = 3563
 type←ilGetInteger 3563 ⍝ Image type/pixel component size
 :Select type
 :Case 5120     ⍝#define IL_BYTE            0x1400  = 5120
     stype←'Byte'
 :Case 5121     ⍝#define IL_UNSIGNED_BYTE   0x1401  = 5121
     stype←'Unsigned Byte'
 :Case 5122     ⍝#define IL_SHORT           0x1402  = 5122
     stype←'Short'
 :Case 5123     ⍝#define IL_UNSIGNED_SHORT  0x1403  = 5123
     stype←'Unsigned short'
 :Case 5124     ⍝#define IL_INT             0x1404  = 5124
     stype←'Integer'
 :Case 5125     ⍝#define IL_UNSIGNED_INT    0x1405  = 5125
     stype←'Unsigned integer'
 :Case 5126     ⍝#define IL_FLOAT           0x1406  = 5126
     stype←'Float'
 :Case 5130     ⍝#define IL_DOUBLE          0x140A  = 5130
     stype←'Double'
 :EndSelect

 format←GetFormat;sformat
⍝ Returns format of image ALREADY LOADED in DevIL, e.g. by Load

 format←ilGetInteger 3562 ⍝ Image format
 :Select format
 :Case 6400     ⍝#define IL_COLOUR_INDEX    0x1900  = 6400
     sformat←'Indexed/palette'
 :Case 6407     ⍝#define IL_RGB             0x1907  = 6407
     sformat←'RGB'
 :Case 6408     ⍝#define IL_RGBA            0x1908  = 6408
     sformat←'RGB+⍺'
 :Case 32992    ⍝#define IL_BGR             0x80E0  = 32992
     sformat←'BGR'
 :Case 32993    ⍝#define IL_BGRA            0x80E1  = 32993
     sformat←'BGR+⍺'
 :Case 6409     ⍝#define IL_LUMINANCE       0x1909  = 6409 (default/reported when error)
     sformat←'Luminance'
 :Case 6410     ⍝#define IL_LUMINANCE_ALPHA 0x190A  = 6410
     sformat←'Luminance+⍺'
 :EndSelect

 cmap←GetCMap;dummy;format;img;PAL
⍝ Retreives palette from image in DevIL
 cmap←0 3⍴0

 format←GetFormat
 :If format≠6400
 ⍝ No palette to return
     →0
 :EndIf

 'PAL'⎕NS''
 PAL.type←ilGetInteger 3564   ⍝#define IL_PALETTE_TYPE       0x0DEC  = 3564
 PAL.size←ilGetInteger 3565   ⍝#define IL_PALETTE_SIZE       0x0DED  = 3565
 PAL.bpp←ilGetInteger 3566    ⍝#define IL_PALETTE_BPP        0x0DEE  = 3566 "bytes per pixel"
 PAL.ncols←ilGetInteger 3567  ⍝#define IL_PALETTE_NUM_COLS   0x0DEF  = 3567
 PAL.btype←ilGetInteger 3568  ⍝#define IL_PALETTE_BASE_TYPE  0x0DF0  = 3568 ???
 PAL.nbytes←PAL.bpp×PAL.ncols ⍝ Number of bytes in Cmap
 PAL.handle←ilGetPalette      ⍝ All DevIL gives us is a measly pointer!
 cmap←CopyPalette PAL.nbytes PAL.handle PAL.nbytes
 :Select PAL.type
 :Case 1024
   ⍝#define IL_PAL_NONE           0x0400  = 1024
                                ⍝ Huh? No palette? That's surely a mistake!
 :CaseList 1025 1028            ⍝ 24-bits palette
   ⍝#define IL_PAL_RGB24          0x0401  = 1025
   ⍝#define IL_PAL_BGR24          0x0404  = 1028
     cmap←PAL.ncols 3⍴cmap
 :CaseList 1027 1030            ⍝ 32-bits palette with ⍺-channel
   ⍝#define IL_PAL_RGBA32         0x0403  = 1027
   ⍝#define IL_PAL_BGRA32         0x0406  = 1030
     cmap←3↑[2]PAL.ncols 4⍴cmap ⍝ For now, just discard the ⍺-channel
 :CaseList 1026 1029
   ⍝#define IL_PAL_RGB32          0x0402  = 1026
   ⍝#define IL_PAL_BGR32          0x0405  = 1029
     ⍝ 32-bit colours... Don't know the format of those
     ⍝ Must somehow be converted to 24-bits in order for Dyalog to accept them
 :EndSelect
 :If PAL.type∊1028 1030 ⋄ cmap←⌽cmap ⋄ :EndIf ⍝ Convert BGR to RGB

 r←GetBits;depth;err;format;height;img;nbytes;ntype;size;type;width
⍝ Retreives pixels from image in DevIL
⍝ Returns: pixel matrix
 width←ilGetInteger 3556  ⍝ Image width (pixels)
 height←ilGetInteger 3557 ⍝ Image height (pixels)
 depth←ilGetInteger 3558  ⍝ Image depth (pixels)
 format←GetFormat         ⍝ Image format
 type←ilGetInteger 3563   ⍝ Image type/pixel component size
 nbytes←ilGetInteger 3559 ⍝ Size of bulk data
 size←height width

 err img←ilCopyPixels 0 0 0 width height depth format type nbytes
 ErrorHandler err

 :Select format       ⍝ Image format
 :CaseList 6400 6409  ⍝ Indexed or grey scale (luminance) image
     img←size⍴img     ⍝ Put some structure to the bulky data
 :CaseList 6407 32992 ⍝ RGB or BGR
     img←(size,3)⍴img ⍝ Put some structure to this bulky data
     :If format∊32992 32993
         img←⌽img     ⍝ Convert BGR to RGB
     :EndIf
     img←2 3 1⍉img    ⍝ Restructure for "encode"
     img←256⊥img
 :Case 6410           ⍝ Grey scale with ¸-channel
     img←(size,2)⍴img ⍝ Put some structure to the bulky data
     img←⊃¨1↓[3]img   ⍝ For now, just discard the ¸-channel
 :CaseList 6408 32993 ⍝ RGB or BGR, with ⍺-channel
     img←(size,4)⍴img ⍝ Put some structure to this bulky data
     img←3↑[3]img     ⍝ For now, just discard the ⍺-channel
     :If format∊32992 32993
         img←⌽img     ⍝ Convert BGR to RGB
     :EndIf
     img←2 3 1⍉img    ⍝ Restructure for "encode"
     img←256⊥img
 :EndSelect
 r←img

 r←GetCBits;format;palette;pixels
⍝ Just like GetBits but converts any "paletted" image to RGB
 pixels←GetBits

 format←GetFormat              ⍝ Image format
 :Select format
 :Case 6400     ⍝#define IL_COLOUR_INDEX  0x1900  = 6400
     palette←GetCMap           ⍝ Acquire palette
     pixels←palette[1+pixels;] ⍝ Convert to cbits
     pixels←EncodeRGB pixels
 :Case 6409     ⍝#define IL_LUMINANCE     0x1909  = 6409 (default/reported when error)
     palette←3/,[1.5]¯1+⍳256   ⍝ Generate a grey scale palette
     pixels←palette[1+pixels;] ⍝ Convert to cbits
     pixels←EncodeRGB pixels
 :EndSelect
 r←pixels

 Set data;bpp;bits;cmap;err;fmt;max;type
⍝ Sets up an image in DevIL
⍝ Takes: bits or bits+cmap

⍝ So far supports following formats:
⍝ B/W: byte, short/word
⍝ Colour: 256/palette, 3-byte RGB
⍝ So far no support for: 4-byte RGB, ⍺-channel in general

 :Select ≡data
 :Case 1
     bits←data ⋄ cmap←0 3⍴0
 :Case 2
     bits cmap←data
 :EndSelect

 max←⌈/⌈/bits
 bpp←+/0 255 32767<max
 type←(1+max>255)⊃5121 5123 ⍝ IL_UNSIGNED_BYTE IL_UNSIGNED_SHORT

 :If 0=⊃⍴cmap
 ⍝ No palette -> pixels are B/W or RGB
     :If max≤32767 ⍝ 8-bits and 16-bits are considered B/W
         fmt←6409  ⍝ IL_LUMINANCE
     :Else
         ⍝ 24-bits is considered RGB
         fmt←6407  ⍝ IL_RGB
         type←5121 ⍝ IL_UNSIGNED_BYTE
         bits←DecodeRGB bits ⍝ Split the Cbits in RGB components
     :EndIf
 :Else
 ⍝ Palette -> check that indices are within range
     fmt←6400      ⍝ IL_COLOUR_INDEX
     'bits out of cmap range'⎕SIGNAL(max>¯1+⊃⍴cmap)/3
 :EndIf

 ⍝ Now, copy pixel values to DevIL
 →(~type∊5120 5121)/0 ⍝ So far only byte-sized components are supported
 ⍝ ILboolean ilTexImage(ILuint Width, ILuint Height, ILuint Depth, ILubyte Bpp, ILenum Format, ILenum Type, ILvoid *Data);
 ErrorHandler ilTexImage(⌽2↑⍴bits),1 bpp fmt type((,⊖bits),,cmap)
 ⍝ilSetData
 ⍝ilSetPixels

 Load fn
⍝ Loads an image from file fn into instance of DevIL
⍝ Does NOT load image into ws but keeps it in the DLL

 ErrorHandler ilLoadImage⊂fn

 'PAL'⎕NS'' ⋄ PAL.type←1024       ⍝ Default empty palette
 :If GetFormat=6400               ⍝ Indexed image
     PAL.type←ilGetInteger 3564   ⍝ Palette type
     PAL.bpp←ilGetInteger 3566    ⍝ Palette "bytes per pixel"
     PAL.ncols←ilGetInteger 3567  ⍝ Palette number of cols
     PAL.nbytes←PAL.bpp×PAL.ncols ⍝ Number of bytes in "Cmap"
 :EndIf
 :Select PAL.type
 :Case 1024     ⍝#define IL_PAL_NONE   0x0400  = 1024
     PAL.stype←'None'
 :Case 1025     ⍝#define IL_PAL_RGB24  0x0401  = 1025
     PAL.stype←'RGB24'
 :Case 1026     ⍝#define IL_PAL_RGB32  0x0402  = 1026
     PAL.stype←'RGB32'
 :Case 1027     ⍝#define IL_PAL_RGBA32 0x0403  = 1027
     PAL.stype←'RGB⍺32'
 :Case 1028     ⍝#define IL_PAL_BGR24  0x0404  = 1028
     PAL.stype←'BGR24'
 :Case 1029     ⍝#define IL_PAL_BGR32  0x0405  = 1029
     PAL.stype←'BGR32'
 :Case 1030     ⍝#define IL_PAL_BGRA32 0x0406  = 1030
     PAL.stype←'BGR⍺32'
 :EndSelect

 {type_ext}Save fn;_
⍝ Saves image, which is currently in DLL
⍝ DLL determines image file format from file name extension
⍝ If type_ext is supplied then it overrules the file name extension

 _←ilSetInteger 1809(⌊jpegQuality) ⍝ Transfer JPEG image file format quality to DLL
 :If 0=⎕NC'type_ext'
    ⍝ ILboolean ilSaveImage(const char *FileName);
     ErrorHandler ilSaveImage⊂fn ⍝ Determines image format from the file extension
 :Else
⍝                  BMP  JPEG PCX  PSD  PNG  PNM  SGI  TGA  TIFF C-h  Raw  DDS  Jasc-pal
     :If ~type_ext∊1056 1061 1064 1065 1066 1067 1068 1069 1070 1071 1072 1079 1141
         type_ext←0 ⍝ Unknown type
     :EndIf
    ⍝ ILboolean ilSave(ILenum Type, const char *FileName);
     ErrorHandler ilSave type_ext fn
 :EndIf

⍝ Image file formats valid for saving:
⍝#define IL_TYPE_UNKNOWN 0x0000
⍝#define IL_BMP       0x0420  = 1056  Windows & OS/2 bitmap
⍝#define IL_CHEAD     0x042F  = 1071  C-style header (.h)
⍝#define IL_DDS       0x0437  = 1079  DirectX DirectDraw Surface
⍝#define IL_JPG       0x0425  = 1061
⍝#define IL_PCX       0x0428  = 1064  ZSoft PCX
⍝#define IL_PNG       0x042A  = 1066  Portable Network Graphics
⍝#define IL_PNM       0x042B  = 1067  Portable AnyMap (.pbm, .pgm or .ppm)
⍝#define IL_PSD       0x0439  = 1065  Adobe Photo Shop
⍝#define IL_RAW       0x0430  = 1072  Raw dump of image data
⍝#define IL_SGI       0x042C  = 1068  Silicon Graphics IRIS Graphic File (.bw, .rgb, .rgba or .sgi)
⍝#define IL_TGA       0x042D  = 1069  TrueVision Targa
⍝#define IL_TIF       0x042E  = 1070  Tagged image file format
⍝#define IL_JASC_PAL  0x0475  = 1141 Paint Shop Pro (Jasc) palette (.pal)

⍝ The following not mentioned in the manual as valid for saving:
⍝#define IL_CUT       0x0421
⍝#define IL_DOOM      0x0422
⍝#define IL_DOOM_FLAT 0x0423
⍝#define IL_ICO       0x0424
⍝#define IL_JFIF      0x0425
⍝#define IL_LBM       0x0426
⍝#define IL_PCD       0x0427
⍝#define IL_PIC       0x0429
⍝#define IL_MDL       0x0431
⍝#define IL_WAL       0x0432
⍝#define IL_LIF       0x0434
⍝#define IL_MNG       0x0435
⍝#define IL_JNG       0x0435
⍝#define IL_GIF       0x0436
⍝#define IL_DCX       0x0438
⍝#define IL_EXIF      0x043A
⍝#define IL_PSP       0x043B
⍝#define IL_PIX       0x043C
⍝#define IL_PXR       0x043D
⍝#define IL_XPM       0x043E

 {method}Resize new_size;arg;dummy;err;method
⍝ Resizes an image that has ALREADY BEEN LOADED into DevIL, using e.g. Load
⍝ new_size: Vector with 2 or 3 elements, specifying the new size (z×)y×x
⍝ method: Filter to use when performing the resizing (see the DevIL manual)

⍝ If DevIL refuses to do a scaling, returning "illegal operation"
⍝ try first converting the image to RGB, calling ilConvertImage

⍝ Check validity of argument
 :If 0=⎕NC'method' ⋄ method←9731 ⋄ :EndIf
 :If ~method∊9729 9730 9731 9732 9733 9734 9735 9736 9737
     method←9731
 :EndIf

⍝ Resizing with filters ≥9732 requires image to be in RGB format
 :If method≥9732
 :AndIf 6400=GetFormat
⍝#define IL_COLOUR_INDEX    0x1900  = 6400
     'selected method/filter can resize full colour images only, call e.g. ilConvertImage 6407 GetType'⎕SIGNAL 11
 :EndIf

⍝ First set the method to use for the resizing. The fancy ones don't work with indexed images...
 dummy←iluImageParameter 9728 method
⍝#define ILU_FILTER         0x2600  = 9728
⍝ Filters/methods for resizing:
⍝#define ILU_NEAREST        0x2601  = 9729 \
⍝#define ILU_LINEAR         0x2602  = 9730  | OK for all images, also indexed
⍝#define ILU_BILINEAR       0x2603  = 9731 /
⍝#define ILU_SCALE_BOX      0x2604  = 9732 \
⍝#define ILU_SCALE_TRIANGLE 0x2605  = 9733  |
⍝#define ILU_SCALE_BELL     0x2606  = 9734  | Work only on RGB images
⍝#define ILU_SCALE_BSPLINE  0x2607  = 9735  |
⍝#define ILU_SCALE_LANCZOS3 0x2608  = 9736  |
⍝#define ILU_SCALE_MITCHELL 0x2609  = 9737 /

 ErrorHandler iluScale 3↑(⌽new_size),1 1 1 ⍝ DON'T suppress any axes! It makes DevIL unstable!

Getting started

The following two functions don't go in the DevIL namespace; they just make use of it:

 data←FileRead fn;bits;cmap;IMG
⍝ Calls DevIL to load image from file fn
⍝ Returns: bits, cmap (no rows in case of RGB)

⍝ Supported image file types:
⍝ BMP CUT DCX DDS EXIF GIF ICO JNG JPEG LBM LIF LMP/Doom MDL/Half Life
⍝ MNG PBM PCD PCX PGM PIC PIX PNG PNM PPM PSD PSP PXR RAW SGI TGA TIFF WAL XPM

 bits←0 0⍴0 ⋄ cmap←0 3⍴0
 IMG←DevIL.New
 IMG.Load fn
 bits←IMG.GetBits
 →(2≠⍴⍴bits)/exit
 cmap←IMG.GetCMap
exit:
 data←bits cmap

 data FileWrite fn;bits;cmap;IMG
⍝ Calls DevIL to save image to file fn
⍝ Takes: bits, cmap

⍝ Supported image file types:
⍝ BMP DDS h/C-header JPG PAL/Jasc PBM PCX PGM PNG PNM PPM PSD RAW SGI TGA TIFF

 'invalid argument'⎕SIGNAL(2≠⊃⍴data)/5
 bits cmap←data
 'bits is not 2D'⎕SIGNAL(2≠⍴⍴bits)/4
 'cmap is not 2D'⎕SIGNAL(2≠⍴⍴cmap)/4

 IMG←DevIL.New
 IMG.Set bits cmap
 IMG.Save fn

This DevIL "object" can now be used to read and write image files to and from the APL workspace.

 bits cmap←#.DevIL.FileRead filename
 bits cmap #.DevIL.FileWrite filename

Deploying DevIL

The DevIL is perfectly capable of keeping and handling the image data itself, which the "object approach" makes very easy.

Image data in and out

An image is accessed in the following way

 IMG←#.DevIL.New
 IMG.Load 'photo.jpg'
 bits←IMG.GetBits
 cmap←IMG.GetCMap
 cbits←IMG.GetCBits
 size←IMG.GetSize

Manipulation of image data

An image can be resized at will – specifying the method is optional:
 IMG.Resize 200 320
... rotated...
 IMG.iluRotate ¯5.5
... gamma corrected...
 IMG.iluGammaCorrect 1.6
... inverted...
 IMG.iluNegative
... pixelised...
 IMG.iluPixelize 3
... as well as many other things. See the New function and the DevIL manual.

Current work

DevIL can also handle animations but so far I have only been able to read and create animations. Saving them seems to be a problem since DevIL supports saving in neither of the obvious formats GIF and MNG.

 cbits←AnimGet filename;dur;i;iImg;IMG;n;scrap
⍝ Loads an animated (GIF) file and extracts the individual bitmaps

 IMG←DevIL.New
⍝ The documentation strongly recommends binding any to-be-worked-with image.
⍝ The DevIL "window" normally is pointed to image slot #1 by default,
⍝ but just in case it isn't, a new slot number is generated and used:
 iImg←IMG.ilGenImages 1 1   ⍝ Generate a new image "name" (slot number, really) in the DLL
 scrap←IMG.ilBindImage iImg ⍝ Point the DevIL "window" to the image slot with the new "name"

 IMG.Load filename          ⍝ Load the animated image (GIF or MNG)
 cbits←,[2.5]IMG.GetCBits   ⍝ Get the 1st subimage
 dur←IMG.ilGetInteger 3576  ⍝ Get the duration of this 1st subimage
 n←IMG.ilGetInteger 3569    ⍝ Get the number of additional subimages in the animation

 :For i :In ⍳n              ⍝ ilActiveImage uses index origin 0
     scrap←IMG.ilBindImage iImg           ⍝ Manual says "specify 0 for ImageNum to return to the base image" but this errs
     IMG.ErrorHandler IMG.ilActiveImage i ⍝ Notice how the subimage number is counted from subimage #0
     cbits,←IMG.GetCBits
     dur,←IMG.ilGetInteger 3576 ⍝ IL_IMAGE_DURATION
 :EndFor
 ⎕←'extracted ',(⍕3⊃⍴cbits),' images'
 ⎕←'matching the first bitmap against all other:'
 ⎕←(⊂cbits[;;1])≡¨⊂[1 2]cbits

 img AnimPut filename;bits;dur;i;iImg;IMG;n;scrap
⍝ Builds an animated image, but what file formats can be used to save it?
⍝ img: CBits, animation along 3rd axis

 IMG←DevIL.New
⍝ iImg←IMG.ilGenImages 1 1
⍝ scrap←IMG.ilBindImage iImg
 iImg←IMG.ilGetInteger 3575          ⍝ IL_CUR_IMAGE
 IMG.Set img[;;1]
 scrap←IMG.ilSetInteger 3576 100     ⍝ IL_IMAGE_DURATION

 :For i :In ⍳¯1+3⊃⍴img
     IMG.ErrorHandler IMG.ilCreateSubImage 1664 i ⍝ IL_SUB_NEXT
     IMG.ErrorHandler IMG.ilActiveImage i ⍝ Changing subimage invalidates the value of IL_NUM_IMAGES (reads as 0, probably because it's "blit'able")
     ⎕←IMG.ilGetInteger 3572              ⍝ ... but IL_ACTIVE_IMAGE gives away our whereabouts
     IMG.Set img[;;1+i]
     scrap←IMG.ilSetInteger 3576 100      ⍝ IL_IMAGE_DURATION
     scrap←IMG.ilBindImage iImg           ⍝ Re-binding the image re-validates the value of IL_NUM_IMAGES
 :EndFor

 ⎕←IMG.ilGetInteger 3569             ⍝ IL_NUM_IMAGES
⍝#define IL_JPG    0x0425  = 1061  support for M-JPEG included?
⍝#define IL_PNG    0x042A  = 1066  support for APNG included?
⍝#define IL_MNG    0x0435  = 1077  Multiple-image Network Graphics
⍝#define IL_GIF    0x0436  = 1078
 IMG.Save filename

ImageFilesWithDyalog (last edited 2011-08-26 13:35:09 by KaiJaeger)