/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ /* Copyright (c) 2008-2014 Oleksandr Shneyder */ /* Copyright (c) 2014-2016 Ulrich Sibiller */ /* Copyright (c) 2014-2016 Mihai Moldovan */ /* Copyright (c) 2011-2016 Mike Gabriel */ /* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ /* */ /* NXCOMPEXT, NX protocol compression and NX extensions to this software */ /* are copyright of the aforementioned persons and companies. */ /* */ /* Redistribution and use of the present software is allowed according */ /* to terms specified in the file LICENSE which comes in the source */ /* distribution. */ /* */ /* All rights reserved. */ /* */ /* NOTE: This software has received contributions from various other */ /* contributors, only the core maintainers and supporters are listed as */ /* copyright holders. Please contact us, if you feel you should be listed */ /* as copyright holder, as well. */ /* */ /**************************************************************************/ #include #include #include #include #include "Compext.h" #include "Mask.h" #include "Png.h" #include "../Utils.h" #define PANIC #define WARNING #undef TEST #undef DEBUG /* * Selected ZLIB compression level. */ #define PNG_Z_LEVEL 4 /* * Local function prototypes. */ static void PrepareRowForPng(CARD8 *dst, int y, int count); static void PrepareRowForPng24(CARD8 *dst, int y, int count); static void PrepareRowForPng16(CARD8 *dst, int y, int count); static void PrepareRowForPng32(CARD8 *dst, int y, int count); static void PngWriteData(png_structp png_ptr, png_bytep data, png_size_t length); static void PngFlushData(png_structp png_ptr); /* * Image characteristics. */ static int bytesPerLine; static int byteOrder; static CARD8 bitsPerPixel; static CARD16 redMax, greenMax, blueMax; static CARD8 redShift, greenShift, blueShift; /* * Other variables used for the Png * encoding. */ png_byte color_type; png_structp png_ptr; png_infop info_ptr; png_colorp palette; static char *pngCompBuf; static int pngDataLen; static char *pngBeforeBuf = NULL; /* * Allocate data for the compressed image. * We need to ensure that there is enough * space to include the palette and the * header. */ #define PNG_DEST_SIZE(width, height) ((width) * 3 * (height) + 1024 + 256) /* * Just for debug purposes. */ #ifdef DEBUG static int pngId; static char pngName[10]; static FILE *pngFile; #endif int PngCompareColorTable(NXColorTable *c1, NXColorTable *c2) { return (c1 -> pixel - c2 -> pixel); } #define NB_COLOR_MAX 256 int NXCreatePalette32(XImage *src_image, NXColorTable *color_table, CARD8 *image_index, int nb_max) { int x, y, t, p; CARD8 *fbptr; CARD32 pixel; fbptr = (CARD8 *) (src_image -> data); /* * TODO: Find a more intelligent way to * estimate the number of colors. */ memset(color_table, 0, nb_max * sizeof(NXColorTable)); for (x = 0, p = 0; x < src_image -> height; x++) { for (y = 0; y < src_image -> width; y++) { if (byteOrder == LSBFirst) { pixel = (CARD32) *(fbptr + 3); pixel = (pixel << 8) | (CARD32) *(fbptr + 2); pixel = (pixel << 8) | (CARD32) *(fbptr + 1); pixel = (pixel << 8) | (CARD32) *fbptr; } else { pixel = (CARD32) *fbptr; pixel = (pixel << 8) | (CARD32) *(fbptr + 1); pixel = (pixel << 8) | (CARD32) *(fbptr + 2); pixel = (pixel << 8) | (CARD32) *(fbptr + 3); } fbptr += 4; for (t = 0; t < nb_max; t++) { if (color_table[t].found == 0) { color_table[t].pixel = pixel; color_table[t].found = 1; p++; image_index[((x * src_image -> width) + y)] = t; break; } else if ((CARD32)(color_table[t].pixel) == pixel) { image_index[((x * src_image -> width) + y)] = t; break; } } if (p == nb_max) { return nb_max + 1; } } } return p; } int NXCreatePalette16(XImage *src_image, NXColorTable *color_table, CARD8 *image_index, int nb_max) { int x, y, t, p; CARD8 *fbptr; CARD16 pixel; fbptr = (CARD8 *) (src_image -> data); /* * TODO: Find a more intelligent way to * estimate the number of colors. */ memset(color_table, 0, nb_max * sizeof(NXColorTable)); for (x = 0, p = 0; x < src_image -> height; x++) { for (y = 0; y < src_image -> width; y++) { if (byteOrder == LSBFirst) { pixel = (CARD16) *(fbptr + 1); pixel = (pixel << 8) | (CARD16) *fbptr; } else { pixel = (CARD16) *fbptr; pixel = (pixel << 8) | (CARD16) *(fbptr + 1); } fbptr += 2; for (t = 0; t < nb_max; t++) { if (color_table[t].found == 0) { color_table[t].pixel = pixel; color_table[t].found = 1; p++; image_index[((x * src_image -> width) + y)] = t; break; } else if ((color_table[t].pixel) == pixel) { image_index[((x * src_image -> width) + y)] = t; break; } } /* * In case the number of 16bit words is not even * we have 2 padding bytes that we have to skip. */ if ((y == src_image -> width - 1) && (src_image -> width % 2 == 1)) fbptr += 2; if (p == nb_max) { return nb_max + 1; } } } return p; } char *PngCompressData(XImage *image, int *compressed_size) { unsigned int num = 0; CARD8 *srcBuf; int dy, w, h; int nb_colors; NXColorTable color_table[NB_COLOR_MAX]; CARD8 *image_index; *compressed_size = 0; pngDataLen = 0; /* * Initialize the image stuff. */ bitsPerPixel = image -> bits_per_pixel; bytesPerLine = image -> bytes_per_line; byteOrder = image -> byte_order; if (bitsPerPixel < 15) { #ifdef PANIC fprintf(stderr, "******PngCompressData: PANIC! Can't compress images with [%d] bits per pixel.\n", bitsPerPixel); #endif return NULL; } redShift = FindLSB(image -> red_mask) - 1; greenShift = FindLSB(image -> green_mask) - 1; blueShift = FindLSB(image -> blue_mask) - 1; redMax = image -> red_mask >> redShift; greenMax = image -> green_mask >> greenShift; blueMax = image -> blue_mask >> blueShift; w = image -> width; h = image -> height; pngBeforeBuf = image -> data; #ifdef DEBUG fprintf(stderr, "******PngCompressData: Compressing image with width [%d] height [%d].\n", w, h ); #endif /* * Initialize the PNG stuff. */ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { #ifdef PANIC fprintf(stderr, "******PngCompressData: PANIC! Failed creating the png_create_write_struct.\n"); #endif return NULL; } info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { #ifdef PANIC fprintf(stderr, "******PngCompressData: PANIC! Failed creating the png_create_info_struct.\n"); #endif png_destroy_write_struct(&png_ptr, NULL); return NULL; } if (setjmp(png_jmpbuf(png_ptr))) { #ifdef PANIC fprintf(stderr, "******PngCompressData: PANIC! Error during compression initialization.\n"); #endif png_destroy_write_struct(&png_ptr, &info_ptr); return NULL; } /* * Be sure we allocate enough data. */ #ifdef TEST fprintf(stderr, "******PngCompressData: Allocating [%d] bytes for the destination data.\n", PNG_DEST_SIZE(w, h)); #endif pngCompBuf = malloc(PNG_DEST_SIZE(w, h)); if (pngCompBuf == NULL) { #ifdef PANIC fprintf(stderr, "******PngCompressData: PANIC! Error allocating [%d] bytes for the Png data.\n", PNG_DEST_SIZE(w, h)); #endif return NULL; } png_set_write_fn(png_ptr, (void *) pngCompBuf, PngWriteData, PngFlushData); if (setjmp(png_jmpbuf(png_ptr))) { #ifdef PANIC fprintf(stderr, "******PngCompressData: PANIC! Error writing the header.\n"); #endif png_destroy_write_struct(&png_ptr, &info_ptr); SAFE_free(pngCompBuf); return NULL; } image_index = (CARD8 *) calloc(1, (image -> height) * (image -> width) * sizeof(CARD8)); if (image_index == NULL) { #ifdef PANIC fprintf(stderr, "******PngCompressData: PANIC! Could not alloc image_index.\n"); #endif SAFE_free(pngCompBuf); return NULL; } /* * TODO: Be sure the padded bytes are cleaned. * It would be better to set to zero the bytes * that are not aligned to the word boundary * at the end of the procedure. */ png_set_compression_level(png_ptr, PNG_Z_LEVEL); if (bitsPerPixel == 16) { nb_colors = NXCreatePalette16(image, color_table, image_index, NB_COLOR_MAX); } else { nb_colors = NXCreatePalette32(image, color_table, image_index, NB_COLOR_MAX); } if (nb_colors <= NB_COLOR_MAX) { color_type = PNG_COLOR_TYPE_PALETTE; } else { color_type = PNG_COLOR_TYPE_RGB; } png_set_IHDR(png_ptr, info_ptr, w, h, 8, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); if (color_type == PNG_COLOR_TYPE_PALETTE) { palette = png_malloc(png_ptr, sizeof(*palette) * 256); /* * TODO: Do we need to clean these bytes? * * memset(palette, 0, sizeof(*palette) * 256); */ for (num = 0; num < 256 && color_table[num].found != 0; num++) { if (bitsPerPixel == 24) { palette[num].red = (color_table[num].pixel >> redShift) & redMax; palette[num].green = (color_table[num].pixel >> greenShift) & greenMax; palette[num].blue = color_table[num].pixel >> blueShift & blueMax; } else { int inRed, inGreen, inBlue; inRed = (color_table[num].pixel >> redShift) & redMax; inGreen = (color_table[num].pixel >> greenShift) & greenMax; inBlue = color_table[num].pixel >> blueShift & blueMax; palette[num].red = (CARD8)((inRed * 255 + redMax / 2) / redMax); palette[num].green = (CARD8)((inGreen * 255 + greenMax / 2) / greenMax); palette[num].blue = (CARD8)((inBlue * 255 + blueMax / 2) / blueMax); } #ifdef DEBUG fprintf(stderr, "******PngCompressData: pixel[%d] r[%d] g[%d] b[%d].\n", (int) color_table[num].pixel,palette[num].red,palette[num].green,palette[num].blue); #endif } png_set_PLTE(png_ptr, info_ptr, palette, num); #ifdef DEBUG fprintf(stderr, "******PngCompressedData: Setting palette.\n"); #endif } /* * End of palette. */ png_write_info(png_ptr, info_ptr); /* * Allocate space for one line of * the image, 3 bytes per pixel. */ #ifdef DEBUG fprintf(stderr, "******PngCompressedData: Initialization finished.\n"); #endif if (setjmp(png_jmpbuf(png_ptr))) { #ifdef PANIC fprintf(stderr, "******PngCompressData: PANIC! Error while writing the image rows.\n"); #endif png_destroy_write_struct(&png_ptr, &info_ptr); SAFE_free(pngCompBuf); SAFE_free(image_index); return NULL; } int count; if (color_type == PNG_COLOR_TYPE_PALETTE) { count = w; } else { count = 3 * w; } srcBuf = (CARD8 *) calloc(count, sizeof(CARD8)); if (srcBuf == NULL) { #ifdef PANIC fprintf(stderr, "******PngCompressData: PANIC! Cannot allocate [%d] bytes.\n", (int) (count * sizeof(CARD8))); #endif SAFE_free(pngCompBuf); SAFE_free(image_index); return NULL; } /* * TODO: Be sure the padded bytes are cleaned. * It would be better to set to zero the bytes * that are not aligned to the word boundary * at the end of the procedure. */ for (dy = 0; dy < h; dy++) { if (color_type == PNG_COLOR_TYPE_RGB) { PrepareRowForPng(srcBuf, dy, w); } else { memcpy(srcBuf, image_index + (dy * w), w); } png_write_row(png_ptr, srcBuf); } #ifdef DEBUG fprintf(stderr, "******PngCompressedData: Compression finished. Lines handled [%d,%d].\n", dy, h); #endif SAFE_free(srcBuf); SAFE_free(image_index); if (setjmp(png_jmpbuf(png_ptr))) { #ifdef PANIC fprintf(stderr, "******PngCompressData: PANIC! error during end of write.\n"); #endif png_destroy_write_struct(&png_ptr, &info_ptr); SAFE_free(pngCompBuf); return NULL; } png_write_end(png_ptr, NULL); if (color_type == PNG_COLOR_TYPE_PALETTE) { png_free(png_ptr, palette); } png_destroy_write_struct(&png_ptr, &info_ptr); /* * Check the size of the resulting data. */ if (pngDataLen > 0) { #ifdef DEBUG int i = 0; fprintf(stderr, "******PngCompressedData: Compressed size [%d].\n", pngDataLen); pngId++; sprintf(pngName, "png%d", pngId); pngFile = fopen(pngName, "w"); for (i = 0; i < pngDataLen; i++) { fprintf(pngFile, "%c", *(pngCompBuf + i)); } fclose(pngFile); #endif *compressed_size = pngDataLen; return pngCompBuf; } else { #ifdef DEBUG fprintf(stderr, "******PngCompressedData: PANIC! Invalid size of the compressed data [%d].\n", pngDataLen); #endif SAFE_free(pngCompBuf); return NULL; } } static void PngWriteData(png_structp _png_ptr, png_bytep data, png_size_t length) { memcpy(((char *) png_get_io_ptr(_png_ptr) + pngDataLen), data, length); pngDataLen += length; } static void PngFlushData(png_structp _png_ptr) { } void PrepareRowForPng(CARD8 *dst, int y, int count) { if (bitsPerPixel == 32) { if (redMax == 0xff && greenMax == 0xff && blueMax == 0xff) { PrepareRowForPng24(dst, y, count); } else { PrepareRowForPng32(dst, y, count); } } else if (bitsPerPixel == 24) { memcpy(dst, pngBeforeBuf + y * bytesPerLine, count * 3); } else { /* * 16 bpp assumed. */ PrepareRowForPng16(dst, y, count); } } void PrepareRowForPng24(CARD8 *dst, int y, int count) { CARD8 *fbptr; CARD32 pix; fbptr = (CARD8 *) (pngBeforeBuf + y * bytesPerLine); while (count--) { if (byteOrder == LSBFirst) { pix = (CARD32) *(fbptr + 2); pix = (pix << 8) | (CARD32) *(fbptr+1); pix = (pix << 8) | (CARD32) *fbptr; } else { pix = (CARD32) *(fbptr + 1); pix = (pix << 8) | (CARD32) *(fbptr + 2); pix = (pix << 8) | (CARD32) *(fbptr + 3); } *dst++ = (CARD8)(pix >> redShift); *dst++ = (CARD8)(pix >> greenShift); *dst++ = (CARD8)(pix >> blueShift); fbptr+=4; } } #define DEFINE_PNG_GET_ROW_FUNCTION(bpp) \ \ void PrepareRowForPng##bpp(CARD8 *dst, int y, int count) \ { \ CARD8 *fbptr; \ CARD##bpp pix; \ int inRed, inGreen, inBlue; \ int i; \ \ fbptr = (CARD8 *) (pngBeforeBuf + y * bytesPerLine); \ \ while (count--) \ { \ pix = 0; \ \ if (byteOrder == LSBFirst) \ { \ for (i = (bpp >> 3) - 1; i >= 0; i--) \ { \ pix = (pix << 8) | (CARD32) *(fbptr + i); \ } \ } \ else \ { \ for (i = 0; i < (bpp >> 3); i++) \ { \ pix = (pix << 8) | (CARD32) *(fbptr + i); \ } \ } \ \ fbptr += (bpp >> 3); \ \ inRed = (int) \ (pix >> redShift & redMax); \ inGreen = (int) \ (pix >> greenShift & greenMax); \ inBlue = (int) \ (pix >> blueShift & blueMax); \ *dst++ = (CARD8)((inRed * 255 + redMax / 2) / \ redMax); \ *dst++ = (CARD8)((inGreen * 255 + greenMax / 2) / \ greenMax); \ *dst++ = (CARD8)((inBlue * 255 + blueMax / 2) / \ blueMax); \ } \ } DEFINE_PNG_GET_ROW_FUNCTION(16) DEFINE_PNG_GET_ROW_FUNCTION(32)