/* * Baltisot * Copyright (C) 1999-2007 Nicolas "Pixel" Noble * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #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 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(); } Image::~Image() { free(img); } bool Image::CanWrite() const { return false; } String Image::GetName() const { return String(_("Image ")) + x + "x" + y; } void Image::Fill(Color c) { for (unsigned int i = 0; i < x * y; i++) { img[i] = c; } } Color Image::GetPixel(unsigned int px, unsigned int py) const { if ((px >= x) || (py >= y)) { return Color(0, 0, 0, 0); } return img[x * py + px]; } void Image::SetPixel(unsigned int px, unsigned int py, Color c) { if ((px >= x) || (py >= y)) { return; } img[x * py + px] = c; } unsigned char * Image::GetBuffer() { return (unsigned char *) img; } #ifndef WORDS_BIGENDIAN #define WORDS_BIGENDIAN 0 #else #undef WORDS_BIGENDIAN #define WORDS_BIGENDIAN 1 #endif bool Image::SupportsFormat(unsigned int f) { switch(f) { case FORMAT_TGA_BASIC: return true; case FORMAT_JPEG: #ifndef HAVE_LIBJPEG return false; #else return true; #endif } 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: TGAHeader Header; TGAFooter Footer; Header.IDLength = 0; Header.ColorMapType = 0; Header.ImageType = 2; Header.CM_FirstEntry = 0; Header.CM_Length = 0; Header.CM_EntrySize = 0; Header.IS_XOrigin = 0; Header.IS_YOrigin = 0; Header.IS_Width = WORDS_BIGENDIAN ? ((x & 0xff) << 8) | ((x & 0xff00) >> 8) : x; Header.IS_Height = WORDS_BIGENDIAN ? ((y & 0xff) << 8) | ((y & 0xff00) >> 8) : y; Header.IS_Depth = 32; Header.IS_Descriptor = 0x20; Footer.ExtOffset = 0; Footer.DevOffset = 0; strcpy(Footer.Sig, "TRUEVISION-XFILE."); write(&Header, sizeof(Header)); write(img, x * y * sizeof(Color)); write(&Footer, sizeof(Footer)); 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; iy++) { for (ix = 0; ix < x; ix++) { rgb_buffer[(ix + iy * x) * 3 + 0] = img[ix + iy * x].B; rgb_buffer[(ix + iy * x) * 3 + 1] = img[ix + iy * x].G; rgb_buffer[(ix + iy * x) * 3 + 2] = img[ix + iy * x].R; } } 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, 95, 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); free(rgb_buffer); } #endif break; default: throw GeneralException("Image format unkown."); return false; } }