juce_saveJpegImageToFile

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

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

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

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

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

eheh jules… you rock !

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:

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:

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

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…

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

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

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

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.

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

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

[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.

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

…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!

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