Browse Source

JPEG compression

Brian Fiete 4 years ago
parent
commit
4211c267a6

+ 21 - 1
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<String> 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;
+}

+ 9 - 0
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)

+ 1 - 0
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();

+ 142 - 1
BeefySysLib/img/JPEGData.cpp

@@ -141,6 +141,53 @@ struct JpegMemSource
 		
 };
 
+#define JPEGMEMDEST_BLOCK_SIZE 16384
+
+struct JpegMemDest : jpeg_destination_mgr
+{
+	Array<uint8> 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);
 }

+ 1 - 0
BeefySysLib/img/JPEGData.h

@@ -13,6 +13,7 @@ class JPEGData : public ImageData
 {
 public:
 	bool					ReadData();
+	void					Compress(int quality);
 };
 
 NS_BF_END;