summaryrefslogtreecommitdiff
path: root/Runtime/Graphics/ImageConversion.cpp
diff options
context:
space:
mode:
authorchai <chaifix@163.com>2019-08-14 22:50:43 +0800
committerchai <chaifix@163.com>2019-08-14 22:50:43 +0800
commit15740faf9fe9fe4be08965098bbf2947e096aeeb (patch)
treea730ec236656cc8cab5b13f088adfaed6bb218fb /Runtime/Graphics/ImageConversion.cpp
+Unity Runtime codeHEADmaster
Diffstat (limited to 'Runtime/Graphics/ImageConversion.cpp')
-rw-r--r--Runtime/Graphics/ImageConversion.cpp621
1 files changed, 621 insertions, 0 deletions
diff --git a/Runtime/Graphics/ImageConversion.cpp b/Runtime/Graphics/ImageConversion.cpp
new file mode 100644
index 0000000..fe0b462
--- /dev/null
+++ b/Runtime/Graphics/ImageConversion.cpp
@@ -0,0 +1,621 @@
+#include "UnityPrefix.h"
+#include "ImageConversion.h"
+#include "Runtime/Utilities/File.h"
+#include "Texture2D.h"
+#include "Image.h"
+#include "Runtime/Utilities/BitUtility.h"
+#include "Runtime/Math/Color.h"
+#include "DXTCompression.h"
+#if ENABLE_PNG_JPG
+#include "External/ProphecySDK/src/extlib/pnglib/png.h"
+#include "External/ProphecySDK/src/extlib/jpglib/jpeglib.h"
+#include "Runtime/Export/JPEGMemsrc.h"
+#endif
+#include "Runtime/Shaders/GraphicsCaps.h"
+#include "Runtime/GfxDevice/GfxDeviceConfigure.h"
+#include <setjmp.h>
+#include "Runtime/Network/PlayerCommunicator/PlayerConnection.h"
+
+using namespace std;
+
+// --------------------------------------------------------------------------
+// PNG
+
+#if ENABLE_PNG_JPG
+static void PngWriteToMemoryFunc( png_structp png, png_bytep data, png_size_t size )
+{
+ MemoryBuffer* buffer = (MemoryBuffer*)png->io_ptr;
+ buffer->insert( buffer->end(), data, data+size );
+}
+static void PngWriteFlushFunc( png_structp png )
+{
+}
+
+
+bool ConvertImageToPNGBuffer( const ImageReference& inputImage, MemoryBuffer& buffer )
+{
+ png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,(png_voidp)NULL, NULL, NULL);
+ if (!png_ptr)
+ return false;
+ png_infop info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr)
+ return false;
+
+ if (setjmp(png_jmpbuf(png_ptr)))
+ return false;
+
+ buffer.reserve( 4096 );
+ png_set_write_fn( png_ptr, &buffer, PngWriteToMemoryFunc, PngWriteFlushFunc );
+
+ png_set_compression_level(png_ptr, Z_BEST_SPEED);
+
+ int format = kTexFormatRGBA32;
+ if (inputImage.GetFormat() == kTexFormatRGB24 || inputImage.GetFormat() == kTexFormatRGB565)
+ format = kTexFormatRGB24;
+
+ Image image( inputImage.GetWidth(), inputImage.GetHeight(), format );
+ image.BlitImage( inputImage );
+
+ png_set_IHDR(png_ptr, info_ptr,
+ inputImage.GetWidth(),
+ inputImage.GetHeight(),
+ 8,
+ format == kTexFormatRGB24 ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA,
+ PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_DEFAULT,
+ PNG_FILTER_TYPE_DEFAULT);
+
+ png_write_info(png_ptr, info_ptr);
+ for (int i = 0; i < image.GetHeight(); i++)
+ png_write_row(png_ptr, image.GetRowPtr(image.GetHeight() - i - 1));
+
+ png_write_end(png_ptr, info_ptr);
+
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+
+ return !buffer.empty();
+}
+
+
+#if CAPTURE_SCREENSHOT_AVAILABLE
+
+bool ConvertImageToPNGFile (const ImageReference& inputImage, const string& path)
+{
+ MemoryBuffer buffer;
+ if( !ConvertImageToPNGBuffer( inputImage, buffer ) )
+ return false;
+
+ #if UNITY_PEPPER
+ // no file access in pepper. write screenshot to stdout.
+ // we need to write hex, as string reading in mono may perform
+ // string encoding translation on stdout, breaking the binary data.
+ printf_console("SCREENSHOT_MARKER=%sSCREENSHOTSTART_MARKER", path.c_str());
+ for (int i=0; i<buffer.size(); i++)
+ printf_console("%02x", buffer[i]);
+ printf_console("SCREENSHOTEND_MARKER\n");
+ return true;
+ #else
+#if ENABLE_PLAYERCONNECTION
+ TransferFileOverPlayerConnection(path, &buffer[0], buffer.size());
+#endif
+ return WriteBytesToFile (&buffer[0], buffer.size(), path);
+ #endif
+}
+
+#endif
+
+struct PngMemoryReadContext {
+ const unsigned char* inputPointer;
+ size_t inputSizeLeft;
+};
+
+static void PngReadFromMemoryFunc( png_structp png_ptr, png_bytep data, png_size_t len )
+{
+ PngMemoryReadContext* context = (PngMemoryReadContext*)png_ptr->io_ptr;
+ // check for overflow
+ if( len > context->inputSizeLeft )
+ len = context->inputSizeLeft;
+
+ memcpy( data, context->inputPointer, len );
+ context->inputPointer += len;
+ context->inputSizeLeft -= len;
+}
+
+
+static void PngReadWarningFunc( png_struct* png_ptr, png_const_charp warning_msg )
+{
+}
+
+bool ImageSizeBoundCheck(unsigned int width, unsigned int height) {
+ int tmp, tmp2;
+ if (width + 3 < width ||
+ height + 3 < height) {
+ return 0;
+ }
+
+ tmp = width * height;
+ if (width != 0 && height != tmp / width) {
+ return 0;
+ }
+
+ tmp2 = tmp * 16;
+ if (tmp != tmp2/16) {
+ return 0;
+ }
+ return 1;
+}
+
+static bool LoadPngIntoTexture( Texture2D& texture, const void* data, size_t size, bool compressTexture, UInt8** outRGBABaseLevelForDXTMips )
+{
+ PngMemoryReadContext context;
+ context.inputPointer = static_cast<const unsigned char*>( data );
+ context.inputSizeLeft = size;
+
+ if( !data )
+ return false;
+
+ // check png header
+ if( size < 8 || !png_check_sig( const_cast<unsigned char*>(context.inputPointer), 8 ) )
+ return false;
+
+ png_structp png_ptr;
+ png_infop info_ptr;
+ png_uint_32 width, height;
+ int bit_depth, color_type, interlace_type;
+
+ double image_gamma = 0.45;
+ int number_passes = 0;
+
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,&PngReadWarningFunc);
+ if( png_ptr == NULL )
+ return false;
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if( info_ptr == NULL )
+ {
+ png_destroy_read_struct(&png_ptr,(png_infopp)NULL,(png_infopp)NULL);
+ return false;
+ }
+
+ if( setjmp(png_ptr->jmpbuf) )
+ {
+ png_destroy_read_struct(&png_ptr,&info_ptr,(png_infopp)NULL);
+ return false;
+ }
+
+ png_set_read_fn( png_ptr, &context, &PngReadFromMemoryFunc );
+ png_read_info( png_ptr, info_ptr );
+
+ png_get_IHDR( png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL );
+
+ png_set_strip_16(png_ptr); // strip 16 bit channels to 8 bit
+ png_set_packing(png_ptr); // separate palettized channels
+
+ // palette -> rgb
+ if( color_type == PNG_COLOR_TYPE_PALETTE )
+ {
+ png_set_expand(png_ptr);
+ }
+
+ // grayscale -> 8 bits
+ if( !(color_type & PNG_COLOR_MASK_COLOR) && bit_depth < 8 ) png_set_expand(png_ptr);
+
+ // if exists, expand tRNS to alpha channel
+ if( png_get_valid(png_ptr,info_ptr,PNG_INFO_tRNS) ) png_set_expand(png_ptr);
+
+ // expand gray to RGB
+ if( color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA )
+ png_set_gray_to_rgb(png_ptr);
+
+ // we need to get ARGB format for raw textures, and RGBA if we will compress it
+ if( !compressTexture )
+ png_set_swap_alpha(png_ptr);
+
+ png_set_filler( png_ptr, 0xFF, PNG_FILLER_BEFORE ); // force alpha byte
+
+ // Only apply gamma correction if the image has gamma information. In this case
+ // assume our display gamma is 2.0 (a compromise between Mac and PC...).
+ double screen_gamma = 2.0f;
+ image_gamma = 0.0;
+ if ( png_get_gAMA(png_ptr,info_ptr,&image_gamma) )
+ {
+ png_set_gamma(png_ptr,screen_gamma,image_gamma);
+ }
+
+ number_passes = png_set_interlace_handling(png_ptr);
+ png_read_update_info(png_ptr,info_ptr); // update gamma, etc.
+
+ // If texture size or format differs, reformat the texture.
+ TextureFormat wantedFormat = compressTexture ? kTexFormatDXT5 : kTexFormatARGB32;
+ bool mipMaps = texture.HasMipMap();
+ if( width != texture.GetDataWidth () || height != texture.GetDataHeight () || kTexFormatARGB32 != texture.GetTextureFormat())
+ texture.InitTexture( width, height, wantedFormat, mipMaps ? Texture2D::kMipmapMask : Texture2D::kNoMipmap, 1 );
+
+ ImageReference ref;
+ AssertIf( !outRGBABaseLevelForDXTMips );
+ if( compressTexture ) {
+ int imageByteSize;
+ if (mipMaps) {
+ int miplevel = CalculateMipMapCount3D(width, height, 1);
+ int iter;
+ unsigned int completeSize = 0;
+ unsigned int prevcompleteSize = 0;
+
+ if (!ImageSizeBoundCheck(width, height)) {
+ longjmp(png_ptr->jmpbuf, 1);
+ }
+
+ for (iter = 0; iter < miplevel; iter++) {
+ prevcompleteSize = completeSize;
+ completeSize += CalculateImageSize (std::max (width >> iter, (png_uint_32)1), std::max (height >> iter, (png_uint_32)1), kTexFormatRGBA32);
+ if (completeSize < prevcompleteSize) {
+ longjmp(png_ptr->jmpbuf, 1);
+ }
+ }
+ imageByteSize = CalculateImageMipMapSize( width, height, kTexFormatRGBA32 );
+ } else {
+ if (!ImageSizeBoundCheck(width, height)) {
+ longjmp(png_ptr->jmpbuf, 1);
+ }
+ imageByteSize = CalculateImageSize( width, height, kTexFormatRGBA32 );
+ }
+
+ *outRGBABaseLevelForDXTMips = new UInt8[imageByteSize];
+ ref = ImageReference( width, height, width*4, kTexFormatRGBA32, *outRGBABaseLevelForDXTMips );
+ } else {
+ if( !texture.GetWriteImageReference(&ref, 0, 0) ) {
+ png_destroy_read_struct( &png_ptr,&info_ptr,(png_infopp)NULL );
+ return false;
+ }
+ }
+
+ size_t len = sizeof(png_bytep) * height;
+
+ /* boundcheck for integer overflow */
+ if (height != len / sizeof(png_bytep) ) {
+ png_destroy_read_struct( &png_ptr,&info_ptr,(png_infopp)NULL );
+ return false;
+ }
+
+ png_bytep* row_pointers = new png_bytep[height];
+ for( png_uint_32 row = 0; row<height; ++row )
+ {
+ row_pointers[row] = ref.GetRowPtr(height-1-row);
+ }
+
+ for( int pass = 0; pass < number_passes; pass++ )
+ {
+ png_read_rows( png_ptr,row_pointers,NULL,height );
+ }
+
+ // cleanup
+ png_read_end( png_ptr,info_ptr );
+ png_destroy_read_struct( &png_ptr,&info_ptr,(png_infopp)NULL );
+ delete[] row_pointers;
+
+ return true;
+}
+
+// --------------------------------------------------------------------------
+// JPG
+
+/* Custom error manager. As the default one in libjpeg crashes Unity on error */
+struct unity_jpeg_error_mgr {
+ struct jpeg_error_mgr pub; /* "public" fields */
+ jmp_buf setjmp_buffer; /* for return to caller */
+};
+typedef struct unity_jpeg_error_mgr * unity_jpeg_error_ptr;
+
+/*
+ * Here's the routine that will replace the standard error_exit method:
+ */
+METHODDEF(void)
+unity_jpeg_error_exit (j_common_ptr cinfo)
+{
+ /* cinfo->err really points to a unity_jpeg_error_mgr struct, so coerce pointer */
+ unity_jpeg_error_ptr myerr = (unity_jpeg_error_ptr) cinfo->err;
+ /* Always display the message. */
+ /* We could postpone this until after returning, if we chose. */
+ (*cinfo->err->output_message) (cinfo);
+ /* Return control to the setjmp point */
+ longjmp(myerr->setjmp_buffer, 1);
+}
+
+static void HandleError (struct jpeg_decompress_struct& cinfo, Texture2D* tex)
+{
+ jpeg_destroy_decompress (&cinfo);
+}
+
+static int LoadJpegIntoTexture(Texture2D& tex, const UInt8* jpegData, size_t jpegDataSz, bool compressTexture, UInt8** outRGBABaseLevelForDXTMips)
+{
+#if UNITY_WII
+ AssertIf ("ERROR: LoadJpegIntoTexture is not supported!");
+ return 0;
+#else
+ struct jpeg_decompress_struct cinfo;
+
+ /* We use our private extension JPEG error handler.
+ * Note that this struct must live as long as the main JPEG parameter
+ * struct, to avoid dangling-pointer problems.
+ */
+ struct unity_jpeg_error_mgr jerr;
+
+ JSAMPARRAY in;
+ int row_stride;
+ unsigned char *out;
+
+ // set up the decompression.
+ cinfo.err = jpeg_std_error(&jerr.pub);
+ jerr.pub.error_exit = unity_jpeg_error_exit;
+
+ /* Establish the setjmp return context for my_error_exit to use. */
+ if (setjmp(jerr.setjmp_buffer)) {
+ /* If we get here, the JPEG code has signaled an error.
+ * We need to clean up the JPEG object, close the input file, and return.
+ */
+ HandleError(cinfo, &tex);
+ return 0;
+ }
+
+ jpeg_create_decompress (&cinfo);
+
+ // inititalize the source
+ jpeg_memory_src (&cinfo, (unsigned char*)jpegData, jpegDataSz);
+
+ // initialize decompression
+ (void) jpeg_read_header (&cinfo, TRUE);
+ (void) jpeg_start_decompress (&cinfo);
+
+ // set up the width and height for return
+ int width = cinfo.image_width;
+ int height = cinfo.image_height;
+
+ // initialize the input buffer - we'll use the in-built memory management routines in the
+ // JPEG library because it will automatically free the used memory for us when we destroy
+ // the decompression structure. cool.
+ row_stride = cinfo.output_width * cinfo.output_components;
+ if (cinfo.output_width != 0 && cinfo.output_components != row_stride / cinfo.output_width) {
+ HandleError(cinfo, &tex);
+ return 0;
+ }
+ in = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
+
+ // we only support three channel RGB or one channel grayscale formats
+
+ if (cinfo.output_components != 3 && cinfo.output_components != 1)
+ {
+ HandleError(cinfo, &tex);
+ return 0;
+ }
+
+ // If texture size or format differs, reformat the texture.
+ TextureFormat wantedFormat = compressTexture ? kTexFormatDXT1 : kTexFormatRGB24;
+ bool mipMaps = tex.HasMipMap();
+ if (width != tex.GetDataWidth () || height != tex.GetDataHeight () || wantedFormat != tex.GetTextureFormat())
+ tex.InitTexture (width, height, wantedFormat, mipMaps ? Texture2D::kMipmapMask : Texture2D::kNoMipmap, 1);
+
+ ImageReference ref;
+ AssertIf( !outRGBABaseLevelForDXTMips );
+ if( compressTexture ) {
+ int imageByteSize;
+ if (mipMaps) {
+ int miplevel = CalculateMipMapCount3D(width, height, 1);
+ int iter;
+ unsigned int completeSize = 0;
+ unsigned int prevcompleteSize = 0;
+
+ if (!ImageSizeBoundCheck(width, height)) {
+ HandleError(cinfo, &tex);
+ return 0;
+ }
+
+ for (iter = 0; iter < miplevel; iter++) {
+ prevcompleteSize = completeSize;
+ completeSize += CalculateImageSize (std::max (width >> iter, 1), std::max (height >> iter, 1), kTexFormatRGBA32);
+ if (completeSize < prevcompleteSize) {
+ HandleError(cinfo, &tex);
+ return 0;
+ }
+ }
+ imageByteSize = CalculateImageMipMapSize( width, height, kTexFormatRGBA32 );
+ } else {
+ if (!ImageSizeBoundCheck(width, height)) {
+ HandleError(cinfo, &tex);
+ return 0;
+ }
+ imageByteSize = CalculateImageSize( width, height, kTexFormatRGBA32 );
+ }
+ *outRGBABaseLevelForDXTMips = new UInt8[imageByteSize];
+ ref = ImageReference( width, height, width*4, kTexFormatRGBA32, *outRGBABaseLevelForDXTMips );
+ } else {
+ if( !tex.GetWriteImageReference(&ref, 0, 0) ) {
+ jpeg_destroy_decompress (&cinfo);
+ return 0;
+ }
+ }
+
+ UInt8* dst;
+ int scanline = height-1;
+ int result = 1;
+
+ if (scanline < 0) {
+ HandleError(cinfo, &tex);
+ return 0;
+ }
+
+ // three channel output
+ if( cinfo.output_components == 3 )
+ {
+ while (cinfo.output_scanline < cinfo.output_height)
+ {
+ jpeg_read_scanlines(&cinfo, in, 1);
+ dst = ref.GetRowPtr(scanline);
+ out = in[0];
+ if( !compressTexture ) {
+ for( int i = 0; i < row_stride; i += 3, dst += 3 )
+ {
+ dst[0] = out[i+2];
+ dst[1] = out[i+1];
+ dst[2] = out[i];
+ }
+ } else {
+ for( int i = 0; i < row_stride; i += 3, dst += 4 )
+ {
+ dst[0] = out[i+2];
+ dst[1] = out[i+1];
+ dst[2] = out[i];
+ dst[3] = 255;
+ }
+ }
+ --scanline;
+ }
+ }
+ // one channel output
+ else if( cinfo.output_components == 1 )
+ {
+ while (cinfo.output_scanline < cinfo.output_height)
+ {
+ jpeg_read_scanlines(&cinfo, in, 1);
+ dst = ref.GetRowPtr(scanline);
+ out = in[0];
+ if( !compressTexture ) {
+ for( int i = 0; i < row_stride; ++i, dst += 3 )
+ {
+ UInt8 v = out[i];
+ dst[0] = v;
+ dst[1] = v;
+ dst[2] = v;
+ }
+ } else {
+ for( int i = 0; i < row_stride; ++i, dst += 4 )
+ {
+ UInt8 v = out[i];
+ dst[0] = v;
+ dst[1] = v;
+ dst[2] = v;
+ dst[4] = 255;
+ }
+ }
+ --scanline;
+ }
+ }
+ else
+ {
+ AssertString("unsupported number of output components in JPEG");
+ result = 0;
+ }
+
+ // finish decompression and destroy the jpeg
+ (void) jpeg_finish_decompress (&cinfo);
+ jpeg_destroy_decompress (&cinfo);
+
+
+ return result;
+#endif
+}
+
+#endif
+
+// --------------------------------------------------------------------------
+// Generic byte->texture loading
+
+
+// Simple image to load into the texture on error: "?"
+static const char* kDummyErrorImage =
+"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\x00\x00\xff\xff\xff\xff\xff\xff"
+"\xff\xff\xff\xff\x00\x00\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\x00\x00\xff\xff\xff"
+"\xff\xff\xff\xff\x00\x00\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\x00\x00\xff\xff\xff"
+"\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\x00\x00\xff\x00\x00\xff\x00\x00\xff\xff\xff\xff\xff\xff";
+
+#if UNITY_FLASH
+//Leaky temporary workaround to deal with not having setjmp/longjmp on Flash. This is only to be able to return correctly, but doesn't clean up anything...beware!
+
+__attribute__ ((noinline)) static int LoadJpegIntoTextureFlash(Texture2D& tex, const UInt8* jpegData, size_t jpegDataSz, bool compressTexture, UInt8** outRGBABaseLevelForDXTMips)
+{
+ __asm __volatile__("try{");
+ int val = LoadJpegIntoTexture(tex,jpegData,jpegDataSz,compressTexture,outRGBABaseLevelForDXTMips);
+ __asm __volatile__("}catch(e:Error){r_g0 = 0; return;}");
+ return val;
+}
+
+__attribute__ ((noinline)) static bool LoadPngIntoTextureFlash( Texture2D& texture, const void* data, size_t size, bool compressTexture, UInt8** outRGBABaseLevelForDXTMips )
+{
+ __asm __volatile__("try{");
+ bool val = LoadPngIntoTexture(texture,data,size,compressTexture,outRGBABaseLevelForDXTMips);
+ __asm __volatile__("}catch(e:Error){r_g0 = 0; return;}");
+ return val;
+}
+
+#define LoadJpegIntoTexture LoadJpegIntoTextureFlash
+#define LoadPngIntoTexture LoadPngIntoTextureFlash
+#endif
+
+bool LoadMemoryBufferIntoTexture( Texture2D& tex, const UInt8* data, size_t size, LoadImageCompression compression, bool markNonReadable )
+{
+ UInt8* rgbaBaseLevel = NULL;
+ if( !gGraphicsCaps.hasS3TCCompression )
+ compression = kLoadImageUncompressed;
+
+#if ENABLE_PNG_JPG
+ if( !LoadJpegIntoTexture( tex, data, size, compression!=kLoadImageUncompressed, &rgbaBaseLevel ) )
+ {
+ delete[] rgbaBaseLevel; rgbaBaseLevel = NULL;
+ if( !LoadPngIntoTexture( tex, data, size, compression!=kLoadImageUncompressed, &rgbaBaseLevel ) )
+ {
+ // Store a dummy image into the texture
+ tex.InitTexture( 8, 8, kTexFormatRGB24, Texture2D::kNoMipmap, 1 );
+ unsigned char* textureData = (unsigned char*)tex.GetRawImageData();
+ memcpy( textureData, kDummyErrorImage, 8*8*3 );
+ }
+ }
+#endif
+ // Compress the image if needed
+ bool isDXTCompressed = IsCompressedDXTTextureFormat(tex.GetTextureFormat());
+ if( isDXTCompressed ) {
+ #if !GFX_SUPPORTS_DXT_COMPRESSION
+ delete[] rgbaBaseLevel;
+ return false;
+ #else
+
+ AssertIf( !rgbaBaseLevel );
+ // do final image compression
+ int width = tex.GetDataWidth();
+ int height = tex.GetDataHeight();
+ TextureFormat texFormat = tex.GetTextureFormat();
+ AssertIf( texFormat != kTexFormatDXT1 && texFormat != kTexFormatDXT5 );
+ FastCompressImage( width, height, rgbaBaseLevel, tex.GetRawImageData(), texFormat == kTexFormatDXT5, compression==kLoadImageDXTCompressDithered );
+
+ bool mipMaps = tex.HasMipMap();
+ if( mipMaps ) {
+ // compute mip representations from the base level
+ CreateMipMap (rgbaBaseLevel, width, height, 1, kTexFormatRGBA32);
+ // DXT compress them
+ int mipCount = tex.CountDataMipmaps();
+ for( int mip = 1; mip < mipCount; ++mip )
+ {
+ const UInt8* srcMipData = rgbaBaseLevel + CalculateMipMapOffset(width, height, kTexFormatRGBA32, mip);
+ UInt8* dstMipData = tex.GetRawImageData() + CalculateMipMapOffset(width, height, texFormat, mip);
+ FastCompressImage( std::max(width>>mip,1), std::max(height>>mip,1), srcMipData, dstMipData, texFormat == kTexFormatDXT5, compression==kLoadImageDXTCompressDithered );
+ }
+ }
+ #endif // GFX_SUPPORTS_DXT_COMPRESSION
+ }
+ delete[] rgbaBaseLevel;
+
+ if(markNonReadable)
+ {
+ tex.SetIsReadable(false);
+ tex.SetIsUnreloadable(true);
+ }
+
+ if( !IsCompressedDXTTextureFormat(tex.GetTextureFormat()) )
+ tex.UpdateImageData();
+ else
+ tex.UpdateImageDataDontTouchMipmap();
+
+ return true;
+}