#include <sys/types.h>
#include "img.h"
#include "img_conv.h"
#include "Image.h"
#include "Icon.h"
#include <gif_lib.h>
#ifdef __cplusplus
extern
"C"
{
#endif
static
char
* gifext[] = {
"gif"
, NULL };
static
char
* gifver[] = {
"gif87a"
,
"gif89a"
, NULL };
static
int
gifbpp[] = {
imbpp1, imbpp1 | imGrayScale,
imbpp4,
imbpp8, imbpp8 | imGrayScale,
0
};
static
char
* loadOutput[] = {
"screenWidth"
,
"screenHeight"
,
"screenColorResolution"
,
"screenBackGroundColor"
,
"screenPalette"
,
"delayTime"
,
"disposalMethod"
,
"userInput"
,
"transparentColorIndex"
,
"comment"
,
"left"
,
"top"
,
"interlaced"
,
"loopCount"
,
NULL
};
static
char
* mime[] = {
"image/gif"
,
NULL
};
static
ImgCodecInfo codec_info = {
"GIFLIB"
,
1, 0,
gifext,
"Graphics Interchange Format"
,
"GIF"
,
gifver,
"Prima::Image::gif"
,
"Prima::Image::gif"
,
IMG_LOAD_FROM_FILE | IMG_LOAD_MULTIFRAME | IMG_LOAD_FROM_STREAM |
IMG_SAVE_TO_FILE | IMG_SAVE_MULTIFRAME | IMG_SAVE_TO_STREAM,
gifbpp,
loadOutput,
mime
};
static
int
img_gif_error_code = 0;
#if defined(GIFLIB_MAJOR) && GIFLIB_MAJOR >= 5
#define GIF_ERROR_ARG ,&img_gif_error_code
#define MakeMapObject GifMakeMapObject
#define FreeMapObject GifFreeMapObject
#if (GIFLIB_MAJOR == 5 && GIFLIB_MINOR >= 1) || (GIFLIB_MAJOR > 5)
#define GIF_ERROR_ARG_51 ,&img_gif_error_code
#else
#define GIF_ERROR_ARG_51
#endif
static
int
EGifPutExtensionFirst(GifFileType * GifFile,
int
ExtCode,
int
ExtLen,
const
void
* Extension) {
int
r = EGifPutExtensionLeader( GifFile, ExtCode );
if
( r != GIF_OK )
return
r;
return
EGifPutExtensionBlock( GifFile, ExtLen, Extension );
}
static
int
EGifPutExtensionLast(GifFileType * GifFile,
int
ExtCode,
int
ExtLen,
const
void
* Extension) {
int
r = EGifPutExtensionBlock( GifFile, ExtLen, Extension );
if
( r != GIF_OK )
return
r;
return
EGifPutExtensionTrailer( GifFile);
}
#else
#define GIF_ERROR_ARG
#define GIF_ERROR_ARG_51
#endif
#define GIF_CALL img_gif_error_code =
#define GIF_CALL_FAILED img_gif_error_code != GIF_OK
static
void
*
init( ImgCodecInfo ** info,
void
* param)
{
*info = &codec_info;
#if defined(GIFLIB_MAJOR)
codec_info.versionMaj = GIFLIB_MAJOR;
codec_info.versionMin = GIFLIB_MINOR;
#else
sscanf
( GIF_LIB_VERSION,
"%*s %d.%d"
, &codec_info.versionMaj, &codec_info.versionMin);
if
(codec_info.versionMaj > 4) EGifSetGifVersion(
"89a"
);
#endif
return
(
void
*)1;
}
typedef
struct
_LoadRec {
GifFileType * gft;
GifRecordType grt;
int
passed;
int
transparent;
} LoadRec;
static
SV *
make_palette_sv( ColorMapObject * pal)
{
AV * av = newAV();
SV * sv = newRV_noinc(( SV *) av);
if
( pal) {
int
i;
GifColorType * c = pal-> Colors;
for
( i = 0; i < pal-> ColorCount; i++) {
av_push( av, newSViv((
int
) c-> Blue));
av_push( av, newSViv((
int
) c-> Green));
av_push( av, newSViv((
int
) c-> Red));
c++;
}
}
return
sv;
}
static
void
copy_palette( PImage i, ColorMapObject * pal)
{
int
j, last_non_null = -1, first_null = -1;
PRGBColor r = i-> palette;
GifColorType * c;
if
( !pal)
return
;
c = pal-> Colors;
memset
( r, 0, 768);
i-> palSize = ( pal-> ColorCount > 256) ? 256 : pal-> ColorCount;
for
( j = 0; j < i-> palSize; j++) {
r-> r = c-> Red;
r-> g = c-> Green;
r-> b = c-> Blue;
if
( r-> r != 0 || r-> g != 0 || r-> b != 0)
last_non_null = j;
else
if
( first_null < 0 && r-> r == 0 && r-> g == 0 && r-> b == 0)
first_null = j;
c++;
r++;
}
i-> palSize = last_non_null + 1;
if
( first_null > last_non_null && i-> palSize < 256) i-> palSize++;
}
static
void
format_error(
int
errorID,
char
* errbuf,
int
line)
{
char
* msg = NULL;
switch
( errorID) {
case
D_GIF_ERR_OPEN_FAILED:
case
E_GIF_ERR_OPEN_FAILED:
msg =
"open failed"
;
break
;
case
D_GIF_ERR_READ_FAILED:
msg =
"read failed"
;
break
;
case
D_GIF_ERR_NOT_GIF_FILE:
msg =
"not a valid GIF file"
;
break
;
case
D_GIF_ERR_NO_SCRN_DSCR:
msg =
"no screen description block in the file"
;
break
;
case
D_GIF_ERR_NO_IMAG_DSCR:
msg =
"no image description block in the file"
;
break
;
case
D_GIF_ERR_NO_COLOR_MAP:
msg =
"no color map in the file"
;
break
;
case
D_GIF_ERR_WRONG_RECORD:
msg =
"wrong record type"
;
break
;
case
D_GIF_ERR_DATA_TOO_BIG:
msg =
"queried data size is too big"
;
break
;
case
D_GIF_ERR_NOT_ENOUGH_MEM:
case
E_GIF_ERR_NOT_ENOUGH_MEM:
msg =
"not enough memory"
;
break
;
case
D_GIF_ERR_CLOSE_FAILED:
case
E_GIF_ERR_CLOSE_FAILED:
msg =
"close failed"
;
break
;
case
D_GIF_ERR_NOT_READABLE:
msg =
"file is not readable"
;
break
;
case
D_GIF_ERR_IMAGE_DEFECT:
msg =
"image defect detected"
;
break
;
case
D_GIF_ERR_EOF_TOO_SOON:
msg =
"unexpected end of file reached"
;
break
;
case
E_GIF_ERR_WRITE_FAILED:
msg =
"write failed"
;
break
;
case
E_GIF_ERR_HAS_SCRN_DSCR:
msg =
"screen descriptor already been set"
;
break
;
case
E_GIF_ERR_HAS_IMAG_DSCR:
msg =
"image descriptor is still active"
;
break
;
case
E_GIF_ERR_NO_COLOR_MAP:
msg =
"no color map specified"
;
break
;
case
E_GIF_ERR_DATA_TOO_BIG:
msg =
"data is too big (exceeds size of the image)"
;
break
;
case
E_GIF_ERR_DISK_IS_FULL:
msg =
"storage capacity exceeded"
;
break
;
case
E_GIF_ERR_NOT_WRITEABLE:
msg =
"file opened not for writing"
;
break
;
}
if
( msg) snprintf( errbuf, 256,
"%s"
, msg);
}
static
int
my_gif_read( GifFileType * gif, GifByteType * buf,
int
size)
{
return
req_read( ( PImgIORequest) (gif-> UserData), size, buf);
}
static
void
*
open_load( PImgCodec instance, PImgLoadFileInstance fi)
{
LoadRec * l =
malloc
(
sizeof
( LoadRec));
HV * profile = fi-> fileProperties;
if
( !l)
return
NULL;
memset
( l, 0,
sizeof
( LoadRec));
GIF_CALL 0;
if
( !( l-> gft = DGifOpen( fi-> req, my_gif_read GIF_ERROR_ARG))) {
free
( l);
return
NULL;
}
fi-> stop =
true
;
l-> passed = -1;
l-> transparent = -1;
if
( fi-> loadExtras) {
pset_i( screenWidth, l-> gft-> SWidth);
pset_i( screenHeight, l-> gft-> SHeight);
pset_i( screenColorResolution, l-> gft-> SColorResolution);
pset_i( screenBackGroundColor, l-> gft-> SBackGroundColor);
pset_sv_noinc( screenPalette, make_palette_sv( l-> gft-> SColorMap));
}
return
l;
}
#pragma pack(1)
typedef
struct
_GIFGraphControlExt {
Byte packedFields;
Byte delayTimeLo;
Byte delayTimeHi;
Byte transparentColorIndex;
} GIFGraphControlExt, *PGIFGraphControlExt;
typedef
struct
_GIFNetscapeLoopExt {
Byte subid;
Byte loopCountLo;
Byte loopCountHi;
} GIFNetscapeLoopExt, *PGIFNetscapeLoopExt;
#pragma pack()
#if defined(GIFLIB_MAJOR) && GIFLIB_MAJOR >= 5
#define out(l) { format_error( l->Error, fi-> errbuf, __LINE__); return false;}
#else
#define out(l) { format_error( img_gif_error_code, fi-> errbuf, __LINE__); return false;}
#endif
#define outc(x){ strlcpy( fi-> errbuf, x, 256); return false;}
#define outcm(dd){ snprintf( fi-> errbuf, 256, "Not enough memory (%d bytes)", dd); return false;}
#define NETSCAPE_EXT_FUNC_CODE 255
static
Bool
load_extension( PImgLoadFileInstance fi,
int
code, Byte * data)
{
LoadRec * l = ( LoadRec *) fi-> instance;
HV * profile = fi-> frameProperties;
switch
( code) {
case
GRAPHICS_EXT_FUNC_CODE: {
PGIFGraphControlExt ext = ( PGIFGraphControlExt) (data + 1);
Byte p = ext-> packedFields;
if
( fi-> loadExtras) {
pset_i( delayTime, ext-> delayTimeLo + ext-> delayTimeHi * 256);
pset_i( disposalMethod, ( p & 0x1c) >> 2);
pset_i( userInput, ( p & 2) ? 1 : 0);
}
if
( p & 1) {
if
( fi-> loadExtras)
pset_i( transparentColorIndex, ext-> transparentColorIndex);
l-> transparent = ext-> transparentColorIndex;
}
break
;
}
case
COMMENT_EXT_FUNC_CODE:
if
( fi-> loadExtras) {
SV *sv, *mainsv = newSVpv((
char
*) data + 1, *data);
pset_sv_noinc( comment, mainsv);
while
( 1) {
if
(( GIF_CALL DGifGetExtensionNext( l-> gft, &data)) != GIF_OK ) out(l->gft);
if
( data == NULL)
break
;
sv = newSVpv((
char
*) data + 1, *data);
sv_catsv( mainsv, sv);
sv_free( sv);
}
break
;
}
case
NETSCAPE_EXT_FUNC_CODE:
if
( *data == 11 &&
memcmp
( data + 1,
"NETSCAPE2.0"
, 11) == 0) {
LoadRec * l = ( LoadRec *) fi-> instance;
if
((GIF_CALL DGifGetExtensionNext( l-> gft, &data)) != GIF_OK) out(l->gft);
if
( data && *data == 3 && fi-> loadExtras) {
PGIFNetscapeLoopExt ext = ( PGIFNetscapeLoopExt) ( data + 1);
pset_i( loopCount, ext-> loopCountLo + 256 * ext-> loopCountHi);
}
}
break
;
}
while
( data) {
if
(( GIF_CALL DGifGetExtensionNext( l-> gft, &data)) != GIF_OK) out(l->gft);
}
return
true
;
}
static
int
interlaceOffs[] = { 0, 4, 2, 1};
static
int
interlaceStep[] = { 8, 8, 4, 2};
static
Bool
load( PImgCodec instance, PImgLoadFileInstance fi)
{
PImage i = ( PImage) fi-> object;
LoadRec * l = ( LoadRec *) fi-> instance;
HV * profile = fi-> frameProperties;
Bool loop =
true
;
if
( fi-> frame <= l-> passed) {
DGifCloseFile( l-> gft GIF_ERROR_ARG_51);
l-> gft = NULL;
if
( req_seek( fi-> req, 0, SEEK_SET)) {
snprintf( fi-> errbuf, 256,
"Can't rewind GIF stream, seek() error:%s"
,
strerror
(req_error( fi-> req)));
return
false
;
}
GIF_CALL 0;
if
( !( l-> gft = DGifOpen( fi-> req, my_gif_read GIF_ERROR_ARG))) out(l->gft);
l-> passed = -1;
l-> transparent = -1;
}
while
( loop) {
GIF_CALL DGifGetRecordType( l-> gft, &l-> grt);
if
( GIF_CALL_FAILED ) {
if
( fi-> frameCount < 0 && l-> passed <= fi-> frame)
fi-> frameCount = l-> passed + 1;
out(l->gft);
}
switch
( l-> grt) {
case
SCREEN_DESC_RECORD_TYPE:
break
;
case
TERMINATE_RECORD_TYPE:
fi-> frameCount = l-> passed + 1;
outc(
"Frame index out of range"
);
case
IMAGE_DESC_RECORD_TYPE:
if
(( GIF_CALL DGifGetImageDesc( l-> gft)) != GIF_OK) out(l->gft);
if
( ++l-> passed != fi-> frame) {
int
sz;
Byte * block = NULL;
if
((GIF_CALL DGifGetCode( l-> gft, &sz, &block)) != GIF_OK) out(l->gft);
while
( block) {
if
(( GIF_CALL DGifGetCodeNext( l-> gft, &block)) != GIF_OK) out(l->gft);
}
break
;
}
{
int
type = l-> gft-> Image.ColorMap ? l-> gft-> Image.ColorMap-> BitsPerPixel :
( l-> gft-> SColorMap ? l-> gft-> SColorMap-> BitsPerPixel : imbpp1);
if
( type != 1) type = ( type <= 4) ? 4 : 8;
i-> self-> create_empty(( Handle) i, 1, 1, type);
}
if
( l-> gft-> Image.ColorMap != NULL)
copy_palette( i, l-> gft-> Image. ColorMap);
else
if
( l-> gft-> SColorMap) {
copy_palette( i, l-> gft-> SColorMap);
if
( fi-> loadExtras) pset_i( useScreenPalette, 1);
}
else
i-> palSize = 0;
if
( fi-> loadExtras) {
pset_i( interlaced, l-> gft-> Image. Interlace ? 1 : 0);
pset_i( left, l-> gft-> Image. Left);
pset_i( top, l-> gft-> Image. Top);
}
if
( fi-> noImageData) {
int
sz;
Byte * block = NULL;
pset_i( width, l-> gft-> Image. Width);
pset_i( height, l-> gft-> Image. Height);
if
(( GIF_CALL DGifGetCode( l-> gft, &sz, &block)) != GIF_OK) out(l->gft);
while
( block) {
if
(( GIF_CALL DGifGetCodeNext( l-> gft, &block)) != GIF_OK) out(l->gft);
}
}
else
{
GifPixelType * data;
int
ls =
sizeof
( GifPixelType) * l-> gft-> Image. Width, ps = i-> palSize;
i-> self-> create_empty(( Handle) i,
l-> gft-> Image. Width, l-> gft-> Image. Height, i-> type);
i-> palSize = ps;
data = ( GifPixelType *)
malloc
( ls * i-> h);
if
( !data) outcm( ls * i-> h);
GIF_CALL DGifGetLine( l-> gft, data, i-> w * i-> h);
if
( GIF_CALL_FAILED ) {
fi-> wasTruncated = 1;
if
( fi-> noIncomplete ) {
free
( data);
out(l->gft);
}
loop =
false
;
}
{
int
j, k, pass = 0, idst = 0;
Byte * src = data, *dst = i-> data;
for
( j = 0; j < i-> h; j++) {
dst = i-> data + ( i-> h - 1 - idst) * i-> lineSize;
for
( k = 0; k < i-> w; k++)
if
( src[k] >= i-> palSize) i-> palSize = src[k] + 1;
if
( l-> gft-> Image. Interlace) {
idst += interlaceStep[ pass];
if
( idst >= i-> h && pass < 3)
idst = interlaceOffs[ ++pass];
}
else
idst++;
switch
( i-> type & imBPP) {
case
imbpp1:
bc_byte_mono_cr( src, dst, i-> w, map_stdcolorref);
break
;
case
imbpp4:
bc_byte_nibble_cr( src, dst, i-> w, map_stdcolorref);
break
;
default
:
memcpy
( dst, src, i-> w);
}
src += ls;
}
}
free
( data);
if
( kind_of( fi-> object, CIcon) &&
( l-> transparent >= 0) &&
( l-> transparent < PIcon( fi-> object)-> palSize)) {
PIcon( fi-> object)-> maskIndex = l-> transparent;
PIcon( fi-> object)-> autoMasking = amMaskIndex;
}
}
loop =
false
;
break
;
case
EXTENSION_RECORD_TYPE:
{
int
code = -1;
Byte * data = NULL;
if
(( GIF_CALL DGifGetExtension( l-> gft, &code, &data)) != GIF_OK) out(l->gft);
if
( data) {
if
( 1 + l-> passed != fi-> frame)
code = -1;
if
( !load_extension( fi, code, data))
return
false
;
}
}
break
;
default
:;
}
}
return
true
;
}
static
void
close_load( PImgCodec instance, PImgLoadFileInstance fi)
{
LoadRec * l = ( LoadRec *) fi-> instance;
if
( l-> gft) DGifCloseFile( l-> gft GIF_ERROR_ARG_51);
free
( l);
}
static
HV *
save_defaults( PImgCodec c)
{
HV * profile = newHV();
AV * av = newAV();
pset_i( screenWidth, -1);
pset_i( screenHeight, -1);
pset_i( screenBackGroundColor, 0);
av_push( av, newSViv(0));
av_push( av, newSViv(0));
av_push( av, newSViv(0));
av_push( av, newSViv(0xff));
av_push( av, newSViv(0xff));
av_push( av, newSViv(0xff));
pset_sv_noinc( screenPalette, newRV_noinc(( SV *) av));
pset_i( delayTime, 1);
pset_i( disposalMethod, 0);
pset_i( userInput, 0);
pset_i( transparentColorIndex, 0);
pset_i( loopCount, 1);
pset_c( comment,
""
);
pset_i( left, 0);
pset_i( top, 0);
pset_i( interlaced, 0);
return
profile;
}
static
int
my_gif_write( GifFileType * gif,
const
GifByteType * buf,
int
size)
{
return
req_write( (( PImgIORequest) (gif-> UserData)), size, (
void
*) buf);
}
static
void
*
open_save( PImgCodec instance, PImgSaveFileInstance fi)
{
GifFileType * g;
GIF_CALL 0;
if
( !( g = EGifOpen( fi-> req, my_gif_write GIF_ERROR_ARG)))
return
NULL;
return
g;
}
static
ColorMapObject *
make_colormap( PRGBColor r,
int
sz)
{
int
j, psz;
ColorMapObject * ret;
GifColorType * c;
if
( sz <= 2) psz = 2;
else
if
( sz <= 4) psz = 4;
else
if
( sz <= 8) psz = 8;
else
if
( sz <= 16) psz = 16;
else
if
( sz <= 32) psz = 32;
else
if
( sz <= 64) psz = 64;
else
if
( sz <= 128) psz = 128;
else
psz = 256;
ret = MakeMapObject( psz, NULL);
#if defined(GIFLIB_MAJOR) && GIFLIB_MAJOR >= 5
ret->SortFlag = 0;
#endif
if
( !ret)
return
NULL;
c = ret-> Colors;
for
( j = 0; j < sz; j++) {
c-> Red = r-> r;
c-> Green = r-> g;
c-> Blue = r-> b;
c++;
r++;
}
for
( j = sz; j < psz; j++) {
c-> Red = c-> Green = c-> Blue = 0;
c++;
}
return
ret;
}
static
Bool
save( PImgCodec instance, PImgSaveFileInstance fi)
{
dPROFILE;
GifFileType * g = ( GifFileType *) fi-> instance;
PImage i = ( PImage) fi-> object;
HV * profile = fi-> extras;
if
( fi-> frame == 0) {
int
w = i-> w, h = i-> h, bg = 0, ps = i-> palSize;
RGBColor * r = i-> palette;
RGBColor rgbc[ 256];
ColorMapObject * c;
if
( pexist( screenWidth)) w = pget_i( screenWidth);
if
( pexist( screenHeight)) h = pget_i( screenHeight);
if
( pexist( screenBackGroundColor)) bg = pget_i( screenBackGroundColor);
if
( pexist( screenPalette))
ps = apc_img_read_palette( r = rgbc, pget_sv( screenPalette),
true
);
c = make_colormap( r, ps);
if
( !c) outcm( ps * 3);
if
( w < 0) w = i-> w;
if
( h < 0) h = i-> h;
GIF_CALL EGifPutScreenDesc( g, w, h, c-> BitsPerPixel, bg, c);
if
(GIF_CALL_FAILED) {
FreeMapObject( c);
out(g);
}
FreeMapObject( c);
}
if
( pexist( comment))
EGifPutComment( g, pget_c( comment));
if
( pexist( delayTime) ||
pexist( disposalMethod) ||
pexist( userInput) ||
pexist( transparentColorIndex)) {
GIFGraphControlExt ext = { 0, 0, 0};
int
disposalMethod = 0, userInput = 0, transparent = 0;
if
( pexist( delayTime)) {
int
dt = pget_i( delayTime);
ext. delayTimeLo = dt % 256;
ext. delayTimeHi = dt / 256;
}
if
( pexist( disposalMethod)) disposalMethod = pget_i( disposalMethod);
if
( pexist( userInput)) userInput = pget_i( userInput);
if
( pexist( transparentColorIndex)) {
int
t = pget_i( transparentColorIndex);
if
( t >= 0) {
transparent = 1;
ext. transparentColorIndex = t;
}
}
if
( disposalMethod < 0 || disposalMethod > 3) outc(
"disposalMethod not in 0..3"
);
ext. packedFields =
( transparent ? 1 : 0) |
( userInput ? 2 : 0) |
(( disposalMethod & 7) << 2);
if
(( GIF_CALL EGifPutExtension( g, GRAPHICS_EXT_FUNC_CODE,
sizeof
( GIFGraphControlExt), &ext)) != GIF_OK) out(g);
}
if
( pexist( loopCount)) {
int
lc = pget_i( loopCount);
GIFNetscapeLoopExt ext = { 1, lc % 256, lc / 256};
if
(( GIF_CALL EGifPutExtensionFirst( g, NETSCAPE_EXT_FUNC_CODE,
11,
"NETSCAPE2.0"
)) != GIF_OK) out(g);
if
(( GIF_CALL EGifPutExtensionLast( g, 0,
sizeof
( GIFNetscapeLoopExt), &ext)) != GIF_OK) out(g);
}
{
ColorMapObject * c = make_colormap( i-> palette, i-> palSize);
int
ox = 0, oy = 0, interlaced = 0;
GifPixelType * data;
int
ls =
sizeof
( GifPixelType) * i-> w;
int
j, pass = 0, idst = 0;
Byte * src, * dst;
if
( !c) outcm( i-> palSize * 3);
if
( pexist( left)) ox = pget_i( left);
if
( pexist( top )) oy = pget_i( top);
if
( pexist( interlaced )) interlaced = pget_i( interlaced);
GIF_CALL EGifPutImageDesc( g, ox, oy, i-> w, i-> h, interlaced, c);
if
( GIF_CALL_FAILED ) {
FreeMapObject( c);
out(g);
}
FreeMapObject( c);
data =
malloc
( ls * i-> h);
if
( !data) outcm( ls * i-> h);
dst = data;
for
( j = 0; j < i-> h; j++) {
src = i-> data + ( i-> h - 1 - idst) * i-> lineSize;
if
( interlaced) {
idst += interlaceStep[ pass];
if
( idst >= i-> h && pass < 3)
idst = interlaceOffs[ ++pass];
}
else
idst++;
switch
( i-> type & imBPP) {
case
imbpp1:
bc_mono_byte( src, dst, i-> w);
break
;
case
imbpp4:
bc_nibble_byte( src, dst, i-> w);
break
;
default
:
memcpy
( dst, src, i-> w);
}
dst += ls;
}
GIF_CALL EGifPutLine( g, data, i-> w * i-> h);
if
( GIF_CALL_FAILED ) {
free
( data);
out(g);
}
free
( data);
}
return
true
;
}
static
void
close_save( PImgCodec instance, PImgSaveFileInstance fi)
{
EGifCloseFile(( GifFileType *) fi-> instance GIF_ERROR_ARG_51);
}
void
apc_img_codec_gif(
void
)
{
struct
ImgCodecVMT vmt;
memcpy
( &vmt, &CNullImgCodecVMT,
sizeof
( CNullImgCodecVMT));
vmt. init = init;
vmt. open_load = open_load;
vmt. load = load;
vmt. close_load = close_load;
vmt. save_defaults = save_defaults;
vmt. open_save = open_save;
vmt. save = save;
vmt. close_save = close_save;
apc_img_register( &vmt, NULL);
}
#undef out
#undef outc
#ifdef __cplusplus
}
#endif