diff --git a/BeefySysLib/ResLib.cpp b/BeefySysLib/ResLib.cpp index 3dd5e648..7b4b72f8 100644 --- a/BeefySysLib/ResLib.cpp +++ b/BeefySysLib/ResLib.cpp @@ -4,10 +4,14 @@ #include "gfx/RenderDevice.h" #include "gfx/Texture.h" #include "util/PerfTimer.h" +#include "util/TLSingleton.h" +#include "img/JPEGData.h" + +#pragma warning(disable:4190) USING_NS_BF; -static UTF16String gTempUTF16String; +static TLSingleton gResLib_TLStrReturn; BF_EXPORT PSDReader* BF_CALLTYPE Res_OpenPSD(const char* fileName) { @@ -88,3 +92,19 @@ BF_EXPORT int BF_CALLTYPE Res_PSDLayer_IsVisible(PSDLayerInfo* layerInfo) { return layerInfo->mVisible ? 1 : 0; } + +/// + +BF_EXPORT StringView BF_CALLTYPE Res_JPEGCompress(uint32* bits, int width, int height, int quality) +{ + String& outString = *gResLib_TLStrReturn.Get(); + JPEGData jpegData; + jpegData.mBits = bits; + jpegData.mWidth = width; + jpegData.mHeight = height; + jpegData.Compress(quality); + jpegData.mBits = NULL; + outString.Clear(); + outString.Insert(0, (char*)jpegData.mSrcData, jpegData.mSrcDataLen); + return outString; +} \ No newline at end of file diff --git a/BeefySysLib/img/ImageData.cpp b/BeefySysLib/img/ImageData.cpp index 9998f682..78fedbb7 100644 --- a/BeefySysLib/img/ImageData.cpp +++ b/BeefySysLib/img/ImageData.cpp @@ -100,6 +100,14 @@ ImageData* ImageData::Duplicate() return copy; } +bool ImageData::LoadFromMemory(void* ptr, int size) +{ + SetSrcData((uint8*)ptr, size); + bool result = ReadData(); + mSrcData = NULL; + return result; +} + bool ImageData::LoadFromFile(const StringImpl& path) { int size = 0; @@ -126,6 +134,7 @@ void ImageData::SetSrcData(uint8* data, int dataLen) mSrcDataLen = dataLen; } + void ImageData::PremultiplyAlpha() { if (mBits == NULL) diff --git a/BeefySysLib/img/ImageData.h b/BeefySysLib/img/ImageData.h index c261c924..a5a1dacd 100644 --- a/BeefySysLib/img/ImageData.h +++ b/BeefySysLib/img/ImageData.h @@ -43,6 +43,7 @@ public: void Fill(uint32 color); virtual ImageData* Duplicate(); void SetSrcData(uint8* data, int dataLen); + virtual bool LoadFromMemory(void* ptr, int size); virtual bool LoadFromFile(const StringImpl& path); virtual bool ReadData() { return false; } virtual void PremultiplyAlpha(); diff --git a/BeefySysLib/img/JPEGData.cpp b/BeefySysLib/img/JPEGData.cpp index 0f998fe7..c139b04d 100644 --- a/BeefySysLib/img/JPEGData.cpp +++ b/BeefySysLib/img/JPEGData.cpp @@ -141,6 +141,53 @@ struct JpegMemSource }; +#define JPEGMEMDEST_BLOCK_SIZE 16384 + +struct JpegMemDest : jpeg_destination_mgr +{ + Array mData; + + JpegMemDest(JPEGData* buffer, j_compress_ptr cinfo) + { + if (cinfo->dest == NULL) + { + cinfo->dest = this; + } + + init_destination = &InitDestination; + empty_output_buffer = &EmptyOutputBuffer; + term_destination = &TermDestination; + } + + ~JpegMemDest() + { + } + + static void InitDestination(j_compress_ptr cinfo) + { + auto self = (JpegMemDest*)cinfo->dest; + self->mData.Resize(JPEGMEMDEST_BLOCK_SIZE); + cinfo->dest->next_output_byte = &self->mData[0]; + cinfo->dest->free_in_buffer = self->mData.size(); + } + + static boolean EmptyOutputBuffer(j_compress_ptr cinfo) + { + auto self = (JpegMemDest*)cinfo->dest; + size_t oldsize = self->mData.size(); + self->mData.Resize(oldsize + JPEGMEMDEST_BLOCK_SIZE); + cinfo->dest->next_output_byte = &self->mData[oldsize]; + cinfo->dest->free_in_buffer = self->mData.size() - oldsize; + return true; + } + + static void TermDestination(j_compress_ptr cinfo) + { + auto self = (JpegMemDest*)cinfo->dest; + self->mData.Resize(self->mData.size() - cinfo->dest->free_in_buffer); + } +}; + bool JPEGData::ReadData() { jpeg_decompress_struct cinfo; @@ -154,7 +201,7 @@ bool JPEGData::ReadData() } jpeg_create_decompress( &cinfo ); - JpegMemSource(this, &cinfo ); + JpegMemSource(this, &cinfo ); jpeg_read_header( &cinfo, TRUE ); jpeg_start_decompress( &cinfo ); @@ -205,4 +252,98 @@ bool JPEGData::ReadData() jpeg_destroy_decompress( &cinfo ); return true; +} + +void JPEGData::Compress(int quality) +{ + jpeg_compress_struct cinfo; + + /* Now we can initialize the JPEG compression object. */ + jpeg_create_compress(&cinfo); + + ErrorHandler err(&cinfo); + if (setjmp(err.setjmpBuffer)) + { + // ErrorHandler::OnErrorExit will longjmp back to here from + // within the ReadImage call below. + jpeg_destroy_compress(&cinfo); + return; + } + JSAMPROW row_pointer[1]; + + JpegMemDest jpegMemDest(this, &cinfo); + + //jpeg_stdio_dest(&cinfo, outfile); + + /* Step 3: set parameters for compression */ + + /* First we supply a description of the input image. + * Four fields of the cinfo struct must be filled in: + */ + cinfo.image_width = mWidth; /* image width and height, in pixels */ + cinfo.image_height = mHeight; + cinfo.input_components = 3; /* # of color components per pixel */ + cinfo.in_color_space = JCS_RGB; /* colorspace of input image */ + /* Now use the library's routine to set default compression parameters. + * (You must set at least cinfo.in_color_space before calling this, + * since the defaults depend on the source color space.) + */ + jpeg_set_defaults(&cinfo); + /* Now you can set any non-default parameters you wish to. + * Here we just illustrate the use of quality (quantization table) scaling: + */ + jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */); + + /* Step 4: Start compressor */ + + /* TRUE ensures that we will write a complete interchange-JPEG file. + * Pass TRUE unless you are very sure of what you're doing. + */ + jpeg_start_compress(&cinfo, TRUE); + + /* Step 5: while (scan lines remain to be written) */ + /* jpeg_write_scanlines(...); */ + + /* Here we use the library's state variable cinfo.next_scanline as the + * loop counter, so that we don't have to keep track ourselves. + * To keep things simple, we pass one scanline per call; you can pass + * more if you wish, though. + */ + int row_stride = mWidth * 3; /* JSAMPLEs per row in image_buffer */ + + uint8* line = new uint8[mWidth * 3]; + row_pointer[0] = (JSAMPROW)line; + + while (cinfo.next_scanline < cinfo.image_height) + { + uint8* src = (uint8*)&mBits[(mHeight - cinfo.next_scanline - 1) * mWidth]; + + uint8* dest = line; + for (int x = 0; x < mWidth; x++) + { + *(dest++) = src[2]; + *(dest++) = src[1]; + *(dest++) = src[0]; + src += 4; + } + + (void)jpeg_write_scanlines(&cinfo, row_pointer, 1); + } + + delete line; + + /* Step 6: Finish compression */ + + jpeg_finish_compress(&cinfo); + /* After finish_compress, we can close the output file. */ + //fclose(outfile); + + /* Step 7: release JPEG compression object */ + + /* This is an important step since it will release a good deal of memory. */ + jpeg_destroy_compress(&cinfo); + + mSrcDataLen = (int)jpegMemDest.mData.size(); + mSrcData = new uint8[mSrcDataLen]; + memcpy(mSrcData, &jpegMemDest.mData[0], mSrcDataLen); } \ No newline at end of file diff --git a/BeefySysLib/img/JPEGData.h b/BeefySysLib/img/JPEGData.h index af3950d2..c4113afa 100644 --- a/BeefySysLib/img/JPEGData.h +++ b/BeefySysLib/img/JPEGData.h @@ -13,6 +13,7 @@ class JPEGData : public ImageData { public: bool ReadData(); + void Compress(int quality); }; NS_BF_END;