diff options
-rw-r--r-- | include/Image.h | 32 | ||||
-rw-r--r-- | lib/Image.cc | 169 |
2 files changed, 172 insertions, 29 deletions
diff --git a/include/Image.h b/include/Image.h index 633ab55..55e96a5 100644 --- a/include/Image.h +++ b/include/Image.h @@ -6,45 +6,23 @@ #include <Color.h> enum { - FORMAT_TGA_BASIC + FORMAT_TGA_BASIC, + FORMAT_JPEG, }; class Image : public Buffer { public: - Image(unsigned int, unsigned int); + Image(unsigned int width, unsigned int height); virtual ~Image(); Color GetPixel(unsigned int, unsigned int) const; + unsigned char * GetBuffer(); void SetPixel(unsigned int, unsigned int, Color); - bool Prepare(unsigned int = FORMAT_TGA_BASIC); + bool Prepare(unsigned int = FORMAT_TGA_BASIC) throw (GeneralException); void Fill(Color = Color(0, 0, 0)); virtual String GetName() const; virtual bool CanWrite() const; private: - typedef unsigned char Byte; - typedef unsigned short int Word; - typedef unsigned long int DWord; - struct TGAHeader { - Byte IDLength; - Byte ColorMapType; - Byte ImageType; - Word CM_FirstEntry; - Word CM_Length; - Byte CM_EntrySize; - Word IS_XOrigin; - Word IS_YOrigin; - Word IS_Width; - Word IS_Height; - Byte IS_Depth; - Byte IS_Descriptor; - } PACKED; - - struct TGAFooter { - DWord ExtOffset; - DWord DevOffset; - char Sig[18]; - } PACKED; - unsigned int x, y; bool r; Color * img; diff --git a/lib/Image.cc b/lib/Image.cc index d098de3..a2e7a7c 100644 --- a/lib/Image.cc +++ b/lib/Image.cc @@ -4,6 +4,120 @@ #include "Image.h" #include "gettext.h" +#ifdef HAVE_LIBJPEG +#include "jpeglib.h" +#include "jerror.h" + +extern "C" { + +typedef struct { + struct jpeg_destination_mgr pub; /* public fields */ + + Handle * outfile; /* target stream */ + JOCTET * buffer; /* start of buffer */ +} my_destination_mgr; + +#define OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite'able size */ +#define SIZEOF(object) ((size_t) sizeof(object)) + + +typedef my_destination_mgr * my_dest_ptr; + +METHODDEF(void) +init_destination (j_compress_ptr cinfo) +{ + my_dest_ptr dest = (my_dest_ptr) cinfo->dest; + + /* Allocate the output buffer --- it will be released when done with image */ + dest->buffer = (JOCTET *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + OUTPUT_BUF_SIZE * SIZEOF(JOCTET)); + + dest->pub.next_output_byte = dest->buffer; + dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; +} + +METHODDEF(boolean) +empty_output_buffer (j_compress_ptr cinfo) +{ + my_dest_ptr dest = (my_dest_ptr) cinfo->dest; + + if (dest->outfile->write(dest->buffer, OUTPUT_BUF_SIZE) != + (size_t) OUTPUT_BUF_SIZE) + ERREXIT(cinfo, JERR_FILE_WRITE); + + dest->pub.next_output_byte = dest->buffer; + dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; + + return TRUE; +} + +METHODDEF(void) +term_destination (j_compress_ptr cinfo) +{ + my_dest_ptr dest = (my_dest_ptr) cinfo->dest; + size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer; + + /* Write any data remaining in the buffer */ + if (datacount > 0) { + if (dest->outfile->write(dest->buffer, datacount) != datacount) + ERREXIT(cinfo, JERR_FILE_WRITE); + } +} + +GLOBAL(void) +jpeg_handle_dest (j_compress_ptr cinfo, Handle * outfile) +{ + my_dest_ptr dest; + + /* The destination object is made permanent so that multiple JPEG images + * can be written to the same file without re-executing jpeg_stdio_dest. + * This makes it dangerous to use this manager and a different destination + * manager serially with the same JPEG object, because their private object + * sizes may be different. Caveat programmer. + */ + if (cinfo->dest == NULL) { /* first time for this JPEG object? */ + cinfo->dest = (struct jpeg_destination_mgr *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + SIZEOF(my_destination_mgr)); + } + + dest = (my_dest_ptr) cinfo->dest; + dest->pub.init_destination = init_destination; + dest->pub.empty_output_buffer = empty_output_buffer; + dest->pub.term_destination = term_destination; + dest->outfile = outfile; +} + +} +#endif + +typedef unsigned char Byte; +typedef unsigned short int Word; +typedef unsigned long int DWord; + +struct TGAHeader { + Byte IDLength; + Byte ColorMapType; + Byte ImageType; + Word CM_FirstEntry; + Word CM_Length; + Byte CM_EntrySize; + Word IS_XOrigin; + Word IS_YOrigin; + Word IS_Width; + Word IS_Height; + Byte IS_Depth; + Byte IS_Descriptor; +} PACKED; + +struct TGAFooter { + DWord ExtOffset; + DWord DevOffset; + char Sig[18]; +} PACKED; + + Image::Image(unsigned int ax, unsigned int ay) : x(ax), y(ay), img((Color *) malloc(x * y * sizeof(Color))) { Fill(); } @@ -42,6 +156,10 @@ void Image::SetPixel(unsigned int px, unsigned int py, Color c) { img[x * py + px] = c; } +unsigned char * Image::GetBuffer() { + return (unsigned char *) img; +} + #ifndef WORDS_BIGENDIAN #define WORDS_BIGENDIAN 0 #else @@ -49,8 +167,11 @@ void Image::SetPixel(unsigned int px, unsigned int py, Color c) { #define WORDS_BIGENDIAN 1 #endif -bool Image::Prepare(unsigned int f) { - if (GetSize()) return false; +bool Image::Prepare(unsigned int f) throw (GeneralException) { + int ix, iy; + + if (GetSize()) + throw GeneralException("Image already prepared."); switch (f) { case FORMAT_TGA_BASIC: @@ -81,7 +202,51 @@ bool Image::Prepare(unsigned int f) { return true; break; + case FORMAT_JPEG: +#ifndef HAVE_LIBJPEG + throw GeneralException("You can't create a jpeg image when the library libjpeg isn't linked in."); +#else + { + char * rgb_buffer = (char *) malloc(x * y * 3); + for (iy = 0; iy < y; y++) { + for (ix = 0; ix < x; x++) { + rgb_buffer[(ix + iy * x) * 3 + 0] = img[ix + iy * x].R; + rgb_buffer[(ix + iy * x) * 3 + 1] = img[ix + iy * x].G; + rgb_buffer[(ix + iy * x) * 3 + 2] = img[ix + iy * x].B; + } + } + + struct jpeg_compress_struct cinfo; + JSAMPROW row_pointer[1]; + struct jpeg_error_mgr jerr; + + + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cinfo); + jpeg_handle_dest(&cinfo, this); + + cinfo.image_width = x; + cinfo.image_height = y; + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo, 9, TRUE /* limit to baseline-JPEG values */); + + jpeg_start_compress(&cinfo, TRUE); + + int row_stride = x * 3; + + while (cinfo.next_scanline < cinfo.image_height) { + row_pointer[0] = (JSAMPLE *) &rgb_buffer[cinfo.next_scanline * row_stride]; + (void) jpeg_write_scanlines(&cinfo, row_pointer, 1); + } + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); + } +#endif + break; default: + throw GeneralException("Image format unkown."); return false; } } |