From 008863daec1249d1f17bc69e1105e336db690d63 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 7 May 2010 15:18:04 +0000 Subject: Merge image related changes from the render branch. This includes the image tile cache code in imbuf, but it is not hooked up to the render engine. Imbuf module: some small refactoring and removing a lot of unused or old code (about 6.5k lines). * Added a ImFileType struct with callbacks to make adding an file format type, or making changes to the API easier. * Move imbuf init/exit code into IMB_init()/IMB_exit() functions. * Increased mipmap levels from 10 to 20, you run into this limit already with a 2k image. * Removed hamx, amiga, anim5 format support. * Removed colormap saving, only simple colormap code now for reading tga. * Removed gen_dynlibtiff.py, editing this is almost as much work as just editing the code directly. * Functions removed that were only used for sequencer plugin API: IMB_anim_nextpic, IMB_clever_double, IMB_antialias, IMB_gamwarp, IMB_scalefieldImBuf, IMB_scalefastfieldImBuf, IMB_onethird, IMB_halflace, IMB_dit0, IMB_dit2, IMB_cspace * Write metadata info into OpenEXR images. Can be viewed with the command line utility 'exrheader' For the image tile cache code, see this page: http://wiki.blender.org/index.php/Dev:2.5/Source/Imaging/ImageTileCache --- source/blender/imbuf/intern/tiff.c | 284 +++++++++++++++++++++++-------------- 1 file changed, 179 insertions(+), 105 deletions(-) (limited to 'source/blender/imbuf/intern/tiff.c') diff --git a/source/blender/imbuf/intern/tiff.c b/source/blender/imbuf/intern/tiff.c index dd18c77c5c6..e7309eefb56 100644 --- a/source/blender/imbuf/intern/tiff.c +++ b/source/blender/imbuf/intern/tiff.c @@ -43,16 +43,17 @@ #include #include "imbuf.h" -#include "imbuf_patch.h" #include "BKE_global.h" +#include "BLI_string.h" + #include "IMB_imbuf_types.h" #include "IMB_imbuf.h" #include "IMB_allocimbuf.h" -#include "IMB_cmap.h" -#include "IMB_tiff.h" +#include "IMB_filetype.h" +#include "IMB_filter.h" #include "dynlibtiff.h" @@ -72,12 +73,12 @@ static void imb_tiff_DummyUnmapProc(thandle_t fd, tdata_t base, toff_t size); /* Structure for in-memory TIFF file. */ -struct ImbTIFFMemFile { +typedef struct ImbTIFFMemFile { unsigned char *mem; /* Location of first byte of TIFF file. */ toff_t offset; /* Current offset within the file. */ tsize_t size; /* Size of the TIFF file. */ -}; -#define IMB_TIFF_GET_MEMFILE(x) ((struct ImbTIFFMemFile*)(x)); +} ImbTIFFMemFile; +#define IMB_TIFF_GET_MEMFILE(x) ((ImbTIFFMemFile*)(x)); @@ -108,28 +109,28 @@ static int imb_tiff_DummyMapProc(thandle_t fd, tdata_t* pbase, toff_t* psize) static tsize_t imb_tiff_ReadProc(thandle_t handle, tdata_t data, tsize_t n) { tsize_t nRemaining, nCopy; - struct ImbTIFFMemFile* mfile; + ImbTIFFMemFile* mfile; void *srcAddr; /* get the pointer to the in-memory file */ mfile = IMB_TIFF_GET_MEMFILE(handle); - if (!mfile || !mfile->mem) { + if(!mfile || !mfile->mem) { fprintf(stderr, "imb_tiff_ReadProc: !mfile || !mfile->mem!\n"); return 0; } /* find the actual number of bytes to read (copy) */ nCopy = n; - if ((tsize_t)mfile->offset >= mfile->size) + if((tsize_t)mfile->offset >= mfile->size) nRemaining = 0; else nRemaining = mfile->size - mfile->offset; - if (nCopy > nRemaining) + if(nCopy > nRemaining) nCopy = nRemaining; /* on EOF, return immediately and read (copy) nothing */ - if (nCopy <= 0) + if(nCopy <= 0) return (0); /* all set -> do the read (copy) */ @@ -171,12 +172,12 @@ static tsize_t imb_tiff_WriteProc(thandle_t handle, tdata_t data, tsize_t n) */ static toff_t imb_tiff_SeekProc(thandle_t handle, toff_t ofs, int whence) { - struct ImbTIFFMemFile *mfile; + ImbTIFFMemFile *mfile; toff_t new_offset; /* get the pointer to the in-memory file */ mfile = IMB_TIFF_GET_MEMFILE(handle); - if (!mfile || !mfile->mem) { + if(!mfile || !mfile->mem) { fprintf(stderr, "imb_tiff_SeekProc: !mfile || !mfile->mem!\n"); return (-1); } @@ -218,11 +219,11 @@ static toff_t imb_tiff_SeekProc(thandle_t handle, toff_t ofs, int whence) */ static int imb_tiff_CloseProc(thandle_t handle) { - struct ImbTIFFMemFile *mfile; + ImbTIFFMemFile *mfile; /* get the pointer to the in-memory file */ mfile = IMB_TIFF_GET_MEMFILE(handle); - if (!mfile || !mfile->mem) { + if(!mfile || !mfile->mem) { fprintf(stderr,"imb_tiff_CloseProc: !mfile || !mfile->mem!\n"); return (0); } @@ -244,11 +245,11 @@ static int imb_tiff_CloseProc(thandle_t handle) */ static toff_t imb_tiff_SizeProc(thandle_t handle) { - struct ImbTIFFMemFile* mfile; + ImbTIFFMemFile* mfile; /* get the pointer to the in-memory file */ mfile = IMB_TIFF_GET_MEMFILE(handle); - if (!mfile || !mfile->mem) { + if(!mfile || !mfile->mem) { fprintf(stderr,"imb_tiff_SizeProc: !mfile || !mfile->mem!\n"); return (0); } @@ -257,14 +258,23 @@ static toff_t imb_tiff_SizeProc(thandle_t handle) return (toff_t)(mfile->size); } +static TIFF *imb_tiff_client_open(ImbTIFFMemFile *memFile, unsigned char *mem, int size) +{ + /* open the TIFF client layer interface to the in-memory file */ + memFile->mem = mem; + memFile->offset = 0; + memFile->size = size; + return libtiff_TIFFClientOpen("(Blender TIFF Interface Layer)", + "r", (thandle_t)(memFile), + imb_tiff_ReadProc, imb_tiff_WriteProc, + imb_tiff_SeekProc, imb_tiff_CloseProc, + imb_tiff_SizeProc, imb_tiff_DummyMapProc, imb_tiff_DummyUnmapProc); +} /** * Checks whether a given memory buffer contains a TIFF file. * - * FIXME: Possible memory leak if mem is less than IMB_TIFF_NCB bytes long. - * However, changing this will require up-stream modifications. - * * This method uses the format identifiers from: * http://www.faqs.org/faqs/graphics/fileformats-faq/part4/section-9.html * The first four bytes of big-endian and little-endian TIFF files @@ -278,7 +288,7 @@ static toff_t imb_tiff_SizeProc(thandle_t handle) * hence my manual comparison. - Jonathan Merritt (lancelet) 4th Sept 2005. */ #define IMB_TIFF_NCB 4 /* number of comparison bytes used */ -int imb_is_a_tiff(void *mem) +int imb_is_a_tiff(unsigned char *mem) { char big_endian[IMB_TIFF_NCB] = { 0x4d, 0x4d, 0x00, 0x2a }; char lil_endian[IMB_TIFF_NCB] = { 0x49, 0x49, 0x2a, 0x00 }; @@ -287,7 +297,31 @@ int imb_is_a_tiff(void *mem) (memcmp(lil_endian, mem, IMB_TIFF_NCB) == 0) ); } +static int imb_read_tiff_pixels(ImBuf *ibuf, TIFF *image, int premul) +{ + ImBuf *tmpibuf; + int success; + + tmpibuf= IMB_allocImBuf(ibuf->x, ibuf->y, 32, IB_rect, 0); + success= libtiff_TIFFReadRGBAImage(image, ibuf->x, ibuf->y, tmpibuf->rect, 0); + + if(ENDIAN_ORDER == B_ENDIAN) + IMB_convert_rgba_to_abgr(tmpibuf); + if(premul) { + IMB_premultiply_alpha(tmpibuf); + ibuf->flags |= IB_premul; + } + + /* assign rect last */ + ibuf->rect= tmpibuf->rect; + ibuf->mall |= IB_rect; + ibuf->flags |= IB_rect; + tmpibuf->mall &= ~IB_rect; + IMB_freeImBuf(tmpibuf); + + return success; +} /** * Loads a TIFF file. @@ -303,52 +337,42 @@ int imb_is_a_tiff(void *mem) * * @return: A newly allocated ImBuf structure if successful, otherwise NULL. */ -struct ImBuf *imb_loadtiff(unsigned char *mem, int size, int flags) +ImBuf *imb_loadtiff(unsigned char *mem, int size, int flags) { TIFF *image = NULL; - struct ImBuf *ibuf = NULL; - struct ImbTIFFMemFile memFile; + ImBuf *ibuf = NULL, *hbuf; + ImbTIFFMemFile memFile; uint32 width, height; - int bytesperpixel, bitspersample; - int success; - unsigned int pixel_i, byte_i; - uint32 *raster = NULL; - uint32 pixel; - unsigned char *to = NULL; + char *format = NULL; + int level; - memFile.mem = mem; - memFile.offset = 0; - memFile.size = size; + if(!G.have_libtiff) + return NULL; /* check whether or not we have a TIFF file */ - if (size < IMB_TIFF_NCB) { + if(size < IMB_TIFF_NCB) { fprintf(stderr, "imb_loadtiff: size < IMB_TIFF_NCB\n"); return NULL; } - if (imb_is_a_tiff(mem) == 0) + if(imb_is_a_tiff(mem) == 0) return NULL; - /* open the TIFF client layer interface to the in-memory file */ - image = libtiff_TIFFClientOpen("(Blender TIFF Interface Layer)", - "r", (thandle_t)(&memFile), - imb_tiff_ReadProc, imb_tiff_WriteProc, - imb_tiff_SeekProc, imb_tiff_CloseProc, - imb_tiff_SizeProc, imb_tiff_DummyMapProc, imb_tiff_DummyUnmapProc); - if (image == NULL) { + image = imb_tiff_client_open(&memFile, mem, size); + + if(image == NULL) { printf("imb_loadtiff: could not open TIFF IO layer.\n"); return NULL; } /* allocate the image buffer */ - bytesperpixel = 4; /* 1 byte per channel, 4 channels */ libtiff_TIFFGetField(image, TIFFTAG_IMAGEWIDTH, &width); libtiff_TIFFGetField(image, TIFFTAG_IMAGELENGTH, &height); - libtiff_TIFFGetField(image, TIFFTAG_BITSPERSAMPLE, &bitspersample); - ibuf = IMB_allocImBuf(width, height, 8*bytesperpixel, 0, 0); - if (ibuf) { + ibuf = IMB_allocImBuf(width, height, 32, 0, 0); + if(ibuf) { ibuf->ftype = TIF; ibuf->profile = IB_PROFILE_SRGB; - } else { + } + else { fprintf(stderr, "imb_loadtiff: could not allocate memory for TIFF " \ "image.\n"); @@ -356,65 +380,109 @@ struct ImBuf *imb_loadtiff(unsigned char *mem, int size, int flags) return NULL; } - /* read in the image data */ - if (!(flags & IB_test)) { + /* if testing, we're done */ + if(flags & IB_test) { + libtiff_TIFFClose(image); + return ibuf; + } - /* allocate memory for the ibuf->rect */ - imb_addrectImBuf(ibuf); + /* detect if we are reading a tiled/mipmapped texture, in that case + we don't read pixels but leave it to the cache to load tiles */ + if(flags & IB_tilecache) { + format= NULL; + libtiff_TIFFGetField(image, TIFFTAG_PIXAR_TEXTUREFORMAT, &format); - /* perform actual read */ - raster = (uint32*)libtiff__TIFFmalloc( - width*height * sizeof(uint32)); - if (raster == NULL) { - libtiff_TIFFClose(image); - return NULL; - } - success = libtiff_TIFFReadRGBAImage( - image, width, height, raster, 0); - if (!success) { - fprintf(stderr, - "imb_loadtiff: This TIFF format is not " - "currently supported by Blender.\n"); - libtiff__TIFFfree(raster); - libtiff_TIFFClose(image); - return NULL; - } + if(format && strcmp(format, "Plain Texture")==0 && libtiff_TIFFIsTiled(image)) { + int numlevel = libtiff_TIFFNumberOfDirectories(image); - /* copy raster to ibuf->rect; we do a fast copy if possible, - * otherwise revert to a slower component-wise copy */ - if (sizeof(unsigned int) == sizeof(uint32)) { - memcpy(ibuf->rect, raster, - width*height*sizeof(uint32)); - } else { - /* this may not be entirely necessary, but is put here - * in case sizeof(unsigned int) is not a 32-bit - * quantity */ - fprintf(stderr, - "imb_loadtiff: using (slower) component-wise " - "buffer copy.\n"); - to = (unsigned char*)ibuf->rect; - for (pixel_i=0; pixel_i < width*height; pixel_i++) - { - byte_i = sizeof(unsigned int)*pixel_i; - pixel = raster[pixel_i]; - - to[byte_i++] = (unsigned char)TIFFGetR(pixel); - to[byte_i++] = (unsigned char)TIFFGetG(pixel); - to[byte_i++] = (unsigned char)TIFFGetB(pixel); - to[byte_i++] = (unsigned char)TIFFGetA(pixel); + /* create empty mipmap levels in advance */ + for(level=0; level 0) { + width= (width > 1)? width/2: 1; + height= (height > 1)? height/2: 1; + + hbuf= IMB_allocImBuf(width, height, 32, 0, 0); + hbuf->miplevel= level; + hbuf->flags |= IB_tilecache; + hbuf->ftype= ibuf->ftype; + ibuf->mipmap[level-1] = hbuf; + + if(flags & IB_premul) + hbuf->flags |= IB_premul; + } + else + hbuf= ibuf; + + libtiff_TIFFGetField(image, TIFFTAG_TILEWIDTH, &hbuf->tilex); + libtiff_TIFFGetField(image, TIFFTAG_TILELENGTH, &hbuf->tiley); + + hbuf->xtiles= ceil(hbuf->x/(float)hbuf->tilex); + hbuf->ytiles= ceil(hbuf->y/(float)hbuf->tiley); + + imb_addtilesImBuf(hbuf); + + ibuf->miptot++; } } + } - libtiff__TIFFfree(raster); + /* read pixels */ + if(!(ibuf->flags & IB_tilecache) && !imb_read_tiff_pixels(ibuf, image, 0)) { + fprintf(stderr, "imb_loadtiff: Failed to read tiff image.\n"); + libtiff_TIFFClose(image); + return NULL; } /* close the client layer interface to the in-memory file */ libtiff_TIFFClose(image); - if (ENDIAN_ORDER == B_ENDIAN) IMB_convert_rgba_to_abgr(ibuf); - /* return successfully */ - return (ibuf); + return ibuf; +} + +void imb_loadtiletiff(ImBuf *ibuf, unsigned char *mem, int size, int tx, int ty, unsigned int *rect) +{ + TIFF *image = NULL; + uint32 width, height; + ImbTIFFMemFile memFile; + + image = imb_tiff_client_open(&memFile, mem, size); + + if(image == NULL) { + printf("imb_loadtiff: could not open TIFF IO layer for loading mipmap level.\n"); + return; + } + + if(libtiff_TIFFSetDirectory(image, ibuf->miplevel)) { + /* allocate the image buffer */ + libtiff_TIFFGetField(image, TIFFTAG_IMAGEWIDTH, &width); + libtiff_TIFFGetField(image, TIFFTAG_IMAGELENGTH, &height); + + if(width == ibuf->x && height == ibuf->y) { + if(rect) { + /* tiff pixels are bottom to top, tiles are top to bottom */ + if(libtiff_TIFFReadRGBATile(image, tx*ibuf->tilex, (ibuf->ytiles - 1 - ty)*ibuf->tiley, rect) == 1) { + if(ibuf->tiley > ibuf->y) + memmove(rect, rect+ibuf->tilex*(ibuf->tiley - ibuf->y), sizeof(int)*ibuf->tilex*ibuf->y); + + if(ibuf->flags & IB_premul) + IMB_premultiply_rect(rect, 32, ibuf->tilex, ibuf->tiley); + } + else + printf("imb_loadtiff: failed to read tiff tile at mipmap level %d\n", ibuf->miplevel); + } + } + else + printf("imb_loadtiff: mipmap level %d has unexpected size %dx%d instead of %dx%d\n", ibuf->miplevel, width, height, ibuf->x, ibuf->y); + } + else + printf("imb_loadtiff: could not find mipmap level %d\n", ibuf->miplevel); + + /* close the client layer interface to the in-memory file */ + libtiff_TIFFClose(image); } /** @@ -435,7 +503,7 @@ struct ImBuf *imb_loadtiff(unsigned char *mem, int size, int flags) #define FTOUSHORT(val) ((val >= 1.0f-0.5f/65535)? 65535: (val <= 0.0f)? 0: (unsigned short)(val*65535.0f + 0.5f)) -short imb_savetiff(struct ImBuf *ibuf, char *name, int flags) +int imb_savetiff(ImBuf *ibuf, char *name, int flags) { TIFF *image = NULL; uint16 samplesperpixel, bitspersample; @@ -446,12 +514,17 @@ short imb_savetiff(struct ImBuf *ibuf, char *name, int flags) float *fromf = NULL; int x, y, from_i, to_i, i; int extraSampleTypes[1] = { EXTRASAMPLE_ASSOCALPHA }; + + if(!G.have_libtiff) { + fprintf(stderr, "imb_savetiff: no tiff library available.\n"); + return (0); + } /* check for a valid number of bytes per pixel. Like the PNG writer, * the TIFF writer supports 1, 3 or 4 bytes per pixel, corresponding * to gray, RGB, RGBA respectively. */ samplesperpixel = (uint16)((ibuf->depth + 7) >> 3); - if ((samplesperpixel > 4) || (samplesperpixel == 2)) { + if((samplesperpixel > 4) || (samplesperpixel == 2)) { fprintf(stderr, "imb_savetiff: unsupported number of bytes per " "pixel: %d\n", samplesperpixel); @@ -464,17 +537,18 @@ short imb_savetiff(struct ImBuf *ibuf, char *name, int flags) bitspersample = 8; /* open TIFF file for writing */ - if (flags & IB_mem) { + if(flags & IB_mem) { /* bork at the creation of a TIFF in memory */ fprintf(stderr, "imb_savetiff: creation of in-memory TIFF files is " "not yet supported.\n"); return (0); - } else { + } + else { /* create image as a file */ image = libtiff_TIFFOpen(name, "w"); } - if (image == NULL) { + if(image == NULL) { fprintf(stderr, "imb_savetiff: could not open TIFF for writing.\n"); return (0); @@ -489,7 +563,7 @@ short imb_savetiff(struct ImBuf *ibuf, char *name, int flags) pixels = (unsigned char*)libtiff__TIFFmalloc(npixels * samplesperpixel * sizeof(unsigned char)); - if (pixels == NULL && pixels16 == NULL) { + if(pixels == NULL && pixels16 == NULL) { fprintf(stderr, "imb_savetiff: could not allocate pixels array.\n"); libtiff_TIFFClose(image); @@ -529,17 +603,17 @@ short imb_savetiff(struct ImBuf *ibuf, char *name, int flags) } /* copy pixel data. While copying, we flip the image vertically. */ - for (x = 0; x < ibuf->x; x++) { - for (y = 0; y < ibuf->y; y++) { + for(x = 0; x < ibuf->x; x++) { + for(y = 0; y < ibuf->y; y++) { from_i = 4*(y*ibuf->x+x); to_i = samplesperpixel*((ibuf->y-y-1)*ibuf->x+x); if(pixels16) { - for (i = 0; i < samplesperpixel; i++, to_i++, from_i++) + for(i = 0; i < samplesperpixel; i++, to_i++, from_i++) to16[to_i] = FTOUSHORT(fromf[from_i]); } else { - for (i = 0; i < samplesperpixel; i++, to_i++, from_i++) + for(i = 0; i < samplesperpixel; i++, to_i++, from_i++) to[to_i] = from[from_i]; } } @@ -555,7 +629,7 @@ short imb_savetiff(struct ImBuf *ibuf, char *name, int flags) libtiff_TIFFSetField(image, TIFFTAG_XRESOLUTION, 150.0); libtiff_TIFFSetField(image, TIFFTAG_YRESOLUTION, 150.0); libtiff_TIFFSetField(image, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH); - if (libtiff_TIFFWriteEncodedStrip(image, 0, + if(libtiff_TIFFWriteEncodedStrip(image, 0, (bitspersample == 16)? (unsigned char*)pixels16: pixels, ibuf->x*ibuf->y*samplesperpixel*bitspersample/8) == -1) { fprintf(stderr, -- cgit v1.2.3