diff --git a/doomsday/client/include/de_graphics.h b/doomsday/client/include/de_graphics.h index ce68f25a18..a26a7f8959 100644 --- a/doomsday/client/include/de_graphics.h +++ b/doomsday/client/include/de_graphics.h @@ -34,8 +34,4 @@ # include "gl/gl_texmanager.h" #endif -#include "resource/pcx.h" -#include "resource/tga.h" -#include "resource/hq2x.h" - #endif /* LIBDENG_GRAPHICS */ diff --git a/doomsday/client/src/gl/gl_tex.cpp b/doomsday/client/src/gl/gl_tex.cpp index eee090a892..91baab997f 100644 --- a/doomsday/client/src/gl/gl_tex.cpp +++ b/doomsday/client/src/gl/gl_tex.cpp @@ -1505,9 +1505,9 @@ void SharpenPixels(uint8_t* pixels, int width, int height, int comps) /** * @return @c true, if the given color is either (0,255,255) or (255,0,255). */ -static __inline boolean ColorKey(uint8_t* color) +static inline bool isKeyedColor(uint8_t *color) { - assert(color); + DENG2_ASSERT(color); return color[CB] == 0xff && ((color[CR] == 0xff && color[CG] == 0) || (color[CR] == 0 && color[CG] == 0xff)); } @@ -1515,21 +1515,21 @@ static __inline boolean ColorKey(uint8_t* color) /** * Buffer must be RGBA. Doesn't touch the non-keyed pixels. */ -static void DoColorKeying(uint8_t* rgbaBuf, int width) +static void doColorKeying(uint8_t *rgbaBuf, int width) { - assert(rgbaBuf); - { int i; - for(i = 0; i < width; ++i, rgbaBuf += 4) + DENG2_ASSERT(rgbaBuf); + + for(int i = 0; i < width; ++i, rgbaBuf += 4) { - if(!ColorKey(rgbaBuf)) - continue; + if(!isKeyedColor(rgbaBuf)) continue; + rgbaBuf[3] = rgbaBuf[2] = rgbaBuf[1] = rgbaBuf[0] = 0; - }} + } } -uint8_t* ApplyColorKeying(uint8_t* buf, int width, int height, int pixelSize) +uint8_t *ApplyColorKeying(uint8_t *buf, int width, int height, int pixelSize) { - assert(buf); + DENG2_ASSERT(buf); if(width <= 0 || height <= 0) return buf; @@ -1538,19 +1538,20 @@ uint8_t* ApplyColorKeying(uint8_t* buf, int width, int height, int pixelSize) // required number of color components. if(pixelSize < 4) { - const long numpels = width * height; - uint8_t* ckdest = (uint8_t *) M_Malloc(4 * numpels); - uint8_t* in, *out; + long const numpels = width * height; + uint8_t *ckdest = (uint8_t *) M_Malloc(4 * numpels); + uint8_t *in, *out; long i; + for(in = buf, out = ckdest, i = 0; i < numpels; ++i, in += pixelSize, out += 4) { - if(ColorKey(in)) + if(isKeyedColor(in)) { - memset(out, 0, 4); // Totally black. + std::memset(out, 0, 4); // Totally black. continue; } - memcpy(out, in, 3); // The color itself. + std::memcpy(out, in, 3); // The color itself. out[CA] = 255; // Opaque. } return ckdest; @@ -1558,9 +1559,9 @@ uint8_t* ApplyColorKeying(uint8_t* buf, int width, int height, int pixelSize) // We can do the keying in-buffer. // This preserves the alpha values of non-keyed pixels. - { int i; - for(i = 0; i < height; ++i) - DoColorKeying(buf + 4 * i * width, height); + for(int i = 0; i < height; ++i) + { + doColorKeying(buf + 4 * i * width, height); } return buf; } diff --git a/doomsday/client/src/gl/gl_texmanager.cpp b/doomsday/client/src/gl/gl_texmanager.cpp index d11c480332..fd3e798fea 100644 --- a/doomsday/client/src/gl/gl_texmanager.cpp +++ b/doomsday/client/src/gl/gl_texmanager.cpp @@ -41,6 +41,7 @@ #include "de_ui.h" #include "def_main.h" +#include "resource/hq2x.h" #include #include @@ -62,19 +63,6 @@ enum uploadcontentmethod_t METHOD_DEFERRED }; -struct GraphicFileType -{ - /// Symbolic name of the resource type. - String name; - - /// Known file extension. - String ext; - - bool (*interpretFunc)(de::FileHandle &hndl, String filePath, image_t &img); - - char const *(*getLastErrorFunc)(); ///< Can be NULL. -}; - struct texturevariantspecificationlist_node_t { texturevariantspecificationlist_node_t *next; @@ -94,11 +82,6 @@ void GL_DoUpdateTexParams(); static int hashDetailVariantSpecification(detailvariantspecification_t const &spec); -static bool interpretPcx(de::FileHandle &hndl, String filePath, image_t &img); -static bool interpretPng(de::FileHandle &hndl, String filePath, image_t &img); -static bool interpretJpg(de::FileHandle &hndl, String filePath, image_t &img); -static bool interpretTga(de::FileHandle &hndl, String filePath, image_t &img); - static TexSource loadExternalTexture(image_t &image, String searchPath, String optionalSuffix = ""); static TexSource loadFlat(image_t &image, de::FileHandle &file); @@ -141,15 +124,6 @@ DGLuint sysFlareTextures[NUM_SYSFLARE_TEXTURES]; static boolean initedOk = false; // Init done. -// Graphic resource types. -static GraphicFileType const graphicTypes[] = { - { "PNG", "png", interpretPng, 0 }, - { "JPG", "jpg", interpretJpg, 0 }, // TODO: add alternate "jpeg" extension - { "TGA", "tga", interpretTga, TGA_LastError }, - { "PCX", "pcx", interpretPcx, PCX_LastError }, - { "", "", 0, 0 } // Terminate. -}; - static variantspecificationlist_t *variantSpecs; /// @c TST_DETAIL type specifications are stored separately into a set of @@ -1312,135 +1286,6 @@ void GL_PruneTextureVariantSpecifications() #endif } -static bool interpretPcx(de::FileHandle &hndl, String /*filePath*/, image_t &img) -{ - Image_Init(&img); - img.pixels = PCX_Load(reinterpret_cast(&hndl), - &img.size.width, &img.size.height, &img.pixelSize); - return (0 != img.pixels); -} - -static bool interpretJpg(de::FileHandle &hndl, String /*filePath*/, image_t &img) -{ - return Image_LoadFromFileWithFormat(&img, "JPG", reinterpret_cast(&hndl)); -} - -static bool interpretPng(de::FileHandle &hndl, String /*filePath*/, image_t &img) -{ - /* - Image_Init(&img); - img.pixels = PNG_Load(reinterpret_cast(&hndl), - &img.size.width, &img.size.height, &img.pixelSize); - return (0 != img.pixels); - */ - return Image_LoadFromFileWithFormat(&img, "PNG", reinterpret_cast(&hndl)); -} - -static bool interpretTga(de::FileHandle &hndl, String /*filePath*/, image_t &img) -{ - Image_Init(&img); - img.pixels = TGA_Load(reinterpret_cast(&hndl), - &img.size.width, &img.size.height, &img.pixelSize); - return (0 != img.pixels); -} - -GraphicFileType const *guessGraphicFileTypeFromFileName(String fileName) -{ - // The path must have an extension for this. - String ext = fileName.fileNameExtension(); - if(!ext.isEmpty()) - { - for(int i = 0; !graphicTypes[i].ext.isEmpty(); ++i) - { - GraphicFileType const &type = graphicTypes[i]; - if(!ext.compareWithoutCase(type.ext)) - { - return &type; - } - } - } - return 0; // Unknown. -} - -static void interpretGraphic(de::FileHandle &hndl, String filePath, image_t &img) -{ - // Firstly try the interpreter for the guessed resource types. - GraphicFileType const *rtypeGuess = guessGraphicFileTypeFromFileName(filePath); - if(rtypeGuess) - { - rtypeGuess->interpretFunc(hndl, filePath, img); - } - - // If not yet interpreted - try each recognisable format in order. - if(!img.pixels) - { - // Try each recognisable format instead. - /// @todo Order here should be determined by the resource locator. - for(int i = 0; !graphicTypes[i].name.isEmpty(); ++i) - { - GraphicFileType const *graphicType = &graphicTypes[i]; - - // Already tried this? - if(graphicType == rtypeGuess) continue; - - graphicTypes[i].interpretFunc(hndl, filePath, img); - if(img.pixels) break; - } - } -} - -/// @return @c true if the file name in @a path ends with the "color key" suffix. -static inline bool isColorKeyed(String path) -{ - return path.fileNameWithoutExtension().endsWith("-ck", Qt::CaseInsensitive); -} - -uint8_t *Image_LoadFromFile(image_t *img, filehandle_s *_file) -{ - DENG_ASSERT(img && _file); - de::FileHandle &file = reinterpret_cast(*_file); - LOG_AS("Image_LoadFromFile"); - - String filePath = file.file().composePath(); - - Image_Init(img); - interpretGraphic(file, filePath, *img); - - // Still not interpreted? - if(!img->pixels) - { - LOG_VERBOSE("\"%s\" unrecognized, trying fallback loader...") - << NativePath(filePath).pretty(); - return NULL; // Not a recognised format. It may still be loadable, however. - } - - // How about some color-keying? - if(isColorKeyed(filePath)) - { - uint8_t *out = ApplyColorKeying(img->pixels, img->size.width, img->size.height, img->pixelSize); - if(out != img->pixels) - { - // Had to allocate a larger buffer, free the old and attach the new. - M_Free(img->pixels); - img->pixels = out; - } - - // Color keying is done; now we have 4 bytes per pixel. - img->pixelSize = 4; - } - - // Any alpha pixels? - if(Image_HasAlpha(img)) - { - img->flags |= IMGF_IS_MASKED; - } - - LOG_VERBOSE("\"%s\" (%ix%i)") - << NativePath(filePath).pretty() << img->size.width << img->size.height; - - return img->pixels; -} - uint8_t *GL_LoadImage(image_t &image, String nativePath) { try diff --git a/doomsday/client/src/resource/image.cpp b/doomsday/client/src/resource/image.cpp index 3ff271ba4c..4144018882 100644 --- a/doomsday/client/src/resource/image.cpp +++ b/doomsday/client/src/resource/image.cpp @@ -19,28 +19,144 @@ * 02110-1301 USA */ +#include + #include "de_base.h" #include "de_console.h" #include "de_filesys.h" #include "m_misc.h" -#include "resource/image.h" - +#ifdef __CLIENT__ +# include "resource/pcx.h" +# include "resource/tga.h" +# include "gl/gl_tex.h" +#endif #include -#include -#include #ifdef __CLIENT__ +# include +# include # include #endif +#include "resource/image.h" + #ifndef DENG2_QT_4_7_OR_NEWER // older than 4.7? # define constBits bits #endif using namespace de; +#ifdef __CLIENT__ + +struct GraphicFileType +{ + /// Symbolic name of the resource type. + String name; + + /// Known file extension. + String ext; + + bool (*interpretFunc)(de::FileHandle &hndl, String filePath, image_t &img); + + char const *(*getLastErrorFunc)(); ///< Can be NULL. +}; + +static bool interpretPcx(de::FileHandle &hndl, String /*filePath*/, image_t &img) +{ + Image_Init(&img); + img.pixels = PCX_Load(reinterpret_cast(&hndl), + &img.size.width, &img.size.height, &img.pixelSize); + return (0 != img.pixels); +} + +static bool interpretJpg(de::FileHandle &hndl, String /*filePath*/, image_t &img) +{ + return Image_LoadFromFileWithFormat(&img, "JPG", reinterpret_cast(&hndl)); +} + +static bool interpretPng(de::FileHandle &hndl, String /*filePath*/, image_t &img) +{ + /* + Image_Init(&img); + img.pixels = PNG_Load(reinterpret_cast(&hndl), + &img.size.width, &img.size.height, &img.pixelSize); + return (0 != img.pixels); + */ + return Image_LoadFromFileWithFormat(&img, "PNG", reinterpret_cast(&hndl)); +} + +static bool interpretTga(de::FileHandle &hndl, String /*filePath*/, image_t &img) +{ + Image_Init(&img); + img.pixels = TGA_Load(reinterpret_cast(&hndl), + &img.size.width, &img.size.height, &img.pixelSize); + return (0 != img.pixels); +} + +// Graphic resource types. +static GraphicFileType const graphicTypes[] = { + { "PNG", "png", interpretPng, 0 }, + { "JPG", "jpg", interpretJpg, 0 }, // TODO: add alternate "jpeg" extension + { "TGA", "tga", interpretTga, TGA_LastError }, + { "PCX", "pcx", interpretPcx, PCX_LastError }, + { "", "", 0, 0 } // Terminate. +}; + +static GraphicFileType const *guessGraphicFileTypeFromFileName(String fileName) +{ + // The path must have an extension for this. + String ext = fileName.fileNameExtension(); + if(!ext.isEmpty()) + { + for(int i = 0; !graphicTypes[i].ext.isEmpty(); ++i) + { + GraphicFileType const &type = graphicTypes[i]; + if(!ext.compareWithoutCase(type.ext)) + { + return &type; + } + } + } + return 0; // Unknown. +} + +static void interpretGraphic(de::FileHandle &hndl, String filePath, image_t &img) +{ + // Firstly try the interpreter for the guessed resource types. + GraphicFileType const *rtypeGuess = guessGraphicFileTypeFromFileName(filePath); + if(rtypeGuess) + { + rtypeGuess->interpretFunc(hndl, filePath, img); + } + + // If not yet interpreted - try each recognisable format in order. + if(!img.pixels) + { + // Try each recognisable format instead. + /// @todo Order here should be determined by the resource locator. + for(int i = 0; !graphicTypes[i].name.isEmpty(); ++i) + { + GraphicFileType const *graphicType = &graphicTypes[i]; + + // Already tried this? + if(graphicType == rtypeGuess) continue; + + graphicTypes[i].interpretFunc(hndl, filePath, img); + if(img.pixels) break; + } + } +} + +/// @return @c true if the file name in @a path ends with the "color key" suffix. +static inline bool isColorKeyed(String path) +{ + return path.fileNameWithoutExtension().endsWith("-ck", Qt::CaseInsensitive); +} + +#endif // __CLIENT__ + void Image_Init(image_t *img) { - DENG_ASSERT(img); + DENG2_ASSERT(img); img->size.width = 0; img->size.height = 0; img->pixelSize = 0; @@ -51,31 +167,30 @@ void Image_Init(image_t *img) void Image_Destroy(image_t *img) { - DENG_ASSERT(img); + DENG2_ASSERT(img); if(!img->pixels) return; M_Free(img->pixels); img->pixels = 0; } -void Image_PrintMetadata(image_t const *image) +void Image_PrintMetadata(image_t const *img) { - DENG_ASSERT(image); - Con_Printf("dimensions:[%ix%i] flags:%i %s:%i\n", image->size.width, image->size.height, - image->flags, 0 != image->paletteId? "colorpalette" : "pixelsize", - 0 != image->paletteId? image->paletteId : image->pixelSize); + DENG2_ASSERT(img); + Con_Message("dimensions:[%ix%i] flags:%i %s:%i", img->size.width, img->size.height, + img->flags, 0 != img->paletteId? "colorpalette" : "pixelsize", + 0 != img->paletteId? img->paletteId : img->pixelSize); } -void Image_ConvertToLuminance(image_t *image, boolean retainAlpha) +void Image_ConvertToLuminance(image_t *img, boolean retainAlpha) { - DENG_ASSERT(image); + DENG_ASSERT(img); LOG_AS("GL_ConvertToLuminance"); - uint8_t* alphaChannel = 0, *ptr = 0; - long p, numPels; + uint8_t *alphaChannel = 0, *ptr = 0; // Is this suitable? - if(0 != image->paletteId || (image->pixelSize < 3 && (image->flags & IMGF_IS_MASKED))) + if(0 != img->paletteId || (img->pixelSize < 3 && (img->flags & IMGF_IS_MASKED))) { #if _DEBUG LOG_WARNING("Attempt to convert paletted/masked image. I don't know this format!"); @@ -83,64 +198,63 @@ void Image_ConvertToLuminance(image_t *image, boolean retainAlpha) return; } - numPels = image->size.width * image->size.height; + long numPels = img->size.width * img->size.height; // Do we need to relocate the alpha data? - if(retainAlpha && image->pixelSize == 4) + if(retainAlpha && img->pixelSize == 4) { // Yes. Take a copy. - alphaChannel = reinterpret_cast(malloc(numPels)); - if(!alphaChannel) Con_Error("GL_ConvertToLuminance: Failed on allocation of %lu bytes for pixel alpha relocation buffer.", (unsigned long) numPels); + alphaChannel = reinterpret_cast(M_Malloc(numPels)); - ptr = image->pixels; - for(p = 0; p < numPels; ++p, ptr += image->pixelSize) + ptr = img->pixels; + for(long p = 0; p < numPels; ++p, ptr += img->pixelSize) { alphaChannel[p] = ptr[3]; } } // Average the RGB colors. - ptr = image->pixels; - for(p = 0; p < numPels; ++p, ptr += image->pixelSize) + ptr = img->pixels; + for(long p = 0; p < numPels; ++p, ptr += img->pixelSize) { int min = MIN_OF(ptr[0], MIN_OF(ptr[1], ptr[2])); int max = MAX_OF(ptr[0], MAX_OF(ptr[1], ptr[2])); - image->pixels[p] = (min == max? min : (min + max) / 2); + img->pixels[p] = (min == max? min : (min + max) / 2); } // Do we need to relocate the alpha data? if(alphaChannel) { - memcpy(image->pixels + numPels, alphaChannel, numPels); - image->pixelSize = 2; + std::memcpy(img->pixels + numPels, alphaChannel, numPels); + img->pixelSize = 2; M_Free(alphaChannel); return; } - image->pixelSize = 1; + img->pixelSize = 1; } -void Image_ConvertToAlpha(image_t *image, boolean makeWhite) +void Image_ConvertToAlpha(image_t *img, boolean makeWhite) { - DENG_ASSERT(image); + DENG2_ASSERT(img); - Image_ConvertToLuminance(image, true); + Image_ConvertToLuminance(img, true); - long total = image->size.width * image->size.height; + long total = img->size.width * img->size.height; for(long p = 0; p < total; ++p) { - image->pixels[total + p] = image->pixels[p]; - if(makeWhite) image->pixels[p] = 255; + img->pixels[total + p] = img->pixels[p]; + if(makeWhite) img->pixels[p] = 255; } - image->pixelSize = 2; + img->pixelSize = 2; } -boolean Image_HasAlpha(image_t const *image) +boolean Image_HasAlpha(image_t const *img) { - DENG_ASSERT(image); + DENG2_ASSERT(img); LOG_AS("Image_HasAlpha"); - if(0 != image->paletteId || (image->flags & IMGF_IS_MASKED)) + if(0 != img->paletteId || (img->flags & IMGF_IS_MASKED)) { #if _DEBUG LOG_WARNING("Attempt to determine alpha for paletted/masked image. I don't know this format!"); @@ -148,15 +262,15 @@ boolean Image_HasAlpha(image_t const *image) return false; } - if(image->pixelSize == 3) + if(img->pixelSize == 3) { return false; } - if(image->pixelSize == 4) + if(img->pixelSize == 4) { - long const numpels = image->size.width * image->size.height; - uint8_t const *in = image->pixels; + long const numpels = img->size.width * img->size.height; + uint8_t const *in = img->pixels; for(long i = 0; i < numpels; ++i, in += 4) { if(in[3] < 255) @@ -168,14 +282,66 @@ boolean Image_HasAlpha(image_t const *image) return false; } +uint8_t *Image_LoadFromFile(image_t *img, filehandle_s *_file) +{ +#ifdef __CLIENT__ + DENG2_ASSERT(img && _file); + de::FileHandle &file = reinterpret_cast(*_file); + LOG_AS("Image_LoadFromFile"); + + String filePath = file.file().composePath(); + + Image_Init(img); + interpretGraphic(file, filePath, *img); + + // Still not interpreted? + if(!img->pixels) + { + LOG_VERBOSE("\"%s\" unrecognized, trying fallback loader...") + << NativePath(filePath).pretty(); + return NULL; // Not a recognised format. It may still be loadable, however. + } + + // How about some color-keying? + if(isColorKeyed(filePath)) + { + uint8_t *out = ApplyColorKeying(img->pixels, img->size.width, img->size.height, img->pixelSize); + if(out != img->pixels) + { + // Had to allocate a larger buffer, free the old and attach the new. + M_Free(img->pixels); + img->pixels = out; + } + + // Color keying is done; now we have 4 bytes per pixel. + img->pixelSize = 4; + } + + // Any alpha pixels? + if(Image_HasAlpha(img)) + { + img->flags |= IMGF_IS_MASKED; + } + + LOG_VERBOSE("\"%s\" (%ix%i)") + << NativePath(filePath).pretty() << img->size.width << img->size.height; + + return img->pixels; +#else + // Server does not load image files. + DENG2_UNUSED2(img, _file); + return false; +#endif +} + boolean Image_LoadFromFileWithFormat(image_t *img, char const *format, filehandle_s *_hndl) { #ifdef __CLIENT__ /// @todo There are too many copies made here. It would be best if image_t /// contained an instance of QImage. -jk - DENG_ASSERT(img); - DENG_ASSERT(_hndl); + DENG2_ASSERT(img); + DENG2_ASSERT(_hndl); de::FileHandle &hndl = *reinterpret_cast(_hndl); // It is assumed that file's position stays the same (could be trying multiple interpreters). @@ -224,6 +390,7 @@ boolean Image_LoadFromFileWithFormat(image_t *img, char const *format, filehandl return true; #else // Server does not load image files. + DENG2_UNUSED3(img, format, _hndl); return false; #endif } @@ -231,7 +398,7 @@ boolean Image_LoadFromFileWithFormat(image_t *img, char const *format, filehandl boolean Image_Save(image_t const *img, char const *filePath) { #ifdef __CLIENT__ - DENG_ASSERT(img); + DENG2_ASSERT(img); // Compose the full path. String fullPath = String(filePath); @@ -252,6 +419,8 @@ boolean Image_Save(image_t const *img, char const *filePath) return CPP_BOOL(image.save(NativePath(fullPath))); #else + // Server does not save images. + DENG2_UNUSED2(img, filePath); return false; #endif }