/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ /* Copyright (c) 2008-2017 Oleksandr Shneyder */ /* Copyright (c) 2014-2022 Ulrich Sibiller */ /* Copyright (c) 2014-2019 Mihai Moldovan */ /* Copyright (c) 2011-2022 Mike Gabriel */ /* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ /* */ /* NXCOMP, 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.nxcomp 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. */ /* */ /**************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif // // This file obviously supports PNG // decompression. It was renamed to // avoid name clashes on Windows. // #include #ifdef ANDROID #include #endif #include #include #include #include "Unpack.h" #include "Pgn.h" #define PANIC #define WARNING #undef TEST #undef DEBUG #define RGB24_TO_PIXEL(bpp,r,g,b) \ ((((CARD##bpp)(r) & 0xFF) * srcRedMax2 + 127) / 255 \ << srcRedShift2 | \ (((CARD##bpp)(g) & 0xFF) * srcGreenMax2 + 127) / 255 \ << srcGreenShift2 | \ (((CARD##bpp)(b) & 0xFF) * srcBlueMax2 + 127) / 255 \ << srcBlueShift2) #define RGB24_TO_PIXEL32(r,g,b) \ (((CARD32)(r) & 0xFF) << srcRedShift2 | \ ((CARD32)(g) & 0xFF) << srcGreenShift2 | \ ((CARD32)(b) & 0xFF) << srcBlueShift2) // // Functions from Unpack.cpp // extern int Unpack32To32(const T_colormask *colormask, const unsigned int *data, unsigned int *out, unsigned int *end); extern int Unpack24To24(const T_colormask *colormask, const unsigned char *data, unsigned char *out, unsigned char *end); extern int Unpack16To16(const T_colormask *colormask, const unsigned char *data, unsigned char *out, unsigned char *end); // // Local functions used for the png decompression. // static int DecompressPng16(unsigned char *compressedData, int compressedLen, unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder); static int DecompressPng24(unsigned char *compressedData, int compressedLen, unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder); static int DecompressPng32(unsigned char *compressedData, int compressedLen, unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder); static void PngReadData(png_structp png_ptr, png_bytep data, png_size_t length); // // Colormap stuff. // CARD16 srcRedMax2, srcGreenMax2, srcBlueMax2; CARD8 srcRedShift2, srcGreenShift2, srcBlueShift2; // // Attributes used for the png decompression. // static char *tmpBuf; static int tmpBufSize = 0; static int streamPos; int UnpackPng(T_geometry *geometry, unsigned char method, unsigned char *srcData, int srcSize, int dstBpp, int dstWidth, int dstHeight, unsigned char *dstData, int dstSize) { int byteOrder = geometry -> image_byte_order; // // Check if data is coming from a failed unsplit. // if (srcSize < 2 || (srcData[0] == SPLIT_PATTERN && srcData[1] == SPLIT_PATTERN)) { #ifdef WARNING *logofs << "UnpackPng: WARNING! Skipping unpack of dummy data.\n" << logofs_flush; #endif return -1; } #ifdef DEBUG *logofs << "UnpackPng: Decompressing image with " << "srcSize " << srcSize << " and bpp " << dstBpp << ".\n" << logofs_flush; #endif srcRedShift2 = ffs(geometry -> red_mask) - 1; srcGreenShift2 = ffs(geometry -> green_mask) - 1; srcBlueShift2 = ffs(geometry -> blue_mask) - 1; srcRedMax2 = geometry -> red_mask >> srcRedShift2; srcGreenMax2 = geometry -> green_mask >> srcGreenShift2; srcBlueMax2 = geometry -> blue_mask >> srcBlueShift2; // // Make enough space in the temporary // buffer to have one complete row of // the image with 3 bytes per pixel. // tmpBufSize = dstWidth * 3; tmpBuf = new char [tmpBufSize]; if (tmpBuf == NULL) { #ifdef PANIC *logofs << "UnpackPng: PANIC! Cannot allocate " << dstWidth * 3 << " bytes for PNG " << "decompressed data.\n" << logofs_flush; #endif delete [] tmpBuf; return -1; } int result = 1; switch (dstBpp) { case 8: { // // Simply move the data from srcData to dstData // taking into consideration the correct padding. // int row; unsigned char * dstBuff = dstData; unsigned char * srcBuff = srcData; for (row = 0; row < dstHeight; row++) { memcpy(dstBuff, srcBuff, dstWidth ); dstBuff += RoundUp4(dstWidth); srcBuff += dstWidth; } } case 16: { result = DecompressPng16(srcData, srcSize, dstWidth, dstHeight, dstData, byteOrder); break; } case 24: { result = DecompressPng24(srcData, srcSize, dstWidth, dstHeight, dstData, byteOrder); break; } case 32: { result = DecompressPng32(srcData, srcSize, dstWidth, dstHeight, dstData, byteOrder); break; } default: { #ifdef PANIC *logofs << "UnpackPng: PANIC! Error in PNG compression. " << " Unsupported Bpp value " << dstBpp << " for the PNG compression" << ".\n" << logofs_flush; #endif delete [] tmpBuf; result = -1; } } if (result == -1) { delete [] tmpBuf; return result; } // // Apply the correction for the brightness // int maskMethod; switch (method) { case PACK_PNG_8_COLORS: { maskMethod = MASK_8_COLORS; break; } case PACK_PNG_64_COLORS: { maskMethod = MASK_64_COLORS; break; } case PACK_PNG_256_COLORS: { maskMethod = MASK_256_COLORS; break; } case PACK_PNG_512_COLORS: { maskMethod = MASK_512_COLORS; break; } case PACK_PNG_4K_COLORS: { maskMethod = MASK_4K_COLORS; break; } case PACK_PNG_32K_COLORS: { maskMethod = MASK_32K_COLORS; break; } case PACK_PNG_64K_COLORS: { maskMethod = MASK_64K_COLORS; break; } case PACK_PNG_256K_COLORS: { maskMethod = MASK_256K_COLORS; break; } case PACK_PNG_2M_COLORS: { maskMethod = MASK_2M_COLORS; break; } case PACK_PNG_16M_COLORS: { maskMethod = MASK_16M_COLORS; break; } default: { #ifdef PANIC *logofs << "DecompressPng16: PANIC! " << " No matching decompression method.\n" << logofs_flush; #endif delete [] tmpBuf; return -1; } } const T_colormask *colorMask = MethodColorMask(maskMethod); unsigned char *dstBuff = dstData; switch (dstBpp) { case 16: { Unpack16To16(colorMask, dstBuff, dstBuff, dstBuff + dstSize); break; } case 24: { break; } case 32: { Unpack32To32(colorMask, (unsigned int *)dstBuff, (unsigned int *)dstBuff, (unsigned int *)(dstBuff + dstSize)); break; } default: { #ifdef PANIC *logofs << "DecompressPng16: PANIC! " << " No matching destination bits per plane.\n" << logofs_flush; #endif delete [] tmpBuf; return -1; } } delete [] tmpBuf; return 1; } // // Functions that actually do // the PNG decompression. // int DecompressPng16(unsigned char *compressedData, int compressedLen, unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder) { unsigned char *data; unsigned int dx, dy; png_structp pngPtr; png_infop infoPtr; png_bytep rowPointers; streamPos = 0; pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!pngPtr) { #ifdef PANIC *logofs << "DecompressPng16: PANIC! " << " Failed png_create_read_struct operation" << ".\n" << logofs_flush; #endif return -1; } infoPtr = png_create_info_struct(pngPtr); if (!infoPtr) { #ifdef PANIC *logofs << "DecompressPng16: PANIC! " << "Failed png_create_info_struct operation" << ".\n" << logofs_flush; #endif png_destroy_read_struct(&pngPtr, NULL, NULL); return -1; } if (setjmp(png_jmpbuf(pngPtr))) { #ifdef PANIC *logofs << "DecompressPng16: PANIC! " << "Error during IO initialization" << ".\n" << logofs_flush; #endif png_destroy_read_struct(&pngPtr, &infoPtr, NULL); return -1; } png_set_read_fn(pngPtr, (void *)compressedData, PngReadData); if (setjmp(png_jmpbuf(pngPtr))) { #ifdef PANIC *logofs << "DecompressPng16: PANIC! " << "Error during read of PNG header" << ".\n" << logofs_flush; #endif png_destroy_read_struct(&pngPtr, &infoPtr, NULL); return -1; } png_read_info(pngPtr, infoPtr); if (png_get_color_type(pngPtr, infoPtr) == PNG_COLOR_TYPE_PALETTE) { png_set_expand(pngPtr); } // // data points to dstBuf which is // already padded correctly for the final // image to put // data = dstBuf; rowPointers = (png_byte *) tmpBuf; // // We use setjmp() to save our context. // The PNG library will call longjmp() // in case of error. // if (setjmp(png_jmpbuf(pngPtr))) { #ifdef PANIC *logofs << "DecompressPng16: PANIC! " << "Error during read of PNG rows" << ".\n" << logofs_flush; #endif png_destroy_read_struct(&pngPtr, &infoPtr, NULL); return -1; } unsigned long pixel; for (dy = 0; dy < h; dy++) { png_read_row(pngPtr, rowPointers, NULL); for (dx = 0; dx < w; dx++) { pixel = RGB24_TO_PIXEL(16, tmpBuf[dx*3], tmpBuf[dx*3+1], tmpBuf[dx*3+2]); // // Follow the server byte order when arranging data. // if (byteOrder == LSBFirst) { data[0] = (unsigned char) (pixel & 0xff); data[1] = (unsigned char) ((pixel >> 8) & 0xff); } else { data[1] = (unsigned char) (pixel & 0xff); data[0] = (unsigned char) ((pixel >> 8) & 0xff); } data += 2; } // // Move pixelPtr at the beginning of the // next line. // data = data + (RoundUp4(w * 2) - w * 2); } png_destroy_read_struct(&pngPtr, &infoPtr,NULL); #ifdef DEBUG *logofs << "DecompressPng16: Decompression finished." << dy << " lines handled.\n" << logofs_flush; #endif return 1; } int DecompressPng24(unsigned char *compressedData, int compressedLen, unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder) { static CARD8 *pixelPtr = NULL; unsigned int dx, dy; png_structp pngPtr; png_infop infoPtr; png_bytep rowPointers; streamPos = 0; pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!pngPtr) { #ifdef PANIC *logofs << "DecompressPng24: PANIC! " << "Failed png_create_read_struct operation" << ".\n" << logofs_flush; #endif return -1; } infoPtr = png_create_info_struct(pngPtr); if (!infoPtr) { #ifdef PANIC *logofs << "DecompressPng24: PANIC! " << "Failed png_create_info_struct operation" << ".\n" << logofs_flush; #endif png_destroy_read_struct(&pngPtr, NULL, NULL); return -1; } if (setjmp(png_jmpbuf(pngPtr))) { #ifdef PANIC *logofs << "DecompressPng24: PANIC! " << "Error during IO initialization" << ".\n" << logofs_flush; #endif png_destroy_read_struct(&pngPtr, &infoPtr, NULL); return -1; } png_set_read_fn(pngPtr, (void *)compressedData, PngReadData); if (setjmp(png_jmpbuf(pngPtr))) { #ifdef PANIC *logofs << "DecompressPng24: PANIC! " << "Error during read of PNG header" << ".\n" << logofs_flush; #endif png_destroy_read_struct(&pngPtr, &infoPtr, NULL); return -1; } png_read_info( pngPtr, infoPtr ) ; if (png_get_color_type(pngPtr, infoPtr) == PNG_COLOR_TYPE_PALETTE) { png_set_expand(pngPtr); } // // PixelPtr points to dstBuf which is // already padded correctly for the final // image to put // pixelPtr = (CARD8 *) dstBuf; rowPointers = (png_byte *)tmpBuf; if (setjmp(png_jmpbuf(pngPtr))) { #ifdef PANIC *logofs << "DecompressPng24: PANIC! " << "Error during read of PNG rows" << ".\n" << logofs_flush; #endif png_destroy_read_struct(&pngPtr, &infoPtr, NULL); return -1; } for (dy = 0; dy < h; dy++) { png_read_row(pngPtr, rowPointers, NULL); for (dx = 0; dx < w; dx++) { // // Follow the server byte order when arranging data. // if (byteOrder == LSBFirst) { pixelPtr[0] = tmpBuf[dx * 3]; pixelPtr[1] = tmpBuf[dx * 3 + 1]; pixelPtr[2] = tmpBuf[dx * 3 + 2]; } else { pixelPtr[2] = tmpBuf[dx * 3]; pixelPtr[1] = tmpBuf[dx * 3 + 1]; pixelPtr[0] = tmpBuf[dx * 3 + 2]; } pixelPtr += 3; } // // Go to the next line. // pixelPtr = (CARD8 *) (((char *) pixelPtr) + (RoundUp4(w * 3) - w * 3)); } png_destroy_read_struct(&pngPtr, &infoPtr,NULL); #ifdef DEBUG *logofs << "DecompressPng24: Decompression finished." << dy << " lines handled.\n" << logofs_flush; #endif return 1; } int DecompressPng32(unsigned char *compressedData, int compressedLen, unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder) { unsigned char *data; unsigned int dx, dy; png_structp pngPtr; png_infop infoPtr; png_bytep rowPointers; streamPos = 0; pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!pngPtr) { #ifdef PANIC *logofs << "DecompressPng32: PANIC! " << "Failed png_create_read_struct operation" << ".\n" << logofs_flush; #endif return -1; } infoPtr = png_create_info_struct(pngPtr); if (!infoPtr) { #ifdef PANIC *logofs << "DecompressPng32: PANIC! " << "Failed png_create_info_struct operation." << ".\n" << logofs_flush; #endif png_destroy_read_struct(&pngPtr, NULL, NULL); return -1; } if (setjmp(png_jmpbuf(pngPtr))) { #ifdef PANIC *logofs << "DecompressPng32: PANIC! " << "Error during IO initialization" << ".\n" << logofs_flush; #endif png_destroy_read_struct(&pngPtr, &infoPtr, NULL); return -1; } png_set_read_fn(pngPtr, (void *)compressedData, PngReadData); if (setjmp(png_jmpbuf(pngPtr))) { #ifdef PANIC *logofs << "DecompressPng32: PANIC! " << "Error during read of PNG header" << ".\n" << logofs_flush; #endif png_destroy_read_struct(&pngPtr, &infoPtr, NULL); return -1; } png_read_info(pngPtr, infoPtr) ; if (png_get_color_type(pngPtr, infoPtr) == PNG_COLOR_TYPE_PALETTE) { png_set_expand(pngPtr); } // // data points to dstBuf which is // already padded correctly for the final // image to put // data = dstBuf; rowPointers = (png_byte *) tmpBuf; if (setjmp(png_jmpbuf(pngPtr))) { #ifdef PANIC *logofs << "DecompressPng32: PANIC! " << "Error during read of PNG rows" << ".\n" << logofs_flush; #endif png_destroy_read_struct(&pngPtr, &infoPtr, NULL); return -1; } unsigned long pixel; int i; for (dy = 0; dy < h; dy++) { png_read_row(pngPtr, rowPointers, NULL); for (dx = 0; dx < w; dx++) { pixel = RGB24_TO_PIXEL(32, tmpBuf[dx * 3], tmpBuf[dx * 3 + 1], tmpBuf[dx * 3 + 2]); // // Follow the server byte order when arranging data. // if (byteOrder == LSBFirst) { for (i = 0; i < 4; i++) { data[i] = (unsigned char)(pixel & 0xff); pixel >>= 8; } } else { for (i = 3; i >= 0; i--) { data[i] = (unsigned char) (pixel & 0xff); pixel >>= 8; } } data += 4; } } png_destroy_read_struct(&pngPtr, &infoPtr,NULL); #ifdef DEBUG *logofs << "DecompressPng32: Decompression finished." << dy << " lines handled.\n" << logofs_flush; #endif return 1; } static void PngReadData(png_structp png_ptr, png_bytep data, png_size_t length) { memcpy((char *) data, (char *) png_get_io_ptr(png_ptr) + streamPos, length); streamPos += length; }