Sign in to follow this  
Magos

Anyone with experience using the IJG JPEG library?

Recommended Posts

Magos    218
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]

Share this post


Link to post
Share on other sites
Wudan    211
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.)

Share this post


Link to post
Share on other sites
coelurus    259
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]

Share this post


Link to post
Share on other sites
Magos    218
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 - 16
BYTE[5] Type - 'J F I F \0'
WORD Version - 01 01 (1.1)
BYTE Units - 01 (Dots per inch)
WORD DensityX - 72
WORD DensityY - 72
BYTE ThumbnailWidth - 0
BYTE ThumbnailHeight - 0

WORD Marker - FF DB (Quantization table)
WORD Length - 67
BYTE[65] ??? - ???

WORD Marker - FF DB (Quantization table)
WORD Length - 67
BYTE[65] ??? - ???

WORD Marker - FF C0 (Baseline DCT)
WORD Length - 17
BYTE[15] ??? - ???

WORD Marker - FF C4 (Huffman table)
WORD Length - 31
BYTE[29] ??? - ???

WORD Marker - FF C4 (Huffman table)
WORD Length - 181
BYTE[179] ??? - ???

WORD Marker - FF C4 (Huffman table)
WORD Length - 31
BYTE[29] ??? - ???

WORD Marker - FF C4 (Huffman table)
WORD Length - 181
BYTE[179] ??? - ???

WORD Marker - FF DA (Start of scan)
WORD Length - 12
BYTE[10] ??? - ???

BYTE ??? - F2
BYTE ??? - CA
BYTE ??? - 28
BYTE ??? - A2
BYTE ??? - BF
BYTE ??? - 3A
BYTE ??? - 3F
BYTE ??? - B2
BYTE ??? - CF

WORD Marker - FF D9 (End of Image)


+----------------------
| ImageThatWasSaved.jpg
+----------------------
WORD Marker - FF D8 (Start of image)

WORD Marker - FF DB (Quantization table)
WORD Length - 67
BYTE[65] ??? - ???

WORD Marker - FF C0 (Baseline DCT)
WORD Length - 20
BYTE[18] ??? - ???

WORD Marker - FF C4 (Huffman table)
WORD Length - 31
BYTE[29] ??? - ???

WORD Marker - FF C4 (Huffman table)
WORD Length - 181
BYTE[179] ??? - ???

WORD Marker - FF DA (Start of scan)
WORD Length - 14
BYTE[12] ??? - ???

BYTE ??? - FD
BYTE ??? - 50
BYTE ??? - AF
BYTE ??? - CA
BYTE ??? - AA
BYTE ??? - FC
BYTE ??? - AA
BYTE ??? - AF
BYTE ??? - D5
BYTE ??? - 3A

Share this post


Link to post
Share on other sites
T1Oracle    100
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.

Share this post


Link to post
Share on other sites
Magos    218
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...

Share this post


Link to post
Share on other sites
Wudan    211
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.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this