Image Files with Dyalog
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