From 5c325a1966c2e165fc399748702bc838cad07818 Mon Sep 17 00:00:00 2001 From: leo Date: Sat, 16 Sep 2000 17:18:01 +0000 Subject: Moved jpg and gif functions to separate files Fixed warnings related to setjmp() git-svn-id: http://svn.leocad.org/trunk@118 c7d43263-9d01-0410-8a33-9dba5d9f93d6 --- common/im_gif.cpp | 699 ++++++++++++++++++++++++++++++++++ common/im_jpg.cpp | 188 ++++++++++ common/im_png.cpp | 641 +++++++++++++++---------------- common/image.cpp | 1079 ++++++----------------------------------------------- common/module.mk | 16 +- 5 files changed, 1332 insertions(+), 1291 deletions(-) create mode 100644 common/im_gif.cpp create mode 100644 common/im_jpg.cpp (limited to 'common') diff --git a/common/im_gif.cpp b/common/im_gif.cpp new file mode 100644 index 0000000..2f1fbe4 --- /dev/null +++ b/common/im_gif.cpp @@ -0,0 +1,699 @@ +#include +#include +#include "image.h" +#include "quant.h" +#include "file.h" +extern "C" { +#include +} + +// ============================================================================= + +typedef struct +{ + unsigned char colormap[3][256]; + + // State for GetCode and LZWReadByte + char code_buf[256+4]; + int last_byte; // # of bytes in code_buf + int last_bit; // # of bits in code_buf + int cur_bit; // next bit index to read + bool out_of_blocks; // true if hit terminator data block + + int input_code_size; // codesize given in GIF file + int clear_code,end_code;// values for Clear and End codes + + int code_size; // current actual code size + int limit_code; // 2^code_size + int max_code; // first unused code value + bool first_time; // flags first call to LZWReadByte + + // Private state for LZWReadByte + int oldcode; // previous LZW symbol + int firstcode; // first byte of oldcode's expansion + + // LZW symbol table and expansion stack + UINT16 FAR *symbol_head; // => table of prefix symbols + UINT8 FAR *symbol_tail; // => table of suffix bytes + UINT8 FAR *symbol_stack; // => stack for symbol expansions + UINT8 FAR *sp; // stack pointer + + // State for interlaced image processing + bool is_interlaced; // true if have interlaced image +// jvirt_sarray_ptr interlaced_image; // full image in interlaced order + unsigned char* interlaced_image; + JDIMENSION cur_row_number; // need to know actual row number + JDIMENSION pass2_offset; // # of pixel rows in pass 1 + JDIMENSION pass3_offset; // # of pixel rows in passes 1&2 + JDIMENSION pass4_offset; // # of pixel rows in passes 1,2,3 + + File* input_file; + bool first_interlace; + unsigned char* buffer;//JSAMPARRAY buffer; + unsigned int width, height; +} gif_source_struct; + +typedef gif_source_struct *gif_source_ptr; + +// Macros for extracting header data --- note we assume chars may be signed +#define LM_to_uint(a,b) ((((b)&0xFF) << 8) | ((a)&0xFF)) +#define BitSet(byte, bit) ((byte) & (bit)) +#define INTERLACE 0x40 // mask for bit signifying interlaced image +#define COLORMAPFLAG 0x80 // mask for bit signifying colormap presence + +#undef LZW_TABLE_SIZE +#define MAX_LZW_BITS 12 // maximum LZW code size +#define LZW_TABLE_SIZE (1<input_file->GetChar(); + if (count > 0) + sinfo->input_file->Read(buf, count); + return count; +} + +static int GetCode (gif_source_ptr sinfo) +{ + register INT32 accum; + int offs, ret, count; + + while ((sinfo->cur_bit + sinfo->code_size) > sinfo->last_bit) + { + if (sinfo->out_of_blocks) + return sinfo->end_code; // fake something useful + sinfo->code_buf[0] = sinfo->code_buf[sinfo->last_byte-2]; + sinfo->code_buf[1] = sinfo->code_buf[sinfo->last_byte-1]; + if ((count = GetDataBlock(sinfo, &sinfo->code_buf[2])) == 0) + { + sinfo->out_of_blocks = true; + return sinfo->end_code; // fake something useful + } + sinfo->cur_bit = (sinfo->cur_bit - sinfo->last_bit) + 16; + sinfo->last_byte = 2 + count; + sinfo->last_bit = sinfo->last_byte * 8; + } + + offs = sinfo->cur_bit >> 3; // byte containing cur_bit + accum = sinfo->code_buf[offs+2] & 0xFF; + accum <<= 8; + accum |= sinfo->code_buf[offs+1] & 0xFF; + accum <<= 8; + accum |= sinfo->code_buf[offs] & 0xFF; + accum >>= (sinfo->cur_bit & 7); + ret = ((int) accum) & ((1 << sinfo->code_size) - 1); + + sinfo->cur_bit += sinfo->code_size; + return ret; +} + +static int LZWReadByte (gif_source_ptr sinfo) +{ + register int code; // current working code + int incode; // saves actual input code + + // First time, just eat the expected Clear code(s) and return next code, + // which is expected to be a raw byte. + if (sinfo->first_time) + { + sinfo->first_time = false; + code = sinfo->clear_code; // enables sharing code with Clear case + } + else + { + // If any codes are stacked from a previously read symbol, return them + if (sinfo->sp > sinfo->symbol_stack) + return (int) *(-- sinfo->sp); + + // Time to read a new symbol + code = GetCode(sinfo); + } + + if (code == sinfo->clear_code) + { + sinfo->code_size = sinfo->input_code_size + 1; + sinfo->limit_code = sinfo->clear_code << 1; // 2^code_size + sinfo->max_code = sinfo->clear_code + 2; // first unused code value + sinfo->sp = sinfo->symbol_stack; // init stack to empty + do + { + code = GetCode(sinfo); + } while (code == sinfo->clear_code); + + if (code > sinfo->clear_code) + code = 0; // use something valid + sinfo->firstcode = sinfo->oldcode = code; + return code; + } + + if (code == sinfo->end_code) + { + if (!sinfo->out_of_blocks) + { + char buf[256]; + while (GetDataBlock(sinfo, buf) > 0) + ; // skip + sinfo->out_of_blocks = true; + } + return 0; // fake something usable + } + + incode = code; // save for a moment + + if (code >= sinfo->max_code) + { + // special case for not-yet-defined symbol + // code == max_code is OK; anything bigger is bad data + if (code > sinfo->max_code) + incode = 0; // prevent creation of loops in symbol table + // this symbol will be defined as oldcode/firstcode + *(sinfo->sp++) = (UINT8) sinfo->firstcode; + code = sinfo->oldcode; + } + + while (code >= sinfo->clear_code) + { + *(sinfo->sp++) = sinfo->symbol_tail[code]; // tail is a byte value + code = sinfo->symbol_head[code]; // head is another LZW symbol + } + sinfo->firstcode = code; // save for possible future use + + if ((code = sinfo->max_code) < LZW_TABLE_SIZE) + { + sinfo->symbol_head[code] = sinfo->oldcode; + sinfo->symbol_tail[code] = (UINT8) sinfo->firstcode; + sinfo->max_code++; + if ((sinfo->max_code >= sinfo->limit_code) && + (sinfo->code_size < MAX_LZW_BITS)) + { + sinfo->code_size++; + sinfo->limit_code <<= 1; // keep equal to 2^code_size + } + } + + sinfo->oldcode = incode; // save last input symbol for future use + return sinfo->firstcode; // return first byte of symbol's expansion +} + +LC_IMAGE* OpenGIF(File* file) +{ + gif_source_ptr source; + source = (gif_source_ptr)malloc (sizeof(gif_source_struct)); + source->input_file = file; + + char hdrbuf[10]; + unsigned int width, height; + int colormaplen, aspectRatio; + int c; + + source->input_file->Read(hdrbuf, 6); + if ((hdrbuf[0] != 'G' || hdrbuf[1] != 'I' || hdrbuf[2] != 'F') || + ((hdrbuf[3] != '8' || hdrbuf[4] != '7' || hdrbuf[5] != 'a') && + (hdrbuf[3] != '8' || hdrbuf[4] != '9' || hdrbuf[5] != 'a'))) + return NULL; + + source->input_file->Read(hdrbuf, 7); + width = LM_to_uint(hdrbuf[0],hdrbuf[1]); + height = LM_to_uint(hdrbuf[2],hdrbuf[3]); + source->height = height; + source->width = width; + colormaplen = 2 << (hdrbuf[4] & 0x07); + aspectRatio = hdrbuf[6] & 0xFF; + + if (BitSet(hdrbuf[4], COLORMAPFLAG)) + for (int i = 0; i < colormaplen; i++) + { + source->colormap[0][i] = source->input_file->GetChar(); + source->colormap[1][i] = source->input_file->GetChar(); + source->colormap[2][i] = source->input_file->GetChar(); + } + + for (;;) + { + c = source->input_file->GetChar(); + +// if (c == ';') +// ERREXIT(cinfo, JERR_GIF_IMAGENOTFOUND); + + if (c == '!') + { + int extlabel; + char buf[256]; + + extlabel = source->input_file->GetChar(); + while (GetDataBlock(source, buf) > 0) + ; // skip + continue; + } + + if (c != ',') + continue; + + source->input_file->Read(hdrbuf, 9); + width = LM_to_uint(hdrbuf[4],hdrbuf[5]); + height = LM_to_uint(hdrbuf[6],hdrbuf[7]); + source->is_interlaced = (hdrbuf[8] & INTERLACE) != 0; + + if (BitSet(hdrbuf[8], COLORMAPFLAG)) + { + colormaplen = 2 << (hdrbuf[8] & 0x07); + for (int i = 0; i < colormaplen; i++) + { + source->colormap[0][i] = source->input_file->GetChar(); + source->colormap[1][i] = source->input_file->GetChar(); + source->colormap[2][i] = source->input_file->GetChar(); + } + } + + source->input_code_size = source->input_file->GetChar(); +// if (source->input_code_size < 2 || source->input_code_size >= MAX_LZW_BITS) +// ERREXIT1(cinfo, JERR_GIF_CODESIZE, source->input_code_size); + + break; + } + + source->symbol_head = (UINT16 FAR *) malloc(LZW_TABLE_SIZE * sizeof(UINT16)); + source->symbol_tail = (UINT8 FAR *) malloc (LZW_TABLE_SIZE * sizeof(UINT8)); + source->symbol_stack = (UINT8 FAR *) malloc (LZW_TABLE_SIZE * sizeof(UINT8)); + source->last_byte = 2; // make safe to "recopy last two bytes" + source->last_bit = 0; // nothing in the buffer + source->cur_bit = 0; // force buffer load on first call + source->out_of_blocks = false; + source->clear_code = 1 << source->input_code_size; + source->end_code = source->clear_code + 1; + source->first_time = true; + source->code_size = source->input_code_size + 1; + source->limit_code = source->clear_code << 1; // 2^code_size + source->max_code = source->clear_code + 2; // first unused code value + source->sp = source->symbol_stack; // init stack to empty + + if (source->is_interlaced) + { + source->first_interlace = true; + source->interlaced_image = (unsigned char*)malloc(width*height); + } + else + source->first_interlace = false; + + source->buffer = (unsigned char*)malloc(width*3); + LC_IMAGE* image = (LC_IMAGE*)malloc(width*height*3 + sizeof(LC_IMAGE)); + image->width = width; + image->height = height; + image->bits = (char*)image + sizeof(LC_IMAGE); + unsigned char* buf = (unsigned char*)image->bits; + + for (unsigned long scanline = 0; scanline < height; scanline++) + { + if (source->is_interlaced) + { + if (source->first_interlace) + { + register JSAMPROW sptr; + register JDIMENSION col; + JDIMENSION row; + + for (row = 0; row < source->height; row++) + { + sptr = &source->interlaced_image[row*source->width]; + for (col = source->width; col > 0; col--) + *sptr++ = (JSAMPLE) LZWReadByte(source); + } + + source->first_interlace = false; + source->cur_row_number = 0; + source->pass2_offset = (source->height + 7) / 8; + source->pass3_offset = source->pass2_offset + (source->height + 3) / 8; + source->pass4_offset = source->pass3_offset + (source->height + 1) / 4; + } + + register int c; + register JSAMPROW sptr, ptr; + register JDIMENSION col; + JDIMENSION irow; + + // Figure out which row of interlaced image is needed, and access it. + switch ((int) (source->cur_row_number & 7)) + { + case 0: // first-pass row + irow = source->cur_row_number >> 3; + break; + case 4: // second-pass row + irow = (source->cur_row_number >> 3) + source->pass2_offset; + break; + case 2: // third-pass row + case 6: + irow = (source->cur_row_number >> 2) + source->pass3_offset; + break; + default: // fourth-pass row + irow = (source->cur_row_number >> 1) + source->pass4_offset; + break; + } + sptr = &source->interlaced_image[irow*source->width]; + ptr = source->buffer; + for (col = source->width; col > 0; col--) + { + c = GETJSAMPLE(*sptr++); + *ptr++ = source->colormap[0][c]; + *ptr++ = source->colormap[1][c]; + *ptr++ = source->colormap[2][c]; + } + source->cur_row_number++; // for next time + } + else + { + register int c; + register JSAMPROW ptr; + register JDIMENSION col; + + ptr = source->buffer; + for (col = source->width; col > 0; col--) + { + c = LZWReadByte(source); + *ptr++ = source->colormap[0][c]; + *ptr++ = source->colormap[1][c]; + *ptr++ = source->colormap[2][c]; + } + } + + memcpy (buf+(width*scanline*3), source->buffer, 3*width); + } + + if (source->is_interlaced) + free(source->interlaced_image); + free(source->buffer); + free(source->symbol_head); + free(source->symbol_tail); + free(source->symbol_stack); + free(source); + + return image; +} + +// ============================================================================= + +#undef LZW_TABLE_SIZE +#define MAX_LZW_BITS 12 +typedef INT16 code_int; +#define LZW_TABLE_SIZE ((code_int) 1 << MAX_LZW_BITS) +#define HSIZE 5003 +typedef int hash_int; +#define MAXCODE(n_bits) (((code_int) 1 << (n_bits)) - 1) +typedef INT32 hash_entry; +#define HASH_ENTRY(prefix,suffix) ((((hash_entry) (prefix)) << 8) | (suffix)) + +typedef struct +{ + int n_bits; + code_int maxcode; + int init_bits; + INT32 cur_accum; + int cur_bits; + code_int waiting_code; + bool first_byte; + code_int ClearCode; + code_int EOFCode; + code_int free_code; + code_int *hash_code; + hash_entry FAR *hash_value; + int bytesinpkt; + char packetbuf[256]; + File* output_file; + void* buffer;//JSAMPARRAY buffer; +} gif_dest_struct; + +typedef gif_dest_struct* gif_dest_ptr; + +// Emit a 16-bit word, LSB first +static void put_word(File* output_file, unsigned int w) +{ + output_file->PutChar(w & 0xFF); + output_file->PutChar((w >> 8) & 0xFF); +} + +static void flush_packet(gif_dest_ptr dinfo) +{ + if (dinfo->bytesinpkt > 0) + { + dinfo->packetbuf[0] = (char) dinfo->bytesinpkt++; + dinfo->output_file->Write(dinfo->packetbuf, dinfo->bytesinpkt); + dinfo->bytesinpkt = 0; + } +} + +static void output(gif_dest_ptr dinfo, code_int code) +{ + dinfo->cur_accum |= ((INT32) code) << dinfo->cur_bits; + dinfo->cur_bits += dinfo->n_bits; + + while (dinfo->cur_bits >= 8) + { + (dinfo)->packetbuf[++(dinfo)->bytesinpkt] = (char) (dinfo->cur_accum & 0xFF); + if ((dinfo)->bytesinpkt >= 255) + flush_packet(dinfo); + + dinfo->cur_accum >>= 8; + dinfo->cur_bits -= 8; + } + + if (dinfo->free_code > dinfo->maxcode) + { + dinfo->n_bits++; + if (dinfo->n_bits == MAX_LZW_BITS) + dinfo->maxcode = LZW_TABLE_SIZE; + else + dinfo->maxcode = MAXCODE(dinfo->n_bits); + } +} + +// Accept and compress one 8-bit byte +static void compress_byte (gif_dest_ptr dinfo, int c) +{ + register hash_int i; + register hash_int disp; + register hash_entry probe_value; + + if (dinfo->first_byte) + { + dinfo->waiting_code = c; + dinfo->first_byte = FALSE; + return; + } + + i = ((hash_int) c << (MAX_LZW_BITS-8)) + dinfo->waiting_code; + if (i >= HSIZE) + i -= HSIZE; + + probe_value = HASH_ENTRY(dinfo->waiting_code, c); + + if (dinfo->hash_code[i] != 0) + { + if (dinfo->hash_value[i] == probe_value) + { + dinfo->waiting_code = dinfo->hash_code[i]; + return; + } + if (i == 0) + disp = 1; + else + disp = HSIZE - i; + for (;;) + { + i -= disp; + if (i < 0) + i += HSIZE; + if (dinfo->hash_code[i] == 0) + break; + if (dinfo->hash_value[i] == probe_value) + { + dinfo->waiting_code = dinfo->hash_code[i]; + return; + } + } + } + + output(dinfo, dinfo->waiting_code); + if (dinfo->free_code < LZW_TABLE_SIZE) + { + dinfo->hash_code[i] = dinfo->free_code++; + dinfo->hash_value[i] = probe_value; + } + else + { + memset(dinfo->hash_code, 0, HSIZE * sizeof(code_int)); + dinfo->free_code = dinfo->ClearCode + 2; + output(dinfo, dinfo->ClearCode); + dinfo->n_bits = dinfo->init_bits; + dinfo->maxcode = MAXCODE(dinfo->n_bits); + } + dinfo->waiting_code = c; +} + +bool SaveGIF(File* file, LC_IMAGE* image, bool transparent, bool interlaced, unsigned char* background) +{ + int InitCodeSize, FlagByte, i; + unsigned char pal[3][256]; + unsigned char* colormappedbuffer = (unsigned char*)malloc(image->width*image->height); + dl1quant((unsigned char*)image->bits, colormappedbuffer, image->width, image->height, 256, true, pal); + + gif_dest_ptr dinfo; + dinfo = (gif_dest_ptr) malloc (sizeof(gif_dest_struct)); + dinfo->output_file = file; + dinfo->buffer = malloc(image->width*sizeof(JDIMENSION)); + dinfo->hash_code = (code_int*) malloc(HSIZE * sizeof(code_int)); + dinfo->hash_value = (hash_entry FAR*)malloc(HSIZE*sizeof(hash_entry)); + + InitCodeSize = 8; + // Write the GIF header. + file->PutChar('G'); + file->PutChar('I'); + file->PutChar('F'); + file->PutChar('8'); + file->PutChar(transparent ? '9' : '7'); + file->PutChar('a'); + // Write the Logical Screen Descriptor + put_word(file, (unsigned int)image->width); + put_word(file, (unsigned int)image->height); + FlagByte = 0x80; + FlagByte |= (7) << 4; // color resolution + FlagByte |= (7); // size of global color table + file->PutChar(FlagByte); + file->PutChar(0); // Background color index + file->PutChar(0); // Reserved (aspect ratio in GIF89) + // Write the Global Color Map + for (i = 0; i < 256; i++) + { + file->PutChar(pal[0][i]); + file->PutChar(pal[1][i]); + file->PutChar(pal[2][i]); + } + + // Write out extension for transparent colour index, if necessary. + if (transparent) + { + unsigned char index = 0; + + for (i = 0; i < 256; i++) + if (background[0] == pal[0][i] && + background[1] == pal[1][i] && + background[2] == pal[2][i]) + { + index = i; + break; + } + + file->PutChar('!'); + file->PutChar(0xf9); + file->PutChar(4); + file->PutChar(1); + file->PutChar(0); + file->PutChar(0); + file->PutChar(index); + file->PutChar(0); + } + + // Write image separator and Image Descriptor + file->PutChar(','); + put_word(file, 0); + put_word(file, 0); + put_word(file, (unsigned int)image->width); + put_word(file, (unsigned int)image->height); + // flag byte: interlaced + if (interlaced) + file->PutChar(0x40); + else + file->PutChar(0x00); + file->PutChar(InitCodeSize);// Write Initial Code Size byte + + // Initialize for LZW compression of image data + dinfo->n_bits = dinfo->init_bits = InitCodeSize+1; + dinfo->maxcode = MAXCODE(dinfo->n_bits); + dinfo->ClearCode = ((code_int) 1 << (InitCodeSize)); + dinfo->EOFCode = dinfo->ClearCode + 1; + dinfo->free_code = dinfo->ClearCode + 2; + dinfo->first_byte = TRUE; + dinfo->bytesinpkt = 0; + dinfo->cur_accum = 0; + dinfo->cur_bits = 0; + memset(dinfo->hash_code, 0, HSIZE * sizeof(code_int)); + output(dinfo, dinfo->ClearCode); + + int scanline = 0; + int pass = 0; + while (scanline < image->height) + { + memcpy(dinfo->buffer, colormappedbuffer+(scanline*image->width), image->width); + + register JSAMPROW ptr; + register JDIMENSION col; + + ptr = (unsigned char*)dinfo->buffer; + for (col = image->width; col > 0; col--) + compress_byte(dinfo, GETJSAMPLE(*ptr++)); + + if (interlaced) + { + switch (pass) + { + case 0: + { + scanline += 8; + if (scanline >= image->height) + { + pass++; + scanline = 4; + } + } break; + + case 1: + { + scanline += 8; + if (scanline >= image->height) + { + pass++; + scanline = 2; + } + } break; + + case 2: + { + scanline += 4; + if (scanline >= image->height) + { + pass++; + scanline = 1; + } + } break; + + case 3: + { + scanline += 2; + } break; + } + } + else + scanline++; + } + + // Finish up at the end of the file. + if (!dinfo->first_byte) + output(dinfo, dinfo->waiting_code); + output(dinfo, dinfo->EOFCode); + if (dinfo->cur_bits > 0) + { + (dinfo)->packetbuf[++(dinfo)->bytesinpkt] = (char) (dinfo->cur_accum & 0xFF); + if ((dinfo)->bytesinpkt >= 255) + flush_packet(dinfo); + } + + flush_packet(dinfo); + file->PutChar(0); + file->PutChar(';'); + file->Flush(); + + free(dinfo->buffer); + free(dinfo->hash_code); + free(dinfo->hash_value); + free(dinfo); + free(colormappedbuffer); + return true; +} diff --git a/common/im_jpg.cpp b/common/im_jpg.cpp new file mode 100644 index 0000000..9940839 --- /dev/null +++ b/common/im_jpg.cpp @@ -0,0 +1,188 @@ +#include +#include +#include +#include "image.h" +extern "C" { +#include +} + +typedef struct bt_jpeg_error_mgr +{ + struct jpeg_error_mgr pub; // "public" fields + jmp_buf setjmp_buffer; // for return to caller +} bt_jpeg_error_mgr; + +static void bt_jpeg_error_exit (j_common_ptr cinfo) +{ + bt_jpeg_error_mgr* myerr = (bt_jpeg_error_mgr*) cinfo->err; + char buffer[JMSG_LENGTH_MAX]; + (*cinfo->err->format_message) (cinfo, buffer); +// MessageBox(NULL, buffer, "JPEG Fatal Error", MB_ICONSTOP); + longjmp(myerr->setjmp_buffer, 1); +} + +// stash a scanline +static void j_putRGBScanline(unsigned char* jpegline, int widthPix, unsigned char* outBuf, int row) +{ + int offset = row * widthPix * 3; + int count; + for (count = 0; count < widthPix; count++) + { + unsigned char iRed, iBlu, iGrn; + unsigned char *oRed, *oBlu, *oGrn; + + iRed = *(jpegline + count * 3 + 0); + iGrn = *(jpegline + count * 3 + 1); + iBlu = *(jpegline + count * 3 + 2); + + oRed = outBuf + offset + count * 3 + 0; + oGrn = outBuf + offset + count * 3 + 1; + oBlu = outBuf + offset + count * 3 + 2; + + *oRed = iRed; + *oGrn = iGrn; + *oBlu = iBlu; + } +} + +// stash a gray scanline +static void j_putGrayScanlineToRGB(unsigned char* jpegline, int widthPix, unsigned char* outBuf, int row) +{ + int offset = row * widthPix * 3; + int count; + for (count = 0; count < widthPix; count++) + { + unsigned char iGray; + unsigned char *oRed, *oBlu, *oGrn; + + // get our grayscale value + iGray = *(jpegline + count); + + oRed = outBuf + offset + count * 3; + oGrn = outBuf + offset + count * 3 + 1; + oBlu = outBuf + offset + count * 3 + 2; + + *oRed = iGray; + *oGrn = iGray; + *oBlu = iGray; + } +} + +// ============================================================================= + +LC_IMAGE* OpenJPG (char* filename) +{ + struct jpeg_decompress_struct cinfo; + struct bt_jpeg_error_mgr jerr; + FILE* infile = NULL; + JSAMPARRAY buffer; // Output row buffer + int row_stride; // physical row width in output buffer + + if ((infile = fopen(filename, "rb")) == NULL) + return NULL; + + cinfo.err = jpeg_std_error(&jerr.pub); + jerr.pub.error_exit = bt_jpeg_error_exit; + + if (setjmp(jerr.setjmp_buffer)) + { + jpeg_destroy_decompress(&cinfo); + + if (infile != NULL) + fclose(infile); + return NULL; + } + + jpeg_create_decompress(&cinfo); + jpeg_stdio_src(&cinfo, infile); + jpeg_read_header(&cinfo, TRUE); + jpeg_start_decompress(&cinfo); + + // get our buffer set to hold data + LC_IMAGE* image = (LC_IMAGE*)malloc(cinfo.output_width*cinfo.output_height*3 + sizeof(LC_IMAGE)); + + if (image == NULL) + { +// MessageBox(NULL, "Error", "Cannot allocate memory", MB_ICONSTOP); + jpeg_destroy_decompress(&cinfo); + fclose(infile); + return NULL; + } + + image->width = cinfo.output_width; + image->height = cinfo.output_height; + image->bits = (char*)image + sizeof(LC_IMAGE); + + row_stride = cinfo.output_width * cinfo.output_components; + buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); + + while (cinfo.output_scanline < cinfo.output_height) + { + jpeg_read_scanlines(&cinfo, buffer, 1); + + if (cinfo.out_color_components == 3) + j_putRGBScanline(buffer[0], image->width, (unsigned char*)image->bits, cinfo.output_scanline-1); + else if (cinfo.out_color_components == 1) + j_putGrayScanlineToRGB(buffer[0], image->width, (unsigned char*)image->bits, cinfo.output_scanline-1); + } + + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + fclose(infile); + + return image; +} + +// ============================================================================= + +bool SaveJPG (char* filename, LC_IMAGE* image, int quality, bool progressive) +{ + struct jpeg_compress_struct cinfo; + struct bt_jpeg_error_mgr jerr; + int row_stride; // physical row widthPix in image buffer + FILE* outfile; + + // allocate and initialize JPEG compression object + cinfo.err = jpeg_std_error(&jerr.pub); + jerr.pub.error_exit = bt_jpeg_error_exit; + + if (setjmp(jerr.setjmp_buffer)) + { + // jpeg_destroy_compress(&cinfo); + // if (outfile != NULL) + // fclose(outfile); + return false; + } + + jpeg_create_compress(&cinfo); + if ((outfile = fopen(filename, "wb")) == NULL) + return false; + + jpeg_stdio_dest(&cinfo, outfile); + + cinfo.image_width = image->width; + cinfo.image_height = image->height; + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + + jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo, quality, TRUE); + + if (progressive) + jpeg_simple_progression(&cinfo); + + jpeg_start_compress(&cinfo, TRUE); + row_stride = image->width * 3; + + while (cinfo.next_scanline < cinfo.image_height) + { + unsigned char* outRow = (unsigned char*)image->bits + (cinfo.next_scanline * image->width * 3); + jpeg_write_scanlines(&cinfo, &outRow, 1); + } + + jpeg_finish_compress(&cinfo); + fclose(outfile); + jpeg_destroy_compress(&cinfo); + + return true; +} diff --git a/common/im_png.cpp b/common/im_png.cpp index 1fc3512..0dab0cf 100755 --- a/common/im_png.cpp +++ b/common/im_png.cpp @@ -9,348 +9,355 @@ (composite) = (unsigned char)((temp + (temp >> 8)) >> 8); \ } -// ======================================================== +// ============================================================================= LC_IMAGE* OpenPNG(char* filename) { - unsigned char sig[8], red, green, blue; - unsigned char *image_data = NULL; - unsigned char *src, *dest; - unsigned char r, g, b, a; - unsigned long i, row; - unsigned long image_rowbytes; - png_color_16p pBackground; - png_structp png_ptr = NULL; - png_infop info_ptr = NULL; - png_uint_32 width, height; - png_bytepp row_pointers = NULL; - int bit_depth, color_type; - int image_channels; - double gamma; - FILE* f; - - f = fopen(filename, "rb"); - if (f == NULL) - return NULL; - - fread(sig, 1, 8, f); - if (!png_check_sig(sig, 8)) - { - fclose(f); - return NULL; // bad signature - } - - png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (!png_ptr) - { - fclose(f); - return NULL; // out of memory - } - - info_ptr = png_create_info_struct(png_ptr); - if (!info_ptr) - { - png_destroy_read_struct(&png_ptr, NULL, NULL); - fclose(f); - return NULL; // out of memory - } - - if (setjmp(png_ptr->jmpbuf)) - { - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - fclose(f); - return NULL; - } - - png_init_io(png_ptr, f); - png_set_sig_bytes(png_ptr, 8); // we already read the 8 signature bytes + unsigned char sig[8], red, green, blue; + unsigned char *image_data = NULL; + unsigned char *src, *dest; + unsigned char r, g, b, a; + unsigned long i, row; + unsigned long image_rowbytes; + png_color_16p pBackground; + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + png_uint_32 width, height; + png_bytepp row_pointers = NULL; + int bit_depth, color_type; + int image_channels; + double gamma; + FILE* f; + + f = fopen(filename, "rb"); + if (f == NULL) + return NULL; + + fread(sig, 1, 8, f); + if (!png_check_sig(sig, 8)) + { + fclose(f); + return NULL; // bad signature + } + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) + { + fclose(f); + return NULL; // out of memory + } + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_read_struct(&png_ptr, NULL, NULL); + fclose(f); + return NULL; // out of memory + } + + if (setjmp(png_ptr->jmpbuf)) + { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + fclose(f); + return NULL; + } + + png_init_io(png_ptr, f); + png_set_sig_bytes(png_ptr, 8); // we already read the 8 signature bytes + + png_read_info(png_ptr, info_ptr); // read all PNG info up to image data + png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, + NULL, NULL, NULL); + + if (setjmp(png_ptr->jmpbuf)) + { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + fclose(f); + return NULL; + } + + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD)) + { + png_get_bKGD(png_ptr, info_ptr, &pBackground); + + if (setjmp (png_ptr->jmpbuf)) + { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + fclose(f); + return NULL; + } - png_read_info(png_ptr, info_ptr); // read all PNG info up to image data - png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, - NULL, NULL, NULL); + // however, it always returns the raw bKGD data, regardless of any + // bit-depth transformations, so check depth and adjust if necessary + if (bit_depth == 16) + { + red = pBackground->red >> 8; + green = pBackground->green >> 8; + blue = pBackground->blue >> 8; + } + else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) + { + if (bit_depth == 1) + red = green = blue = pBackground->gray? 255 : 0; + else if (bit_depth == 2) + red = green = blue = (255/3) * pBackground->gray; + else // bit_depth == 4 + red = green = blue = (255/15) * pBackground->gray; + } + else + { + red = (unsigned char)pBackground->red; + green = (unsigned char)pBackground->green; + blue = (unsigned char)pBackground->blue; + } + } + else + { + if (setjmp (png_ptr->jmpbuf)) + { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + fclose(f); + return NULL; + } - if (setjmp(png_ptr->jmpbuf)) + red = green = blue = 0; + } + + // expand palette images to RGB, low-bit-depth grayscale images to 8 bits, + // transparency chunks to full alpha channel; strip 16-bit-per-sample + // images to 8 bits per sample; and convert grayscale to RGB[A] + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_expand(png_ptr); + if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) + png_set_expand(png_ptr); + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) + png_set_expand(png_ptr); + if (bit_depth == 16) + png_set_strip_16(png_ptr); + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(png_ptr); + + if (png_get_gAMA(png_ptr, info_ptr, &gamma)) + png_set_gamma(png_ptr, 2.2, gamma); + + // all transformations have been registered; now update info_ptr data, + // get rowbytes and channels, and allocate image memory + png_read_update_info(png_ptr, info_ptr); + + image_rowbytes = png_get_rowbytes(png_ptr, info_ptr); + image_channels = (int)png_get_channels(png_ptr, info_ptr); + + if ((image_data = (unsigned char*)malloc(image_rowbytes*height)) == NULL) + { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + fclose(f); + return NULL; + } + + if ((row_pointers = (png_bytepp)malloc(height*sizeof(png_bytep))) == NULL) + { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + free(image_data); + fclose(f); + return NULL; + } + + // set the individual row_pointers to point at the correct offsets + for (i = 0; i < height; ++i) + row_pointers[i] = image_data + i*image_rowbytes; + + // now we can go ahead and just read the whole image + png_read_image(png_ptr, row_pointers); + + // and we're done! (png_read_end() can be omitted if no processing of + // post-IDAT text/time/etc. is desired) + free(row_pointers); + row_pointers = NULL; + + png_read_end(png_ptr, NULL); + + // done with PNG file, so clean up to minimize memory usage + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + fclose(f); + + if (!image_data) + return NULL; + + // get our buffer set to hold data + LC_IMAGE* image = (LC_IMAGE*)malloc(width*height*3 + sizeof(LC_IMAGE)); + + if (image == NULL) + { + free(image_data); + return NULL; + } + + image->width = (unsigned short)width; + image->height = (unsigned short)height; + image->bits = (char*)image + sizeof(LC_IMAGE); + + for (row = 0; row < height; row++) + { + src = image_data + row*image_rowbytes; + dest = (unsigned char*)image->bits + row*image_rowbytes; + if (image_channels == 3) + { + for (i = width; i > 0; i--) + { + r = *src++; + g = *src++; + b = *src++; + *dest++ = r; + *dest++ = g; + *dest++ = b; + } + } + else // if (image_channels == 4) + { + for (i = width; i > 0; i--) + { + r = *src++; + g = *src++; + b = *src++; + a = *src++; + + if (a == 255) { - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - fclose(f); - return NULL; + *dest++ = r; + *dest++ = g; + *dest++ = b; } - - if (png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD)) + else if (a == 0) { - png_get_bKGD(png_ptr, info_ptr, &pBackground); - - // however, it always returns the raw bKGD data, regardless of any - // bit-depth transformations, so check depth and adjust if necessary - if (bit_depth == 16) - { - red = pBackground->red >> 8; - green = pBackground->green >> 8; - blue = pBackground->blue >> 8; - } - else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) - { - if (bit_depth == 1) - red = green = blue = pBackground->gray? 255 : 0; - else if (bit_depth == 2) - red = green = blue = (255/3) * pBackground->gray; - else // bit_depth == 4 - red = green = blue = (255/15) * pBackground->gray; - } - else - { - red = (unsigned char)pBackground->red; - green = (unsigned char)pBackground->green; - blue = (unsigned char)pBackground->blue; - } + *dest++ = red; + *dest++ = green; + *dest++ = blue; } else - red = green = blue = 0; - - // decode the image, all at once - if (setjmp(png_ptr->jmpbuf)) - { - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - fclose(f); - return NULL; - } - - // expand palette images to RGB, low-bit-depth grayscale images to 8 bits, - // transparency chunks to full alpha channel; strip 16-bit-per-sample - // images to 8 bits per sample; and convert grayscale to RGB[A] - if (color_type == PNG_COLOR_TYPE_PALETTE) - png_set_expand(png_ptr); - if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) - png_set_expand(png_ptr); - if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) - png_set_expand(png_ptr); - if (bit_depth == 16) - png_set_strip_16(png_ptr); - if (color_type == PNG_COLOR_TYPE_GRAY || - color_type == PNG_COLOR_TYPE_GRAY_ALPHA) - png_set_gray_to_rgb(png_ptr); - - if (png_get_gAMA(png_ptr, info_ptr, &gamma)) - png_set_gamma(png_ptr, 2.2, gamma); - - // all transformations have been registered; now update info_ptr data, - // get rowbytes and channels, and allocate image memory - png_read_update_info(png_ptr, info_ptr); - - image_rowbytes = png_get_rowbytes(png_ptr, info_ptr); - image_channels = (int)png_get_channels(png_ptr, info_ptr); - - if ((image_data = (unsigned char*)malloc(image_rowbytes*height)) == NULL) - { - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - fclose(f); - return NULL; - } - - if ((row_pointers = (png_bytepp)malloc(height*sizeof(png_bytep))) == NULL) { - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - free(image_data); - fclose(f); - return NULL; - } - - // set the individual row_pointers to point at the correct offsets - for (i = 0; i < height; ++i) - row_pointers[i] = image_data + i*image_rowbytes; - - // now we can go ahead and just read the whole image - png_read_image(png_ptr, row_pointers); - - // and we're done! (png_read_end() can be omitted if no processing of - // post-IDAT text/time/etc. is desired) - free(row_pointers); - row_pointers = NULL; - - png_read_end(png_ptr, NULL); - - // done with PNG file, so clean up to minimize memory usage - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - fclose(f); - - if (!image_data) - return NULL; - - // get our buffer set to hold data - LC_IMAGE* image = (LC_IMAGE*)malloc(width*height*3 + sizeof(LC_IMAGE)); - - if (image == NULL) - { - free(image_data); - return NULL; - } - - image->width = (unsigned short)width; - image->height = (unsigned short)height; - image->bits = (char*)image + sizeof(LC_IMAGE); - - for (row = 0; row < height; row++) - { - src = image_data + row*image_rowbytes; - dest = (unsigned char*)image->bits + row*image_rowbytes; - if (image_channels == 3) - { - for (i = width; i > 0; i--) - { - r = *src++; - g = *src++; - b = *src++; - *dest++ = r; - *dest++ = g; - *dest++ = b; - } - } - else // if (image_channels == 4) - { - for (i = width; i > 0; i--) - { - r = *src++; - g = *src++; - b = *src++; - a = *src++; - - if (a == 255) - { - *dest++ = r; - *dest++ = g; - *dest++ = b; - } - else if (a == 0) - { - *dest++ = red; - *dest++ = green; - *dest++ = blue; - } - else - { - // this macro (copied from png.h) composites the - // foreground and background values and puts the - // result into the first argument; there are no - // side effects with the first argument - alpha_composite(*dest++, r, a, red); - alpha_composite(*dest++, g, a, green); - alpha_composite(*dest++, b, a, blue); - } - } - } + // this macro (copied from png.h) composites the + // foreground and background values and puts the + // result into the first argument; there are no + // side effects with the first argument + alpha_composite(*dest++, r, a, red); + alpha_composite(*dest++, g, a, green); + alpha_composite(*dest++, b, a, blue); } + } + } + } - free(image_data); - return image; + free(image_data); + return image; } -// ======================================================== +// ============================================================================= bool SavePNG(char* filename, LC_IMAGE* image, bool transparent, bool interlaced, unsigned char* background) { - FILE *fp; - png_structp png_ptr; - png_infop info_ptr; - png_bytepp row_pointers = NULL; - png_color_8 sig_bit; - png_color_16 bg; - int i; - - fp = fopen(filename, "wb"); - if (!fp) - return false; - - png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (!png_ptr) - { - fclose(fp); - return false; - } - - info_ptr = png_create_info_struct(png_ptr); - if (!info_ptr) - { - png_destroy_write_struct(&png_ptr, NULL); - fclose(fp); - return false; - } - - if (setjmp(png_ptr->jmpbuf)) - { - png_destroy_write_struct(&png_ptr, (png_infopp)NULL); - fclose(fp); - return false; - } - - png_init_io(png_ptr, fp); - - png_set_IHDR(png_ptr, info_ptr, image->width, image->height, 8, + FILE *fp; + png_structp png_ptr; + png_infop info_ptr; + png_bytepp row_pointers = NULL; + png_color_8 sig_bit; + png_color_16 bg; + int i; + + fp = fopen(filename, "wb"); + if (!fp) + return false; + + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) + { + fclose(fp); + return false; + } + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_write_struct(&png_ptr, NULL); + fclose(fp); + return false; + } + + if (setjmp(png_ptr->jmpbuf)) + { + png_destroy_write_struct(&png_ptr, (png_infopp)NULL); + fclose(fp); + return false; + } + + png_init_io(png_ptr, fp); + + png_set_IHDR (png_ptr, info_ptr, image->width, image->height, 8, transparent ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB, interlaced ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); - bg.red = background[0]; - bg.green = background[1]; - bg.blue = background[2]; - png_set_bKGD(png_ptr, info_ptr, &bg); + bg.red = background[0]; + bg.green = background[1]; + bg.blue = background[2]; + png_set_bKGD(png_ptr, info_ptr, &bg); + + png_write_info(png_ptr, info_ptr); + + // Set the true bit depth of the image data + sig_bit.red = 8; + sig_bit.green = 8; + sig_bit.blue = 8; + sig_bit.alpha = 8; + + png_set_sBIT(png_ptr, info_ptr, &sig_bit); + + if ((row_pointers = (png_bytepp)malloc(image->height*sizeof(png_bytep))) == NULL) + { + png_destroy_write_struct(&png_ptr, (png_infopp)NULL); + fclose(fp); + return false; + } + + // set the individual row_pointers to point at the correct offsets + if (transparent) + { + unsigned char *buf, *src, *dst, alpha; + dst = buf = (unsigned char*)malloc(image->width*image->height*4); + src = (unsigned char*)image->bits; + + for (i = 0; i < image->width*image->height; i++) + { + if ((src[0] == background[0]) && + (src[1] == background[1]) && + (src[2] == background[2])) + alpha = 0; + else + alpha = 255; + *dst++ = *src++; + *dst++ = *src++; + *dst++ = *src++; + *dst++ = alpha; + } - png_write_info(png_ptr, info_ptr); + for (i = 0; i < image->height; i++) + row_pointers[i] = buf + i*image->width*4; + png_write_image(png_ptr, row_pointers); - // Set the true bit depth of the image data - sig_bit.red = 8; - sig_bit.green = 8; - sig_bit.blue = 8; - sig_bit.alpha = 8; + free(buf); + } + else + { + for (i = 0; i < image->height; i++) + row_pointers[i] = (unsigned char*)image->bits + i*image->width*3; + png_write_image(png_ptr, row_pointers); + } - png_set_sBIT(png_ptr, info_ptr, &sig_bit); + free(row_pointers); - if ((row_pointers = (png_bytepp)malloc(image->height*sizeof(png_bytep))) == NULL) - { - png_destroy_write_struct(&png_ptr, (png_infopp)NULL); - fclose(fp); - return false; - } + png_write_end(png_ptr, info_ptr); + png_destroy_write_struct(&png_ptr, &info_ptr); + fclose(fp); - // set the individual row_pointers to point at the correct offsets - if (transparent) - { - unsigned char *buf, *src, *dst, alpha; - dst = buf = (unsigned char*)malloc(image->width*image->height*4); - src = (unsigned char*)image->bits; - - for (i = 0; i < image->width*image->height; i++) - { - if ((src[0] == background[0]) && - (src[1] == background[1]) && - (src[2] == background[2])) - alpha = 0; - else - alpha = 255; - *dst++ = *src++; - *dst++ = *src++; - *dst++ = *src++; - *dst++ = alpha; - } - - for (i = 0; i < image->height; i++) - row_pointers[i] = buf + i*image->width*4; - png_write_image(png_ptr, row_pointers); - - free(buf); - } - else - { - for (i = 0; i < image->height; i++) - row_pointers[i] = (unsigned char*)image->bits + i*image->width*3; - png_write_image(png_ptr, row_pointers); - } - - free(row_pointers); - - png_write_end(png_ptr, info_ptr); - png_destroy_write_struct(&png_ptr, &info_ptr); - fclose(fp); - - return true; + return true; } - diff --git a/common/image.cpp b/common/image.cpp index 950f211..02b9638 100644 --- a/common/image.cpp +++ b/common/image.cpp @@ -1,1032 +1,167 @@ // Image I/O routines // -#include #include #include #include #include #include "image.h" -#include "quant.h" #include "file.h" -extern "C" { -#include -} - -///////////////////////////////////////////////////////////////////////////// -// static declarations - -static LC_IMAGE* OpenJPG(char* filename); -LC_IMAGE* OpenBMP(char* filename); -LC_IMAGE* OpenPNG(char* filename); -static LC_IMAGE* OpenGIF(File* file); - -static bool SaveJPG(char* filename, LC_IMAGE* image, int quality, bool progressive); -bool SaveBMP(char* filename, LC_IMAGE* image, bool quantize); -bool SavePNG(char* filename, LC_IMAGE* image, bool transparent, bool interlaced, unsigned char* background); -static bool SaveGIF(File* file, LC_IMAGE* image, bool transparent, bool interlaced, unsigned char* background); - -typedef struct bt_jpeg_error_mgr -{ - struct jpeg_error_mgr pub; // "public" fields - jmp_buf setjmp_buffer; // for return to caller -} bt_jpeg_error_mgr; - -static void bt_jpeg_error_exit (j_common_ptr cinfo) -{ - bt_jpeg_error_mgr* myerr = (bt_jpeg_error_mgr*) cinfo->err; - char buffer[JMSG_LENGTH_MAX]; - (*cinfo->err->format_message) (cinfo, buffer); -// MessageBox(NULL, buffer, "JPEG Fatal Error", MB_ICONSTOP); - longjmp(myerr->setjmp_buffer, 1); -} - -// stash a scanline -static void j_putRGBScanline(unsigned char* jpegline, int widthPix, unsigned char* outBuf, int row) -{ - int offset = row * widthPix * 3; - int count; - for (count = 0; count < widthPix; count++) - { - unsigned char iRed, iBlu, iGrn; - unsigned char *oRed, *oBlu, *oGrn; - - iRed = *(jpegline + count * 3 + 0); - iGrn = *(jpegline + count * 3 + 1); - iBlu = *(jpegline + count * 3 + 2); - - oRed = outBuf + offset + count * 3 + 0; - oGrn = outBuf + offset + count * 3 + 1; - oBlu = outBuf + offset + count * 3 + 2; - - *oRed = iRed; - *oGrn = iGrn; - *oBlu = iBlu; - } -} -// stash a gray scanline -static void j_putGrayScanlineToRGB(unsigned char* jpegline, int widthPix, unsigned char* outBuf, int row) -{ - int offset = row * widthPix * 3; - int count; - for (count = 0; count < widthPix; count++) - { - unsigned char iGray; - unsigned char *oRed, *oBlu, *oGrn; +// ============================================================================= +// Function declarations (functions from the im_xxx.cpp files) - // get our grayscale value - iGray = *(jpegline + count); +LC_IMAGE* OpenJPG (char* filename); +LC_IMAGE* OpenBMP (char* filename); +LC_IMAGE* OpenPNG (char* filename); +LC_IMAGE* OpenGIF (File* file); - oRed = outBuf + offset + count * 3; - oGrn = outBuf + offset + count * 3 + 1; - oBlu = outBuf + offset + count * 3 + 2; +bool SaveJPG (char* filename, LC_IMAGE* image, int quality, bool progressive); +bool SaveBMP (char* filename, LC_IMAGE* image, bool quantize); +bool SavePNG (char* filename, LC_IMAGE* image, bool transparent, bool interlaced, unsigned char* background); +bool SaveGIF (File* file, LC_IMAGE* image, bool transparent, bool interlaced, unsigned char* background); - *oRed = iGray; - *oGrn = iGray; - *oBlu = iGray; - } -} +// ============================================================================= +// Static functions static LC_IMAGE* ResizeImage(LC_IMAGE* image) { - int i, j; - long shifted_x, shifted_y; - if (image == NULL) - return NULL; + int i, j; + long shifted_x, shifted_y; + if (image == NULL) + return NULL; - shifted_x = image->width; - for (i = 0; ((i < 16) && (shifted_x != 0)); i++) - shifted_x = shifted_x >> 1; - shifted_x = (i != 0) ? 1 << (i-1) : 1; + shifted_x = image->width; + for (i = 0; ((i < 16) && (shifted_x != 0)); i++) + shifted_x = shifted_x >> 1; + shifted_x = (i != 0) ? 1 << (i-1) : 1; - shifted_y = image->height; - for (i = 0; ((i < 16) && (shifted_y != 0)); i++) - shifted_y = shifted_y >> 1; - shifted_y = (i != 0) ? 1 << (i-1) : 1; + shifted_y = image->height; + for (i = 0; ((i < 16) && (shifted_y != 0)); i++) + shifted_y = shifted_y >> 1; + shifted_y = (i != 0) ? 1 << (i-1) : 1; - if ((shifted_x == image->width) && (shifted_y == image->height)) - return image; + if ((shifted_x == image->width) && (shifted_y == image->height)) + return image; - LC_IMAGE* newimage = (LC_IMAGE*)malloc(shifted_x*shifted_y*3+sizeof(LC_IMAGE)); - newimage->width = (unsigned short)shifted_x; - newimage->height = (unsigned short)shifted_y; - newimage->bits = (unsigned char*)newimage + sizeof(LC_IMAGE); - memset(newimage->bits, 0, shifted_x*shifted_y*3); + LC_IMAGE* newimage = (LC_IMAGE*)malloc(shifted_x*shifted_y*3+sizeof(LC_IMAGE)); + newimage->width = (unsigned short)shifted_x; + newimage->height = (unsigned short)shifted_y; + newimage->bits = (unsigned char*)newimage + sizeof(LC_IMAGE); + memset(newimage->bits, 0, shifted_x*shifted_y*3); - float accumx, accumy; - int stx, sty; - unsigned char *oldbits = (unsigned char*)image->bits, - *newbits = (unsigned char*)newimage->bits; + float accumx, accumy; + int stx, sty; + unsigned char *oldbits = (unsigned char*)image->bits, + *newbits = (unsigned char*)newimage->bits; - for (j = 0; j < image->height; j++) - { - accumy = (float)newimage->height*j/(float)image->height; - sty = (int)floor(accumy); + for (j = 0; j < image->height; j++) + { + accumy = (float)newimage->height*j/(float)image->height; + sty = (int)floor(accumy); - for (i = 0; i < image->width; i++) - { - accumx = (float)newimage->width*i/(float)image->width; - stx = (int)floor(accumx); + for (i = 0; i < image->width; i++) + { + accumx = (float)newimage->width*i/(float)image->width; + stx = (int)floor(accumx); - newbits[(stx+sty*newimage->width)*3] = oldbits[(i+j*image->width)*3]; - newbits[(stx+sty*newimage->width)*3+1] = oldbits[(i+j*image->width)*3+1]; - newbits[(stx+sty*newimage->width)*3+2] = oldbits[(i+j*image->width)*3+2]; - } - } + newbits[(stx+sty*newimage->width)*3] = oldbits[(i+j*image->width)*3]; + newbits[(stx+sty*newimage->width)*3+1] = oldbits[(i+j*image->width)*3+1]; + newbits[(stx+sty*newimage->width)*3+2] = oldbits[(i+j*image->width)*3+2]; + } + } - free(image); - return newimage; + free(image); + return newimage; } -///////////////////////////////////////////////////////////////////////////// -// functions +// ============================================================================= +// Global functions // Reads a file from disk LC_IMAGE* OpenImage(char* filename) { - char ext[5]; - if (strlen(filename) != 0) - { - char *p = strrchr(filename, '.'); - if (p != NULL) - strcpy (ext, p+1); - } - strlwr(ext); - - if ((strcmp(ext, "jpg") == 0) || (strcmp (ext, "jpeg") == 0)) - return ResizeImage(OpenJPG(filename)); - if (strcmp(ext, "bmp") == 0) - return ResizeImage(OpenBMP(filename)); - if (strcmp(ext, "png") == 0) - return ResizeImage(OpenPNG(filename)); - if ((strcmp (ext, "gif") == 0) || (strcmp (ext, "tmp") == 0)) - { - FileDisk file; - if (!file.Open(filename, "rb")) - return NULL; - LC_IMAGE* image = ResizeImage(OpenGIF(&file)); - file.Close(); - return image; - } + char ext[5]; + if (strlen(filename) != 0) + { + char *p = strrchr(filename, '.'); + if (p != NULL) + strcpy (ext, p+1); + } + strlwr(ext); + + if ((strcmp(ext, "jpg") == 0) || (strcmp (ext, "jpeg") == 0)) + return ResizeImage(OpenJPG(filename)); + if (strcmp(ext, "bmp") == 0) + return ResizeImage(OpenBMP(filename)); + if (strcmp(ext, "png") == 0) + return ResizeImage(OpenPNG(filename)); + if ((strcmp (ext, "gif") == 0) || (strcmp (ext, "tmp") == 0)) + { + FileDisk file; + if (!file.Open(filename, "rb")) + return NULL; + LC_IMAGE* image = ResizeImage(OpenGIF(&file)); + file.Close(); + return image; + } // MessageBox (NULL, "Unknown File Format", "Error", MB_ICONSTOP); - return NULL; + return NULL; } LC_IMAGE* OpenImage(File* file, unsigned char format) { - if (format != LC_IMAGE_GIF) - return NULL; - return OpenGIF(file); + if (format != LC_IMAGE_GIF) + return NULL; + return OpenGIF(file); } bool SaveImage(char* filename, LC_IMAGE* image, LC_IMAGE_OPTS* opts) { - char ext[5]; - if (strlen(filename) != 0) - { - char *p = strrchr(filename, '.'); - if (p != NULL) - strcpy(ext, p+1); - } - strlwr(ext); + char ext[5]; + if (strlen(filename) != 0) + { + char *p = strrchr(filename, '.'); + if (p != NULL) + strcpy(ext, p+1); + } + strlwr(ext); - if ((strcmp (ext, "jpg") == 0) || (strcmp (ext, "jpeg") == 0)) - return SaveJPG(filename, image, opts->quality, opts->interlaced); + if ((strcmp (ext, "jpg") == 0) || (strcmp (ext, "jpeg") == 0)) + return SaveJPG(filename, image, opts->quality, opts->interlaced); - if (strcmp (ext, "gif") == 0) - { - FileDisk file; - if (!file.Open(filename, "wb")) - return false; + if (strcmp (ext, "gif") == 0) + { + FileDisk file; + if (!file.Open(filename, "wb")) + return false; - bool ret = SaveGIF(&file, image, opts->transparent, opts->interlaced, opts->background); - file.Close(); - return ret; - } + bool ret = SaveGIF(&file, image, opts->transparent, opts->interlaced, opts->background); + file.Close(); + return ret; + } - if (strcmp (ext, "bmp") == 0) - return SaveBMP(filename, image, opts->truecolor == false); + if (strcmp (ext, "bmp") == 0) + return SaveBMP(filename, image, opts->truecolor == false); - if (strcmp (ext, "png") == 0) - return SavePNG(filename, image, opts->transparent, opts->interlaced, opts->background); + if (strcmp (ext, "png") == 0) + return SavePNG(filename, image, opts->transparent, opts->interlaced, opts->background); // MessageBox (NULL, "Could not save file", "Error", MB_ICONSTOP); - return false; + return false; } bool SaveImage(File* file, LC_IMAGE* image, LC_IMAGE_OPTS* opts) { - if (opts->format != LC_IMAGE_GIF) - return false; - return SaveGIF(file, image, opts->transparent, opts->interlaced, opts->background); -} - -///////////////////////////////////////////////////////////////////////////// -// read functions - -static LC_IMAGE* OpenJPG(char* filename) -{ - struct jpeg_decompress_struct cinfo; - struct bt_jpeg_error_mgr jerr; - FILE* infile = NULL; - JSAMPARRAY buffer; // Output row buffer - int row_stride; // physical row width in output buffer - - if ((infile = fopen(filename, "rb")) == NULL) - return NULL; - - cinfo.err = jpeg_std_error(&jerr.pub); - jerr.pub.error_exit = bt_jpeg_error_exit; - - if (setjmp(jerr.setjmp_buffer)) - { - jpeg_destroy_decompress(&cinfo); - - if (infile != NULL) - fclose(infile); - return NULL; - } - - jpeg_create_decompress(&cinfo); - jpeg_stdio_src(&cinfo, infile); - jpeg_read_header(&cinfo, TRUE); - jpeg_start_decompress(&cinfo); - - // get our buffer set to hold data - LC_IMAGE* image = (LC_IMAGE*)malloc(cinfo.output_width*cinfo.output_height*3 + sizeof(LC_IMAGE)); - - if (image == NULL) - { -// MessageBox(NULL, "Error", "Cannot allocate memory", MB_ICONSTOP); - jpeg_destroy_decompress(&cinfo); - fclose(infile); - return NULL; - } - - image->width = cinfo.output_width; - image->height = cinfo.output_height; - image->bits = (char*)image + sizeof(LC_IMAGE); - - row_stride = cinfo.output_width * cinfo.output_components; - buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); - - while (cinfo.output_scanline < cinfo.output_height) - { - jpeg_read_scanlines(&cinfo, buffer, 1); - - if (cinfo.out_color_components == 3) - j_putRGBScanline(buffer[0], image->width, (unsigned char*)image->bits, cinfo.output_scanline-1); - else if (cinfo.out_color_components == 1) - j_putGrayScanlineToRGB(buffer[0], image->width, (unsigned char*)image->bits, cinfo.output_scanline-1); - } - - jpeg_finish_decompress(&cinfo); - jpeg_destroy_decompress(&cinfo); - fclose(infile); - - return image; -} - -typedef struct -{ - unsigned char colormap[3][256]; - - // State for GetCode and LZWReadByte - char code_buf[256+4]; - int last_byte; // # of bytes in code_buf - int last_bit; // # of bits in code_buf - int cur_bit; // next bit index to read - bool out_of_blocks; // true if hit terminator data block - - int input_code_size; // codesize given in GIF file - int clear_code,end_code;// values for Clear and End codes - - int code_size; // current actual code size - int limit_code; // 2^code_size - int max_code; // first unused code value - bool first_time; // flags first call to LZWReadByte - - // Private state for LZWReadByte - int oldcode; // previous LZW symbol - int firstcode; // first byte of oldcode's expansion - - // LZW symbol table and expansion stack - UINT16 FAR *symbol_head; // => table of prefix symbols - UINT8 FAR *symbol_tail; // => table of suffix bytes - UINT8 FAR *symbol_stack; // => stack for symbol expansions - UINT8 FAR *sp; // stack pointer - - // State for interlaced image processing - bool is_interlaced; // true if have interlaced image -// jvirt_sarray_ptr interlaced_image; // full image in interlaced order - unsigned char* interlaced_image; - JDIMENSION cur_row_number; // need to know actual row number - JDIMENSION pass2_offset; // # of pixel rows in pass 1 - JDIMENSION pass3_offset; // # of pixel rows in passes 1&2 - JDIMENSION pass4_offset; // # of pixel rows in passes 1,2,3 - - File* input_file; - bool first_interlace; - unsigned char* buffer;//JSAMPARRAY buffer; - unsigned int width, height; -} gif_source_struct; - -typedef gif_source_struct *gif_source_ptr; - -// Macros for extracting header data --- note we assume chars may be signed -#define LM_to_uint(a,b) ((((b)&0xFF) << 8) | ((a)&0xFF)) -#define BitSet(byte, bit) ((byte) & (bit)) -#define INTERLACE 0x40 // mask for bit signifying interlaced image -#define COLORMAPFLAG 0x80 // mask for bit signifying colormap presence - -#undef LZW_TABLE_SIZE -#define MAX_LZW_BITS 12 // maximum LZW code size -#define LZW_TABLE_SIZE (1<input_file->GetChar(); - if (count > 0) - sinfo->input_file->Read(buf, count); - return count; -} - -static int GetCode (gif_source_ptr sinfo) -{ - register INT32 accum; - int offs, ret, count; - - while ( (sinfo->cur_bit + sinfo->code_size) > sinfo->last_bit) { - if (sinfo->out_of_blocks) - return sinfo->end_code; // fake something useful - sinfo->code_buf[0] = sinfo->code_buf[sinfo->last_byte-2]; - sinfo->code_buf[1] = sinfo->code_buf[sinfo->last_byte-1]; - if ((count = GetDataBlock(sinfo, &sinfo->code_buf[2])) == 0) { - sinfo->out_of_blocks = true; - return sinfo->end_code; // fake something useful - } - sinfo->cur_bit = (sinfo->cur_bit - sinfo->last_bit) + 16; - sinfo->last_byte = 2 + count; - sinfo->last_bit = sinfo->last_byte * 8; - } - - offs = sinfo->cur_bit >> 3; // byte containing cur_bit - accum = sinfo->code_buf[offs+2] & 0xFF; - accum <<= 8; - accum |= sinfo->code_buf[offs+1] & 0xFF; - accum <<= 8; - accum |= sinfo->code_buf[offs] & 0xFF; - accum >>= (sinfo->cur_bit & 7); - ret = ((int) accum) & ((1 << sinfo->code_size) - 1); - - sinfo->cur_bit += sinfo->code_size; - return ret; -} - -static int LZWReadByte (gif_source_ptr sinfo) -{ - register int code; // current working code - int incode; // saves actual input code - - // First time, just eat the expected Clear code(s) and return next code, - // which is expected to be a raw byte. - if (sinfo->first_time) - { - sinfo->first_time = false; - code = sinfo->clear_code; // enables sharing code with Clear case - } - else - { - // If any codes are stacked from a previously read symbol, return them - if (sinfo->sp > sinfo->symbol_stack) - return (int) *(-- sinfo->sp); - - // Time to read a new symbol - code = GetCode(sinfo); - - } - - if (code == sinfo->clear_code) - { - sinfo->code_size = sinfo->input_code_size + 1; - sinfo->limit_code = sinfo->clear_code << 1; // 2^code_size - sinfo->max_code = sinfo->clear_code + 2; // first unused code value - sinfo->sp = sinfo->symbol_stack; // init stack to empty - do - { - code = GetCode(sinfo); - } while (code == sinfo->clear_code); - - if (code > sinfo->clear_code) - code = 0; // use something valid - sinfo->firstcode = sinfo->oldcode = code; - return code; - } - - if (code == sinfo->end_code) - { - if (!sinfo->out_of_blocks) - { - char buf[256]; - while (GetDataBlock(sinfo, buf) > 0) - ; // skip - sinfo->out_of_blocks = true; - } - return 0; // fake something usable - } - - incode = code; // save for a moment - - if (code >= sinfo->max_code) - { - // special case for not-yet-defined symbol - // code == max_code is OK; anything bigger is bad data - if (code > sinfo->max_code) - incode = 0; // prevent creation of loops in symbol table - // this symbol will be defined as oldcode/firstcode - *(sinfo->sp++) = (UINT8) sinfo->firstcode; - code = sinfo->oldcode; - } - - while (code >= sinfo->clear_code) - { - *(sinfo->sp++) = sinfo->symbol_tail[code]; // tail is a byte value - code = sinfo->symbol_head[code]; // head is another LZW symbol - } - sinfo->firstcode = code; // save for possible future use - - if ((code = sinfo->max_code) < LZW_TABLE_SIZE) - { - sinfo->symbol_head[code] = sinfo->oldcode; - sinfo->symbol_tail[code] = (UINT8) sinfo->firstcode; - sinfo->max_code++; - if ((sinfo->max_code >= sinfo->limit_code) && - (sinfo->code_size < MAX_LZW_BITS)) { - sinfo->code_size++; - sinfo->limit_code <<= 1; // keep equal to 2^code_size - } - } - - sinfo->oldcode = incode; // save last input symbol for future use - return sinfo->firstcode; // return first byte of symbol's expansion -} - -static LC_IMAGE* OpenGIF(File* file) -{ - gif_source_ptr source; - source = (gif_source_ptr)malloc (sizeof(gif_source_struct)); - source->input_file = file; - - char hdrbuf[10]; - unsigned int width, height; - int colormaplen, aspectRatio; - int c; - - source->input_file->Read(hdrbuf, 6); - if ((hdrbuf[0] != 'G' || hdrbuf[1] != 'I' || hdrbuf[2] != 'F') || - ((hdrbuf[3] != '8' || hdrbuf[4] != '7' || hdrbuf[5] != 'a') && - (hdrbuf[3] != '8' || hdrbuf[4] != '9' || hdrbuf[5] != 'a'))) - return NULL; - - source->input_file->Read(hdrbuf, 7); - width = LM_to_uint(hdrbuf[0],hdrbuf[1]); - height = LM_to_uint(hdrbuf[2],hdrbuf[3]); - source->height = height; - source->width = width; - colormaplen = 2 << (hdrbuf[4] & 0x07); - aspectRatio = hdrbuf[6] & 0xFF; - - if (BitSet(hdrbuf[4], COLORMAPFLAG)) - for (int i = 0; i < colormaplen; i++) - { - source->colormap[0][i] = source->input_file->GetChar(); - source->colormap[1][i] = source->input_file->GetChar(); - source->colormap[2][i] = source->input_file->GetChar(); - } - - for (;;) - { - c = source->input_file->GetChar(); - -// if (c == ';') -// ERREXIT(cinfo, JERR_GIF_IMAGENOTFOUND); - - if (c == '!') - { - int extlabel; - char buf[256]; - - extlabel = source->input_file->GetChar(); - while (GetDataBlock(source, buf) > 0) - ; // skip - continue; - } - - if (c != ',') - continue; - - source->input_file->Read(hdrbuf, 9); - width = LM_to_uint(hdrbuf[4],hdrbuf[5]); - height = LM_to_uint(hdrbuf[6],hdrbuf[7]); - source->is_interlaced = (hdrbuf[8] & INTERLACE) != 0; - - if (BitSet(hdrbuf[8], COLORMAPFLAG)) - { - colormaplen = 2 << (hdrbuf[8] & 0x07); - for (int i = 0; i < colormaplen; i++) - { - source->colormap[0][i] = source->input_file->GetChar(); - source->colormap[1][i] = source->input_file->GetChar(); - source->colormap[2][i] = source->input_file->GetChar(); - } - } - - source->input_code_size = source->input_file->GetChar(); -// if (source->input_code_size < 2 || source->input_code_size >= MAX_LZW_BITS) -// ERREXIT1(cinfo, JERR_GIF_CODESIZE, source->input_code_size); - - break; - } - - source->symbol_head = (UINT16 FAR *) malloc(LZW_TABLE_SIZE * sizeof(UINT16)); - source->symbol_tail = (UINT8 FAR *) malloc (LZW_TABLE_SIZE * sizeof(UINT8)); - source->symbol_stack = (UINT8 FAR *) malloc (LZW_TABLE_SIZE * sizeof(UINT8)); - source->last_byte = 2; // make safe to "recopy last two bytes" - source->last_bit = 0; // nothing in the buffer - source->cur_bit = 0; // force buffer load on first call - source->out_of_blocks = false; - source->clear_code = 1 << source->input_code_size; - source->end_code = source->clear_code + 1; - source->first_time = true; - source->code_size = source->input_code_size + 1; - source->limit_code = source->clear_code << 1;// 2^code_size - source->max_code = source->clear_code + 2; // first unused code value - source->sp = source->symbol_stack; // init stack to empty - - if (source->is_interlaced) - { - source->first_interlace = true; - source->interlaced_image = (unsigned char*)malloc(width*height); - } - else - source->first_interlace = false; - - source->buffer = (unsigned char*)malloc(width*3); - LC_IMAGE* image = (LC_IMAGE*)malloc(width*height*3 + sizeof(LC_IMAGE)); - image->width = width; - image->height = height; - image->bits = (char*)image + sizeof(LC_IMAGE); - unsigned char* buf = (unsigned char*)image->bits; - - for (unsigned long scanline = 0; scanline < height; scanline++) - { - if (source->is_interlaced) - { - if (source->first_interlace) - { - register JSAMPROW sptr; - register JDIMENSION col; - JDIMENSION row; - - for (row = 0; row < source->height; row++) - { - sptr = &source->interlaced_image[row*source->width]; - for (col = source->width; col > 0; col--) - *sptr++ = (JSAMPLE) LZWReadByte(source); - } - - source->first_interlace = false; - source->cur_row_number = 0; - source->pass2_offset = (source->height + 7) / 8; - source->pass3_offset = source->pass2_offset + (source->height + 3) / 8; - source->pass4_offset = source->pass3_offset + (source->height + 1) / 4; - } - - register int c; - register JSAMPROW sptr, ptr; - register JDIMENSION col; - JDIMENSION irow; - - // Figure out which row of interlaced image is needed, and access it. - switch ((int) (source->cur_row_number & 7)) - { - case 0: // first-pass row - irow = source->cur_row_number >> 3; - break; - case 4: // second-pass row - irow = (source->cur_row_number >> 3) + source->pass2_offset; - break; - case 2: // third-pass row - case 6: - irow = (source->cur_row_number >> 2) + source->pass3_offset; - break; - default: // fourth-pass row - irow = (source->cur_row_number >> 1) + source->pass4_offset; - break; - } - sptr = &source->interlaced_image[irow*source->width]; - ptr = source->buffer; - for (col = source->width; col > 0; col--) - { - c = GETJSAMPLE(*sptr++); - *ptr++ = source->colormap[0][c]; - *ptr++ = source->colormap[1][c]; - *ptr++ = source->colormap[2][c]; - } - source->cur_row_number++; // for next time - } - else - { - register int c; - register JSAMPROW ptr; - register JDIMENSION col; - - ptr = source->buffer; - for (col = source->width; col > 0; col--) - { - c = LZWReadByte(source); - *ptr++ = source->colormap[0][c]; - *ptr++ = source->colormap[1][c]; - *ptr++ = source->colormap[2][c]; - } - } - - memcpy (buf+(width*scanline*3), source->buffer, 3*width); - } - - if (source->is_interlaced) - free(source->interlaced_image); - free(source->buffer); - free(source->symbol_head); - free(source->symbol_tail); - free(source->symbol_stack); - free(source); - - return image; -} - -///////////////////////////////////////////////////////////////////////////// -// save functions - -static bool SaveJPG (char* filename, LC_IMAGE* image, int quality, bool progressive) -{ - struct jpeg_compress_struct cinfo; - FILE* outfile = NULL; - int row_stride; // physical row widthPix in image buffer - struct bt_jpeg_error_mgr jerr; - - // allocate and initialize JPEG compression object - cinfo.err = jpeg_std_error(&jerr.pub); - jerr.pub.error_exit = bt_jpeg_error_exit; - - if (setjmp(jerr.setjmp_buffer)) - { - jpeg_destroy_compress(&cinfo); - if (outfile != NULL) - fclose(outfile); - return false; - } - - jpeg_create_compress(&cinfo); - if ((outfile = fopen(filename, "wb")) == NULL) - return false; - - jpeg_stdio_dest(&cinfo, outfile); - - cinfo.image_width = image->width; - cinfo.image_height = image->height; - cinfo.input_components = 3; - cinfo.in_color_space = JCS_RGB; - - jpeg_set_defaults(&cinfo); - jpeg_set_quality(&cinfo, quality, TRUE); - - if (progressive) - jpeg_simple_progression(&cinfo); - - jpeg_start_compress(&cinfo, TRUE); - row_stride = image->width * 3; - - while (cinfo.next_scanline < cinfo.image_height) - { - unsigned char* outRow = (unsigned char*)image->bits + (cinfo.next_scanline * image->width * 3); - jpeg_write_scanlines(&cinfo, &outRow, 1); - } - - jpeg_finish_compress(&cinfo); - fclose(outfile); - jpeg_destroy_compress(&cinfo); - - return true; + if (opts->format != LC_IMAGE_GIF) + return false; + return SaveGIF(file, image, opts->transparent, opts->interlaced, opts->background); } -#undef LZW_TABLE_SIZE -#define MAX_LZW_BITS 12 -typedef INT16 code_int; -#define LZW_TABLE_SIZE ((code_int) 1 << MAX_LZW_BITS) -#define HSIZE 5003 -typedef int hash_int; -#define MAXCODE(n_bits) (((code_int) 1 << (n_bits)) - 1) -typedef INT32 hash_entry; -#define HASH_ENTRY(prefix,suffix) ((((hash_entry) (prefix)) << 8) | (suffix)) - -typedef struct -{ - int n_bits; - code_int maxcode; - int init_bits; - INT32 cur_accum; - int cur_bits; - code_int waiting_code; - bool first_byte; - code_int ClearCode; - code_int EOFCode; - code_int free_code; - code_int *hash_code; - hash_entry FAR *hash_value; - int bytesinpkt; - char packetbuf[256]; - File* output_file; - void* buffer;//JSAMPARRAY buffer; -} gif_dest_struct; - -typedef gif_dest_struct* gif_dest_ptr; - -// Emit a 16-bit word, LSB first -static void put_word(File* output_file, unsigned int w) -{ - output_file->PutChar(w & 0xFF); - output_file->PutChar((w >> 8) & 0xFF); -} - -static void flush_packet(gif_dest_ptr dinfo) -{ - if (dinfo->bytesinpkt > 0) - { - dinfo->packetbuf[0] = (char) dinfo->bytesinpkt++; - dinfo->output_file->Write(dinfo->packetbuf, dinfo->bytesinpkt); - dinfo->bytesinpkt = 0; - } -} - -static void output(gif_dest_ptr dinfo, code_int code) -{ - dinfo->cur_accum |= ((INT32) code) << dinfo->cur_bits; - dinfo->cur_bits += dinfo->n_bits; - - while (dinfo->cur_bits >= 8) - { - (dinfo)->packetbuf[++(dinfo)->bytesinpkt] = (char) (dinfo->cur_accum & 0xFF); - if ((dinfo)->bytesinpkt >= 255) - flush_packet(dinfo); - - dinfo->cur_accum >>= 8; - dinfo->cur_bits -= 8; - } - - if (dinfo->free_code > dinfo->maxcode) { - dinfo->n_bits++; - if (dinfo->n_bits == MAX_LZW_BITS) - dinfo->maxcode = LZW_TABLE_SIZE; - else - dinfo->maxcode = MAXCODE(dinfo->n_bits); - } -} - -// Accept and compress one 8-bit byte -static void compress_byte (gif_dest_ptr dinfo, int c) -{ - register hash_int i; - register hash_int disp; - register hash_entry probe_value; - - if (dinfo->first_byte) { - dinfo->waiting_code = c; - dinfo->first_byte = FALSE; - return; - } - - i = ((hash_int) c << (MAX_LZW_BITS-8)) + dinfo->waiting_code; - if (i >= HSIZE) - i -= HSIZE; - - probe_value = HASH_ENTRY(dinfo->waiting_code, c); - - if (dinfo->hash_code[i] != 0) { - if (dinfo->hash_value[i] == probe_value) { - dinfo->waiting_code = dinfo->hash_code[i]; - return; - } - if (i == 0) - disp = 1; - else - disp = HSIZE - i; - for (;;) { - i -= disp; - if (i < 0) - i += HSIZE; - if (dinfo->hash_code[i] == 0) - break; - if (dinfo->hash_value[i] == probe_value) { - dinfo->waiting_code = dinfo->hash_code[i]; - return; - } - } - } - - output(dinfo, dinfo->waiting_code); - if (dinfo->free_code < LZW_TABLE_SIZE) - { - dinfo->hash_code[i] = dinfo->free_code++; - dinfo->hash_value[i] = probe_value; - } - else - { - memset(dinfo->hash_code, 0, HSIZE * sizeof(code_int)); - dinfo->free_code = dinfo->ClearCode + 2; - output(dinfo, dinfo->ClearCode); - dinfo->n_bits = dinfo->init_bits; - dinfo->maxcode = MAXCODE(dinfo->n_bits); - } - dinfo->waiting_code = c; -} - -static bool SaveGIF(File* file, LC_IMAGE* image, bool transparent, bool interlaced, unsigned char* background) -{ - int InitCodeSize, FlagByte; - int i; - - unsigned char pal[3][256]; - unsigned char* colormappedbuffer = (unsigned char*)malloc(image->width*image->height); - dl1quant((unsigned char*)image->bits, colormappedbuffer, image->width, image->height, 256, true, pal); - - gif_dest_ptr dinfo; - dinfo = (gif_dest_ptr) malloc (sizeof(gif_dest_struct)); - dinfo->output_file = file; - dinfo->buffer = malloc(image->width*sizeof(JDIMENSION)); - dinfo->hash_code = (code_int*) malloc(HSIZE * sizeof(code_int)); - dinfo->hash_value = (hash_entry FAR*)malloc(HSIZE*sizeof(hash_entry)); - - InitCodeSize = 8; - // Write the GIF header. - file->PutChar('G'); - file->PutChar('I'); - file->PutChar('F'); - file->PutChar('8'); - file->PutChar(transparent ? '9' : '7'); - file->PutChar('a'); - // Write the Logical Screen Descriptor - put_word(file, (unsigned int)image->width); - put_word(file, (unsigned int)image->height); - FlagByte = 0x80; - FlagByte |= (7) << 4; // color resolution - FlagByte |= (7); // size of global color table - file->PutChar(FlagByte); - file->PutChar(0); // Background color index - file->PutChar(0); // Reserved (aspect ratio in GIF89) - // Write the Global Color Map - for (i = 0; i < 256; i++) - { - file->PutChar(pal[0][i]); - file->PutChar(pal[1][i]); - file->PutChar(pal[2][i]); - } - - // Write out extension for transparent colour index, if necessary. - if (transparent) - { - unsigned char index = 0; - - for (i = 0; i < 256; i++) - if (background[0] == pal[0][i] && - background[1] == pal[1][i] && - background[2] == pal[2][i]) - { - index = i; - break; - } - - file->PutChar('!'); - file->PutChar(0xf9); - file->PutChar(4); - file->PutChar(1); - file->PutChar(0); - file->PutChar(0); - file->PutChar(index); - file->PutChar(0); - } - - // Write image separator and Image Descriptor - file->PutChar(','); - put_word(file, 0); - put_word(file, 0); - put_word(file, (unsigned int)image->width); - put_word(file, (unsigned int)image->height); - // flag byte: interlaced - if (interlaced) - file->PutChar(0x40); - else - file->PutChar(0x00); - file->PutChar(InitCodeSize);// Write Initial Code Size byte - - // Initialize for LZW compression of image data - dinfo->n_bits = dinfo->init_bits = InitCodeSize+1; - dinfo->maxcode = MAXCODE(dinfo->n_bits); - dinfo->ClearCode = ((code_int) 1 << (InitCodeSize)); - dinfo->EOFCode = dinfo->ClearCode + 1; - dinfo->free_code = dinfo->ClearCode + 2; - dinfo->first_byte = TRUE; - dinfo->bytesinpkt = 0; - dinfo->cur_accum = 0; - dinfo->cur_bits = 0; - memset(dinfo->hash_code, 0, HSIZE * sizeof(code_int)); - output(dinfo, dinfo->ClearCode); - - int scanline = 0; - int pass = 0; - while (scanline < image->height) - { - memcpy(dinfo->buffer, colormappedbuffer+(scanline*image->width), image->width); - - register JSAMPROW ptr; - register JDIMENSION col; - - ptr = (unsigned char*)dinfo->buffer; - for (col = image->width; col > 0; col--) - compress_byte(dinfo, GETJSAMPLE(*ptr++)); - - if (interlaced) - { - switch (pass) - { - case 0: - { - scanline += 8; - if (scanline >= image->height) - { - pass++; - scanline = 4; - } - } break; - - case 1: - { - scanline += 8; - if (scanline >= image->height) - { - pass++; - scanline = 2; - } - } break; - - case 2: - { - scanline += 4; - if (scanline >= image->height) - { - pass++; - scanline = 1; - } - } break; - - case 3: - { - scanline += 2; - } break; - } - } - else - scanline++; - } - - // Finish up at the end of the file. - if (!dinfo->first_byte) - output(dinfo, dinfo->waiting_code); - output(dinfo, dinfo->EOFCode); - if (dinfo->cur_bits > 0) - { - (dinfo)->packetbuf[++(dinfo)->bytesinpkt] = (char) (dinfo->cur_accum & 0xFF); - if ((dinfo)->bytesinpkt >= 255) - flush_packet(dinfo); - } - - flush_packet(dinfo); - file->PutChar(0); - file->PutChar(';'); - file->Flush(); - - free(dinfo->buffer); - free(dinfo->hash_code); - free(dinfo->hash_value); - free(dinfo); - free(colormappedbuffer); - return true; -} #ifdef LC_WINDOWS //#include diff --git a/common/module.mk b/common/module.mk index a22bcbe..70ad3fa 100644 --- a/common/module.mk +++ b/common/module.mk @@ -1,4 +1,16 @@ -SRC += common/camera.cpp common/file.cpp common/globals.cpp common/group.cpp common/image.cpp common/im_bmp.cpp common/im_png.cpp common/library.cpp common/light.cpp common/matrix.cpp common/piece.cpp common/pieceinf.cpp common/project.cpp common/quant.cpp common/terrain.cpp common/texture.cpp common/tr.cpp common/vector.cpp common/opengl.cpp common/object.cpp common/minifig.cpp +SRC += common/camera.cpp common/file.cpp common/globals.cpp common/group.cpp common/image.cpp common/im_bmp.cpp common/im_gif.cpp common/library.cpp common/light.cpp common/matrix.cpp common/piece.cpp common/pieceinf.cpp common/project.cpp common/quant.cpp common/terrain.cpp common/texture.cpp common/tr.cpp common/vector.cpp common/opengl.cpp common/object.cpp common/minifig.cpp -LIBS += -ljpeg -lpng -lz -lm +ifeq ($(HAVE_JPEGLIB), yes) + LIBS += -ljpeg + SRC += common/im_jpg.cpp +endif + +ifeq ($(HAVE_ZLIB), yes) +ifeq ($(HAVE_PNGLIB), yes) + LIBS += -lpng -lz + SRC += common/im_png.cpp +endif +endif + +LIBS += -lm -- cgit v1.2.3