1 //-----------------------------------------------------------------------------
2 //
3 // ImageLib Sources
4 // Copyright (C) 2000-2009 by Denton Woods
5 // Last modified: 02/14/2009
6 //
7 // Filename: src-IL/src/il_jpeg.c
8 //
9 // Description: Jpeg (.jpg) functions
10 //
11 //-----------------------------------------------------------------------------
12 //
13 // Most of the comments here are sufficient, as we're just using libjpeg.
14 //  I have left most of the libjpeg example's comments intact, though.
15 //
16 
17 #include "il_internal.h"
18 
19 #ifndef IL_NO_JPG
20 	#ifndef IL_USE_IJL
21 		#ifdef RGB_RED
22 			#undef RGB_RED
23 			#undef RGB_GREEN
24 			#undef RGB_BLUE
25 		#endif
26 		#define RGB_RED		0
27 		#define RGB_GREEN	1
28 		#define RGB_BLUE	2
29 
30 		#if defined(_MSC_VER)
31 			#pragma warning(push)
32 			#pragma warning(disable : 4005)  // Redefinitions in
33 			#pragma warning(disable : 4142)  //  jmorecfg.h
34 		#endif
35 
36 		#include "jpeglib.h"
37 
38 		#if JPEG_LIB_VERSION < 62
39 			#warning DevIL was designed with libjpeg 6b or higher in mind.  Consider upgrading at www.ijg.org
40 		#endif
41 	#else
42 		#include <ijl.h>
43 		#include <limits.h>
44 	#endif
45 
46 #include "il_jpeg.h"
47 #include "il_manip.h"
48 #include <setjmp.h>
49 
50 
51 #if (defined(_WIN32) || defined(_WIN64)) && defined(IL_USE_PRAGMA_LIBS)
52 	#if defined(_MSC_VER) || defined(__BORLANDC__)
53 		#ifdef IL_USE_IJL
54 			//pragma comment(lib, "ijl15.lib")
55 		#else
56 			#ifndef _DEBUG
57 				#pragma comment(lib, "libjpeg.lib")
58 			#else
59 				#pragma comment(lib, "libjpeg-d.lib")
60 			#endif
61 		#endif//IL_USE_IJL
62 	#endif
63 #endif
64 
65 
66 static ILboolean jpgErrorOccured = IL_FALSE;
67 
68 // define a protype of ilLoadFromJpegStruct
69 ILboolean ilLoadFromJpegStruct(void *_JpegInfo);
70 
71 // Internal function used to get the .jpg header from the current file.
iGetJpgHead(ILubyte * Header)72 void iGetJpgHead(ILubyte *Header)
73 {
74 	Header[0] = igetc();
75 	Header[1] = igetc();
76 	return;
77 }
78 
79 
80 // Internal function used to check if the HEADER is a valid .Jpg header.
iCheckJpg(ILubyte Header[2])81 ILboolean iCheckJpg(ILubyte Header[2])
82 {
83 	if (Header[0] != 0xFF || Header[1] != 0xD8)
84 		return IL_FALSE;
85 	return IL_TRUE;
86 }
87 
88 
89 // Internal function to get the header and check it.
iIsValidJpeg()90 ILboolean iIsValidJpeg()
91 {
92 	ILubyte Head[2];
93 
94 	iGetJpgHead(Head);
95 	iseek(-2, IL_SEEK_CUR);  // Go ahead and restore to previous state
96 
97 	return iCheckJpg(Head);
98 }
99 
100 
101 //! Checks if the file specified in FileName is a valid .jpg file.
ilIsValidJpeg(ILconst_string FileName)102 ILboolean ilIsValidJpeg(ILconst_string FileName)
103 {
104 	ILHANDLE	JpegFile;
105 	ILboolean	bJpeg = IL_FALSE;
106 
107 	if (!iCheckExtension(FileName, IL_TEXT("jpg")) &&
108 		!iCheckExtension(FileName, IL_TEXT("jpe")) &&
109 		!iCheckExtension(FileName, IL_TEXT("jpeg")) &&
110 		!iCheckExtension(FileName, IL_TEXT("jif")) &&
111 		!iCheckExtension(FileName, IL_TEXT("jfif")))
112 	{
113 		ilSetError(IL_INVALID_EXTENSION);
114 		return bJpeg;
115 	}
116 
117 	JpegFile = iopenr(FileName);
118 	if (JpegFile == NULL) {
119 		ilSetError(IL_COULD_NOT_OPEN_FILE);
120 		return bJpeg;
121 	}
122 
123 	bJpeg = ilIsValidJpegF(JpegFile);
124 	icloser(JpegFile);
125 
126 	return bJpeg;
127 }
128 
129 
130 //! Checks if the ILHANDLE contains a valid .jpg file at the current position.
ilIsValidJpegF(ILHANDLE File)131 ILboolean ilIsValidJpegF(ILHANDLE File)
132 {
133 	ILuint		FirstPos;
134 	ILboolean	bRet;
135 
136 	iSetInputFile(File);
137 	FirstPos = itell();
138 	bRet = iIsValidJpeg();
139 	iseek(FirstPos, IL_SEEK_SET);
140 
141 	return bRet;
142 }
143 
144 
ilIsValidJpegL(const void * Lump,ILuint Size)145 ILboolean ilIsValidJpegL(const void *Lump, ILuint Size)
146 {
147 	iSetInputLump(Lump, Size);
148 	return iIsValidJpeg();
149 }
150 
151 
152 #ifndef IL_USE_IJL // Use libjpeg instead of the IJL.
153 
154 // Overrides libjpeg's stupid error/warning handlers. =P
ExitErrorHandle(struct jpeg_common_struct * JpegInfo)155 void ExitErrorHandle (struct jpeg_common_struct *JpegInfo)
156 {
157 	ilSetError(IL_LIB_JPEG_ERROR);
158 	jpgErrorOccured = IL_TRUE;
159 	return;
160 }
OutputMsg(struct jpeg_common_struct * JpegInfo)161 void OutputMsg(struct jpeg_common_struct *JpegInfo)
162 {
163 	return;
164 }
165 
166 
167 //! Reads a jpeg file
ilLoadJpeg(ILconst_string FileName)168 ILboolean ilLoadJpeg(ILconst_string FileName)
169 {
170 	ILHANDLE	JpegFile;
171 	ILboolean	bJpeg = IL_FALSE;
172 
173 	JpegFile = iopenr(FileName);
174 	if (JpegFile == NULL) {
175 		ilSetError(IL_COULD_NOT_OPEN_FILE);
176 		return bJpeg;
177 	}
178 
179 	bJpeg = ilLoadJpegF(JpegFile);
180 	icloser(JpegFile);
181 
182 	return bJpeg;
183 }
184 
185 
186 //! Reads an already-opened jpeg file
ilLoadJpegF(ILHANDLE File)187 ILboolean ilLoadJpegF(ILHANDLE File)
188 {
189 	ILboolean	bRet;
190 	ILuint		FirstPos;
191 
192 	iSetInputFile(File);
193 	FirstPos = itell();
194 	bRet = iLoadJpegInternal();
195 	iseek(FirstPos, IL_SEEK_SET);
196 
197 	return bRet;
198 }
199 
200 
201 // Reads from a memory "lump" containing a jpeg
ilLoadJpegL(const void * Lump,ILuint Size)202 ILboolean ilLoadJpegL(const void *Lump, ILuint Size)
203 {
204 	iSetInputLump(Lump, Size);
205 	return iLoadJpegInternal();
206 }
207 
208 
209 typedef struct {
210   struct jpeg_source_mgr pub;	/* public fields */
211 
212   JOCTET * buffer;		/* start of buffer */
213   boolean start_of_file;	/* have we gotten any data yet? */
214 } iread_mgr;
215 
216 typedef iread_mgr * iread_ptr;
217 
218 #define INPUT_BUF_SIZE  4096  // choose an efficiently iread'able size
219 
220 
221 METHODDEF(void)
init_source(j_decompress_ptr cinfo)222 init_source (j_decompress_ptr cinfo)
223 {
224 	iread_ptr src = (iread_ptr) cinfo->src;
225 	src->start_of_file = TRUE;
226 }
227 
228 
229 METHODDEF(boolean)
fill_input_buffer(j_decompress_ptr cinfo)230 fill_input_buffer (j_decompress_ptr cinfo)
231 {
232 	iread_ptr src = (iread_ptr) cinfo->src;
233 	ILint nbytes;
234 
235 	nbytes = iread(src->buffer, 1, INPUT_BUF_SIZE);
236 
237 	if (nbytes <= 0) {
238 		if (src->start_of_file) {  // Treat empty input file as fatal error
239 			//ERREXIT(cinfo, JERR_INPUT_EMPTY);
240 			jpgErrorOccured = IL_TRUE;
241 		}
242 		//WARNMS(cinfo, JWRN_JPEG_EOF);
243 		// Insert a fake EOI marker
244 		src->buffer[0] = (JOCTET) 0xFF;
245 		src->buffer[1] = (JOCTET) JPEG_EOI;
246 		nbytes = 2;
247 		return IL_FALSE;
248 	}
249 	if (nbytes < INPUT_BUF_SIZE) {
250 		ilGetError();  // Gets rid of the IL_FILE_READ_ERROR.
251 	}
252 
253 	src->pub.next_input_byte = src->buffer;
254 	src->pub.bytes_in_buffer = nbytes;
255 	src->start_of_file = IL_FALSE;
256 
257 	return IL_TRUE;
258 }
259 
260 
261 METHODDEF(void)
skip_input_data(j_decompress_ptr cinfo,long num_bytes)262 skip_input_data (j_decompress_ptr cinfo, long num_bytes)
263 {
264 	iread_ptr src = (iread_ptr) cinfo->src;
265 
266 	if (num_bytes > 0) {
267 		while (num_bytes > (long) src->pub.bytes_in_buffer) {
268 			num_bytes -= (long) src->pub.bytes_in_buffer;
269 			(void) fill_input_buffer(cinfo);
270 		}
271 		src->pub.next_input_byte += (size_t) num_bytes;
272 		src->pub.bytes_in_buffer -= (size_t) num_bytes;
273 	}
274 }
275 
276 
277 METHODDEF(void)
term_source(j_decompress_ptr cinfo)278 term_source (j_decompress_ptr cinfo)
279 {
280 	// no work necessary here
281 }
282 
283 
284 GLOBAL(void)
devil_jpeg_read_init(j_decompress_ptr cinfo)285 devil_jpeg_read_init (j_decompress_ptr cinfo)
286 {
287 	iread_ptr src;
288 
289 	if ( cinfo->src == NULL ) {  // first time for this JPEG object?
290 		cinfo->src = (struct jpeg_source_mgr *)
291 					 (*cinfo->mem->alloc_small)( (j_common_ptr)cinfo, JPOOL_PERMANENT, sizeof(iread_mgr) );
292 		src = (iread_ptr) cinfo->src;
293 		src->buffer = (JOCTET *)
294 					  (*cinfo->mem->alloc_small)( (j_common_ptr)cinfo, JPOOL_PERMANENT,
295 												  INPUT_BUF_SIZE * sizeof(JOCTET) );
296 	}
297 
298 	src = (iread_ptr) cinfo->src;
299 	src->pub.init_source = init_source;
300 	src->pub.fill_input_buffer = fill_input_buffer;
301 	src->pub.skip_input_data = skip_input_data;
302 	src->pub.resync_to_restart = jpeg_resync_to_restart;  // use default method
303 	src->pub.term_source = term_source;
304 	src->pub.bytes_in_buffer = 0;  // forces fill_input_buffer on first read
305 	src->pub.next_input_byte = NULL;  // until buffer loaded
306 }
307 
308 
309 jmp_buf	JpegJumpBuffer;
310 
iJpegErrorExit(j_common_ptr cinfo)311 static void iJpegErrorExit( j_common_ptr cinfo )
312 {
313 	ilSetError( IL_LIB_JPEG_ERROR );
314 	jpeg_destroy( cinfo );
315 	longjmp( JpegJumpBuffer, 1 );
316 }
317 
318 // Internal function used to load the jpeg.
iLoadJpegInternal()319 ILboolean iLoadJpegInternal()
320 {
321 	struct jpeg_error_mgr			Error;
322 	struct jpeg_decompress_struct	JpegInfo;
323 	ILboolean						result;
324 
325 	if (iCurImage == NULL) {
326 		ilSetError(IL_ILLEGAL_OPERATION);
327 		return IL_FALSE;
328 	}
329 
330 	JpegInfo.err = jpeg_std_error(&Error);		// init standard error handlers
331 	Error.error_exit = iJpegErrorExit;				// add our exit handler
332 	Error.output_message = OutputMsg;
333 
334 	if ((result = setjmp(JpegJumpBuffer) == 0) != IL_FALSE) {
335 		jpeg_create_decompress(&JpegInfo);
336 		JpegInfo.do_block_smoothing = IL_TRUE;
337 		JpegInfo.do_fancy_upsampling = IL_TRUE;
338 
339 		//jpeg_stdio_src(&JpegInfo, iGetFile());
340 
341 		devil_jpeg_read_init(&JpegInfo);
342 		jpeg_read_header(&JpegInfo, IL_TRUE);
343 
344 		result = ilLoadFromJpegStruct(&JpegInfo);
345 
346 		jpeg_finish_decompress(&JpegInfo);
347 		jpeg_destroy_decompress(&JpegInfo);
348 
349 	}
350 	else
351 	{
352 		jpeg_destroy_decompress(&JpegInfo);
353 	}
354 
355 	//return ilFixImage();  // No need to call it again (called first in ilLoadFromJpegStruct).
356 	return result;
357 }
358 
359 
360 
361 typedef struct
362 {
363 	struct jpeg_destination_mgr		pub;
364 	JOCTET							*buffer;
365 	ILboolean						bah;
366 } iwrite_mgr;
367 
368 typedef iwrite_mgr *iwrite_ptr;
369 
370 #define OUTPUT_BUF_SIZE 4096
371 
372 
373 METHODDEF(void)
init_destination(j_compress_ptr cinfo)374 init_destination(j_compress_ptr cinfo)
375 {
376 	iwrite_ptr dest = (iwrite_ptr)cinfo->dest;
377 	dest->buffer = (JOCTET *)
378 	  (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
379 				  OUTPUT_BUF_SIZE * sizeof(JOCTET));
380 
381 	dest->pub.next_output_byte = dest->buffer;
382 	dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
383 	return;
384 }
385 
386 METHODDEF(boolean)
empty_output_buffer(j_compress_ptr cinfo)387 empty_output_buffer (j_compress_ptr cinfo)
388 {
389 	iwrite_ptr dest = (iwrite_ptr)cinfo->dest;
390 	iwrite(dest->buffer, 1, OUTPUT_BUF_SIZE);
391 	dest->pub.next_output_byte = dest->buffer;
392 	dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
393 	return IL_TRUE;
394 }
395 
396 METHODDEF(void)
term_destination(j_compress_ptr cinfo)397 term_destination (j_compress_ptr cinfo)
398 {
399 	iwrite_ptr dest = (iwrite_ptr)cinfo->dest;
400 	iwrite(dest->buffer, 1, OUTPUT_BUF_SIZE - (ILuint)dest->pub.free_in_buffer);
401 	return;
402 }
403 
404 
405 GLOBAL(void)
devil_jpeg_write_init(j_compress_ptr cinfo)406 devil_jpeg_write_init(j_compress_ptr cinfo)
407 {
408 	iwrite_ptr dest;
409 
410 	if (cinfo->dest == NULL) {	// first time for this JPEG object?
411 		cinfo->dest = (struct jpeg_destination_mgr *)
412 		  (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
413 					  sizeof(iwrite_mgr));
414 		dest = (iwrite_ptr)cinfo->dest;
415 	}
416 
417 	dest = (iwrite_ptr)cinfo->dest;
418 	dest->pub.init_destination = init_destination;
419 	dest->pub.empty_output_buffer = empty_output_buffer;
420 	dest->pub.term_destination = term_destination;
421 
422 	return;
423 }
424 
425 
426 //! Writes a Jpeg file
ilSaveJpeg(const ILstring FileName)427 ILboolean ilSaveJpeg(const ILstring FileName)
428 {
429 	ILHANDLE	JpegFile;
430 	ILuint		JpegSize;
431 
432 	if (ilGetBoolean(IL_FILE_MODE) == IL_FALSE) {
433 		if (iFileExists(FileName)) {
434 			ilSetError(IL_FILE_ALREADY_EXISTS);
435 			return IL_FALSE;
436 		}
437 	}
438 
439 	JpegFile = iopenw(FileName);
440 	if (JpegFile == NULL) {
441 		ilSetError(IL_COULD_NOT_OPEN_FILE);
442 		return IL_FALSE;
443 	}
444 
445 	JpegSize = ilSaveJpegF(JpegFile);
446 	iclosew(JpegFile);
447 
448 	if (JpegSize == 0)
449 		return IL_FALSE;
450 	return IL_TRUE;
451 }
452 
453 
454 //! Writes a Jpeg to an already-opened file
ilSaveJpegF(ILHANDLE File)455 ILuint ilSaveJpegF(ILHANDLE File)
456 {
457 	ILuint Pos;
458 	iSetOutputFile(File);
459 	Pos = itellw();
460 	if (iSaveJpegInternal() == IL_FALSE)
461 		return 0;  // Error occurred
462 	return itellw() - Pos;  // Return the number of bytes written.
463 }
464 
465 
466 //! Writes a Jpeg to a memory "lump"
ilSaveJpegL(void * Lump,ILuint Size)467 ILuint ilSaveJpegL(void *Lump, ILuint Size)
468 {
469 	ILuint Pos;
470 	iSetOutputLump(Lump, Size);
471 	Pos = itellw();
472 	if (iSaveJpegInternal() == IL_FALSE)
473 		return 0;  // Error occurred
474 	return itellw() - Pos;  // Return the number of bytes written.
475 }
476 
477 
478 // Internal function used to save the Jpeg.
iSaveJpegInternal()479 ILboolean iSaveJpegInternal()
480 {
481 	struct		jpeg_compress_struct JpegInfo;
482 	struct		jpeg_error_mgr Error;
483 	JSAMPROW	row_pointer[1];
484 	ILimage		*TempImage;
485 	ILubyte		*TempData;
486 	ILenum		Type = 0;
487 
488 	if (iCurImage == NULL) {
489 		ilSetError(IL_ILLEGAL_OPERATION);
490 		return IL_FALSE;
491 	}
492 
493 	/*if (iGetHint(IL_COMPRESSION_HINT) == IL_USE_COMPRESSION)
494 		Quality = 85;  // Not sure how low we should dare go...
495 	else
496 		Quality = 99;*/
497 
498 	if ((iCurImage->Format != IL_RGB && iCurImage->Format != IL_LUMINANCE) || iCurImage->Bpc != 1) {
499 		TempImage = iConvertImage(iCurImage, IL_RGB, IL_UNSIGNED_BYTE);
500 		if (TempImage == NULL) {
501 			return IL_FALSE;
502 		}
503 	}
504 	else {
505 		TempImage = iCurImage;
506 	}
507 
508 	if (TempImage->Origin == IL_ORIGIN_LOWER_LEFT) {
509 		TempData = iGetFlipped(TempImage);
510 		if (TempData == NULL) {
511 			if (TempImage != iCurImage)
512 				ilCloseImage(TempImage);
513 			return IL_FALSE;
514 		}
515 	}
516 	else {
517 		TempData = TempImage->Data;
518 	}
519 
520 
521 	JpegInfo.err = jpeg_std_error(&Error);
522 	// Now we can initialize the JPEG compression object.
523 	jpeg_create_compress(&JpegInfo);
524 
525 	//jpeg_stdio_dest(&JpegInfo, JpegFile);
526 	devil_jpeg_write_init(&JpegInfo);
527 
528 	JpegInfo.image_width = TempImage->Width;  // image width and height, in pixels
529 	JpegInfo.image_height = TempImage->Height;
530 	JpegInfo.input_components = TempImage->Bpp;  // # of color components per pixel
531 
532 	// John Villar's addition
533 	if (TempImage->Bpp == 1)
534 		JpegInfo.in_color_space = JCS_GRAYSCALE;
535 	else
536 		JpegInfo.in_color_space = JCS_RGB;
537 
538 	jpeg_set_defaults(&JpegInfo);
539 
540 /*#ifndef IL_USE_JPEGLIB_UNMODIFIED
541 	Type = iGetInt(IL_JPG_SAVE_FORMAT);
542 	if (Type == IL_EXIF) {
543 		JpegInfo.write_JFIF_header = FALSE;
544 		JpegInfo.write_EXIF_header = TRUE;
545 	}
546 	else if (Type == IL_JFIF) {
547 		JpegInfo.write_JFIF_header = TRUE;
548 		JpegInfo.write_EXIF_header = FALSE;
549 	} //EXIF not present in libjpeg...
550 #else*/
551 	Type = Type;
552 	JpegInfo.write_JFIF_header = TRUE;
553 //#endif//IL_USE_JPEGLIB_UNMODIFIED
554 
555 	// Set the quality output
556 	jpeg_set_quality(&JpegInfo, iGetInt(IL_JPG_QUALITY), IL_TRUE);
557 	// Sets progressive saving here
558 	if (ilGetBoolean(IL_JPG_PROGRESSIVE))
559 		jpeg_simple_progression(&JpegInfo);
560 
561 	jpeg_start_compress(&JpegInfo, IL_TRUE);
562 
563 	//row_stride = image_width * 3;	// JSAMPLEs per row in image_buffer
564 
565 	while (JpegInfo.next_scanline < JpegInfo.image_height) {
566 		// jpeg_write_scanlines expects an array of pointers to scanlines.
567 		// Here the array is only one element long, but you could pass
568 		// more than one scanline at a time if that's more convenient.
569 		row_pointer[0] = &TempData[JpegInfo.next_scanline * TempImage->Bps];
570 		(void) jpeg_write_scanlines(&JpegInfo, row_pointer, 1);
571 	}
572 
573 	// Step 6: Finish compression
574 	jpeg_finish_compress(&JpegInfo);
575 
576 	// Step 7: release JPEG compression object
577 
578 	// This is an important step since it will release a good deal of memory.
579 	jpeg_destroy_compress(&JpegInfo);
580 
581 	if (TempImage->Origin == IL_ORIGIN_LOWER_LEFT)
582 		ifree(TempData);
583 	if (TempImage != iCurImage)
584 		ilCloseImage(TempImage);
585 
586 	return IL_TRUE;
587 }
588 
589 
590 
591 #else // Use the IJL instead of libjpeg.
592 
593 
594 
595 //! Reads a jpeg file
ilLoadJpeg(ILconst_string FileName)596 ILboolean ilLoadJpeg(ILconst_string FileName)
597 {
598 	if (!iFileExists(FileName)) {
599 		ilSetError(IL_COULD_NOT_OPEN_FILE);
600 		return IL_FALSE;
601 	}
602 	return iLoadJpegInternal(FileName, NULL, 0);
603 }
604 
605 
606 // Reads from a memory "lump" containing a jpeg
ilLoadJpegL(void * Lump,ILuint Size)607 ILboolean ilLoadJpegL(void *Lump, ILuint Size)
608 {
609 	return iLoadJpegInternal(NULL, Lump, Size);
610 }
611 
612 
613 // Internal function used to load the jpeg.
iLoadJpegInternal(ILstring FileName,void * Lump,ILuint Size)614 ILboolean iLoadJpegInternal(ILstring FileName, void *Lump, ILuint Size)
615 {
616     JPEG_CORE_PROPERTIES Image;
617 
618 	if (iCurImage == NULL) {
619 		ilSetError(IL_ILLEGAL_OPERATION);
620 		return IL_FALSE;
621 	}
622 
623 	if (ijlInit(&Image) != IJL_OK) {
624 		ilSetError(IL_LIB_JPEG_ERROR);
625 		return IL_FALSE;
626 	}
627 
628 	if (FileName != NULL) {
629 		Image.JPGFile = FileName;
630 		if (ijlRead(&Image, IJL_JFILE_READPARAMS) != IJL_OK) {
631 			ilSetError(IL_LIB_JPEG_ERROR);
632 			return IL_FALSE;
633 		}
634 	}
635 	else {
636 		Image.JPGBytes = Lump;
637 		Image.JPGSizeBytes = Size > 0 ? Size : UINT_MAX;
638 		if (ijlRead(&Image, IJL_JBUFF_READPARAMS) != IJL_OK) {
639 			ilSetError(IL_LIB_JPEG_ERROR);
640 			return IL_FALSE;
641 		}
642 	}
643 
644 	switch (Image.JPGChannels)
645 	{
646 		case 1:
647 			Image.JPGColor		= IJL_G;
648 			Image.DIBChannels	= 1;
649 			Image.DIBColor		= IJL_G;
650 			iCurImage->Format	= IL_LUMINANCE;
651 			break;
652 
653 		case 3:
654 			Image.JPGColor		= IJL_YCBCR;
655 			Image.DIBChannels	= 3;
656 			Image.DIBColor		= IJL_RGB;
657 			iCurImage->Format	= IL_RGB;
658 			break;
659 
660         case 4:
661 			Image.JPGColor		= IJL_YCBCRA_FPX;
662 			Image.DIBChannels	= 4;
663 			Image.DIBColor		= IJL_RGBA_FPX;
664 			iCurImage->Format	= IL_RGBA;
665 			break;
666 
667         default:
668 			// This catches everything else, but no
669 			// color twist will be performed by the IJL.
670 			/*Image.DIBColor = (IJL_COLOR)IJL_OTHER;
671 			Image.JPGColor = (IJL_COLOR)IJL_OTHER;
672 			Image.DIBChannels = Image.JPGChannels;
673 			break;*/
674 			ijlFree(&Image);
675 			ilSetError(IL_LIB_JPEG_ERROR);
676 			return IL_FALSE;
677 	}
678 
679 	if (!ilTexImage(Image.JPGWidth, Image.JPGHeight, 1, (ILubyte)Image.DIBChannels, iCurImage->Format, IL_UNSIGNED_BYTE, NULL)) {
680 		ijlFree(&Image);
681 		return IL_FALSE;
682 	}
683 	iCurImage->Origin = IL_ORIGIN_UPPER_LEFT;
684 
685 	Image.DIBWidth		= Image.JPGWidth;
686 	Image.DIBHeight		= Image.JPGHeight;
687 	Image.DIBPadBytes	= 0;
688 	Image.DIBBytes		= iCurImage->Data;
689 
690 	if (FileName != NULL) {
691 		if (ijlRead(&Image, IJL_JFILE_READWHOLEIMAGE) != IJL_OK) {
692 			ijlFree(&Image);
693 			ilSetError(IL_LIB_JPEG_ERROR);
694 			return IL_FALSE;
695 		}
696 	}
697 	else {
698 		if (ijlRead(&Image, IJL_JBUFF_READWHOLEIMAGE) != IJL_OK) {
699 			ijlFree(&Image);
700 			ilSetError(IL_LIB_JPEG_ERROR);
701 			return IL_FALSE;
702 		}
703 	}
704 
705 	ijlFree(&Image);
706 	return ilFixImage();
707 }
708 
709 
710 //! Writes a Jpeg file
ilSaveJpeg(ILconst_string FileName)711 ILboolean ilSaveJpeg(ILconst_string FileName)
712 {
713 	if (ilGetBoolean(IL_FILE_MODE) == IL_FALSE) {
714 		if (iFileExists(FileName)) {
715 			ilSetError(IL_FILE_ALREADY_EXISTS);
716 			return IL_FALSE;
717 		}
718 	}
719 
720 	return iSaveJpegInternal(FileName, NULL, 0);
721 }
722 
723 
724 //! Writes a Jpeg to a memory "lump"
ilSaveJpegL(void * Lump,ILuint Size)725 ILboolean ilSaveJpegL(void *Lump, ILuint Size)
726 {
727 	return iSaveJpegInternal(NULL, Lump, Size);
728 }
729 
730 
731 // Internal function used to save the Jpeg.
iSaveJpegInternal(ILstring FileName,void * Lump,ILuint Size)732 ILboolean iSaveJpegInternal(ILstring FileName, void *Lump, ILuint Size)
733 {
734 	JPEG_CORE_PROPERTIES	Image;
735 	ILuint	Quality;
736 	ILimage	*TempImage;
737 	ILubyte	*TempData;
738 
739 	imemclear(&Image, sizeof(JPEG_CORE_PROPERTIES));
740 
741 	if (iCurImage == NULL) {
742 		ilSetError(IL_ILLEGAL_OPERATION);
743 		return IL_FALSE;
744 	}
745 	if (FileName == NULL && Lump == NULL) {
746 		ilSetError(IL_INVALID_PARAM);
747 		return IL_FALSE;
748 	}
749 
750 	if (iGetHint(IL_COMPRESSION_HINT) == IL_USE_COMPRESSION)
751 		Quality = 85;  // Not sure how low we should dare go...
752 	else
753 		Quality = 99;
754 
755 	if (ijlInit(&Image) != IJL_OK) {
756 		ilSetError(IL_LIB_JPEG_ERROR);
757 		return IL_FALSE;
758 	}
759 
760 	if ((iCurImage->Format != IL_RGB && iCurImage->Format != IL_RGBA && iCurImage->Format != IL_LUMINANCE)
761 		|| iCurImage->Bpc != 1) {
762 		if (iCurImage->Format == IL_BGRA)
763 			Temp = iConvertImage(iCurImage, IL_RGBA, IL_UNSIGNED_BYTE);
764 		else
765 			Temp = iConvertImage(iCurImage, IL_RGB, IL_UNSIGNED_BYTE);
766 		if (Temp == NULL) {
767 			return IL_FALSE;
768 		}
769 	}
770 	else {
771 		Temp = iCurImage;
772 	}
773 
774 	if (TempImage->Origin == IL_ORIGIN_LOWER_LEFT) {
775 		TempData = iGetFlipped(TempImage);
776 		if (TempData == NULL) {
777 			if (TempImage != iCurImage)
778 				ilCloseImage(TempImage);
779 			return IL_FALSE;
780 		}
781 	}
782 	else {
783 		TempData = TempImage->Data;
784 	}
785 
786 	// Setup DIB
787 	Image.DIBWidth		= TempImage->Width;
788 	Image.DIBHeight		= TempImage->Height;
789 	Image.DIBChannels	= TempImage->Bpp;
790 	Image.DIBBytes		= TempData;
791 	Image.DIBPadBytes	= 0;
792 
793 	// Setup JPEG
794 	Image.JPGWidth		= TempImage->Width;
795 	Image.JPGHeight		= TempImage->Height;
796 	Image.JPGChannels	= TempImage->Bpp;
797 
798 	switch (Temp->Bpp)
799 	{
800 		case 1:
801 			Image.DIBColor			= IJL_G;
802 			Image.JPGColor			= IJL_G;
803 			Image.JPGSubsampling	= IJL_NONE;
804 			break;
805 		case 3:
806 			Image.DIBColor			= IJL_RGB;
807 			Image.JPGColor			= IJL_YCBCR;
808 			Image.JPGSubsampling	= IJL_411;
809 			break;
810 		case 4:
811 			Image.DIBColor			= IJL_RGBA_FPX;
812 			Image.JPGColor			= IJL_YCBCRA_FPX;
813 			Image.JPGSubsampling	= IJL_4114;
814 			break;
815 	}
816 
817 	if (FileName != NULL) {
818 		Image.JPGFile = FileName;
819 		if (ijlWrite(&Image, IJL_JFILE_WRITEWHOLEIMAGE) != IJL_OK) {
820 			if (TempImage != iCurImage)
821 				ilCloseImage(TempImage);
822 			ilSetError(IL_LIB_JPEG_ERROR);
823 			return IL_FALSE;
824 		}
825 	}
826 	else {
827 		Image.JPGBytes = Lump;
828 		Image.JPGSizeBytes = Size;
829 		if (ijlWrite(&Image, IJL_JBUFF_WRITEWHOLEIMAGE) != IJL_OK) {
830 			if (TempImage != iCurImage)
831 				ilCloseImage(TempImage);
832 			ilSetError(IL_LIB_JPEG_ERROR);
833 			return IL_FALSE;
834 		}
835 	}
836 
837 	ijlFree(&Image);
838 
839 	if (TempImage->Origin == IL_ORIGIN_LOWER_LEFT)
840 		ifree(TempData);
841 	if (Temp != iCurImage)
842 		ilCloseImage(Temp);
843 
844 	return IL_TRUE;
845 }
846 
847 #endif//IL_USE_IJL
848 
849 
850 // Access point for applications wishing to use the jpeg library directly in
851 // conjunction with DevIL.
852 //
853 // The decompressor must be set up with an input source and all desired parameters
854 // this function is called. The caller must call jpeg_finish_decompress because
855 // the caller may still need decompressor after calling this for e.g. examining
856 // saved markers.
ilLoadFromJpegStruct(void * _JpegInfo)857 ILboolean ilLoadFromJpegStruct(void *_JpegInfo)
858 {
859 #ifndef IL_NO_JPG
860 #ifndef IL_USE_IJL
861 	// sam. void (*errorHandler)(j_common_ptr);
862 	ILubyte	*TempPtr[1];
863 	ILuint	Returned;
864 	j_decompress_ptr JpegInfo = (j_decompress_ptr)_JpegInfo;
865 
866 	//added on 2003-08-31 as explained in sf bug 596793
867 	jpgErrorOccured = IL_FALSE;
868 
869 	// sam. errorHandler = JpegInfo->err->error_exit;
870 	// sam. JpegInfo->err->error_exit = ExitErrorHandle;
871 	jpeg_start_decompress((j_decompress_ptr)JpegInfo);
872 
873 	if (!ilTexImage(JpegInfo->output_width, JpegInfo->output_height, 1, (ILubyte)JpegInfo->output_components, 0, IL_UNSIGNED_BYTE, NULL)) {
874 		return IL_FALSE;
875 	}
876 	iCurImage->Origin = IL_ORIGIN_UPPER_LEFT;
877 
878 	switch (iCurImage->Bpp)
879 	{
880 		case 1:
881 			iCurImage->Format = IL_LUMINANCE;
882 			break;
883 		case 3:
884 			iCurImage->Format = IL_RGB;
885 			break;
886 		case 4:
887 			iCurImage->Format = IL_RGBA;
888 			break;
889 		default:
890 			//@TODO: Anyway to get here?  Need to error out or something...
891 			break;
892 	}
893 
894 	TempPtr[0] = iCurImage->Data;
895 	while (JpegInfo->output_scanline < JpegInfo->output_height) {
896 		Returned = jpeg_read_scanlines(JpegInfo, TempPtr, 1);  // anyway to make it read all at once?
897 		TempPtr[0] += iCurImage->Bps;
898 		if (Returned == 0)
899 			break;
900 	}
901 
902 	// sam. JpegInfo->err->error_exit = errorHandler;
903 
904 	if (jpgErrorOccured)
905 		return IL_FALSE;
906 
907 	return ilFixImage();
908 #endif
909 #endif
910 	return IL_FALSE;
911 }
912 
913 
914 
915 // Access point for applications wishing to use the jpeg library directly in
916 // conjunction with DevIL.
917 //
918 // The caller must set up the desired parameters by e.g. calling
919 // jpeg_set_defaults and overriding the parameters the caller wishes
920 // to change, such as quality, before calling this function. The caller
921 // is also responsible for calling jpeg_finish_compress in case the
922 // caller still needs to compressor for something.
923 //
ilSaveFromJpegStruct(void * _JpegInfo)924 ILboolean ilSaveFromJpegStruct(void *_JpegInfo)
925 {
926 #ifndef IL_NO_JPG
927 #ifndef IL_USE_IJL
928 	void (*errorHandler)();
929 	JSAMPROW	row_pointer[1];
930 	ILimage		*TempImage;
931 	ILubyte		*TempData;
932 	j_compress_ptr JpegInfo = (j_compress_ptr)_JpegInfo;
933 
934 	if (iCurImage == NULL) {
935 		ilSetError(IL_ILLEGAL_OPERATION);
936 		return IL_FALSE;
937 	}
938 
939 	//added on 2003-08-31 as explained in SF bug 596793
940 	jpgErrorOccured = IL_FALSE;
941 
942 	errorHandler = JpegInfo->err->error_exit;
943 	JpegInfo->err->error_exit = ExitErrorHandle;
944 
945 
946 	if ((iCurImage->Format != IL_RGB && iCurImage->Format != IL_LUMINANCE) || iCurImage->Bpc != 1) {
947 		TempImage = iConvertImage(iCurImage, IL_RGB, IL_UNSIGNED_BYTE);
948 		if (TempImage == NULL) {
949 			return IL_FALSE;
950 		}
951 	}
952 	else {
953 		TempImage = iCurImage;
954 	}
955 
956 	if (TempImage->Origin == IL_ORIGIN_LOWER_LEFT) {
957 		TempData = iGetFlipped(TempImage);
958 		if (TempData == NULL) {
959 			if (TempImage != iCurImage)
960 				ilCloseImage(TempImage);
961 			return IL_FALSE;
962 		}
963 	}
964 	else {
965 		TempData = TempImage->Data;
966 	}
967 
968 	JpegInfo->image_width = TempImage->Width;  // image width and height, in pixels
969 	JpegInfo->image_height = TempImage->Height;
970 	JpegInfo->input_components = TempImage->Bpp;  // # of color components per pixel
971 
972 	jpeg_start_compress(JpegInfo, IL_TRUE);
973 
974 	//row_stride = image_width * 3;	// JSAMPLEs per row in image_buffer
975 
976 	while (JpegInfo->next_scanline < JpegInfo->image_height) {
977 		// jpeg_write_scanlines expects an array of pointers to scanlines.
978 		// Here the array is only one element long, but you could pass
979 		// more than one scanline at a time if that's more convenient.
980 		row_pointer[0] = &TempData[JpegInfo->next_scanline * TempImage->Bps];
981 		(void) jpeg_write_scanlines(JpegInfo, row_pointer, 1);
982 	}
983 
984 	if (TempImage->Origin == IL_ORIGIN_LOWER_LEFT)
985 		ifree(TempData);
986 	if (TempImage != iCurImage)
987 		ilCloseImage(TempImage);
988 
989 	return (!jpgErrorOccured);
990 #endif//IL_USE_IJL
991 #endif//IL_NO_JPG
992 	return IL_FALSE;
993 }
994 
995 
996 #if defined(_MSC_VER)
997 	#pragma warning(pop)
998 	//#pragma warning(disable : 4756)  // Disables 'named type definition in parentheses' warning
999 #endif
1000 
1001 #endif//IL_NO_JPG
1002