diff --git a/engine/shared/library/CMakeLists.txt b/engine/shared/library/CMakeLists.txt index af81f7dc..e80264ab 100644 --- a/engine/shared/library/CMakeLists.txt +++ b/engine/shared/library/CMakeLists.txt @@ -7,6 +7,7 @@ add_subdirectory(sharedFoundation) add_subdirectory(sharedFoundationTypes) add_subdirectory(sharedFractal) add_subdirectory(sharedGame) +add_subdirectory(sharedImage) add_subdirectory(sharedLog) add_subdirectory(sharedMath) add_subdirectory(sharedMathArchive) diff --git a/engine/shared/library/sharedImage/CMakeLists.txt b/engine/shared/library/sharedImage/CMakeLists.txt new file mode 100644 index 00000000..6deead4a --- /dev/null +++ b/engine/shared/library/sharedImage/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 2.8) + +project(sharedImage) + +if(WIN32) + add_definitions(/D_CRT_SECURE_NO_WARNINGS) +endif() + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/public) + +add_subdirectory(src) diff --git a/engine/shared/library/sharedImage/include/public/sharedImage/ConfigSharedImage.h b/engine/shared/library/sharedImage/include/public/sharedImage/ConfigSharedImage.h new file mode 100644 index 00000000..025f5fdd --- /dev/null +++ b/engine/shared/library/sharedImage/include/public/sharedImage/ConfigSharedImage.h @@ -0,0 +1 @@ +#include "../../src/shared/ConfigSharedImage.h" diff --git a/engine/shared/library/sharedImage/include/public/sharedImage/FirstSharedImage.h b/engine/shared/library/sharedImage/include/public/sharedImage/FirstSharedImage.h new file mode 100644 index 00000000..5bbeb0b1 --- /dev/null +++ b/engine/shared/library/sharedImage/include/public/sharedImage/FirstSharedImage.h @@ -0,0 +1 @@ +#include "../../src/shared/FirstSharedImage.h" diff --git a/engine/shared/library/sharedImage/include/public/sharedImage/Image.h b/engine/shared/library/sharedImage/include/public/sharedImage/Image.h new file mode 100644 index 00000000..9fda8c31 --- /dev/null +++ b/engine/shared/library/sharedImage/include/public/sharedImage/Image.h @@ -0,0 +1 @@ +#include "../../src/shared/Image.h" diff --git a/engine/shared/library/sharedImage/include/public/sharedImage/ImageFormat.h b/engine/shared/library/sharedImage/include/public/sharedImage/ImageFormat.h new file mode 100644 index 00000000..dcdc7efa --- /dev/null +++ b/engine/shared/library/sharedImage/include/public/sharedImage/ImageFormat.h @@ -0,0 +1 @@ +#include "../../src/shared/ImageFormat.h" diff --git a/engine/shared/library/sharedImage/include/public/sharedImage/ImageFormatList.h b/engine/shared/library/sharedImage/include/public/sharedImage/ImageFormatList.h new file mode 100644 index 00000000..5194b74a --- /dev/null +++ b/engine/shared/library/sharedImage/include/public/sharedImage/ImageFormatList.h @@ -0,0 +1 @@ +#include "../../src/shared/ImageFormatList.h" diff --git a/engine/shared/library/sharedImage/include/public/sharedImage/ImageManipulation.h b/engine/shared/library/sharedImage/include/public/sharedImage/ImageManipulation.h new file mode 100644 index 00000000..9caeef67 --- /dev/null +++ b/engine/shared/library/sharedImage/include/public/sharedImage/ImageManipulation.h @@ -0,0 +1 @@ +#include "../../src/shared/ImageManipulation.h" diff --git a/engine/shared/library/sharedImage/include/public/sharedImage/SetupSharedImage.h b/engine/shared/library/sharedImage/include/public/sharedImage/SetupSharedImage.h new file mode 100644 index 00000000..604284ab --- /dev/null +++ b/engine/shared/library/sharedImage/include/public/sharedImage/SetupSharedImage.h @@ -0,0 +1 @@ +#include "../../src/shared/SetupSharedImage.h" diff --git a/engine/shared/library/sharedImage/include/public/sharedImage/TargaFormat.h b/engine/shared/library/sharedImage/include/public/sharedImage/TargaFormat.h new file mode 100644 index 00000000..6d26dd39 --- /dev/null +++ b/engine/shared/library/sharedImage/include/public/sharedImage/TargaFormat.h @@ -0,0 +1 @@ +#include "../../src/shared/TargaFormat.h" diff --git a/engine/shared/library/sharedImage/src/CMakeLists.txt b/engine/shared/library/sharedImage/src/CMakeLists.txt new file mode 100644 index 00000000..deb90a57 --- /dev/null +++ b/engine/shared/library/sharedImage/src/CMakeLists.txt @@ -0,0 +1,43 @@ + +set(SHARED_SOURCES + shared/FirstSharedImage.h + shared/ConfigSharedImage.cpp + shared/ConfigSharedImage.h + shared/Image.cpp + shared/ImageFormat.cpp + shared/ImageFormat.h + shared/ImageFormatList.cpp + shared/ImageFormatList.h + shared/Image.h + shared/ImageManipulation.cpp + shared/ImageManipulation.h + shared/SetupSharedImage.cpp + shared/SetupSharedImage.h + shared/TargaFormat.cpp + shared/TargaFormat.h +) + +if(WIN32) + set(PLATFORM_SOURCES + win32/FirstSharedImage.cpp + ) + + include_directories(${CMAKE_CURRENT_SOURCE_DIR}/win32) +else() + set(PLATFORM_SOURCES "") +endif() + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/shared + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedDebug/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedFile/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedFoundation/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedFoundationTypes/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedMemoryManager/include/public + ${SWG_EXTERNALS_SOURCE_DIR}/ours/library/fileInterface/include/public +) + +add_library(sharedImage STATIC + ${SHARED_SOURCES} + ${PLATFORM_SOURCES} +) diff --git a/engine/shared/library/sharedImage/src/shared/ConfigSharedImage.cpp b/engine/shared/library/sharedImage/src/shared/ConfigSharedImage.cpp new file mode 100644 index 00000000..a6477089 --- /dev/null +++ b/engine/shared/library/sharedImage/src/shared/ConfigSharedImage.cpp @@ -0,0 +1,42 @@ +// ====================================================================== +// +// ConfigSharedImage.cpp +// copyright 2004 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedImage/FirstSharedImage.h" +#include "sharedImage/ConfigSharedImage.h" + +#include "sharedFoundation/ConfigFile.h" + +// ====================================================================== + +#define KEY_BOOL(a,b) (ms_ ## a = ConfigFile::getKeyBool("SharedImage", #a, (b))) + +// ====================================================================== + +namespace ConfigSharedImageNamespace +{ + bool ms_sample; +} + +using namespace ConfigSharedImageNamespace; + +// ====================================================================== + +void ConfigSharedImage::install() +{ + KEY_BOOL(sample, false); +} + +// ---------------------------------------------------------------------- + +bool ConfigSharedImage::getSample() +{ + return ms_sample; +} + +// ====================================================================== + + diff --git a/engine/shared/library/sharedImage/src/shared/ConfigSharedImage.h b/engine/shared/library/sharedImage/src/shared/ConfigSharedImage.h new file mode 100644 index 00000000..d39c0a57 --- /dev/null +++ b/engine/shared/library/sharedImage/src/shared/ConfigSharedImage.h @@ -0,0 +1,24 @@ +// ====================================================================== +// +// ConfigSharedImage.h +// Copyright 2004, Sony Online Entertainment Inc. +// All Rights Reserved. +// +// ====================================================================== + +#ifndef INCLUDED_ConfigSharedImage_H +#define INCLUDED_ConfigSharedImage_H + +// ====================================================================== + +class ConfigSharedImage +{ +public: + + static void install(); + static bool getSample(); +}; + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedImage/src/shared/FirstSharedImage.h b/engine/shared/library/sharedImage/src/shared/FirstSharedImage.h new file mode 100644 index 00000000..5b93e431 --- /dev/null +++ b/engine/shared/library/sharedImage/src/shared/FirstSharedImage.h @@ -0,0 +1,17 @@ +// ====================================================================== +// +// FirstImage.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_FirstImage_H +#define INCLUDED_FirstImage_H + +// ====================================================================== + +#include "sharedFoundation/FirstSharedFoundation.h" + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedImage/src/shared/Image.cpp b/engine/shared/library/sharedImage/src/shared/Image.cpp new file mode 100644 index 00000000..06c9f21c --- /dev/null +++ b/engine/shared/library/sharedImage/src/shared/Image.cpp @@ -0,0 +1,299 @@ +// ====================================================================== +// +// Image.cpp +// copyright 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedImage/FirstSharedImage.h" +#include "sharedImage/Image.h" + +#include "sharedFoundation/Crc.h" + +// ====================================================================== +// lint supression +// ====================================================================== + +//lint -e1734 // info -- had difficulty compiling template function // maybe bug in lint? + +// ====================================================================== +// class Image non-static member functions +// ====================================================================== + +Image::Image() +: m_width(0), + m_height(0), + m_bitsPerPixel(0), + m_bytesPerPixel(0), + m_stride(0), + m_redMask(0), + m_greenMask(0), + m_blueMask(0), + m_alphaMask(0), + m_ownBuffer(true), + m_buffer(0), + m_bufferSize(0), + m_readOnly(false), + m_isLocked(false), + m_formatDirty(true), + m_format(PF_nonStandard) +{ +} + +// ---------------------------------------------------------------------- + +Image::Image(uint8 *pixelBuffer, int pixelBufferSize, int width, int height, int bitsPerPixel, int bytesPerPixel, int strideInBytes, uint redMask, uint greenMask, uint blueMask, uint alphaMask) +: m_width(width), + m_height(height), + m_bitsPerPixel(bitsPerPixel), + m_bytesPerPixel(bytesPerPixel), + m_stride(strideInBytes), + m_redMask(redMask), + m_greenMask(greenMask), + m_blueMask(blueMask), + m_alphaMask(alphaMask), + m_ownBuffer(false), + m_buffer(pixelBuffer), + m_bufferSize(pixelBufferSize), + m_readOnly(false), + m_isLocked(false), + m_formatDirty(true), + m_format(PF_nonStandard) +{ +} + +// ---------------------------------------------------------------------- + +Image::~Image() +{ + if (m_ownBuffer) + delete [] m_buffer; + else + m_buffer = 0; +} + +// ---------------------------------------------------------------------- + +void Image::setDimensions(int width, int height, int bitsPerPixel, int bytesPerPixel) +{ + VALIDATE_RANGE_INCLUSIVE_INCLUSIVE(1, bitsPerPixel, 32); + VALIDATE_RANGE_INCLUSIVE_INCLUSIVE(1, bytesPerPixel, 4); + DEBUG_FATAL( ((static_cast(bitsPerPixel)+7)>>3) > static_cast(bytesPerPixel), ("invalid bytesPerPixel set, %d bytes with %d bits\n", bytesPerPixel, bitsPerPixel)); + FATAL(m_isLocked, ("image is locked")); + + //-- calculate default stride + const int stride = bytesPerPixel * width; + + //-- set the dimensions + setDimensions(width, height, bitsPerPixel, bytesPerPixel, stride); +} + +// ---------------------------------------------------------------------- + +void Image::setDimensions(int width, int height, int bitsPerPixel, int bytesPerPixel, int strideInBytes) +{ + VALIDATE_RANGE_INCLUSIVE_INCLUSIVE(1, bitsPerPixel, 32); + VALIDATE_RANGE_INCLUSIVE_INCLUSIVE(1, bytesPerPixel, 4); + DEBUG_FATAL( ((static_cast(bitsPerPixel)+7)>>3) > static_cast(bytesPerPixel), ("invalid bytesPerPixel set, %d bytes with %d bits\n", bytesPerPixel, bitsPerPixel)); + FATAL(m_isLocked, ("image is locked")); + + //-- save image dimensions + m_width = width; + m_height = height; + m_bitsPerPixel = bitsPerPixel; + m_bytesPerPixel = bytesPerPixel; + m_stride = strideInBytes; + + //-- calculate buffer size + m_bufferSize = strideInBytes * height; + if (strideInBytes < 0) + m_bufferSize = -m_bufferSize; + FATAL(m_bufferSize < 0, ("bad buffer size [%d], width=[%d], height=[%d], stride=[%d]", m_bufferSize, width, height, strideInBytes)); + + //-- create the buffer + delete [] m_buffer; + m_buffer = new uint8[static_cast(m_bufferSize)]; + + //-- format is now dirty + m_formatDirty = true; +} + +// ---------------------------------------------------------------------- + +void Image::setPixelInformation(uint redMask, uint greenMask, uint blueMask, uint alphaMask) +{ + FATAL(m_isLocked, ("image is locked")); + + m_redMask = redMask; + m_greenMask = greenMask; + m_blueMask = blueMask; + m_alphaMask = alphaMask; + + //-- format info now is dirty + m_formatDirty = true; +} + +// ---------------------------------------------------------------------- + +uint8 *Image::lock(bool optional) +{ + //-- check for error conditions + if (optional) + { + // bail out gracefully on errors + if (m_isLocked) + { + REPORT_LOG(true, ("tried to lock() already locked image\n")); + return m_buffer; + } + + if (m_readOnly) + { + REPORT_LOG(true, ("tried to lock() read-only image\n")); + return 0; + } + } + else + { + FATAL(m_isLocked, ("tried to lock() already locked image\n")); + FATAL(m_readOnly, ("tried to lock() read-only image\n")); + } + + //-- lock it + m_isLocked = true; + return m_buffer; +} + +// ---------------------------------------------------------------------- + +const uint8 *Image::lockReadOnly(bool optional) const +{ + //-- check for error conditions + if (optional) + { + // bail out gracefully on errors + if (m_isLocked) + { + REPORT_LOG(true, ("tried to lockReadOnly() already locked image\n")); + return m_buffer; + } + } + else + { + FATAL(m_isLocked, ("tried to lockReadOnly() already locked image\n")); + } + + //-- lock it + m_isLocked = true; + return m_buffer; +} + +// ---------------------------------------------------------------------- + +void Image::unlock() const +{ + FATAL(!m_isLocked, ("image not locked")); + + m_isLocked = false; + return; +} + +// ---------------------------------------------------------------------- + +uint32 Image::calculateCrc() const +{ + FATAL(!m_buffer, ("no buffer")); + return Crc::calculate(m_buffer, m_bufferSize); +} + +// ---------------------------------------------------------------------- + +Image::PixelFormat Image::calculatePixelFormat() const +{ + const int redShiftCount = GetFirstBitSet(m_redMask); + const int greenShiftCount = GetFirstBitSet(m_greenMask); + const int blueShiftCount = GetFirstBitSet(m_blueMask); + const int alphaShiftCount = GetFirstBitSet(m_alphaMask); + + const int redBitCount = GetBitCount(m_redMask); + const int greenBitCount = GetBitCount(m_greenMask); + const int blueBitCount = GetBitCount(m_blueMask); + const int alphaBitCount = GetBitCount(m_alphaMask); + + if ((blueShiftCount < greenShiftCount) && (greenShiftCount < redShiftCount)) + { + // bgr layout + if (!m_alphaMask) + { + if ((redBitCount == 8) && (greenBitCount == 8) && (blueBitCount == 8)) + return PF_bgr_888; + else + return PF_nonStandard; + } + else + { + if (alphaShiftCount > redShiftCount) + { + if ((redBitCount == 8) && (greenBitCount == 8) && (blueBitCount == 8) && (alphaBitCount == 8)) + return PF_bgra_8888; + else + return PF_nonStandard; + } + else if (alphaShiftCount < blueShiftCount) + { + if ((redBitCount == 8) && (greenBitCount == 8) && (blueBitCount == 8) && (alphaBitCount == 8)) + return PF_abgr_8888; + else + return PF_nonStandard; + } + else + { + // don't know where this alpha is. non-standard + return PF_nonStandard; + } + } + } + else if ((redShiftCount < greenShiftCount) && (greenShiftCount < blueShiftCount)) + { + // rgb layout + if (!m_alphaMask) + { + if ((redBitCount == 8) && (greenBitCount == 8) && (blueBitCount == 8)) + return PF_rgb_888; + else + return PF_nonStandard; + } + else + { + if (alphaShiftCount > blueShiftCount) + { + if ((redBitCount == 8) && (greenBitCount == 8) && (blueBitCount == 8) && (alphaBitCount == 8)) + return PF_rgba_8888; + else + return PF_nonStandard; + } + else if (alphaShiftCount < redShiftCount) + { + if ((redBitCount == 8) && (greenBitCount == 8) && (blueBitCount == 8) && (alphaBitCount == 8)) + return PF_argb_8888; + else + return PF_nonStandard; + } + else + { + // don't know where this alpha is. non-standard + return PF_nonStandard; + } + } + } + else if(m_redMask == 0 && m_greenMask == 0 && m_blueMask == 0 && m_alphaMask == 0 && m_bitsPerPixel == 8) + { + return PF_w_8; // 8bit greyscale bitmap + } + else + { + return PF_nonStandard; + } +} + +// ====================================================================== diff --git a/engine/shared/library/sharedImage/src/shared/Image.h b/engine/shared/library/sharedImage/src/shared/Image.h new file mode 100644 index 00000000..1b42b22e --- /dev/null +++ b/engine/shared/library/sharedImage/src/shared/Image.h @@ -0,0 +1,339 @@ +// ====================================================================== +// +// Image.h +// copyright 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_Image_H +#define INCLUDED_Image_H + +// ====================================================================== + +class Image +{ +public: + + /** + * Memory layout of pixels starting with component at most significant bit of first byte + * of a pixel in memory. + * + * For x86, this is the highest bit of the least significant byte. For big-endian + * architecture, this is the highest bit of the most-significant byte of a pixel. + */ + enum PixelFormat + { + PF_bgra_8888, + PF_bgr_888, + PF_abgr_8888, + PF_rgba_8888, + PF_rgb_888, + PF_argb_8888, + PF_w_8, //8bit greyscale bitmap + PF_nonStandard + }; + + static inline void setPixel(uint8 *&o_pixel, PixelFormat pixelFormat, uint8 red, uint8 green, uint8 blue, uint8 alpha=255); + static inline void getPixel(uint8 &red, uint8 &green, uint8 &blue, uint8 &alpha, const uint8 *&i_pixel, PixelFormat pixelFormat); + static inline void copyPixel(uint8 *&o_pixel, PixelFormat destPixelFormat, const uint8 *&i_pixel, PixelFormat sourcePixelFormat); + +public: + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + class UnlockGuard + { + public: + UnlockGuard(const Image *x) : image(x) {} + ~UnlockGuard() { if (image) image->unlock(); } + private: + UnlockGuard &operator=(const UnlockGuard &); + + const Image *const image; + }; + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + Image(); + Image(uint8 *pixelBuffer, int pixelBufferSize, int width, int height, int bitsPerPixel, int bytesPerPixel, int strideInBytes, uint redMask, uint greenMask, uint blueMask, uint alphaMask); + + ~Image(); + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // image construction interface + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + void setDimensions(int width, int height, int bitsPerPixel, int bytesPerPixel); + void setDimensions(int width, int height, int bitsPerPixel, int bytesPerPixel, int strideInBytes); + + void setPixelInformation(uint redMask, uint greenMask, uint blueMask, uint alphaMask); + + uint8 *lock(bool optional = false); + + void setReadOnly(); + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // image query/read interface + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + int getWidth() const; + int getHeight() const; + int getBitsPerPixel() const; + int getBytesPerPixel() const; + int getStride() const; + int getBufferSize() const; + + uint getRedMask() const; + uint getGreenMask() const; + uint getBlueMask() const; + uint getAlphaMask() const; + + const uint8 *lockReadOnly(bool optional = false) const; + + uint32 calculateCrc() const; + PixelFormat getPixelFormat() const; + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // common interface + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + void unlock() const; + +private: + + PixelFormat calculatePixelFormat() const; + +private: + + int m_width; + int m_height; + int m_bitsPerPixel; + int m_bytesPerPixel; + int m_stride; + + uint m_redMask; + uint m_greenMask; + uint m_blueMask; + uint m_alphaMask; + + bool m_ownBuffer; + uint8 *m_buffer; + int m_bufferSize; + + bool m_readOnly; + mutable bool m_isLocked; + + mutable bool m_formatDirty; + mutable PixelFormat m_format; +}; + +// ====================================================================== + +inline void Image::setReadOnly() +{ + m_readOnly = true; +} + +// ---------------------------------------------------------------------- + +inline int Image::getWidth() const +{ + return m_width; +} + +// ---------------------------------------------------------------------- + +inline int Image::getHeight() const +{ + return m_height; +} + +// ---------------------------------------------------------------------- + +inline int Image::getBitsPerPixel() const +{ + return m_bitsPerPixel; +} + +// ---------------------------------------------------------------------- + +inline int Image::getBytesPerPixel() const +{ + return m_bytesPerPixel; +} + +// ---------------------------------------------------------------------- + +inline int Image::getStride() const +{ + return m_stride; +} + +// ---------------------------------------------------------------------- + +inline int Image::getBufferSize() const +{ + return m_bufferSize; +} + +// ---------------------------------------------------------------------- + +inline uint Image::getRedMask() const +{ + return m_redMask; +} + +// ---------------------------------------------------------------------- + +inline uint Image::getGreenMask() const +{ + return m_greenMask; +} + +// ---------------------------------------------------------------------- + +inline uint Image::getBlueMask() const +{ + return m_blueMask; +} + +// ---------------------------------------------------------------------- + +inline uint Image::getAlphaMask() const +{ + return m_alphaMask; +} + +// ---------------------------------------------------------------------- + +inline Image::PixelFormat Image::getPixelFormat() const +{ + if (m_formatDirty) + { + m_format = calculatePixelFormat(); + m_formatDirty = false; + } + + return m_format; +} + +// ---------------------------------------------------------------------- + +inline void Image::setPixel(uint8 *&o_pixel, PixelFormat pixelFormat, uint8 red, uint8 green, uint8 blue, uint8 alpha) +{ + switch (pixelFormat) + { + case PF_bgra_8888: + o_pixel[0]=blue; + o_pixel[1]=green; + o_pixel[2]=red; + o_pixel[3]=alpha; + o_pixel+=4; + break; + case PF_bgr_888: + o_pixel[0]=blue; + o_pixel[1]=green; + o_pixel[2]=red; + o_pixel+=3; + break; + case PF_abgr_8888: + o_pixel[0]=alpha; + o_pixel[1]=blue; + o_pixel[2]=green; + o_pixel[3]=red; + o_pixel+=4; + break; + case PF_rgba_8888: + o_pixel[0]=red; + o_pixel[1]=green; + o_pixel[2]=blue; + o_pixel[3]=alpha; + o_pixel+=4; + break; + case PF_rgb_888: + o_pixel[0]=red; + o_pixel[1]=green; + o_pixel[2]=blue; + o_pixel+=3; + break; + case PF_argb_8888: + o_pixel[0]=alpha; + o_pixel[1]=red; + o_pixel[2]=green; + o_pixel[3]=blue; + o_pixel+=4; + break; + case PF_w_8: + o_pixel[0]=red; + o_pixel+=1; + break; + case PF_nonStandard: + break; + } +} + +// ---------------------------------------------------------------------- + +inline void Image::getPixel(uint8 &red, uint8 &green, uint8 &blue, uint8 &alpha, const uint8 *&i_pixel, PixelFormat pixelFormat) +{ + switch (pixelFormat) + { + case PF_bgra_8888: + blue =i_pixel[0]; + green=i_pixel[1]; + red =i_pixel[2]; + alpha=i_pixel[3]; + i_pixel+=4; + break; + case PF_bgr_888: + blue =i_pixel[0]; + green=i_pixel[1]; + red =i_pixel[2]; + alpha=255; + i_pixel+=3; + break; + case PF_abgr_8888: + alpha=i_pixel[0]; + blue =i_pixel[1]; + green=i_pixel[2]; + red =i_pixel[3]; + i_pixel+=4; + break; + case PF_rgba_8888: + red =i_pixel[0]; + green=i_pixel[1]; + blue =i_pixel[2]; + alpha=i_pixel[3]; + i_pixel+=4; + break; + case PF_rgb_888: + red =i_pixel[0]; + green=i_pixel[1]; + blue =i_pixel[2]; + alpha=255; + i_pixel+=3; + break; + case PF_argb_8888: + alpha=i_pixel[0]; + red =i_pixel[1]; + green=i_pixel[2]; + blue =i_pixel[3]; + i_pixel+=4; + break; + case PF_w_8: + red=blue=green=i_pixel[0]; + alpha=255; + i_pixel+=1; + break; + case PF_nonStandard: + break; + } +} + +// ---------------------------------------------------------------------- + +inline void Image::copyPixel(uint8 *&o_pixel, PixelFormat destPixelFormat, const uint8 *&i_pixel, PixelFormat sourcePixelFormat) +{ + uint8 r,g,b,a; + getPixel(r,g,b,a,i_pixel,sourcePixelFormat); + setPixel(o_pixel, destPixelFormat, r,g,b,a); +} + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedImage/src/shared/ImageFormat.cpp b/engine/shared/library/sharedImage/src/shared/ImageFormat.cpp new file mode 100644 index 00000000..4f3e60de --- /dev/null +++ b/engine/shared/library/sharedImage/src/shared/ImageFormat.cpp @@ -0,0 +1,65 @@ +// ====================================================================== +// +// ImageFormat.cpp +// copyright 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedImage/FirstSharedImage.h" +#include "sharedImage/ImageFormat.h" + +#include "sharedFile/TreeFile.h" + +// ====================================================================== + +bool ImageFormat::loadFileCreateBuffer(const char *filename, uint8 **buffer, int *bufferSize) +{ + //-- validate args + NOT_NULL(filename); + NOT_NULL(buffer); + NOT_NULL(bufferSize); + + if (!*filename) + { + REPORT_LOG(true, ("empty filename\n")); + return false; + } + + //-- open file + AbstractFile *fileInterface = TreeFile::open(filename, AbstractFile::PriorityData, true); + if(!fileInterface) + { + REPORT_LOG(true, ("failed to open file [%s]\n", filename)); + return false; + } + + //-- create buffer + *bufferSize = fileInterface->length(); + *buffer = NON_NULL(new uint8[static_cast(*bufferSize)]); + + //-- load the buffer + const int readBytes = fileInterface->read(*buffer, *bufferSize); + delete fileInterface; + if (readBytes != *bufferSize) + { + REPORT_LOG(true, ("error: reported file size = %d, we read %d\n", *bufferSize, readBytes)); + return false; + } + + //-- no errors + return true; +} + +// ====================================================================== + +ImageFormat::ImageFormat() +{ +} + +// ---------------------------------------------------------------------- + +ImageFormat::~ImageFormat() +{ +} + +// ====================================================================== diff --git a/engine/shared/library/sharedImage/src/shared/ImageFormat.h b/engine/shared/library/sharedImage/src/shared/ImageFormat.h new file mode 100644 index 00000000..27504e0b --- /dev/null +++ b/engine/shared/library/sharedImage/src/shared/ImageFormat.h @@ -0,0 +1,43 @@ +// ====================================================================== +// +// ImageFormat.h +// copyright 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef IMAGE_FORMAT +#define IMAGE_FORMAT + +// ====================================================================== + +class Image; + +// ====================================================================== + +class ImageFormat +{ +public: + + ImageFormat(); + virtual ~ImageFormat(); + + virtual const char *getName() const = 0; + virtual bool isValidImage(const char *filename) const = 0; + virtual bool loadImage(const char *filename, Image **image) const = 0; + + virtual int getCommonExtensionCount() const = 0; + virtual const char *getCommonExtension(int index) const = 0; + +protected: + + static bool loadFileCreateBuffer(const char *filename, uint8 **buffer, int *bufferSize); + +private: + // disabled + ImageFormat(const ImageFormat&); + ImageFormat &operator =(const ImageFormat&); +}; + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedImage/src/shared/ImageFormatList.cpp b/engine/shared/library/sharedImage/src/shared/ImageFormatList.cpp new file mode 100644 index 00000000..3af7e026 --- /dev/null +++ b/engine/shared/library/sharedImage/src/shared/ImageFormatList.cpp @@ -0,0 +1,255 @@ +// ====================================================================== +// +// ImageFormatList.cpp +// copyright 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedImage/FirstSharedImage.h" +#include "sharedImage/ImageFormatList.h" + +#include "sharedFoundation/ExitChain.h" +#include "sharedImage/Image.h" +#include "sharedImage/ImageFormat.h" + +#include +#include +#include +#include + +// ====================================================================== + +bool ImageFormatList::ms_installed; + +ImageFormatList::ImageFormatVector *ImageFormatList::ms_imageFormats; +ImageFormatList::ExtensionMap *ImageFormatList::ms_extensionMap; + +// ====================================================================== + +void ImageFormatList::install() +{ + DEBUG_FATAL(ms_installed, ("ImageFormatList already installed")); + + ms_installed = true; + ExitChain::add(remove, "ImageFormatList"); + + ms_imageFormats = new ImageFormatVector(); + ms_extensionMap = new ExtensionMap(); +} + +// ---------------------------------------------------------------------- + +void ImageFormatList::remove() +{ + DEBUG_FATAL(!ms_installed, ("ImageFormatList not installed")); + + delete ms_extensionMap; + ms_extensionMap = 0; + + // we don't own the image formats, so don't try to delete them + delete ms_imageFormats; + ms_imageFormats = 0; + + ms_installed = false; +} + +// ---------------------------------------------------------------------- + +void ImageFormatList::addImageFormat(const ImageFormat *imageFormat) +{ + DEBUG_FATAL(!ms_installed, ("ImageFormatList not installed")); + NOT_NULL(ms_extensionMap); + NOT_NULL(ms_imageFormats); + NOT_NULL(imageFormat); + +#ifdef _DEBUG + //-- make sure image format isn't already installed + const ImageFormatVector::const_iterator it = std::find(ms_imageFormats->begin(), ms_imageFormats->end(), imageFormat); + DEBUG_FATAL(it != ms_imageFormats->end(), ("imageFormat [0x%08x, (%s)] already installed", imageFormat->getName())); +#endif + + //-- add image format to list of image formats + ms_imageFormats->push_back(imageFormat); + + //-- add image format to extension map (allows us to try a format by file extension first) + const int extensionCount = imageFormat->getCommonExtensionCount(); + for (int i = 0; i < extensionCount; ++i) + { + //-- get lowercase extension + std::string extension = imageFormat->getCommonExtension(i); + IGNORE_RETURN(std::transform(extension.begin(), extension.end(), extension.begin(), tolower)); + + //-- map extension to this image format + std::pair result = ms_extensionMap->insert(std::make_pair(extension, imageFormat)); + UNREF(result); + DEBUG_REPORT_LOG(!result.second, ("image format [%s] has conflicting default extension [%s] with [%s]\n", imageFormat->getName(), extension.c_str(), (*result.first).second->getName())); + } +} + +// ---------------------------------------------------------------------- + +void ImageFormatList::removeImageFormat(const ImageFormat *imageFormat) +{ + DEBUG_FATAL(!ms_installed, ("ImageFormatList not installed")); + NOT_NULL(ms_extensionMap); + NOT_NULL(ms_imageFormats); + NOT_NULL(imageFormat); + + //-- remove image format from extension map + { + ExtensionMap::iterator it; + ExtensionMap::const_iterator itEnd; + do + { + it = ms_extensionMap->begin(); + itEnd = ms_extensionMap->end(); + for (; it != itEnd; ++it) + { + if ((*it).second == imageFormat) + { + // found one, remove it + ms_extensionMap->erase(it); + + // we invalidate the iterator once we modify the container, so restart search + break; + } + } + } + while (it != itEnd); + } + + //-- remove image format from image format list + { + // find the image format + ImageFormatVector::iterator it = std::find(ms_imageFormats->begin(), ms_imageFormats->end(), imageFormat); + DEBUG_FATAL(it == ms_imageFormats->end(), ("failed to find image format specified for removal")); + + // remove it + // we don't own the formats, so don't erase it + if (it != ms_imageFormats->end()) + IGNORE_RETURN(ms_imageFormats->erase(it)); + } +} + +// ---------------------------------------------------------------------- + +int ImageFormatList::getImageFormatCount() +{ + DEBUG_FATAL(!ms_installed, ("ImageFormatList not installed")); + NOT_NULL(ms_imageFormats); + + return static_cast(ms_imageFormats->size()); +} + +// ---------------------------------------------------------------------- + +const ImageFormat *ImageFormatList::getImageFormat(int index) +{ + DEBUG_FATAL(!ms_installed, ("ImageFormatList not installed")); + NOT_NULL(ms_imageFormats); + VALIDATE_RANGE_INCLUSIVE_EXCLUSIVE(0, index, static_cast(ms_imageFormats->size())); + + return (*ms_imageFormats)[static_cast(index)]; +} + +// ---------------------------------------------------------------------- + +Image *ImageFormatList::loadImage(const char *filename, bool readOnly) +{ + DEBUG_FATAL(!ms_installed, ("ImageFormatList not installed")); + NOT_NULL(filename); + + //-- first try to find format by the extension of the filename + const char *extensionDot = strrchr(filename, '.'); + if (extensionDot) + { + // convert extension to lower case + std::string extension(extensionDot+1); + IGNORE_RETURN(std::transform(extension.begin(), extension.end(), extension.begin(), tolower)); + + // see if we have a file format for this extension + NOT_NULL(ms_extensionMap); + const ExtensionMap::const_iterator it = ms_extensionMap->find(extension); + if (it != ms_extensionMap->end()) + { + // found a file format that thinks it knows how to open this file by its extension + const ImageFormat *const imageFormat = (*it).second; + NOT_NULL(imageFormat); + + Image *image = 0; + const bool loadSuccess = imageFormat->loadImage(filename, &image); + DEBUG_REPORT_LOG(!loadSuccess, ("default image format [%s] thinks image [%s] is invalid, trying alternate formats\n", imageFormat->getName(), filename)); + if (loadSuccess) + { + // loaded the image + NOT_NULL(image); + if (readOnly) + image->setReadOnly(); + return image; + } + } + } + // didn't find an image format that knows how to load by extension, so let's + // try them all. file might be saved as a non-standard extension. + + //-- find the first format that thinks it can load the format + { + NOT_NULL(ms_imageFormats); + + const size_t count = ms_imageFormats->size(); + for (size_t i = 0; i < count; ++i) + { + const ImageFormat *const imageFormat = (*ms_imageFormats)[i]; + + if (imageFormat->isValidImage(filename)) + { + // image format thinks it can load this + Image *image = 0; + const bool loadSuccess = imageFormat->loadImage(filename, &image); + DEBUG_REPORT_LOG(!loadSuccess, ("image format [%s] thought it could load [%s] but failed, trying others\n", imageFormat->getName(), filename)); + if (loadSuccess) + { + // loaded the image + NOT_NULL(image); + if (readOnly) + image->setReadOnly(); + return image; + } + } + } + } + + //-- failed + return 0; +} + +// ---------------------------------------------------------------------- + +Image *ImageFormatList::loadImage(const char *filename, int formatIndex, bool readOnly) +{ + DEBUG_FATAL(!ms_installed, ("ImageFormatList not installed")); + + //-- get the image format + const ImageFormat *const imageFormat = getImageFormat(formatIndex); + NOT_NULL(imageFormat); + + //-- load the image + Image *image = 0; + const bool loadSuccess = imageFormat->loadImage(filename, &image); + DEBUG_REPORT_LOG(!loadSuccess, ("image format [%s] (format specified by user) failed to load [%s]\n", imageFormat->getName(), filename)); + if (loadSuccess) + { + // loaded the image + NOT_NULL(image); + if (readOnly) + image->setReadOnly(); + return image; + } + else + { + // failed to load image + return 0; + } +} + +// ====================================================================== diff --git a/engine/shared/library/sharedImage/src/shared/ImageFormatList.h b/engine/shared/library/sharedImage/src/shared/ImageFormatList.h new file mode 100644 index 00000000..b59076c1 --- /dev/null +++ b/engine/shared/library/sharedImage/src/shared/ImageFormatList.h @@ -0,0 +1,53 @@ +// ====================================================================== +// +// ImageFormatList.h +// copyright 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_ImageFormatList_H +#define INCLUDED_ImageFormatList_H + +// ====================================================================== + +class Image; +class ImageFormat; + +// ====================================================================== + +class ImageFormatList +{ +public: + + static void install(); + + static void addImageFormat(const ImageFormat *imageFormat); + static void removeImageFormat(const ImageFormat *imageFormat); + + static int getImageFormatCount(); + static const ImageFormat *getImageFormat(int index); + + static Image *loadImage(const char *filename, bool readOnly = true); + static Image *loadImage(const char *filename, int formatIndex, bool readOnly = true); + +private: + + static void remove(); + +private: + + typedef stdvector::fwd ImageFormatVector; + typedef stdmap::fwd ExtensionMap; + +private: + + static bool ms_installed; + + static ImageFormatVector *ms_imageFormats; + static ExtensionMap *ms_extensionMap; + +}; + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedImage/src/shared/ImageManipulation.cpp b/engine/shared/library/sharedImage/src/shared/ImageManipulation.cpp new file mode 100644 index 00000000..4495b31e --- /dev/null +++ b/engine/shared/library/sharedImage/src/shared/ImageManipulation.cpp @@ -0,0 +1,532 @@ +// ====================================================================== +// +// ImageManipulation.cpp +// copyright 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedImage/FirstSharedImage.h" +#include "sharedImage/ImageManipulation.h" + +#include "sharedFoundation/ExitChain.h" +#include "sharedImage/Image.h" + +// ====================================================================== + +bool ImageManipulation::ms_installed; + +ImageManipulation::NextMipmapFunction ImageManipulation::ms_nextMipmapFunction; +ImageManipulation::ConvertFormatFunction ImageManipulation::ms_convertFormatFunction; +ImageManipulation::CopyFunction ImageManipulation::ms_copyIgnoreAlphaFunction; +ImageManipulation::CopyFunction ImageManipulation::ms_copyRespectAlphaFunction; +ImageManipulation::BlendFunction ImageManipulation::ms_blendFunction; +ImageManipulation::BlendTwoFunction ImageManipulation::ms_blendTwoFunction; + +// ====================================================================== + +namespace ImageManipulationNamespace +{ + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + template + struct DefaultNextMipmapFunction + { + static void nextMipmapFunction(const Image &sourceImage, Image &destImage); + static void nextMipmapFunction_EqualWidth(const Image &sourceImage, Image &destImage); + static void nextMipmapFunction_EqualHeight(const Image &sourceImage, Image &destImage); + }; + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + template + struct PixelTraitsThreeByOne + { + static int getPixelStride(); + static void copyWithPowerOfTwoDivide(const uint8 *sourcePixel, uint8 *destPixel, uint8 powerOfTwoDivide); + static void addWithPowerOfTwoDivide(const uint8 *sourcePixel, uint8 *destPixel, uint8 powerOfTwoDivide); + }; + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + template + struct PixelTraitsFourByOne + { + static int getPixelStride(); + static void copyWithPowerOfTwoDivide(const uint8 *sourcePixel, uint8 *destPixel, uint8 powerOfTwoDivide); + static void addWithPowerOfTwoDivide(const uint8 *sourcePixel, uint8 *destPixel, uint8 powerOfTwoDivide); + }; +} + +using namespace ImageManipulationNamespace; + +// ====================================================================== + +template +void ImageManipulationNamespace::DefaultNextMipmapFunction::nextMipmapFunction(const Image &sourceImage, Image &destImage) +{ + const uint8 *const sourceBuffer = sourceImage.lockReadOnly(); + const uint8 *sourceLine = sourceBuffer; + const int sourceStride = sourceImage.getStride(); + + const int destHeight = destImage.getHeight(); + const int destWidth = destImage.getWidth(); + uint8 *const destBuffer = destImage.lock(); + uint8 *destLine = destBuffer; + const int destStride = destImage.getStride(); + + // The strategy we follow in this algorithm attempts to optimize cache coherency. + // Each dest pixel is written with a simple 2x2 box filter over the source pixels. + // Each source pixel weights at 25%. So that we always read linearly from the + // source data, we write each dest line in two passes. The first pass writes in + // the pixel contribution to the pixel from the first source line. The + // second pass writes in the contribution from the second source line. + + for (int destY = 0; destY < destHeight; ++destY) + { + //-- read first source line, applying 25% of each source pixel into dest pixel + const uint8 *sourcePixel = sourceLine; + uint8 *destPixel = destLine; + + { + for (int destX = 0; destX < destWidth; ++destX) + { + //-- handle source upper left (first pixel gets copied in) + PixelTraits::copyWithPowerOfTwoDivide(sourcePixel, destPixel, 2); + sourcePixel += PixelTraits::getPixelStride(); + + //-- handle source upper right + PixelTraits::addWithPowerOfTwoDivide(sourcePixel, destPixel, 2); + sourcePixel += PixelTraits::getPixelStride(); + + //-- move on to next dest pixel + destPixel += PixelTraits::getPixelStride(); + } + } + + //-- read second source line, applying 25% of each source pixel into dest pixel + sourceLine += sourceStride; + + sourcePixel = sourceLine; + destPixel = destLine; + + { + for (int destX = 0; destX < destWidth; ++destX) + { + //-- handle source lower left + PixelTraits::addWithPowerOfTwoDivide(sourcePixel, destPixel, 2); + sourcePixel += PixelTraits::getPixelStride(); + + //-- handle source lower right + PixelTraits::addWithPowerOfTwoDivide(sourcePixel, destPixel, 2); + sourcePixel += PixelTraits::getPixelStride(); + + //-- move on to next dest pixel + destPixel += PixelTraits::getPixelStride(); + } + } + + //-- increment source and dest line pointers + destLine += destStride; + sourceLine += sourceStride; + } + + //-- cleanup + destImage.unlock(); + sourceImage.unlock(); +} + +// ---------------------------------------------------------------------- +/** + * This function generates the next smaller mipmap in the Y direction but + * preserves the image in the x direction. + * + * This function would be used when we are mipmapping a 1x8 down to a 1x4. + * The X contribution to color is 50% for the current pixel. + */ + +template +void ImageManipulationNamespace::DefaultNextMipmapFunction::nextMipmapFunction_EqualWidth(const Image &sourceImage, Image &destImage) +{ + DEBUG_FATAL(sourceImage.getWidth() != destImage.getWidth(), ("This function only makes sense when source width [%d] is the same as dest width [%d].", sourceImage.getWidth(), destImage.getWidth())); + + const uint8 *const sourceBuffer = sourceImage.lockReadOnly(); + const uint8 *sourceLine = sourceBuffer; + const int sourceStride = sourceImage.getStride(); + + const int destHeight = destImage.getHeight(); + const int destWidth = destImage.getWidth(); + uint8 *const destBuffer = destImage.lock(); + uint8 *destLine = destBuffer; + const int destStride = destImage.getStride(); + + // The strategy we follow in this algorithm attempts to optimize cache coherency. + // Each dest pixel is written with a simple 1x2 box filter over the source pixels. + // Each source pixel weights at 50%. So that we always read linearly from the + // source data, we write each dest line in two passes. The first pass writes in + // the pixel contribution to the pixel from the first source line. The + // second pass writes in the contribution from the second source line. + + for (int destY = 0; destY < destHeight; ++destY) + { + //-- read first source line, applying 50% of each source pixel into dest pixel + const uint8 *sourcePixel = sourceLine; + uint8 *destPixel = destLine; + + { + for (int destX = 0; destX < destWidth; ++destX) + { + //-- handle upper pixel (first pixel gets copied in) + PixelTraits::copyWithPowerOfTwoDivide(sourcePixel, destPixel, 1); + + //-- move on to next pixel + sourcePixel += PixelTraits::getPixelStride(); + destPixel += PixelTraits::getPixelStride(); + } + } + + //-- read second source line, applying 50% of each source pixel into dest pixel + sourceLine += sourceStride; + + sourcePixel = sourceLine; + destPixel = destLine; + + { + for (int destX = 0; destX < destWidth; ++destX) + { + //-- handle source lower pixel + PixelTraits::addWithPowerOfTwoDivide(sourcePixel, destPixel, 1); + + //-- move on to next dest pixel + sourcePixel += PixelTraits::getPixelStride(); + destPixel += PixelTraits::getPixelStride(); + } + } + + //-- increment source and dest line pointers + destLine += destStride; + sourceLine += sourceStride; + } + + //-- cleanup + destImage.unlock(); + sourceImage.unlock(); +} + +// ---------------------------------------------------------------------- +/** + * This function generates the next smaller mipmap in the X direction but + * preserves the image in the Y direction. + * + * This function would be used when we are mipmapping an 8x1 down to a 4x1. + * The Y contribution to color is 50% for the current pixel. + */ + + +template +void ImageManipulationNamespace::DefaultNextMipmapFunction::nextMipmapFunction_EqualHeight(const Image &sourceImage, Image &destImage) +{ + DEBUG_FATAL(sourceImage.getHeight() != destImage.getHeight(), ("This function only makes sense when source height [%d] is the same as dest height [%d].", sourceImage.getHeight(), destImage.getHeight())); + + const uint8 *const sourceBuffer = sourceImage.lockReadOnly(); + const uint8 *sourceLine = sourceBuffer; + const int sourceStride = sourceImage.getStride(); + + const int destHeight = destImage.getHeight(); + const int destWidth = destImage.getWidth(); + uint8 *const destBuffer = destImage.lock(); + uint8 *destLine = destBuffer; + const int destStride = destImage.getStride(); + + // The strategy we follow in this algorithm attempts to optimize cache coherency. + // Each dest pixel is written with a simple 2x1 box filter over the source pixels. + // Each source pixel weights at 50%. + + for (int destY = 0; destY < destHeight; ++destY) + { + //-- read first source line, applying 50% of each source pixel into dest pixel + const uint8 *sourcePixel = sourceLine; + uint8 *destPixel = destLine; + + { + for (int destX = 0; destX < destWidth; ++destX) + { + //-- handle source left (first pixel gets copied in) + PixelTraits::copyWithPowerOfTwoDivide(sourcePixel, destPixel, 1); + sourcePixel += PixelTraits::getPixelStride(); + + //-- handle source right + PixelTraits::addWithPowerOfTwoDivide(sourcePixel, destPixel, 1); + sourcePixel += PixelTraits::getPixelStride(); + + //-- move on to next dest pixel + destPixel += PixelTraits::getPixelStride(); + } + } + + //-- increment source and dest line pointers + destLine += destStride; + sourceLine += sourceStride; + } + + //-- cleanup + destImage.unlock(); + sourceImage.unlock(); +} + +// ====================================================================== +// template class PixelTraitsThreeByOne +// ====================================================================== + +template +inline int ImageManipulationNamespace::PixelTraitsThreeByOne::getPixelStride() +{ + return 3; +} + +// ---------------------------------------------------------------------- + +template +inline void ImageManipulationNamespace::PixelTraitsThreeByOne::copyWithPowerOfTwoDivide(const uint8 *sourcePixel, uint8 *destPixel, uint8 powerOfTwoDivide) +{ + destPixel[0] = static_cast(sourcePixel[0] >> powerOfTwoDivide); + destPixel[1] = static_cast(sourcePixel[1] >> powerOfTwoDivide); + destPixel[2] = static_cast(sourcePixel[2] >> powerOfTwoDivide); +} + +// ---------------------------------------------------------------------- + +template +inline void ImageManipulationNamespace::PixelTraitsThreeByOne::addWithPowerOfTwoDivide(const uint8 *sourcePixel, uint8 *destPixel, uint8 powerOfTwoDivide) +{ + destPixel[0] = static_cast(destPixel[0] + static_cast(sourcePixel[0] >> powerOfTwoDivide)); + destPixel[1] = static_cast(destPixel[1] + static_cast(sourcePixel[1] >> powerOfTwoDivide)); + destPixel[2] = static_cast(destPixel[2] + static_cast(sourcePixel[2] >> powerOfTwoDivide)); +} + +// ====================================================================== +// template class PixelTraitsFourByOne +// ====================================================================== + +template +inline int ImageManipulationNamespace::PixelTraitsFourByOne::getPixelStride() +{ + return 4; +} + +// ---------------------------------------------------------------------- + +template +inline void ImageManipulationNamespace::PixelTraitsFourByOne::copyWithPowerOfTwoDivide(const uint8 *sourcePixel, uint8 *destPixel, uint8 powerOfTwoDivide) +{ + destPixel[0] = static_cast(sourcePixel[0] >> powerOfTwoDivide); + destPixel[1] = static_cast(sourcePixel[1] >> powerOfTwoDivide); + destPixel[2] = static_cast(sourcePixel[2] >> powerOfTwoDivide); + destPixel[3] = static_cast(sourcePixel[3] >> powerOfTwoDivide); +} + +// ---------------------------------------------------------------------- + +template +inline void ImageManipulationNamespace::PixelTraitsFourByOne::addWithPowerOfTwoDivide(const uint8 *sourcePixel, uint8 *destPixel, uint8 powerOfTwoDivide) +{ + destPixel[0] = static_cast(destPixel[0] + static_cast(sourcePixel[0] >> powerOfTwoDivide)); + destPixel[1] = static_cast(destPixel[1] + static_cast(sourcePixel[1] >> powerOfTwoDivide)); + destPixel[2] = static_cast(destPixel[2] + static_cast(sourcePixel[2] >> powerOfTwoDivide)); + destPixel[3] = static_cast(destPixel[3] + static_cast(sourcePixel[3] >> powerOfTwoDivide)); +} + +// ====================================================================== +// struct ImageManipulation::InstallData +// ====================================================================== + +ImageManipulation::InstallData::InstallData() +: m_nextMipmapFunction(0), + m_convertFormatFunction(0), + m_copyIgnoreAlphaFunction(0), + m_copyRespectAlphaFunction(0), + m_blendFunction(0), + m_blendTwoFunction(0) +{ +} + +// ====================================================================== +// class ImageManipulation +// ====================================================================== + +void ImageManipulation::install(const InstallData &installData) +{ + DEBUG_FATAL(ms_installed, ("ImageManipulation already installed")); + + ms_installed = true; + ExitChain::add(remove, "ImageManipulation"); + + if (installData.m_nextMipmapFunction) + ms_nextMipmapFunction = installData.m_nextMipmapFunction; + else + ms_nextMipmapFunction = defaultNextMipmapFunction; + + //-- make sure we've got valid function pointers for all + NOT_NULL(ms_nextMipmapFunction); +#if 0 + NOT_NULL(ms_convertFormatFunction); + NOT_NULL(ms_copyIgnoreAlphaFunction); + NOT_NULL(ms_copyRespectAlphaFunction); + NOT_NULL(ms_blendFunction); + NOT_NULL(ms_blendTwoFunction); +#endif +} + +// ---------------------------------------------------------------------- + +void ImageManipulation::remove() +{ + DEBUG_FATAL(!ms_installed, ("ImageManipulation not installed")); + + ms_nextMipmapFunction = 0; + ms_convertFormatFunction = 0; + ms_copyIgnoreAlphaFunction = 0; + ms_copyRespectAlphaFunction = 0; + ms_blendFunction = 0; + ms_blendTwoFunction = 0; +} + +// ---------------------------------------------------------------------- + +void ImageManipulation::defaultNextMipmapFunction(const Image &sourceImage, Image &destImage) +{ + const Image::PixelFormat pixelFormat = sourceImage.getPixelFormat(); + + DEBUG_FATAL(pixelFormat != destImage.getPixelFormat(), ("source and dest image have different pixel layout")); + DEBUG_FATAL(pixelFormat == Image::PF_nonStandard, ("cannot handle non-standard pixel layouts")); + + DEBUG_FATAL(!IsPowerOfTwo(sourceImage.getWidth()), ("source width not power of two %d", sourceImage.getWidth())); + DEBUG_FATAL(!IsPowerOfTwo(sourceImage.getHeight()), ("source height not power of two %d", sourceImage.getHeight())); + + const int destWidth = destImage.getWidth(); + const int destHeight = destImage.getHeight(); + const int sourceWidth = sourceImage.getWidth(); + const int sourceHeight = sourceImage.getHeight(); + + DEBUG_FATAL(destWidth < 1, ("destImage width invalid [%d].", destWidth)); + DEBUG_FATAL(sourceWidth < 1, ("sourceImage width invalid [%d].", sourceWidth)); + DEBUG_FATAL(destHeight < 1, ("destImage height invalid [%d].", destHeight)); + DEBUG_FATAL(sourceHeight < 1, ("sourceImage height invalid [%d].", sourceHeight)); + + VALIDATE_RANGE_INCLUSIVE_INCLUSIVE(sourceWidth / 2, destWidth, sourceWidth); + VALIDATE_RANGE_INCLUSIVE_INCLUSIVE(sourceHeight / 2, destHeight, sourceHeight); + + //-- select the proper templated function + NextMipmapFunction workerFunction = 0; + + if ((destWidth == sourceWidth / 2) && (destHeight == sourceHeight / 2)) + { + // Mipmap down in both x and y direction. + switch (pixelFormat) + { + case Image::PF_bgr_888: + workerFunction = DefaultNextMipmapFunction >::nextMipmapFunction; + break; + + case Image::PF_rgb_888: + workerFunction = DefaultNextMipmapFunction >::nextMipmapFunction; + break; + + case Image::PF_bgra_8888: + workerFunction = DefaultNextMipmapFunction >::nextMipmapFunction; + break; + + case Image::PF_abgr_8888: + workerFunction = DefaultNextMipmapFunction >::nextMipmapFunction; + break; + + case Image::PF_rgba_8888: + workerFunction = DefaultNextMipmapFunction >::nextMipmapFunction; + break; + + case Image::PF_argb_8888: + workerFunction = DefaultNextMipmapFunction >::nextMipmapFunction; + break; + + case Image::PF_nonStandard: + default: + FATAL(true, ("unsupported format for conversion")); + } + } + else if ((destWidth == sourceWidth) && (destHeight == sourceHeight / 2)) + { + // Mipmap down in y, preserve x direction. + switch (pixelFormat) + { + case Image::PF_bgr_888: + workerFunction = DefaultNextMipmapFunction >::nextMipmapFunction_EqualWidth; + break; + + case Image::PF_rgb_888: + workerFunction = DefaultNextMipmapFunction >::nextMipmapFunction_EqualWidth; + break; + + case Image::PF_bgra_8888: + workerFunction = DefaultNextMipmapFunction >::nextMipmapFunction_EqualWidth; + break; + + case Image::PF_abgr_8888: + workerFunction = DefaultNextMipmapFunction >::nextMipmapFunction_EqualWidth; + break; + + case Image::PF_rgba_8888: + workerFunction = DefaultNextMipmapFunction >::nextMipmapFunction_EqualWidth; + break; + + case Image::PF_argb_8888: + workerFunction = DefaultNextMipmapFunction >::nextMipmapFunction_EqualWidth; + break; + + case Image::PF_nonStandard: + default: + FATAL(true, ("unsupported format for conversion")); + } + } + else if ((destWidth == sourceWidth / 2) && (destHeight == sourceHeight)) + { + // Mipmap down in y, preserve x direction. + switch (pixelFormat) + { + case Image::PF_bgr_888: + workerFunction = DefaultNextMipmapFunction >::nextMipmapFunction_EqualHeight; + break; + + case Image::PF_rgb_888: + workerFunction = DefaultNextMipmapFunction >::nextMipmapFunction_EqualHeight; + break; + + case Image::PF_bgra_8888: + workerFunction = DefaultNextMipmapFunction >::nextMipmapFunction_EqualHeight; + break; + + case Image::PF_abgr_8888: + workerFunction = DefaultNextMipmapFunction >::nextMipmapFunction_EqualHeight; + break; + + case Image::PF_rgba_8888: + workerFunction = DefaultNextMipmapFunction >::nextMipmapFunction_EqualHeight; + break; + + case Image::PF_argb_8888: + workerFunction = DefaultNextMipmapFunction >::nextMipmapFunction_EqualHeight; + break; + + case Image::PF_nonStandard: + default: + FATAL(true, ("unsupported format for conversion")); + } + } + else + FATAL(true, ("Unexpected error: mipmap generation failing [source w=%d,h=%d], [dest w=%d,h=%d].", sourceWidth, sourceHeight, destWidth, destHeight)); + + NOT_NULL(workerFunction); + + //-- call the function + (*workerFunction)(sourceImage, destImage); +} + +// ====================================================================== diff --git a/engine/shared/library/sharedImage/src/shared/ImageManipulation.h b/engine/shared/library/sharedImage/src/shared/ImageManipulation.h new file mode 100644 index 00000000..4a8d6bbd --- /dev/null +++ b/engine/shared/library/sharedImage/src/shared/ImageManipulation.h @@ -0,0 +1,118 @@ +// ====================================================================== +// +// ImageManipulation.h +// copyright 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_ImageManipulation_H +#define INCLUDED_ImageManipulation_H + +// ====================================================================== + +class Image; + +// ====================================================================== + +/** + * This class encapsulates functionality to manipulate pre-existing Images. + * + * This class provides an interface for manipulating images. This includes + * generating mipmap levels and converting from one format to another. + * This functionality was not included as part of class Image because all + * these functions deal with multiple images. Perhaps more importantly, + * each one of these functions can be implemented in multiple ways on a + * single platform dependent on the processor (e.g. Intel: SSE or CPU ALU). + */ + +class ImageManipulation +{ +public: + + typedef void (*NextMipmapFunction)(const Image &sourceImage, Image &destImage); + typedef void (*ConvertFormatFunction)(const Image &sourceImage, Image &destImage); + typedef void (*CopyFunction)(const Image &sourceImage, int sourceX, int sourceY, int width, int height, Image &destImage, int destX, int destY); + typedef void (*BlendFunction)(float sourceBlendFactor, const Image &sourceImage, int sourceX, int sourceY, int width, int height, Image &destImage, int destX, int destY); + typedef void (*BlendTwoFunction)(float firstSourceBlendFactor, float resultToDestBlendFactor, const Image &sourceImage1, const Image &sourceImage2, int sourceX, int sourceY, int width, int height, Image &destImage, int destX, int destY); + + struct InstallData + { + public: + + InstallData(); + + public: + + NextMipmapFunction m_nextMipmapFunction; + ConvertFormatFunction m_convertFormatFunction; + CopyFunction m_copyIgnoreAlphaFunction; + CopyFunction m_copyRespectAlphaFunction; + BlendFunction m_blendFunction; + BlendTwoFunction m_blendTwoFunction; + }; + +public: + + static void install(const InstallData &installData); + + static void generateNextSmallerMipmap(const Image &sourceImage, Image &destImage); + +#if 0 + static void convertFormatSameSize(const Image &sourceImage, Image &destImage); + + // these functions require same pixel format. they do not stretch or compress pixels. + static void copyIgnoreAlpha(const Image &sourceImage, int sourceX, int sourceY, int width, int height, Image &destImage, int destX, int destY); + static void copyRespectAlpha(const Image &sourceImage, int sourceX, int sourceY, int width, int height, Image &destImage, int destX, int destY); + + // these two always respect alpha + static void blend(float sourceBlendFactor, const Image &sourceImage, int sourceX, int sourceY, int width, int height, Image &destImage, int destX, int destY); + static void blendTwo(float firstSourceBlendFactor, float resultToDestBlendFactor, const Image &sourceImage1, const Image &sourceImage2, int sourceX, int sourceY, int width, int height, Image &destImage, int destX, int destY); +#endif + +private: + + static void remove(); + + static void defaultNextMipmapFunction(const Image &sourceImage, Image &destImage); + +private: + + static bool ms_installed; + + static NextMipmapFunction ms_nextMipmapFunction; + static ConvertFormatFunction ms_convertFormatFunction; + static CopyFunction ms_copyIgnoreAlphaFunction; + static CopyFunction ms_copyRespectAlphaFunction; + static BlendFunction ms_blendFunction; + static BlendTwoFunction ms_blendTwoFunction; + +}; + +// ====================================================================== +/** + * Generate the next smaller mipmap level from sourceImage and place in + * destImage. + * + * sourceImage and destImage must be the same pixel format. + * + * sourceImage width and height must be a power of two, with no length + * less than 1. + * + * for now, assume we always preserve texture length aspect ratio. + * thus, we do not support generating the next mipmap level down from + * an 8x1 texture since we cannot have a length less than one. + * + * sourceImage and destImage are unlocked upon entry. they will be + * unlocked upon exit. + */ + +inline void ImageManipulation::generateNextSmallerMipmap(const Image &sourceImage, Image &destImage) +{ + DEBUG_FATAL(!ms_installed, ("ImageManipulation not installed")); + + (*ms_nextMipmapFunction)(sourceImage, destImage); +} + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedImage/src/shared/SetupSharedImage.cpp b/engine/shared/library/sharedImage/src/shared/SetupSharedImage.cpp new file mode 100644 index 00000000..982bbddb --- /dev/null +++ b/engine/shared/library/sharedImage/src/shared/SetupSharedImage.cpp @@ -0,0 +1,72 @@ +// ====================================================================== +// +// SetupSharedImage.cpp +// copyright 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedImage/FirstSharedImage.h" +#include "sharedImage/SetupSharedImage.h" + +#include "sharedFoundation/ExitChain.h" +#include "sharedImage/ConfigSharedImage.h" +#include "sharedImage/ImageFormatList.h" +#include "sharedImage/ImageManipulation.h" +#include "sharedImage/TargaFormat.h" +#include "sharedDebug/InstallTimer.h" + +// ====================================================================== + +bool SetupSharedImage::ms_installed; +TargaFormat *SetupSharedImage::ms_targaFormat; + +// ====================================================================== + +void SetupSharedImage::install(const Data &data) +{ + InstallTimer const installTimer("SetupSharedImage::install"); + + ConfigSharedImage::install(); + + DEBUG_FATAL(ms_installed, ("SetupSharedImage already installed")); + + ImageFormatList::install(); + ImageManipulation::InstallData imInstallData; + + // -TRF- have ImagePlatform install platform-specific handlers into imInstallData here + ImageManipulation::install(imInstallData); + + ms_installed = true; + ExitChain::add(remove, "SetupSharedImage"); + + // add support for specified formats + if (data.m_supportTarga) + { + ms_targaFormat = new TargaFormat(); + ImageFormatList::addImageFormat(ms_targaFormat); + } +} + +// ---------------------------------------------------------------------- + +void SetupSharedImage::remove() +{ + DEBUG_FATAL(!ms_installed, ("SetupSharedImage not installed")); + + // remove supported formats + if (ms_targaFormat) + { + ImageFormatList::removeImageFormat(ms_targaFormat); + delete ms_targaFormat; + ms_targaFormat = 0; + } +} + +// ---------------------------------------------------------------------- + +void SetupSharedImage::setupDefaultData(Data &data) +{ + data.m_supportTarga = true; +} + +// ====================================================================== diff --git a/engine/shared/library/sharedImage/src/shared/SetupSharedImage.h b/engine/shared/library/sharedImage/src/shared/SetupSharedImage.h new file mode 100644 index 00000000..d61bdd25 --- /dev/null +++ b/engine/shared/library/sharedImage/src/shared/SetupSharedImage.h @@ -0,0 +1,43 @@ +// ====================================================================== +// +// SetupSharedImage.h +// copyright 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_SetupSharedImage_H +#define INCLUDED_SetupSharedImage_H + +// ====================================================================== + +class TargaFormat; + +// ====================================================================== + +class SetupSharedImage +{ +public: + + struct Data + { + bool m_supportTarga; + }; + +public: + + static void setupDefaultData(Data &data); + static void install(const Data &data); + +private: + + static void remove(); + +private: + + static bool ms_installed; + static TargaFormat *ms_targaFormat; +}; + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedImage/src/shared/TargaFormat.cpp b/engine/shared/library/sharedImage/src/shared/TargaFormat.cpp new file mode 100644 index 00000000..e85772d5 --- /dev/null +++ b/engine/shared/library/sharedImage/src/shared/TargaFormat.cpp @@ -0,0 +1,1295 @@ +// ====================================================================== +// +// TargaFormat.cpp +// copyright 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedImage/FirstSharedImage.h" +#include "sharedImage/TargaFormat.h" +#include "sharedImage/Image.h" + +#include "sharedFile/TreeFile.h" + +#include +#include + +#if defined(PLATFORM_LINUX) +#include +#endif + +// ====================================================================== + +#ifndef TARGA_EVEN_PIXEL_EXPANSION +#define TARGA_EVEN_PIXEL_EXPANSION 0 +#endif + +#define TARGA_SIGNATURE_LENGTH 18 + +// ====================================================================== + +#if defined(PLATFORM_WIN32) + +#define PACKING_END_STRUCT + +#elif defined(PLATFORM_LINUX) + +#define PACKING_END_STRUCT __attribute__ ((packed)) + +#else + +#error figure out how to handle structure packing for this platform + +#endif + +namespace TargaFormatNamespace +{ + enum TargaImageTypes + { + IT_NoImageData = 0 + ,IT_UncompressedColorMapped = 1 + ,IT_UncompressedTrueColor = 2 + ,IT_UncompressedBlackAndWhite = 3 + ,IT_RleColorMapped = 9 + ,IT_RleTrueColor =10 + ,IT_RleBlackAndWhite =11 + }; + + const char ms_TargaSignature[TARGA_SIGNATURE_LENGTH] = { 'T','R','U','E','V','I','S','I','O','N','-','X','F','I','L','E','.',0 }; + +#ifdef PLATFORM_WIN32 +#pragma pack(push, 1) +#endif + + struct TargaHeader + { + uint8 m_idLength; + uint8 m_colorMapType; + uint8 m_imageType; + uint16 m_colorMapStart; + uint16 m_colorMapLength; + uint8 m_colorMapDepth; + uint16 m_xOriginOffset; + uint16 m_yOriginOffset; + uint16 m_width; + uint16 m_height; + uint8 m_pixelDepth; + uint8 m_imageDescriptor; + } PACKING_END_STRUCT; + + struct TargaFooter + { + uint32 m_extensionAreaOffset; + uint32 m_developerDirectoryOffset; + char m_signature[TARGA_SIGNATURE_LENGTH]; + } PACKING_END_STRUCT; + +#ifdef _WIN32 +#pragma pack(pop) +#endif + + const uint ms_attributeMask = BINARY2(0000,1111); + const uint ms_xOriginLocationMask = BINARY2(0001,0000); + const uint ms_yOriginLocationMask = BINARY2(0010,0000); + + static bool _loadImage(AbstractFile *, Image **image, Image::PixelFormat format=Image::PF_nonStandard); + + static void _readUncompressedColorMapped1( + uint8 *destData, + Image::PixelFormat destPixelFormat, + AbstractFile *fileInterface, + int width, + int yStart, + int yStop, + int yStep, + int yStride, + uint8 *cmap, + int bytesPerColorEntry + ); + static void _readUncompressedColorMapped2( + uint8 *destData, + Image::PixelFormat destPixelFormat, + AbstractFile *fileInterface, + int width, + int yStart, + int yStop, + int yStep, + int yStride, + uint8 *cmap, + int bytesPerColorEntry + ); + static void _readUncompressedBlackAndWhite( + uint8 *destData, + Image::PixelFormat destPixelFormat, + AbstractFile *fileInterface, + int width, + int yStart, + int yStop, + int yStep, + int yStride + ); + static void _readUncompressedTrueColor3( + uint8 *const destData, + const Image::PixelFormat destPixelFormat, + AbstractFile *const fileInterface, + const int width, + const int yStart, + const int yStop, + const int yStep, + const int yStride + ); + static void _readUncompressedTrueColor4( + uint8 *const destData, + const Image::PixelFormat destPixelFormat, + AbstractFile *const fileInterface, + const int width, + const int yStart, + const int yStop, + const int yStep, + const int yStride + ); + static void _readRleColorMapped( + uint8 *destData, + Image::PixelFormat destPixelFormat, + AbstractFile *fileInterface, + int width, + int yStart, + int yStop, + int yStep, + int yStride, + uint8 *cmap, + int bytesPerColorEntry, + int fileBytesPerPixel + ); + static void _readRleTrueColor( + uint8 *destData, + Image::PixelFormat destPixelFormat, + AbstractFile *fileInterface, + int width, + int yStart, + int yStop, + int yStep, + int yStride, + int fileBytesPerPixel + ); +} + +using namespace TargaFormatNamespace; +// ====================================================================== + +TargaFormat::TargaFormat() +: + ImageFormat() +{ +} + +// ---------------------------------------------------------------------- + +TargaFormat::~TargaFormat() +{ +} + +// ---------------------------------------------------------------------- + +const char *TargaFormat::getName() const +{ + return "Targa"; +} + +// ---------------------------------------------------------------------- + +bool TargaFormat::isValidImage(const char *filename) const +{ + if (!filename || !*filename) + { + REPORT_LOG(true, ("TargaFormat::isValidImage(): bad filename\n")); + return false; + } + + // -TRF- to do + DEBUG_REPORT_LOG(true, ("-TRF- accessing TargaFormat::isValidImage(), not yet implemented (returning false)\n")); + return false; //lint !e527 // unreachable // yes +} + +// ---------------------------------------------------------------------- + +bool TargaFormat::loadImage(const char *filename, Image **image) const +{ + //-- validate args + if (!filename || !*filename) + { + REPORT_LOG(true, ("TargaFormat::loadImage(): bad filename\n")); + return false; + } + + if (!image) + { + REPORT_LOG(true, ("TargaFormat::loadImage(): null image pointer\n")); + return false; + } + *image = 0; + + //-- open file + AbstractFile *fileInterface = TreeFile::open(filename, AbstractFile::PriorityData, true); + if(!fileInterface) + { + REPORT_LOG(true, ("TargaFormat failed to open file [%s]\n", filename)); + return false; + } + + bool result = _loadImage(fileInterface, image); + + delete fileInterface; + return result; +} + + +// ---------------------------------------------------------------------- + +bool TargaFormat::loadImageReformat(const char *filename, Image **image, Image::PixelFormat format) const +{ + //-- validate args + if (!filename || !*filename) + { + REPORT_LOG(true, ("TargaFormat::loadImage(): bad filename\n")); + return false; + } + + if (!image) + { + REPORT_LOG(true, ("TargaFormat::loadImage(): null image pointer\n")); + return false; + } + *image = 0; + + //-- open file + AbstractFile *fileInterface = TreeFile::open(filename, AbstractFile::PriorityData, true); + if(!fileInterface) + { + REPORT_LOG(true, ("TargaFormat failed to open file [%s]\n", filename)); + return false; + } + + bool result=_loadImage(fileInterface, image, format); + + REPORT_LOG(!result, ("Error reading Targa: %s\n", filename)); + + delete fileInterface; + return result; +} + +// ---------------------------------------------------------------------- + +bool TargaFormatNamespace::_loadImage(AbstractFile *const fileInterface, Image **image, Image::PixelFormat imageFormat) +{ + //------------------------------------------ + // File header + TargaHeader header; + if (fileInterface->read(&header, sizeof(header))!=sizeof(header)) + { + REPORT_LOG(true, ("TargaFormat failed to read file header.\n")); + return false; + } + + if ( header.m_imageType!=IT_UncompressedColorMapped + && header.m_imageType!=IT_UncompressedTrueColor + && header.m_imageType!=IT_UncompressedBlackAndWhite + && header.m_imageType!=IT_RleColorMapped + && header.m_imageType!=IT_RleTrueColor + ) + { + // No code here to convert them, so fail + REPORT_LOG(true, ("TargaFormat found unsupported image format.\n")); + return false; + } + //------------------------------------------ + + //------------------------------------------ + // Image ID + const int imageIDOffset = sizeof(header); + //------------------------------------------ + + //------------------------------------------ + // Color Map Data (field 7) + const int colorMapFileOffset = imageIDOffset + static_cast(header.m_idLength); + + if (header.m_colorMapStart!=0 || (header.m_colorMapDepth&0x7)!=0) + { + REPORT_LOG(true, ("TargaFormat found unsupported color-map data.\n")); + return false; + } + + const int colorMapLength = header.m_colorMapLength; + const int bytesPerColorEntry =(header.m_colorMapDepth>>3); + const int colorMapSize = colorMapLength*bytesPerColorEntry; + + uint8 *cmap=0; + if (colorMapSize > 0) + { + if (!fileInterface->seek(AbstractFile::SeekBegin, colorMapFileOffset)) + { + REPORT_LOG(true, ("TargaFormat failed to read color-map data.\n")); + return false; + } + + cmap = (uint8 *)alloca(colorMapSize); + if (fileInterface->read(cmap, colorMapSize)!=colorMapSize) + { + REPORT_LOG(true, ("TargaFormat failed to read color-map data.\n")); + return false; + } + } + //------------------------------------------ + + //------------------------------------------ + // Image Data (field 8) (part 1) + + // seek to image data + const int imageDataFileOffset = colorMapFileOffset + colorMapSize; + if (!fileInterface->seek(AbstractFile::SeekBegin, imageDataFileOffset)) + { + REPORT_LOG(true, ("TargaFormat failed to read image pixel data.\n")); + return false; + } + //------------------------------------------ + + //------------------------------------------ + // analyze file pixel format load pixel format. + bool isTopToBottom; + Image::PixelFormat filePixelFormat; + int attributeBits=0; + int diskBytesPerPixel=0; + + // file pixel format + attributeBits = header.m_imageDescriptor&0xf; + if (attributeBits!=0 && attributeBits!=8) + { + REPORT_LOG(true, ("TargaFormat found unsupported attribute channel.\n")); + return false; + } + + isTopToBottom=(header.m_imageDescriptor&0x20)!=0; + + filePixelFormat=Image::PF_nonStandard; + diskBytesPerPixel=header.m_pixelDepth>>3; + + switch (header.m_imageType) + { + case IT_UncompressedColorMapped: + case IT_RleColorMapped: + if (bytesPerColorEntry==3) + { + filePixelFormat=Image::PF_bgr_888; + } + else if (bytesPerColorEntry==4) + { + filePixelFormat=Image::PF_bgra_8888; + } + break; + + case IT_UncompressedBlackAndWhite: + if (header.m_pixelDepth==8) + { + filePixelFormat=Image::PF_w_8; + } + break; + + case IT_UncompressedTrueColor: + case IT_RleTrueColor: + switch (header.m_pixelDepth) + { + case 24: + if (attributeBits==0) + { + filePixelFormat=Image::PF_bgr_888; + } + else if (attributeBits==8) + { + filePixelFormat=Image::PF_bgra_8888; + diskBytesPerPixel=4; + } + break; + case 32: + filePixelFormat=Image::PF_bgra_8888; + break; + } + break; + }; + + if (filePixelFormat==Image::PF_nonStandard) + { + REPORT_LOG(true, ("TargaFormat found unsupported image pixel format.\n")); + return false; + } + //------------------------------------------ + + //------------------------------------------ + // create destination image + + // allocate new image object. + Image *newImage = NON_NULL(new Image()); + + // set image dimensions and pixel format + const int width = static_cast(header.m_width); + const int height = static_cast(header.m_height); + int destBytesPerPixel=0; + + const Image::PixelFormat destPixelFormat = (imageFormat==Image::PF_nonStandard) ? filePixelFormat : imageFormat; + switch (destPixelFormat) + { + case Image::PF_bgr_888: + destBytesPerPixel=3; + newImage->setPixelInformation(0x00ff0000,0x0000ff00,0x000000ff,0x00000000); + break; + case Image::PF_bgra_8888: + destBytesPerPixel=4; + newImage->setPixelInformation(0x00ff0000,0x0000ff00,0x000000ff,0xff000000); + break; + case Image::PF_w_8: + destBytesPerPixel=1; + newImage->setPixelInformation(0,0,0,0); + break; + default: + DEBUG_FATAL(true, ("Need to set pixel information for destination pixel format.\n")); + } + newImage->setDimensions(width, height, destBytesPerPixel*8, destBytesPerPixel); + + // fill in image data + uint8 *destData = NON_NULL(newImage->lock()); + //------------------------------------------ + + //------------------------------------------ + // Image Data (field 8) (part 2) + int yStart, yStop, yStep, yStride; + + yStride=newImage->getStride(); + + if (isTopToBottom) + { + yStart=0; + yStop=height; + yStep=1; + } + else + { + yStart=height-1; + yStop=-1; + yStep=-1; + } + + switch (header.m_imageType) + { + case IT_UncompressedColorMapped: + { + switch (diskBytesPerPixel) + { + case 1: + { + _readUncompressedColorMapped1( + destData, + destPixelFormat, + fileInterface, + width, + yStart, + yStop, + yStep, + yStride, + cmap, + bytesPerColorEntry + ); + } break; + case 2: + _readUncompressedColorMapped2( + destData, + destPixelFormat, + fileInterface, + width, + yStart, + yStop, + yStep, + yStride, + cmap, + bytesPerColorEntry + ); + break; + + default: DEBUG_FATAL(true, ("TargaFormat: Unhandled diskBytesPerPixel.\n")); + } + } break; + + case IT_UncompressedBlackAndWhite: + { + _readUncompressedBlackAndWhite( + destData, + destPixelFormat, + fileInterface, + width, + yStart, + yStop, + yStep, + yStride + ); + } break; + + case IT_UncompressedTrueColor: + switch (diskBytesPerPixel) + { + case 3: + _readUncompressedTrueColor3( + destData, + destPixelFormat, + fileInterface, + width, + yStart, + yStop, + yStep, + yStride + ); + break; + + case 4: + _readUncompressedTrueColor4( + destData, + destPixelFormat, + fileInterface, + width, + yStart, + yStop, + yStep, + yStride + ); + break; + } + break; + + case IT_RleColorMapped: + _readRleColorMapped( + destData, + destPixelFormat, + fileInterface, + width, + yStart, + yStop, + yStep, + yStride, + cmap, + bytesPerColorEntry, + diskBytesPerPixel + ); + break; + + case IT_RleTrueColor: + _readRleTrueColor( + destData, + destPixelFormat, + fileInterface, + width, + yStart, + yStop, + yStep, + yStride, + diskBytesPerPixel + ); + break; + } + //------------------------------------------ + + //------------------------------------------ + // Finish up. + newImage->unlock(); + *image = newImage; + newImage = 0; + + // no errors + return true; +} + +// ---------------------------------------------------------------------- + +void TargaFormatNamespace::_readUncompressedColorMapped1( + uint8 *const destData, + const Image::PixelFormat destPixelFormat, + AbstractFile *const fileInterface, + const int width, + const int yStart, + const int yStop, + const int yStep, + const int yStride, + uint8 *const cmap, + const int bytesPerColorEntry +) +{ + uint8 red=0, green=0, blue=0, alpha=0; + uint8 *lookup; + const int lineSize = width; + uint8 *line = (uint8 *)alloca(lineSize); + + switch (bytesPerColorEntry) + { + case 1: + { + for (int y=yStart;y!=yStop;y+=yStep) + { + uint8 *destIter = destData + y*yStride; + fileInterface->read(line, lineSize); + for (int x=0;xread(line, lineSize); + for (int x=0;xread(line, lineSize); + for (int x=0;xread(line, lineSize); + for (int x=0;xread(line, lineSize); + for (int x=0;xread(line, lineSize); + for (int x=0;xread(line, lineSize); + for (int x=0;xread(destIter, lineSize); + } + } + else + { + uint8 red=0, green=0, blue=0, alpha=0; + uint8 *line = (uint8 *)alloca(lineSize); + + for (int y=yStart;y!=yStop;y+=yStep) + { + uint8 *destIter = destData + y*yStride; + fileInterface->read(line, lineSize); + for (int x=0;xread(destIter, lineSize); + } + } + else + { + uint8 red=0, green=0, blue=0, alpha=0; + uint8 *line = (uint8 *)alloca(lineSize); + + for (int y=yStart;y!=yStop;y+=yStep) + { + uint8 *destIter = destData + y*yStride; + fileInterface->read(line, lineSize); + for (int x=0;xread(&rle, 1); + if (rle&0x80) // run length packet + { + rle-=127; + x+=rle; + DEBUG_FATAL(x>width, ("TargaFormat: Read invalid RLE packet.\n")); + + index=0; + fileInterface->read(&index, fileBytesPerPixel); + lookup = cmap + index*bytesPerColorEntry; + switch (bytesPerColorEntry) + { + case 1: + { + red =lookup[0]; + green=lookup[0]; + blue =lookup[0]; + alpha=255; + } break; + case 3: + { + red =lookup[0]; + green=lookup[1]; + blue =lookup[2]; + alpha=255; + } break; + case 4: + { + red =lookup[0]; + green=lookup[1]; + blue =lookup[2]; + alpha=lookup[3]; + } break; + } + + while (rle) + { + Image::setPixel(destIter, destPixelFormat, red, green, blue, alpha); + rle--; + } + } + else // raw packet + { + rle++; // rle is now number of of raw pixels + x+=rle; + DEBUG_FATAL(x>width, ("TargaFormat: Read invalid RLE packet.\n")); + fileInterface->read(line, int(rle)*fileBytesPerPixel); + uint8 *lineIter=line; + while (rle) + { + index = (fileBytesPerPixel==1) ? unsigned(*lineIter) : unsigned(*(unsigned short *)lineIter); + lookup = cmap + index*bytesPerColorEntry; + switch (bytesPerColorEntry) + { + case 1: + { + red =lookup[0]; + green=lookup[0]; + blue =lookup[0]; + alpha=255; + } break; + case 3: + { + red =lookup[0]; + green=lookup[1]; + blue =lookup[2]; + alpha=255; + } break; + case 4: + { + red =lookup[0]; + green=lookup[1]; + blue =lookup[2]; + alpha=lookup[3]; + } break; + } + Image::setPixel(destIter, destPixelFormat, red, green, blue, alpha); + lineIter+=fileBytesPerPixel; + rle--; + } + } + } + } +} + +// ---------------------------------------------------------------------- + +void TargaFormatNamespace::_readRleTrueColor( + uint8 *destData, + Image::PixelFormat destPixelFormat, + AbstractFile *fileInterface, + int width, + int yStart, + int yStop, + int yStep, + int yStride, + int fileBytesPerPixel +) +{ + uint8 red=0, green=0, blue=0, alpha=0, p[4]; + const int lineSize = width*fileBytesPerPixel; + uint8 *line = (uint8 *)alloca(lineSize); + uint8 rle; + + for (int y=yStart;y!=yStop;y+=yStep) + { + uint8 *destIter = destData + y*yStride; + int x=0; + while (xread(&rle, 1); + if (rle&0x80) // run length packet + { + rle-=127; + x+=rle; + DEBUG_FATAL(x>width, ("TargaFormat: Read invalid RLE packet.\n")); + + fileInterface->read(p, fileBytesPerPixel); + + switch (fileBytesPerPixel) + { + case 1: + { + red =p[0]; + green=p[0]; + blue =p[0]; + alpha=255; + } break; + case 3: + { + red =p[2]; + green=p[1]; + blue =p[0]; + alpha=255; + } break; + case 4: + { + red =p[2]; + green=p[1]; + blue =p[0]; + alpha=p[3]; + } break; + } + + while (rle) + { + Image::setPixel(destIter, destPixelFormat, red, green, blue, alpha); + rle--; + } + } + else // raw packet + { + rle++; // rle is now number of of raw pixels + x+=rle; + DEBUG_FATAL(x>width, ("TargaFormat: Read invalid RLE packet.\n")); + fileInterface->read(line, int(rle)*fileBytesPerPixel); + uint8 *lineIter=line; + while (rle) + { + switch (fileBytesPerPixel) + { + case 1: + { + red =lineIter[0]; + green=lineIter[0]; + blue =lineIter[0]; + alpha=255; + } break; + case 3: + { + red =lineIter[2]; + green=lineIter[1]; + blue =lineIter[0]; + alpha=255; + } break; + case 4: + { + red =lineIter[2]; + green=lineIter[1]; + blue =lineIter[0]; + alpha=lineIter[3]; + } break; + } + Image::setPixel(destIter, destPixelFormat, red, green, blue, alpha); + + lineIter+=fileBytesPerPixel; + rle--; + } + } + } + } +} + +// ---------------------------------------------------------------------- +// ---------------------------------------------------------------------- + +bool TargaFormat::saveImage(const Image &image, const char *filename) +{ + FILE *f; + + const int width = image.getWidth(); + const int height = image.getHeight(); + const int sourceBytesPerPixel = image.getBytesPerPixel(); + const Image::PixelFormat pixelFormat = image.getPixelFormat(); + + Image::PixelFormat diskPixelFormat; + int bitDepth; + switch (pixelFormat) + { + case Image::PF_bgra_8888: + diskPixelFormat = Image::PF_bgra_8888; + bitDepth = 32; + break; + + case Image::PF_bgr_888: + diskPixelFormat = Image::PF_bgr_888; + bitDepth = 24; + break; + + case Image::PF_abgr_8888: + diskPixelFormat = Image::PF_bgra_8888; + bitDepth = 32; + break; + + case Image::PF_rgba_8888: + diskPixelFormat = Image::PF_bgra_8888; + bitDepth = 32; + break; + + case Image::PF_rgb_888: + diskPixelFormat = Image::PF_bgr_888; + bitDepth = 24; + break; + + case Image::PF_argb_8888: + diskPixelFormat = Image::PF_bgra_8888; + bitDepth = 32; + break; + + //case Image::PF_w_8: //8bit greyscale bitmap + // break; + + default: + DEBUG_WARNING(true, ("Attempt to save un-supported Targa file format.\n")); + return false; + } + + //--------------------------------------------------- + const uint8 *pixels = image.lockReadOnly(true); + if (!pixels) + { + DEBUG_WARNING(true, ("Could not obtain pixel pointer when writing Targa file %s.\n", filename)); + return false; + } + Image::UnlockGuard unlockImage(&image); + //--------------------------------------------------- + + f = fopen(filename, "wb"); + if (!f) + { + DEBUG_WARNING(true, ("Unable to open Targa destination file %s.\n", filename)); + return false; + } + + //------------------------------------------ + //-- Fill header + TargaHeader header; + header.m_idLength = 0; // length of optional identification sequence + header.m_colorMapType = 0; // indicates whether a palette is present + header.m_imageType = 2; // image data type (e.g., uncompressed RGB) + header.m_colorMapStart = 0; // first palette index, if present + header.m_colorMapLength = 0; // number of palette entries, if present + header.m_colorMapDepth = 0; // number of bits per palette entry + header.m_xOriginOffset = 0; // horiz pixel coordinate of lower left of image + header.m_yOriginOffset = 0; // vert pixel coordinate of lower left of image + header.m_width = static_cast(width); // image width in pixels + header.m_height = static_cast(height); // image height in pixels + header.m_pixelDepth = static_cast(bitDepth); // image color depth (bits per pixel) + header.m_imageDescriptor = 0; // image attribute flags (lower-left origin) + //------------------------------------------ + + int numWritten; + + //------------------------------------------ + //-- Write header (fields 1 thru 5 of spec). + numWritten = fwrite(&header, sizeof(header), 1, f); + if (numWritten != 1) + { + DEBUG_WARNING(true, ("Targa header not written successfully.\n")); + return false; + } + //------------------------------------------ + + //------------------------------------------ + // Image ID (field 6) + //------------------------------------------ + // Color Map Data (field 7) + //------------------------------------------ + + //------------------------------------------ + // Image Data (field 8) + const int sourceYStride = width * sourceBytesPerPixel; + const byte *piter = pixels + (height-1)*width*sourceBytesPerPixel; + if (pixelFormat!=diskPixelFormat) + { + const int destBytesPerPixel = bitDepth / 8; + const int destYStride = width*destBytesPerPixel; + byte *destPixelsLine = (byte *)alloca(destYStride); + + while (piter>=pixels) + { + byte *diter = destPixelsLine; + byte *diterStop = destPixelsLine + destYStride; + + while (diter!=diterStop) + { + Image::copyPixel(diter, diskPixelFormat, piter, pixelFormat); + } + numWritten = fwrite(destPixelsLine, destYStride, 1, f); + if (numWritten!=1) + { + DEBUG_WARNING(true, ("Targa image data not written successfully for %s\n", filename)); + return false; + } + + piter-=2*sourceYStride; + } + } + else + { + while (piter>=pixels) + { + numWritten = fwrite(piter, sourceYStride, 1, f); + if (numWritten!=1) + { + DEBUG_WARNING(true, ("Targa image data not written successfully for %s\n", filename)); + return false; + } + piter-=sourceYStride; + } + } + //------------------------------------------ + + //------------------------------------------ + // Developer Data (field 9) + //------------------------------------------ + // Extension Area (field 10) + //------------------------------------------ + + //------------------------------------------ + // File footer + TargaFooter footer; + footer.m_extensionAreaOffset=0; + footer.m_developerDirectoryOffset=0; + memcpy(footer.m_signature, ms_TargaSignature, TARGA_SIGNATURE_LENGTH); + numWritten = fwrite(&footer, sizeof(footer), 1, f); + if (numWritten != 1) + { + DEBUG_WARNING(true, ("Targa footer not written successfully for %s\n", filename)); + return false; + } + //------------------------------------------ + + fclose(f); + return true; +} + +// ---------------------------------------------------------------------- + +int TargaFormat::getCommonExtensionCount() const +{ + return 1; +} + +// ---------------------------------------------------------------------- + +const char *TargaFormat::getCommonExtension(int index) const +{ + UNREF(index); + VALIDATE_RANGE_INCLUSIVE_EXCLUSIVE(0, index, 1); + return "tga"; +} + +// ====================================================================== diff --git a/engine/shared/library/sharedImage/src/shared/TargaFormat.h b/engine/shared/library/sharedImage/src/shared/TargaFormat.h new file mode 100644 index 00000000..c5db4991 --- /dev/null +++ b/engine/shared/library/sharedImage/src/shared/TargaFormat.h @@ -0,0 +1,48 @@ +// ====================================================================== +// +// TargaFormat.h +// copyright 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef TARGA_FORMAT_H +#define TARGA_FORMAT_H + +// ====================================================================== + +#include "sharedImage/ImageFormat.h" +#include "sharedImage/Image.h" + +// ====================================================================== + +namespace TargaHeaderNamespace +{ +struct TargaHeader; +} +class TargaFormat: public ImageFormat +{ +public: + + TargaFormat(); + virtual ~TargaFormat(); + + virtual const char *getName() const; + virtual bool isValidImage(const char *filename) const; + virtual bool loadImage(const char *filename, Image **image) const; + + bool loadImageReformat(const char *filename, Image **image, Image::PixelFormat format) const; + + virtual bool saveImage(const Image &image, const char *filename); + + virtual int getCommonExtensionCount() const; + virtual const char *getCommonExtension(int index) const; + +private: + // disabled + TargaFormat(const TargaFormat&); + TargaFormat &operator =(const TargaFormat&); +}; + +// ====================================================================== + +#endif diff --git a/engine/shared/library/sharedImage/src/win32/FirstSharedImage.cpp b/engine/shared/library/sharedImage/src/win32/FirstSharedImage.cpp new file mode 100644 index 00000000..c57c713f --- /dev/null +++ b/engine/shared/library/sharedImage/src/win32/FirstSharedImage.cpp @@ -0,0 +1,8 @@ +// ====================================================================== +// +// FirstImage.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "sharedImage/FirstSharedImage.h"