483 lines
14 KiB
C
483 lines
14 KiB
C
/**************************************************************************/
|
|
/* */
|
|
/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
|
|
/* Copyright (c) 2008-2017 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
|
|
/* Copyright (c) 2014-2022 Ulrich Sibiller <uli42@gmx.de> */
|
|
/* Copyright (c) 2014-2019 Mihai Moldovan <ionic@ionic.de> */
|
|
/* Copyright (c) 2011-2022 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
|
|
/* 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <nx-X11/X.h>
|
|
#include <nx-X11/Xlib.h>
|
|
#include <nx-X11/Xmd.h>
|
|
|
|
#include <jpeglib.h>
|
|
|
|
#include "Compext.h"
|
|
|
|
#include "Mask.h"
|
|
#include "Jpeg.h"
|
|
|
|
#include "../Utils.h"
|
|
|
|
#define PANIC
|
|
#define WARNING
|
|
#undef TEST
|
|
#undef DEBUG
|
|
|
|
#define JPEG_DEST_SIZE(width, height) ((width) * 3 * (height) + 1024)
|
|
|
|
/*
|
|
* Local function prototypes.
|
|
*/
|
|
|
|
static void PrepareRowForJpeg(CARD8 *dst, int y, int count);
|
|
static void PrepareRowForJpeg24(CARD8 *dst, int y, int count);
|
|
static void PrepareRowForJpeg16(CARD8 *dst, int y, int count);
|
|
static void PrepareRowForJpeg32(CARD8 *dst, int y, int count);
|
|
|
|
static int JpegEmptyOutputBuffer(j_compress_ptr cinfo);
|
|
|
|
static void JpegInitDestination(j_compress_ptr cinfo);
|
|
static void JpegTermDestination(j_compress_ptr cinfo);
|
|
static void JpegSetDstManager(j_compress_ptr cinfo);
|
|
|
|
/*
|
|
* Quality levels.
|
|
*/
|
|
|
|
static int jpegQuality[10] = {20, 30, 40, 50, 55, 60, 65, 70, 75, 80};
|
|
|
|
/*
|
|
* Image characteristics.
|
|
*/
|
|
|
|
static int bytesPerLine;
|
|
|
|
static CARD8 bitsPerPixel;
|
|
static CARD16 redMax, greenMax, blueMax;
|
|
static CARD8 redShift, greenShift, blueShift;
|
|
static int byteOrder;
|
|
|
|
/*
|
|
* Other variables used for the Jpeg
|
|
* encoding.
|
|
*/
|
|
|
|
static char *jpegBeforeBuf = NULL;
|
|
static char *jpegCompBuf;
|
|
static int jpegCompBufSize;
|
|
static int jpegError;
|
|
static int jpegDstDataLen;
|
|
|
|
static struct jpeg_destination_mgr jpegDstManager;
|
|
|
|
/*
|
|
* Just for debugging purpose.
|
|
*/
|
|
|
|
#ifdef DEBUG
|
|
|
|
static int jpegId;
|
|
static char jpegName[10];
|
|
static FILE *jpegFile;
|
|
|
|
#endif
|
|
|
|
/*
|
|
* Function declarations
|
|
*/
|
|
|
|
char *JpegCompressData(XImage *image, int level, int *compressed_size)
|
|
{
|
|
struct jpeg_compress_struct cinfo;
|
|
struct jpeg_error_mgr jerr;
|
|
|
|
CARD8 *srcBuf;
|
|
JSAMPROW rowPointer[1];
|
|
|
|
int dy, w, h;
|
|
|
|
*compressed_size = 0;
|
|
|
|
/*
|
|
* Initialize the image stuff
|
|
*/
|
|
|
|
bitsPerPixel = image -> bits_per_pixel;
|
|
bytesPerLine = image -> bytes_per_line;
|
|
byteOrder = image -> byte_order;
|
|
|
|
#ifdef TEST
|
|
fprintf(stderr, "******JpegCompressData: Image byte order [%d] bitmap bit order [%d].\n",
|
|
image -> byte_order, image -> bitmap_bit_order);
|
|
|
|
fprintf(stderr, "******JpegCompressData: Bits per pixel [%d] bytes per line [%d].\n",
|
|
bitsPerPixel, bytesPerLine);
|
|
#endif
|
|
|
|
redShift = FindLSB(image -> red_mask) - 1;
|
|
greenShift = FindLSB(image -> green_mask) - 1;
|
|
blueShift = FindLSB(image -> blue_mask) - 1;
|
|
|
|
#ifdef TEST
|
|
fprintf(stderr, "******JpegCompressData: Red mask [0x%lx] green mask [0x%lx] blue mask [0x%lx].\n",
|
|
image -> red_mask, image -> green_mask, image -> blue_mask);
|
|
|
|
fprintf(stderr, "******JpegCompressData: Red shift [%d] green shift [%d] blue shift [%d].\n",
|
|
redShift, greenShift, blueShift);
|
|
#endif
|
|
|
|
redMax = image -> red_mask >> redShift;
|
|
greenMax = image -> green_mask >> greenShift;
|
|
blueMax = image -> blue_mask >> blueShift;
|
|
|
|
#ifdef TEST
|
|
fprintf(stderr, "******JpegCompressData: Red max [0x%x] green max [0x%x] blue max [0x%x].\n",
|
|
redMax, greenMax, blueMax);
|
|
#endif
|
|
|
|
w = image -> width;
|
|
h = image -> height;
|
|
|
|
jpegBeforeBuf = image -> data;
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "******JpegCompressData: Width [%d] height [%d] level [%d].\n",
|
|
w, h, level);
|
|
#endif
|
|
|
|
if (bitsPerPixel == 1 ||
|
|
bitsPerPixel == 8)
|
|
{
|
|
#ifdef PANIC
|
|
fprintf(stderr, "******JpegCompressData: PANIC! Invalid bits per pixel [%d].\n",
|
|
bitsPerPixel);
|
|
#endif
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Allocate space for one line of the
|
|
* resulting image, 3 bytes per pixel.
|
|
*/
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "******JpegCompressData: Allocating [%d] bytes for the scanline.\n",
|
|
w * 3);
|
|
#endif
|
|
|
|
srcBuf = (CARD8 *) malloc(w * 3);
|
|
|
|
if (srcBuf == NULL)
|
|
{
|
|
#ifdef PANIC
|
|
fprintf(stderr, "******JpegCompressData: PANIC! Cannot allocate [%d] bytes.\n",
|
|
w * 3);
|
|
#endif
|
|
|
|
return NULL;
|
|
}
|
|
|
|
rowPointer[0] = srcBuf;
|
|
|
|
cinfo.err = jpeg_std_error(&jerr);
|
|
|
|
jpeg_create_compress(&cinfo);
|
|
|
|
cinfo.image_width = w;
|
|
cinfo.image_height = h;
|
|
cinfo.input_components = 3;
|
|
cinfo.in_color_space = JCS_RGB;
|
|
|
|
jpeg_set_defaults(&cinfo);
|
|
jpeg_set_quality(&cinfo, jpegQuality[level], 1);
|
|
|
|
/*
|
|
* Allocate memory for the destination
|
|
* buffer.
|
|
*/
|
|
|
|
jpegCompBufSize = JPEG_DEST_SIZE(w, h);
|
|
|
|
#ifdef TEST
|
|
fprintf(stderr, "******JpegCompressData: Allocating [%d] bytes for the destination data.\n",
|
|
jpegCompBufSize);
|
|
#endif
|
|
|
|
jpegCompBuf = malloc(jpegCompBufSize);
|
|
|
|
if (jpegCompBuf == NULL)
|
|
{
|
|
#ifdef PANIC
|
|
fprintf(stderr, "******JpegCompressData: PANIC! Error allocating [%d] bytes for the Jpeg data.\n",
|
|
jpegCompBufSize);
|
|
#endif
|
|
|
|
return NULL;
|
|
}
|
|
|
|
JpegSetDstManager(&cinfo);
|
|
|
|
jpeg_start_compress(&cinfo, 1);
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "******JpegCompressedData: Initialization finished.\n");
|
|
#endif
|
|
|
|
for (dy = 0; dy < h; dy++)
|
|
{
|
|
PrepareRowForJpeg(srcBuf, dy, w);
|
|
|
|
jpeg_write_scanlines(&cinfo, rowPointer, 1);
|
|
|
|
if (jpegError != 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "******JpegCompressedData: Compression finished. Lines handled [%d,%d]. Error is [%d].\n",
|
|
dy, h, jpegError);
|
|
#endif
|
|
|
|
if (jpegError == 0)
|
|
{
|
|
jpeg_finish_compress(&cinfo);
|
|
}
|
|
|
|
jpeg_destroy_compress(&cinfo);
|
|
|
|
free((char *) srcBuf);
|
|
|
|
if (jpegError != 0)
|
|
{
|
|
#ifdef PANIC
|
|
fprintf(stderr, "******JpegCompressedData: PANIC! Compression failed. Error is [%d].\n",
|
|
jpegError);
|
|
#endif
|
|
|
|
SAFE_free(jpegCompBuf);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Check the size of the resulting data.
|
|
*/
|
|
|
|
if (jpegDstDataLen > 0)
|
|
{
|
|
/*
|
|
* Save the image on disk to help with
|
|
* the debug.
|
|
*/
|
|
|
|
#ifdef DEBUG
|
|
|
|
int i = 0;
|
|
|
|
fprintf(stderr, "******JpegCompressedData: Compressed size [%d].\n",
|
|
jpegDstDataLen);
|
|
|
|
jpegId++;
|
|
|
|
sprintf(jpegName, "jpeg%d", jpegId);
|
|
|
|
jpegFile = fopen(jpegName, "w");
|
|
|
|
for (i = 0; i < jpegDstDataLen; i++)
|
|
{
|
|
fprintf(jpegFile, "%c", *(jpegCompBuf + i));
|
|
}
|
|
|
|
fclose(jpegFile);
|
|
|
|
#endif
|
|
|
|
*compressed_size = jpegDstDataLen;
|
|
|
|
return jpegCompBuf;
|
|
}
|
|
else
|
|
{
|
|
#ifdef PANIC
|
|
fprintf(stderr, "******JpegCompressedData: PANIC! Invalid size of the compressed data [%d].\n",
|
|
jpegDstDataLen);
|
|
#endif
|
|
|
|
SAFE_free(jpegCompBuf);
|
|
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
void PrepareRowForJpeg(CARD8 *dst, int y, int count)
|
|
{
|
|
if (bitsPerPixel == 32)
|
|
{
|
|
if (redMax == 0xff &&
|
|
greenMax == 0xff &&
|
|
blueMax == 0xff)
|
|
{
|
|
PrepareRowForJpeg24(dst, y, count);
|
|
}
|
|
else
|
|
{
|
|
PrepareRowForJpeg32(dst, y, count);
|
|
}
|
|
}
|
|
else if (bitsPerPixel == 24)
|
|
{
|
|
memcpy(dst, jpegBeforeBuf + y * bytesPerLine, count * 3);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* 16 bpp assumed.
|
|
*/
|
|
|
|
PrepareRowForJpeg16(dst, y, count);
|
|
}
|
|
}
|
|
|
|
void PrepareRowForJpeg24(CARD8 *dst, int y, int count)
|
|
{
|
|
CARD8 *fbptr;
|
|
CARD32 pix;
|
|
|
|
fbptr = (CARD8 *) (jpegBeforeBuf + 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_JPEG_GET_ROW_FUNCTION(bpp) \
|
|
\
|
|
void PrepareRowForJpeg##bpp(CARD8 *dst, int y, int count) \
|
|
{ \
|
|
CARD8 *fbptr; \
|
|
CARD##bpp pix; \
|
|
int inRed, inGreen, inBlue; \
|
|
int i; \
|
|
\
|
|
fbptr = (CARD8 *) (jpegBeforeBuf + 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_JPEG_GET_ROW_FUNCTION(16)
|
|
DEFINE_JPEG_GET_ROW_FUNCTION(32)
|
|
|
|
/*
|
|
* Destination manager implementation for JPEG library.
|
|
*/
|
|
|
|
void JpegInitDestination(j_compress_ptr cinfo)
|
|
{
|
|
jpegError = 0;
|
|
|
|
jpegDstManager.next_output_byte = (JOCTET *) jpegCompBuf;
|
|
jpegDstManager.free_in_buffer = (size_t) jpegCompBufSize;
|
|
}
|
|
|
|
int JpegEmptyOutputBuffer(j_compress_ptr cinfo)
|
|
{
|
|
jpegError = 1;
|
|
|
|
jpegDstManager.next_output_byte = (JOCTET *) jpegCompBuf;
|
|
jpegDstManager.free_in_buffer = (size_t) jpegCompBufSize;
|
|
|
|
return 1;
|
|
}
|
|
|
|
void JpegTermDestination(j_compress_ptr cinfo)
|
|
{
|
|
jpegDstDataLen = jpegCompBufSize - jpegDstManager.free_in_buffer;
|
|
}
|
|
|
|
void JpegSetDstManager(j_compress_ptr cinfo)
|
|
{
|
|
jpegDstManager.init_destination = JpegInitDestination;
|
|
jpegDstManager.empty_output_buffer = JpegEmptyOutputBuffer;
|
|
jpegDstManager.term_destination = JpegTermDestination;
|
|
|
|
cinfo -> dest = &jpegDstManager;
|
|
}
|
|
|