289 lines
8.1 KiB
C
289 lines
8.1 KiB
C
/* tiff.c
|
|
* TIFF image decoding
|
|
* (c) 2002 Petr 'Brain' Kulhavy
|
|
* This file is a part of the Links program, released under GPL.
|
|
*
|
|
* Compiles in graphics mode only and only when HAVE_TIFF.
|
|
*/
|
|
#include "cfg.h"
|
|
|
|
#ifdef G
|
|
#include "links.h"
|
|
|
|
#ifdef HAVE_TIFF
|
|
#include <tiffio.h>
|
|
#include "bits.h"
|
|
|
|
struct tiff_decoder{
|
|
unsigned char *tiff_data; /* undecoded data */
|
|
int tiff_size; /* size of undecoded file */
|
|
int tiff_pos;
|
|
int tiff_open; /* 1 if tiff was open, means: tiff_data, tiff_size and tiff_pos are valid */
|
|
};
|
|
|
|
void tiff_start(struct cached_image *cimg)
|
|
{
|
|
struct tiff_decoder * deco;
|
|
|
|
deco=mem_alloc(sizeof(struct tiff_decoder));
|
|
|
|
cimg->decoder=deco;
|
|
deco->tiff_size=0;
|
|
deco->tiff_data=NULL;
|
|
deco->tiff_open=0;
|
|
deco->tiff_pos=0;
|
|
}
|
|
|
|
|
|
void tiff_restart(struct cached_image *cimg, unsigned char *data, int length)
|
|
{
|
|
struct tiff_decoder * deco=(struct tiff_decoder*)cimg->decoder;
|
|
unsigned char *p;
|
|
|
|
if (!deco->tiff_data) {
|
|
if ((unsigned)length > MAXINT) overalloc();
|
|
p=mem_alloc(length);
|
|
} else {
|
|
if ((unsigned)length + (unsigned)deco->tiff_size > MAXINT) overalloc();
|
|
if ((unsigned)length + (unsigned)deco->tiff_size < (unsigned)length) overalloc();
|
|
p=mem_realloc(deco->tiff_data,deco->tiff_size+length);
|
|
}
|
|
deco->tiff_data=p;
|
|
memcpy(deco->tiff_data+deco->tiff_size,data,length);
|
|
deco->tiff_size+=length;
|
|
}
|
|
|
|
static toff_t tiff_size(thandle_t data)
|
|
{
|
|
struct cached_image *cimg=(struct cached_image *)data;
|
|
struct tiff_decoder *deco=(struct tiff_decoder*)cimg->decoder;
|
|
|
|
if (!deco->tiff_open)internal_error("BUG IN LIBTIFF: sizeproc called on closed file. Contact the libtiff authors.\n");
|
|
|
|
return deco->tiff_size;
|
|
}
|
|
|
|
|
|
static tsize_t tiff_read(thandle_t data, tdata_t dest, tsize_t count)
|
|
{
|
|
struct cached_image *cimg=(struct cached_image *)data;
|
|
struct tiff_decoder *deco=(struct tiff_decoder*)cimg->decoder;
|
|
|
|
if (!deco->tiff_open)internal_error("BUG IN LIBTIFF: readproc called on closed file. Contact the libtiff authors.\n");
|
|
|
|
if (count < 0)
|
|
return 0;
|
|
|
|
if (count > deco->tiff_size-deco->tiff_pos)
|
|
count = deco->tiff_size-deco->tiff_pos;
|
|
memcpy(dest,deco->tiff_data+deco->tiff_pos,count);
|
|
deco->tiff_pos+=(int)count;
|
|
return count;
|
|
}
|
|
|
|
|
|
static tsize_t tiff_write(thandle_t data, tdata_t dest, tsize_t count)
|
|
{
|
|
internal_error("BUG IN LIBTIFF: writeproc called on read-only file. Contact the libtiff authors.\n");
|
|
return 0;
|
|
}
|
|
|
|
|
|
static toff_t tiff_seek(thandle_t data, toff_t offset, int whence)
|
|
{
|
|
struct cached_image *cimg=(struct cached_image *)data;
|
|
struct tiff_decoder *deco=(struct tiff_decoder*)cimg->decoder;
|
|
long pos;
|
|
|
|
if (!deco->tiff_open)internal_error("BUG IN LIBTIFF: seekproc called on closed file. Contact the libtiff authors.\n");
|
|
|
|
switch(whence)
|
|
{
|
|
case SEEK_SET:
|
|
pos = offset;
|
|
break;
|
|
case SEEK_CUR:
|
|
pos = (unsigned long)deco->tiff_pos + offset;
|
|
break;
|
|
case SEEK_END:
|
|
pos = (unsigned long)deco->tiff_size + offset;
|
|
break;
|
|
default:
|
|
pos = deco->tiff_pos;
|
|
break;
|
|
}
|
|
if (pos > deco->tiff_size) pos = deco->tiff_size;
|
|
if (pos < 0) pos = 0;
|
|
deco->tiff_pos = (int)pos;
|
|
return deco->tiff_pos;
|
|
}
|
|
|
|
|
|
static int tiff_close(void *data)
|
|
{
|
|
struct cached_image *cimg=(struct cached_image *)data;
|
|
struct tiff_decoder *deco=(struct tiff_decoder*)cimg->decoder;
|
|
|
|
if (!deco->tiff_open)internal_error("BUG IN LIBTIFF: closeproc called on closed file. Contact the libtiff authors.\n");
|
|
tiff_destroy_decoder(cimg);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int tiff_mmap(thandle_t data, tdata_t *dest, toff_t *len)
|
|
{
|
|
struct cached_image *cimg=(struct cached_image *)data;
|
|
struct tiff_decoder *deco=(struct tiff_decoder*)cimg->decoder;
|
|
|
|
if (!deco->tiff_open)internal_error("BUG IN LIBTIFF: mapproc called on closed file. Contact the libtiff authors.\n");
|
|
*dest=deco->tiff_data;
|
|
*len=deco->tiff_size;
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void tiff_munmap(thandle_t data, tdata_t dest, toff_t len)
|
|
{
|
|
struct cached_image *cimg=(struct cached_image *)data;
|
|
struct tiff_decoder *deco=(struct tiff_decoder*)cimg->decoder;
|
|
|
|
if (!deco->tiff_open)internal_error("BUG IN LIBTIFF: unmapproc called on closed file. Contact the libtiff authors.\n");
|
|
}
|
|
|
|
|
|
static void tiff_error_handler(const char* module, const char* fmt, va_list ap)
|
|
{
|
|
}
|
|
|
|
static void flip_buffer(void *buf, int width, int height)
|
|
{
|
|
if (!big_endian) { /* little endian --- ja to chci na intelu rychly!!! */
|
|
#ifdef t4c
|
|
t4c* buffer=(t4c*)buf;
|
|
register t4c a,b;
|
|
t4c *p,*q;
|
|
int i,l;
|
|
|
|
for (l=0,p=buffer,q=buffer+width*(height-1);l<(height>>1);l++,q-=(width<<1))
|
|
for (i=0;i<width;a=*p,b=*q,*p++=b,*q++=a,i++)
|
|
;
|
|
#else
|
|
unsigned char* buffer=(unsigned char*)buf;
|
|
unsigned char *p,*q;
|
|
int l;
|
|
unsigned char *tmp;
|
|
int w=4*width;
|
|
|
|
if ((unsigned)w > MAXINT) overalloc();
|
|
tmp=mem_alloc(w);
|
|
|
|
/* tohle je pomalejsi, protoze se kopiruje pamet->pamet, pamet->pamet */
|
|
/* kdyz mame 4B typek, tak se kopiruje pamet->reg, reg->pamet */
|
|
for (l=0,p=buffer,q=buffer+w*(height-1);l<(height>>1);l++,q-=w,p+=w)
|
|
memcpy(tmp,p,w),memcpy(p,q,w),memcpy(q,tmp,w);
|
|
mem_free(tmp);
|
|
#endif
|
|
} else { /* big endian */
|
|
unsigned char zakazany_uvolneni[4];
|
|
unsigned char* buffer=(unsigned char*)buf;
|
|
int w=width<<2; /* 4 bytes per pixel */
|
|
unsigned char *p,*q;
|
|
int i,l;
|
|
|
|
for (l=0,p=buffer,q=buffer+w*(height-1);l<(height>>1);l++,q-=(w<<1))
|
|
for (i=0;i<width;i++,p+=4,q+=4)
|
|
{
|
|
memcpy(zakazany_uvolneni,p,4);
|
|
p[0]=q[3];
|
|
p[1]=q[2];
|
|
p[2]=q[1];
|
|
p[3]=q[0];
|
|
q[0]=zakazany_uvolneni[3];
|
|
q[1]=zakazany_uvolneni[2];
|
|
q[2]=zakazany_uvolneni[1];
|
|
q[3]=zakazany_uvolneni[0];
|
|
}
|
|
if (height&1) /* flip endianity of line in the middle (if the height is odd) */
|
|
for (i=0;i<width;i++,p+=4)
|
|
{
|
|
memcpy(zakazany_uvolneni,p,4);
|
|
p[0]=zakazany_uvolneni[3];
|
|
p[1]=zakazany_uvolneni[2];
|
|
p[2]=zakazany_uvolneni[1];
|
|
p[3]=zakazany_uvolneni[0];
|
|
}
|
|
}
|
|
}
|
|
|
|
void tiff_finish(struct cached_image *cimg)
|
|
{
|
|
struct tiff_decoder *deco=(struct tiff_decoder*)cimg->decoder;
|
|
int bla, x, y;
|
|
TIFF *t;
|
|
|
|
if (!deco->tiff_size){img_end(cimg);return;}
|
|
deco->tiff_open=1;
|
|
TIFFSetErrorHandler(tiff_error_handler);
|
|
TIFFSetWarningHandler(tiff_error_handler);
|
|
t=TIFFClientOpen(
|
|
"Prave si rek' svy posledni slova. A vybral sis k tomu prihodny misto.",
|
|
"r",
|
|
cimg,
|
|
(TIFFReadWriteProc)tiff_read,
|
|
(TIFFReadWriteProc)tiff_write,
|
|
(TIFFSeekProc)tiff_seek,
|
|
(TIFFCloseProc)tiff_close,
|
|
(TIFFSizeProc)tiff_size,
|
|
(TIFFMapFileProc)tiff_mmap,
|
|
(TIFFUnmapFileProc)tiff_munmap
|
|
);
|
|
if (!t) { img_end(cimg); return; }
|
|
|
|
bla = TIFFGetField(t, TIFFTAG_IMAGEWIDTH, &x);
|
|
if (!bla) { TIFFClose(t); img_end(cimg); return; }
|
|
cimg->width = x;
|
|
bla = TIFFGetField(t, TIFFTAG_IMAGELENGTH, &y);
|
|
if (!bla){ TIFFClose(t); img_end(cimg); return; }
|
|
cimg->height = y;
|
|
cimg->buffer_bytes_per_pixel = 4;
|
|
cimg->red_gamma=cimg->green_gamma=cimg->blue_gamma=(float)sRGB_gamma;
|
|
cimg->strip_optimized=0;
|
|
if (header_dimensions_known(cimg)){TIFFClose(t);img_end(cimg);return;}
|
|
/* int TIFFReadRGBAImage(TIFF* tif, u_long width, u_long height, u_long* raster, int stopOnError) from man page */
|
|
/*TIFFReadRGBAImage(t,cimg->width,cimg->height,(unsigned long*)(cimg->buffer),1);*/ /* 231: warning: passing arg 4 of `TIFFReadRGBAImage' from incompatible pointer type */
|
|
TIFFReadRGBAImage(t, (unsigned)cimg->width, (unsigned)cimg->height, (void *)cimg->buffer, 1);
|
|
TIFFClose(t);
|
|
|
|
/* For some reason the TIFFReadRGBAImage() function chooses the lower
|
|
* left corner as the origin. Vertically mirror scanlines.
|
|
*/
|
|
flip_buffer((void*)cimg->buffer, (int)cimg->width, (int)cimg->height);
|
|
|
|
img_end(cimg);
|
|
}
|
|
|
|
void tiff_destroy_decoder(struct cached_image *cimg)
|
|
{
|
|
struct tiff_decoder *deco=(struct tiff_decoder *)cimg->decoder;
|
|
if (deco->tiff_data) mem_free(deco->tiff_data), deco->tiff_data = NULL;
|
|
deco->tiff_open=0;
|
|
}
|
|
|
|
void add_tiff_version(unsigned char **s, int *l)
|
|
{
|
|
unsigned char *p, *pp;
|
|
int pl;
|
|
add_to_str(s, l, cast_uchar "TIFF (");
|
|
p = (unsigned char *)TIFFGetVersion();
|
|
pp = cast_uchar strstr(cast_const_char p, "LIBTIFF, ");
|
|
if (pp) p = pp + 9;
|
|
pp = cast_uchar strstr(cast_const_char p, "Version ");
|
|
if (pp) p = pp + 8;
|
|
pl = (int)strcspn(cast_const_char p, " \n");
|
|
add_bytes_to_str(s, l, p, pl);
|
|
add_chr_to_str(s, l, ')');
|
|
}
|
|
|
|
#endif /* #ifdef HAVE_TIFF */
|
|
|
|
#endif /* #ifdef G */
|