juce_saveJpegImageToFile


#1

once i needed that for resizing all images in a directory to a specified delta. since JUCE don’t have anything about image saving (only loading, and it’s a pity tho cause sometimes i use manipulate images with it) i finally coded this. nothing special just a callable function to save a Image to file:

[code]bool juce_saveJPEGImageToFile(const Image& img, const String& imageFileName, int quality)
{

/* This struct contains the JPEG compression parameters and pointers to
* working space (which is allocated as needed by the JPEG library).
* It is possible to have several such structures, representing multiple
* compression/decompression processes, in existence at once. We refer
* to any one struct (and its associated working data) as a “JPEG object”.
/
struct jpeg_compress_struct cinfo;
/
This struct represents a JPEG error handler. It is declared separately
* because applications often want to supply a specialized error handler
* (see the second half of this file for an example). But here we just
* take the easy way out and use the standard error handler, which will
* print a message on stderr and call exit() if compression fails.
* Note that this struct must live as long as the main JPEG parameter
* struct, to avoid dangling-pointer problems.
/
struct jpeg_error_mgr jerr;
/
More stuff /
FILE * outfile; /
target file /
int row_stride; /
physical row width in image buffer /
JSAMPARRAY buffer; /
Output row buffer */

/* Step 1: allocate and initialize JPEG compression object */

/* We have to set up the error handler first, in case the initialization
* step fails. (Unlikely, but it could happen if you are out of memory.)
* This routine fills in the contents of struct jerr, and returns jerr’s
* address which we place into the link field in cinfo.
/
cinfo.err = jpeg_std_error(&jerr);
/
Now we can initialize the JPEG compression object. */
jpeg_create_compress(&cinfo);

/* Step 2: specify data destination (eg, a file) /
/
Note: steps 2 and 3 can be done in either order. */

/* Here we use the library-supplied code to send compressed data to a
* stdio stream. You can also write your own code to do something else.
* VERY IMPORTANT: use “b” option to fopen() if you are on a machine that
* requires it in order to write binary files.
*/
if ((outfile = fopen((const char *)imageFileName, “wb”)) == NULL) {
return false;
}
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 = img.getWidth(); // image width and height, in pixels
cinfo.image_height = img.getHeight();
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.
/
row_stride = cinfo.image_width
cinfo.input_components; /* samples per row in image_buffer */

buffer = (*cinfo.mem->alloc_sarray)
((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);

int numRow = 0; // cinfo.image_height -1;
while (cinfo.next_scanline < cinfo.image_height)
{

unsigned char* pix = img.getPixelPointer(0,numRow++);
int k = 0;
for (int i=0; i<img.getWidth(); i++, k+=3)
{
	if (img.hasAlphaChannel() )
		pix++;

	buffer[0][k+2] = *pix++;
	buffer[0][k+1] = *pix++;
	buffer[0][k+0] = *pix++;
}

(void) jpeg_write_scanlines(&cinfo, buffer, 1);

}

/* 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);

/* And we’re done! */
return true;
} [/code]

hope someone is finding this useful. i’ll implement the gif and png ones (and use File or Stream instead of the String parameter)

lou


#2

interesting could be implementing in the ImageFileFormat a

and then subclassit for every image format we have:

[code]class JPEGImageFormat : public ImageFileFormat
{
public:
JPEGImageFormat() throw() {}
~JPEGImageFormat() throw() {}

const String getFormatName()
{
    return T("JPEG");
}

// ...

bool encodeImage (Image& img, OutputStream& out)
{
    // blah blah
    return juce_saveJPEGImageToFile(img,out,defaultQuality);
}

};
[/code]

jules what u think ?

lou


#3

yeah, definitely a good idea - I’ll have to add it to the very long to-do-list!


#4

yeha thanx for the listening and answering.
just another question… is possible give an eye to the todo-list (i know is your work… so don’t mind if you don’t want) ?
just to have a look of what is coming next release…

lou


#5

I don’t actually have one at the moment, just a bunch of scribbled bits of paper…


#6

eheh jules… you rock !


#7

another vote here for native bmp/png/jpg writing in JUCE!

btw kraken, cheers for the code snippet - I was very happy when I found this thread as I had not noticed that JUCE didn’t have write support for jpgs, and guess what my boss needed me to do today! :roll:


#8

Yea, I’ve determined that the quickest way to be able to print an Image from juce would be to write it out to a file and give that to an external program to print that Windows already has. When I went to looking for how to save an image, same thing.

This jpeg code could work, although I would prefer to save it as a normal bitmap if anyone has some code laying around. :slight_smile:


#9

overmindl1: just wait… the png version is on the way. bitmap and gif will fowllowe next week. thought i’m rewriting the ImageFormat class to take encode & decode virtual functions. in this way every image format would be supported for reading/writing. i’ll propose the class changes to jules then :wink:
he can take a look at the code if he don’t want to merge it directly in juce.

lou


#10

oh. just found some strange stuff.
with my routine, the produced jpg files can be read from any other program, but not from juce. i get access violations on juce_loadJPEGImageFromStream when u load the previously saved image with juce_saveJPEGImageToFile, on line:

i’m investigating. i found that we need also to set:

  ...
  cinfo.write_JFIF_header = 1;

  // Now use the library's routine to set default compression parameters.
  jpeg_set_defaults(&cinfo);

  // just setup output resolution (we fix it to 72 dpi)
  cinfo.X_density = 72;
  cinfo.Y_density = 72;
  ...

in save routine to make the jpeg file more compliant. but can’t be read afterwards…


#11

yuhuuuuu found.
jules add some lines NOOOOOW ! very very bad initialization stuff on jpeg loading. look in juce_loadJPEGImageFromStream.
this:

[code] struct jpeg_decompress_struct jpegDecompStruct;

        jpegDecompStruct.err = 0;
        jpeg_create_decompress (&jpegDecompStruct);

[/code]

should be (safe to initialize the error handler, if the lib need to call its functions is better to have it initialized):

[code] struct jpeg_decompress_struct jpegDecompStruct;
struct jpeg_error_mgr jerr;

        jpegDecompStruct.err = jpeg_std_error(&jerr);
        jpeg_create_decompress (&jpegDecompStruct);

[/code]

now image saved with juce_saveJPEGImageToFile can be loaded with juce_loadJPEGImageFromStream !

p.s. fiiiiiiiiiiix it !!!

lou


#12

what does the jpeg std error stuff do that’s useful? I set it to zero just to make it ignore the errors…


#13

ah you hacked the library. but i need the complete unhacked library.
since in a lot of jpeglib code there isn’t NULL or 0 check… is better to initialize it. for example in “reset_input_controller” sub-function that could be called in the “jpeg_read_header” function there is the line:

/* Reset other modules */ (*cinfo->err->reset_error_mgr) ((j_common_ptr) cinfo);

and there are a lot of other places that u get your error handler code get called without NULL or zero check. if “err” is NULL and “reset_input_controller” get called then you get leaks as i’m getting.
just define as std error handling, what are the problems… looking in sourceforge a lot of other image visualizer app when loading jpeg deal with the standard error struct… why you don’t want ?
if you modify the jpeglib we are unable to use juce as image loader/saver.

lou


#14

ah, I see.

Can’t remember why I hacked it - (must have been years ago now!) You’re quite right, and I’ll change it.


#15

ok. you’ll find in the juce user share files the 3 unmodified libraries.
i updated jpeglib to 6b (latest) and added only the files needed for loading/saving. also i updated the pnglib for load/save (1.2.8 ) and the zlib (to 1.2.3 which now are more faster about 30% in decompressing from version 1.1.3 that u have now in juce). just for keep library dependance up to date (less error prone, more fast and checked code, and so on…).

juce_jpeglib.zip
juce_pnglib.zip
juce_ziplib.zip

that u can replace in the juce image_file_format directory.
(for ziplib you need to change some z_xxxx function call to xxxx in the juce_GZIPCompressorOutputStream.cpp , but i’ve done and tested for ya)

lou


#16

Nice one. I’ll get those libs and have a go at integrating it!


#17

[quote=“OvermindDL1”]
This jpeg code could work, although I would prefer to save it as a normal bitmap if anyone has some code laying around. :)[/quote]

If you are in a real hurry, google EasyBMP.


#18

just added a Image 2 PNG file exporter function.

http://www.anticore.org/juce/

gif and bitmap (winzoze only?), targa, pcx, ppm following soon…

lou


#19

…before you get too carried away writing all this stuff, I just had a look at doing the jpeg version and noticed you’re writing to a file. Obviously I’ll have to change it to use an OutputStream instead, so bear that in mind!


#20

jules i know. is just for testing purpose, for someone who wants to have file support rough and easy, without too much complications into the juce code-base. and is a starting point for using after OutputStream…
i’ve to start analyzing how OutputStream works and then i’ll do in that way :wink:

lou