links/tiff.c

290 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 */