From c01566cd2f851a0e530611511300d45813104d7c Mon Sep 17 00:00:00 2001 From: castano Date: Thu, 15 May 2008 09:47:55 +0000 Subject: [PATCH] Add support for FreeImage in nvimage. Add support for floating point input images in nvtt. --- src/nvconfig.h.in | 1 + src/nvimage/CMakeLists.txt | 5 + src/nvimage/ColorSpace.cpp | 10 +- src/nvimage/ColorSpace.h | 1 + src/nvimage/ImageIO.cpp | 224 ++++++++++++++++++++++++++++++++++++- src/nvtt/InputOptions.cpp | 6 +- src/nvtt/InputOptions.h | 2 + 7 files changed, 242 insertions(+), 7 deletions(-) diff --git a/src/nvconfig.h.in b/src/nvconfig.h.in index ac9bd57..a4911ee 100644 --- a/src/nvconfig.h.in +++ b/src/nvconfig.h.in @@ -11,6 +11,7 @@ #cmakedefine HAVE_JPEG #cmakedefine HAVE_TIFF #cmakedefine HAVE_OPENEXR +#cmakedefine HAVE_FREEIMAGE #cmakedefine HAVE_MAYA diff --git a/src/nvimage/CMakeLists.txt b/src/nvimage/CMakeLists.txt index 6222262..ed7ad40 100644 --- a/src/nvimage/CMakeLists.txt +++ b/src/nvimage/CMakeLists.txt @@ -51,6 +51,11 @@ IF(OPENEXR_FOUND) INCLUDE_DIRECTORIES(${OPENEXR_INCLUDE_PATHS}) ENDIF(OPENEXR_FOUND) +IF(FREEIMAGE_FOUND) + SET(LIBS ${LIBS} ${FREEIMAGE_LIBRARIES}) + INCLUDE_DIRECTORIES(${FREEIMAGE_INCLUDE_PATHS}) +ENDIF(FREEIMAGE_FOUND) + # targets ADD_DEFINITIONS(-DNVIMAGE_EXPORTS) diff --git a/src/nvimage/ColorSpace.cpp b/src/nvimage/ColorSpace.cpp index 6ee87c5..f6ac4ce 100644 --- a/src/nvimage/ColorSpace.cpp +++ b/src/nvimage/ColorSpace.cpp @@ -1,7 +1,11 @@ -#include "ColorSpace.h" +// This code is in the public domain -- jim@tilander.org + +#include + #include #include -#include + +#include "ColorSpace.h" namespace nv { @@ -63,4 +67,4 @@ namespace nv } } } -} \ No newline at end of file +} diff --git a/src/nvimage/ColorSpace.h b/src/nvimage/ColorSpace.h index 1451b98..8c35760 100644 --- a/src/nvimage/ColorSpace.h +++ b/src/nvimage/ColorSpace.h @@ -1,3 +1,4 @@ +// This code is in the public domain -- jim@tilander.org #ifndef NV_IMAGE_COLORSPACE_H #define NV_IMAGE_COLORSPACE_H diff --git a/src/nvimage/ImageIO.cpp b/src/nvimage/ImageIO.cpp index 0ed979a..264c945 100644 --- a/src/nvimage/ImageIO.cpp +++ b/src/nvimage/ImageIO.cpp @@ -40,6 +40,10 @@ extern "C" { # include #endif +#if defined(HAVE_FREEIMAGE) +# include +#endif + using namespace nv; namespace { @@ -58,6 +62,9 @@ namespace { } // namespace +static Image * loadFreeImage(FREE_IMAGE_FORMAT fif, Stream & s); +static FloatImage * loadFloatFreeImage(FREE_IMAGE_FORMAT fif, Stream & s); + Image * nv::ImageIO::load(const char * fileName) { @@ -78,10 +85,16 @@ Image * nv::ImageIO::load(const char * fileName, Stream & s) nvDebugCheck(s.isLoading()); const char * extension = Path::extension(fileName); - + if (strCaseCmp(extension, ".tga") == 0) { return ImageIO::loadTGA(s); } +#if defined(HAVE_FREEIMAGE) + FREE_IMAGE_FORMAT fif = FreeImage_GetFIFFromFilename(fileName); + if (fif != FIF_UNKNOWN && FreeImage_FIFSupportsReading(fif)) { + return loadFreeImage(fif, s); + } +#endif #if defined(HAVE_JPEG) if (strCaseCmp(extension, ".jpg") == 0 || strCaseCmp(extension, ".jpeg") == 0) { return loadJPG(s); @@ -92,10 +105,13 @@ Image * nv::ImageIO::load(const char * fileName, Stream & s) return loadPNG(s); } #endif + if (strCaseCmp(extension, ".psd") == 0) { return loadPSD(s); } + // @@ use image plugins? + return NULL; } @@ -157,6 +173,12 @@ FloatImage * nv::ImageIO::loadFloat(const char * fileName, Stream & s) return loadFloatEXR(fileName, s); } #endif +#if defined(HAVE_FREEIMAGE) + FREE_IMAGE_FORMAT fif = FreeImage_GetFIFFromFilename(fileName); + if (fif != FIF_UNKNOWN && FreeImage_FIFSupportsReading(fif)) { + return loadFloatFreeImage(fif, s); + } +#endif /* // @@ Disable temporarily if (strCaseCmp(extension, ".pfm") == 0) { @@ -600,6 +622,206 @@ Image * nv::ImageIO::loadPSD(Stream & s) return img.release(); } + +#if defined(HAVE_FREEIMAGE) + +unsigned ReadProc(void *buffer, unsigned size, unsigned count, fi_handle handle) +{ + Stream * s = (Stream *) handle; + s->serialize(buffer, size * count); + return count; +} + +int SeekProc(fi_handle handle, long offset, int origin) +{ + Stream * s = (Stream *) handle; + + switch(origin) { + case SEEK_SET : + s->seek(offset); + break; + case SEEK_CUR : + s->seek(s->tell() + offset); + break; + default : + return 1; + } + + return 0; +} + +long TellProc(fi_handle handle) +{ + Stream * s = (Stream *) handle; + return s->tell(); +} + + +Image * loadFreeImage(FREE_IMAGE_FORMAT fif, Stream & s) +{ + nvCheck(!s.isError()); + + FreeImageIO io; + io.read_proc = ReadProc; + io.write_proc = NULL; + io.seek_proc = SeekProc; + io.tell_proc = TellProc; + + FIBITMAP * bitmap = FreeImage_LoadFromHandle(fif, &io, (fi_handle)&s, 0); + + if (bitmap == NULL) + { + return NULL; + } + + const int w = FreeImage_GetWidth(bitmap); + const int h = FreeImage_GetHeight(bitmap); + + if (FreeImage_GetImageType(bitmap) == FIT_BITMAP) + { + if (FreeImage_GetBPP(bitmap) != 32) + { + FIBITMAP * tmp = FreeImage_ConvertTo32Bits(bitmap); + FreeImage_Unload(bitmap); + bitmap = tmp; + } + } + else + { + // @@ Use tone mapping? + FIBITMAP * tmp = FreeImage_ConvertToType(bitmap, FIT_BITMAP, true); + FreeImage_Unload(bitmap); + bitmap = tmp; + } + + + Image * image = new Image(); + image->allocate(w, h); + + // Copy the image over to our internal format, FreeImage has the scanlines bottom to top though. + for (int y=0; y < h; y++) + { + const void * src = FreeImage_GetScanLine(bitmap, h - y - 1); + void * dst = image->scanline(y); + + memcpy(dst, src, 4 * w); + } + + FreeImage_Unload(bitmap); + + return image; +} + +FloatImage * loadFloatFreeImage(FREE_IMAGE_FORMAT fif, Stream & s) +{ + nvCheck(!s.isError()); + + FreeImageIO io; + io.read_proc = ReadProc; + io.write_proc = NULL; + io.seek_proc = SeekProc; + io.tell_proc = TellProc; + + FIBITMAP * bitmap = FreeImage_LoadFromHandle(fif, &io, (fi_handle)&s, 0); + + if (bitmap == NULL) + { + return NULL; + } + + const int w = FreeImage_GetWidth(bitmap); + const int h = FreeImage_GetHeight(bitmap); + + FREE_IMAGE_TYPE fit = FreeImage_GetImageType(bitmap); + + FloatImage * floatImage = new FloatImage(); + + switch (fit) + { + case FIT_FLOAT: + floatImage->allocate(1, w, h); + + for (int y=0; y < h; y++) + { + const float * src = (const float *)FreeImage_GetScanLine(bitmap, h - y - 1 ); + float * dst = floatImage->scanline(y, 0); + + for (int x=0; x < w; x++) + { + dst[x] = src[x]; + } + } + break; + case FIT_COMPLEX: + floatImage->allocate(2, w, h); + + for (int y=0; y < h; y++) + { + const FICOMPLEX * src = (const FICOMPLEX *)FreeImage_GetScanLine(bitmap, h - y - 1 ); + + float * dst_real = floatImage->scanline(y, 0); + float * dst_imag = floatImage->scanline(y, 1); + + for (int x=0; x < w; x++) + { + dst_real[x] = src[x].r; + dst_imag[x] = src[x].i; + } + } + break; + case FIT_RGBF: + floatImage->allocate(3, w, h); + + for (int y=0; y < h; y++) + { + const FIRGBF * src = (const FIRGBF *)FreeImage_GetScanLine(bitmap, h - y - 1 ); + + float * dst_red = floatImage->scanline(y, 0); + float * dst_green = floatImage->scanline(y, 1); + float * dst_blue = floatImage->scanline(y, 2); + + for (int x=0; x < w; x++) + { + dst_red[x] = src[x].red; + dst_green[x] = src[x].green; + dst_blue[x] = src[x].blue; + } + } + break; + case FIT_RGBAF: + floatImage->allocate(4, w, h); + + for (int y=0; y < h; y++) + { + const FIRGBAF * src = (const FIRGBAF *)FreeImage_GetScanLine(bitmap, h - y - 1 ); + + float * dst_red = floatImage->scanline(y, 0); + float * dst_green = floatImage->scanline(y, 1); + float * dst_blue = floatImage->scanline(y, 2); + float * dst_alpha = floatImage->scanline(y, 3); + + for (int x=0; x < w; x++) + { + dst_red[x] = src[x].red; + dst_green[x] = src[x].green; + dst_blue[x] = src[x].blue; + dst_alpha[x] = src[x].alpha; + } + } + break; + default: + delete floatImage; + floatImage = NULL; + } + + FreeImage_Unload(bitmap); + + return floatImage; +} + +#endif // defined(HAVE_FREEIMAGE) + + #if defined(HAVE_PNG) static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t length) diff --git a/src/nvtt/InputOptions.cpp b/src/nvtt/InputOptions.cpp index 1dbd0e8..82a23ec 100644 --- a/src/nvtt/InputOptions.cpp +++ b/src/nvtt/InputOptions.cpp @@ -211,7 +211,7 @@ bool InputOptions::setMipmapData(const void * data, int width, int height, int d { image->allocate(width, height); memcpy(image->pixels(), data, width * height * 4); - m.images[idx].data = image; + m.images[idx].uint8data = image; } else { @@ -234,7 +234,7 @@ bool InputOptions::setMipmapData(const void * data, int width, int height, int d } } - m.images[idx].data = image; + m.images[idx].floatdata = image; } else { @@ -467,7 +467,7 @@ const Image * InputOptions::Private::image(uint idx) const return image.uint8data.ptr(); } -const FloatImage * InputOptions::Private::image(uint idx) const +const FloatImage * InputOptions::Private::floatImage(uint idx) const { nvDebugCheck(idx < faceCount * mipmapCount); diff --git a/src/nvtt/InputOptions.h b/src/nvtt/InputOptions.h index 475472e..4906173 100644 --- a/src/nvtt/InputOptions.h +++ b/src/nvtt/InputOptions.h @@ -104,6 +104,8 @@ namespace nvtt { InputImage() {} + bool hasValidData() const { return uint8data != NULL || floatdata != NULL; } + int mipLevel; int face;