Anyone with experience using the IJG JPEG library?

Started by
6 comments, last by Wudan 18 years, 6 months ago
I'm having trouble getting my JPEG loader/saver to work. Every time I try to load a file it keeps crashing when calling the jpeg_start_decompress function (in Read). I'm not sure what could be wrong but guess it's the memory manager (I'm not using FILE:s so I had to write one of my own). I've found very little info on the subject and the official docs aren't that detailed/descriptive IMO. Any idea what might be wrong? Anyway, here's my header:

//+-----------------------------------------------------------------------------
//| Inclusion guard
//+-----------------------------------------------------------------------------
#ifndef MAGOS_JPEG_H
#define MAGOS_JPEG_H


//+-----------------------------------------------------------------------------
//| Prevents stupid redefinitions
//+-----------------------------------------------------------------------------
#define XMD_H


//+-----------------------------------------------------------------------------
//| Included files
//+-----------------------------------------------------------------------------
#include "Buffer.h"


//+-----------------------------------------------------------------------------
//| Included JPEG files (requires C inclusion)
//+-----------------------------------------------------------------------------
extern "C"
{
#include <jpeglib.h>
}


//+-----------------------------------------------------------------------------
//| Source manager structure
//+-----------------------------------------------------------------------------
struct JPEG_SOURCE_MANAGER
{
	JPEG_SOURCE_MANAGER()
	{
		SourceBuffer = NULL;
		SourceBufferSize = 0;
		Buffer = NULL;
	}

	jpeg_source_mgr Manager;
	UCHAR* SourceBuffer;
	LONG SourceBufferSize;
	JOCTET* Buffer;
};


//+-----------------------------------------------------------------------------
//| Destination manager structure
//+-----------------------------------------------------------------------------
struct JPEG_DESTINATION_MANAGER
{
	JPEG_DESTINATION_MANAGER()
	{
		DestinationBuffer = NULL;
		DestinationBufferSize = 0;
		Buffer = NULL;
	}

	jpeg_destination_mgr Manager;
	UCHAR* DestinationBuffer;
	LONG DestinationBufferSize;
	JOCTET* Buffer;
};


//+-----------------------------------------------------------------------------
//| Jpeg class
//+-----------------------------------------------------------------------------
class JPEG
{
	public:
		CONSTRUCTOR JPEG();
		DESTRUCTOR ~JPEG();

		BOOL Write(CONST BUFFER& SourceBuffer, BUFFER& TargetBuffer, INT Width, INT Height, INT Quality);
		BOOL Read(CONST BUFFER& SourceBuffer, BUFFER& TargetBuffer, INT* Width = NULL, INT* Height = NULL);

	protected:
		static VOID SetMemorySource(jpeg_decompress_struct* Info, UCHAR* Buffer, ULONG Size);
		static VOID SetMemoryDestination(jpeg_compress_struct* Info, UCHAR* Buffer, ULONG Size);

		static VOID SourceInit(jpeg_decompress_struct* Info);
		static BOOLEAN SourceFill(jpeg_decompress_struct* Info);
		static VOID SourceSkip(jpeg_decompress_struct* Info, LONG NrOfBytes);
		static VOID SourceTerminate(jpeg_decompress_struct* Info);

		static VOID DestinationInit(jpeg_compress_struct* Info);
		static BOOLEAN DestinationEmpty(jpeg_compress_struct* Info);
		static VOID DestinationTerminate(jpeg_compress_struct* Info);
};


//+-----------------------------------------------------------------------------
//| Global objects
//+-----------------------------------------------------------------------------
extern JPEG Jpeg;


//+-----------------------------------------------------------------------------
//| End of inclusion guard
//+-----------------------------------------------------------------------------
#endif




And my source:

//+-----------------------------------------------------------------------------
//| Included files
//+-----------------------------------------------------------------------------
#include "Jpeg.h"


//+-----------------------------------------------------------------------------
//| Global objects
//+-----------------------------------------------------------------------------
JPEG Jpeg;


//+-----------------------------------------------------------------------------
//| Constructor
//+-----------------------------------------------------------------------------
JPEG::JPEG()
{
	//Empty
}


//+-----------------------------------------------------------------------------
//| Destructor
//+-----------------------------------------------------------------------------
JPEG::~JPEG()
{
	//Empty
}


//+-----------------------------------------------------------------------------
//| Writes JPEG data
//+-----------------------------------------------------------------------------
BOOL JPEG::Write(CONST BUFFER& SourceBuffer, BUFFER& TargetBuffer, INT Width, INT Height, INT Quality)
{
	INT Stride;
	INT RealSize;
	INT DummySize;
	BUFFER TempBuffer;
	JSAMPROW Pointer[1];
	jpeg_compress_struct Info;
	jpeg_error_mgr ErrorManager;

	Info.err = jpeg_std_error(&ErrorManager);

	DummySize = ((Width * Height * 4) * 2) + 10000;
	TempBuffer.Resize(DummySize);

	jpeg_create_compress(&Info);

	SetMemoryDestination(&Info, reinterpret_cast<UCHAR*>(TempBuffer.GetData()), TempBuffer.GetSize());

	Info.image_width = Width;
	Info.image_height = Height;
	Info.input_components = 4;
	Info.in_color_space = JCS_UNKNOWN;

	jpeg_set_defaults(&Info);
	jpeg_set_quality(&Info, Quality, TRUE);
	jpeg_start_compress(&Info, TRUE);

	Stride = Width * 4;
	while(Info.next_scanline < Info.image_height)
	{
		Pointer[0] = reinterpret_cast<JSAMPROW>(&SourceBuffer[Info.next_scanline * Stride]);
		jpeg_write_scanlines(&Info, Pointer, 1);
	}

	RealSize = DummySize - static_cast<INT>(Info.dest->free_in_buffer);
	TargetBuffer.Resize(RealSize);

	std::memcpy(&TargetBuffer[0], &TempBuffer[0], RealSize);

	jpeg_finish_compress(&Info);
	jpeg_destroy_compress(&Info);

	return TRUE;
}


//+-----------------------------------------------------------------------------
//| Reads JPEG data
//+-----------------------------------------------------------------------------
BOOL JPEG::Read(CONST BUFFER& SourceBuffer, BUFFER& TargetBuffer, INT* Width, INT* Height)
{
	INT i;
	INT Stride;
	INT Offset;
	CHAR Opaque;
	JSAMPARRAY Pointer;
	jpeg_decompress_struct Info;
	jpeg_error_mgr ErrorManager;

	Info.err = jpeg_std_error(&ErrorManager);

	jpeg_create_decompress(&Info);
	SetMemorySource(&Info, reinterpret_cast<UCHAR*>(SourceBuffer.GetData()), SourceBuffer.GetSize());
	jpeg_read_header(&Info, TRUE);
	jpeg_start_decompress(&Info);

	if((Info.output_components != 3) && (Info.output_components != 4))
	{
		Error.SetMessage("Nr of channels must be 3 or 4!");
		return FALSE;
	}

	TargetBuffer.Resize(Info.output_width * Info.output_height * 4);
	Stride = Info.output_width * Info.output_components;
	Offset = 0;

	Pointer = (*Info.mem->alloc_sarray)(reinterpret_cast<j_common_ptr>(&Info), JPOOL_IMAGE, Stride, 1);
	while(Info.output_scanline < Info.output_height)
	{
		jpeg_read_scanlines(&Info, Pointer, 1);
		std::memcpy(&TargetBuffer[Offset], Pointer[0], Stride);
		Offset += Stride;
	}

	(*reinterpret_cast<BYTE*>(&Opaque)) = 255;

	if(Info.output_components == 3)
	{
		for(i = (Info.output_width * Info.output_height - 1); i >= 0; i--)
		{
			TargetBuffer[(i * 4) + 3] = Opaque;
			TargetBuffer[(i * 4) + 2] = TargetBuffer[(i * 3) + 2];
			TargetBuffer[(i * 4) + 1] = TargetBuffer[(i * 3) + 1];
			TargetBuffer[(i * 4) + 0] = TargetBuffer[(i * 3) + 0];
		}
	}

	if(Width != NULL) (*Width) = Info.output_width;
	if(Height != NULL) (*Height) = Info.output_height;

	jpeg_finish_decompress(&Info);
	jpeg_destroy_decompress(&Info);

	return TRUE;
}


//+-----------------------------------------------------------------------------
//| Sets the memory source
//+-----------------------------------------------------------------------------
VOID JPEG::SetMemorySource(jpeg_decompress_struct* Info, UCHAR* Buffer, ULONG Size)
{
	JPEG_SOURCE_MANAGER* SourceManager;

	Info->src = reinterpret_cast<jpeg_source_mgr*>((*Info->mem->alloc_small)(
reinterpret_cast<j_common_ptr>(Info), JPOOL_PERMANENT, sizeof(JPEG_SOURCE_MANAGER)));
	SourceManager = reinterpret_cast<JPEG_SOURCE_MANAGER*>(Info->src);

	SourceManager->Buffer = reinterpret_cast<JOCTET*>((*Info->mem->alloc_small)(
reinterpret_cast<j_common_ptr>(Info), JPOOL_PERMANENT, Size * sizeof(JOCTET)));
	SourceManager->SourceBuffer = Buffer;
	SourceManager->SourceBufferSize = Size;
	SourceManager->Manager.init_source = SourceInit;
	SourceManager->Manager.fill_input_buffer = SourceFill;
	SourceManager->Manager.skip_input_data = SourceSkip;
	SourceManager->Manager.resync_to_restart = jpeg_resync_to_restart;
	SourceManager->Manager.term_source = SourceTerminate;
	SourceManager->Manager.bytes_in_buffer = 0;
	SourceManager->Manager.next_input_byte = NULL;
}


//+-----------------------------------------------------------------------------
//| Sets the memory destination
//+-----------------------------------------------------------------------------
VOID JPEG::SetMemoryDestination(jpeg_compress_struct* Info, UCHAR* Buffer, ULONG Size)
{
	JPEG_DESTINATION_MANAGER* DestinationManager;

	Info->dest = reinterpret_cast<jpeg_destination_mgr*>((*Info->mem->alloc_small)(
reinterpret_cast<j_common_ptr>(Info), JPOOL_PERMANENT, sizeof(JPEG_DESTINATION_MANAGER)));
	DestinationManager = reinterpret_cast<JPEG_DESTINATION_MANAGER*>(Info->dest);

	DestinationManager->Buffer = NULL;
	DestinationManager->DestinationBuffer = Buffer;
	DestinationManager->DestinationBufferSize = Size;
	DestinationManager->Manager.init_destination = DestinationInit;
	DestinationManager->Manager.empty_output_buffer = DestinationEmpty;
	DestinationManager->Manager.term_destination = DestinationTerminate;
}


//+-----------------------------------------------------------------------------
//| Initiates the memory source
//+-----------------------------------------------------------------------------
VOID JPEG::SourceInit(jpeg_decompress_struct* Info)
{
	//Empty
}


//+-----------------------------------------------------------------------------
//| Fills the memory source
//+-----------------------------------------------------------------------------
BOOLEAN JPEG::SourceFill(jpeg_decompress_struct* Info)
{
	JPEG_SOURCE_MANAGER* SourceManager;

	SourceManager = reinterpret_cast<JPEG_SOURCE_MANAGER*>(Info->src);

	SourceManager->Buffer = SourceManager->SourceBuffer;
	SourceManager->Manager.next_input_byte = SourceManager->Buffer;
	SourceManager->Manager.bytes_in_buffer = SourceManager->SourceBufferSize;

	return TRUE;
}


//+-----------------------------------------------------------------------------
//| Skips the memory source
//+-----------------------------------------------------------------------------
VOID JPEG::SourceSkip(jpeg_decompress_struct* Info, LONG NrOfBytes)
{
	JPEG_SOURCE_MANAGER* SourceManager;

	SourceManager = reinterpret_cast<JPEG_SOURCE_MANAGER*>(Info->src);

	if(NrOfBytes > 0)
	{
		while(NrOfBytes > static_cast<LONG>(SourceManager->Manager.bytes_in_buffer))
		{
			NrOfBytes -= static_cast<LONG>(SourceManager->Manager.bytes_in_buffer);
			SourceFill(Info);
		}

		SourceManager->Manager.next_input_byte += NrOfBytes;
		SourceManager->Manager.bytes_in_buffer -= NrOfBytes;
	}
}


//+-----------------------------------------------------------------------------
//| Terminates the memory source
//+-----------------------------------------------------------------------------
VOID JPEG::SourceTerminate(jpeg_decompress_struct* Info)
{
	//Empty
}


//+-----------------------------------------------------------------------------
//| Initiates the memory destination
//+-----------------------------------------------------------------------------
VOID JPEG::DestinationInit(jpeg_compress_struct* Info)
{
	JPEG_DESTINATION_MANAGER* DestinationManager;

	DestinationManager = reinterpret_cast<JPEG_DESTINATION_MANAGER*>(Info->dest);

	DestinationManager->Buffer = DestinationManager->DestinationBuffer;
	DestinationManager->Manager.next_output_byte = DestinationManager->Buffer;
	DestinationManager->Manager.free_in_buffer = DestinationManager->DestinationBufferSize;
}


//+-----------------------------------------------------------------------------
//| Empties the memory destination
//+-----------------------------------------------------------------------------
BOOLEAN JPEG::DestinationEmpty(jpeg_compress_struct* Info)
{
	JPEG_DESTINATION_MANAGER* DestinationManager;

	DestinationManager = reinterpret_cast<JPEG_DESTINATION_MANAGER*>(Info->dest);

	DestinationManager->Manager.next_output_byte = DestinationManager->Buffer;
	DestinationManager->Manager.free_in_buffer = DestinationManager->DestinationBufferSize;

	return TRUE;
}


//+-----------------------------------------------------------------------------
//| Terminates the memory destination
//+-----------------------------------------------------------------------------
VOID JPEG::DestinationTerminate(jpeg_compress_struct* Info)
{
	//Empty
}

Also, I have to define XMD_H or otherwise I'd get lots of multiple redefinition errors. Is this correct? [Edited by - Magos on October 8, 2005 8:48:20 AM]
----------------------------------------MagosX.com
Advertisement
Any reason why you're using Intel's JPEG library instead of jpeglib?

I used the Intel's JPEG library because it worked when the JPEG library was just too confusing for me. In my defense, this was years ago, my knowledge and skills have increased exponentially since then.

I'd seriously rethink using Intel's library. It's not supported anymore (as far as I'm aware.)
There's an old thread similar to this one here where I posted a working jpeg loader using the ijg lib. I don't have the time to look through your code, but I hope you got some time to look through code that I've posted that works [smile]
I've updated the code in my first post, the loader can load JPEG's now and it can *almsot* save them. By that I mean the saved JPEG can be opened in Paint Shop Pro but not by my own loader.

I did some hex-hacking on an image (2x2 red image) and on the resulting image if loaded by my loader then saved by my saver. I noticed the saved image does not have an End-Of-Image marker. Shouldn't the IJG saver routines put one automatically?

Also I found some unknown bytes at the end of the file. Is the Start-Of-Scan block simply a header and the loose bytes is the actual data? (if so why are the header sizes different in the two images?)

NOTE: Big endian+----------------------| ImageToBeloaded.jpg+----------------------WORD Marker - FF D8 (Start of image)WORD Marker          - FF E0 (App 0)WORD Length          - 16BYTE[5] Type         - 'J F I F \0'WORD Version         - 01 01 (1.1)BYTE Units           - 01 (Dots per inch)WORD DensityX        - 72WORD DensityY        - 72BYTE ThumbnailWidth  - 0BYTE ThumbnailHeight - 0WORD Marker  - FF DB (Quantization table)WORD Length  - 67BYTE[65] ??? - ???WORD Marker  - FF DB (Quantization table)WORD Length  - 67BYTE[65] ??? - ???WORD Marker  - FF C0 (Baseline DCT)WORD Length  - 17BYTE[15] ??? - ???WORD Marker  - FF C4 (Huffman table)WORD Length  - 31BYTE[29] ??? - ???WORD Marker   - FF C4 (Huffman table)WORD Length   - 181BYTE[179] ??? - ???WORD Marker  - FF C4 (Huffman table)WORD Length  - 31BYTE[29] ??? - ???WORD Marker   - FF C4 (Huffman table)WORD Length   - 181BYTE[179] ??? - ???WORD Marker  - FF DA (Start of scan)WORD Length  - 12BYTE[10] ??? - ???BYTE ??? - F2BYTE ??? - CABYTE ??? - 28BYTE ??? - A2BYTE ??? - BFBYTE ??? - 3ABYTE ??? - 3FBYTE ??? - B2BYTE ??? - CFWORD Marker - FF D9 (End of Image)+----------------------| ImageThatWasSaved.jpg+----------------------WORD Marker - FF D8 (Start of image)WORD Marker  - FF DB (Quantization table)WORD Length  - 67BYTE[65] ??? - ???WORD Marker  - FF C0 (Baseline DCT)WORD Length  - 20BYTE[18] ??? - ???WORD Marker  - FF C4 (Huffman table)WORD Length  - 31BYTE[29] ??? - ???WORD Marker   - FF C4 (Huffman table)WORD Length   - 181BYTE[179] ??? - ???WORD Marker  - FF DA (Start of scan)WORD Length  - 14BYTE[12] ??? - ???BYTE ??? - FDBYTE ??? - 50BYTE ??? - AFBYTE ??? - CABYTE ??? - AABYTE ??? - FCBYTE ??? - AABYTE ??? - AFBYTE ??? - D5BYTE ??? - 3A
----------------------------------------MagosX.com
Where do you get "jpeglib" I just googled it and I don't see anything that makes sense. Are you sure it's better than IJL? I tried using DevIL for JPEGs and found that it puts a little bit of noise into the image while IJL loads them perfectly. Anyway, I see one link that mentions "jpeglib" but that site says
Quote:The SMALL lib written in MASM allows you to decode BASELINE-DCT-encoded JPEG pictures with smapling 2:1:1.

Which doesn't sound too encouraging.
Programming since 1995.
I think it's the one at http://www.ijg.org/
Any ideas why it doesn't save an end-of-file marker? If I manually append 0xFF and 0xD9 at the end it *seems* to work fine. None of the example code has to do this manually so I'm a bit skeptic...
----------------------------------------MagosX.com
Sorry, I thought by 'IJPG' you meant you were using Intel's lib, not the Independent JPEG group's lib. There's a release for MingW of the IJPEG group's library called 'libjpeg', I would imagine so that it mirrors the name of 'libpng'. Anyway, forget I said 'png' and let's talk JPEG.

If it loads a JPEG just fine, that's all I have to offer, my use of the API is limited to loading.

This topic is closed to new replies.

Advertisement