blob: c1ef3c7d1f1b81c88970382ad3f2f6716849e2fa [file] [log] [blame]
DRCcd2c5d42011-08-11 11:18:34 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
2 * Copyright (C) 2011 D. R. Commander. All Rights Reserved.
3 *
4 * This is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This software is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this software; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17 * USA.
18 */
19
20#include <rfb/JpegCompressor.h>
21#include <rdr/Exception.h>
22#include <rfb/Rect.h>
23#include <rfb/PixelFormat.h>
Pierre Ossmanb948a912014-01-15 13:23:43 +010024#include <rfb/ConnParams.h>
DRC99009722011-08-17 11:24:58 +000025#include <os/print.h>
DRCcd2c5d42011-08-11 11:18:34 +000026
Pierre Ossman9144ae02011-09-07 11:35:04 +000027#include <stdio.h>
28extern "C" {
29#include <jpeglib.h>
30}
31#include <setjmp.h>
32
DRCcd2c5d42011-08-11 11:18:34 +000033using namespace rfb;
34
35//
36// Error manager implmentation for the JPEG library
37//
38
Pierre Ossman9144ae02011-09-07 11:35:04 +000039struct JPEG_ERROR_MGR {
40 struct jpeg_error_mgr pub;
41 jmp_buf jmpBuffer;
42 char lastError[JMSG_LENGTH_MAX];
43};
44
DRCcd2c5d42011-08-11 11:18:34 +000045static void
46JpegErrorExit(j_common_ptr cinfo)
47{
48 JPEG_ERROR_MGR *err = (JPEG_ERROR_MGR *)cinfo->err;
49
50 (*cinfo->err->output_message)(cinfo);
51 longjmp(err->jmpBuffer, 1);
52}
53
54static void
55JpegOutputMessage(j_common_ptr cinfo)
56{
57 JPEG_ERROR_MGR *err = (JPEG_ERROR_MGR *)cinfo->err;
58
59 (*cinfo->err->format_message)(cinfo, err->lastError);
60}
61
62//
63// Destination manager implementation for the JPEG library.
64//
65
Pierre Ossman9144ae02011-09-07 11:35:04 +000066struct JPEG_DEST_MGR {
67 struct jpeg_destination_mgr pub;
68 JpegCompressor *instance;
69};
70
DRCcd2c5d42011-08-11 11:18:34 +000071static void
72JpegInitDestination(j_compress_ptr cinfo)
73{
74 JPEG_DEST_MGR *dest = (JPEG_DEST_MGR *)cinfo->dest;
75 JpegCompressor *jc = dest->instance;
76
77 jc->clear();
78 dest->pub.next_output_byte = jc->getptr();
79 dest->pub.free_in_buffer = jc->getend() - jc->getptr();
80}
81
82static boolean
83JpegEmptyOutputBuffer(j_compress_ptr cinfo)
84{
85 JPEG_DEST_MGR *dest = (JPEG_DEST_MGR *)cinfo->dest;
86 JpegCompressor *jc = dest->instance;
87
88 jc->setptr(dest->pub.next_output_byte);
89 jc->overrun(jc->getend() - jc->getstart(), 1);
90 dest->pub.next_output_byte = jc->getptr();
91 dest->pub.free_in_buffer = jc->getend() - jc->getptr();
92
93 return TRUE;
94}
95
96static void
97JpegTermDestination(j_compress_ptr cinfo)
98{
99 JPEG_DEST_MGR *dest = (JPEG_DEST_MGR *)cinfo->dest;
100 JpegCompressor *jc = dest->instance;
101
102 jc->setptr(dest->pub.next_output_byte);
103}
104
105JpegCompressor::JpegCompressor(int bufferLen) : MemOutStream(bufferLen)
106{
Pierre Ossman9144ae02011-09-07 11:35:04 +0000107 cinfo = new jpeg_compress_struct;
DRCcd2c5d42011-08-11 11:18:34 +0000108
Pierre Ossman9144ae02011-09-07 11:35:04 +0000109 err = new struct JPEG_ERROR_MGR;
110 cinfo->err = jpeg_std_error(&err->pub);
111 snprintf(err->lastError, JMSG_LENGTH_MAX, "No error");
112 err->pub.error_exit = JpegErrorExit;
113 err->pub.output_message = JpegOutputMessage;
114
115 if(setjmp(err->jmpBuffer)) {
DRCcd2c5d42011-08-11 11:18:34 +0000116 // this will execute if libjpeg has an error
Pierre Ossman9144ae02011-09-07 11:35:04 +0000117 throw rdr::Exception(err->lastError);
DRCcd2c5d42011-08-11 11:18:34 +0000118 }
119
Pierre Ossman9144ae02011-09-07 11:35:04 +0000120 jpeg_create_compress(cinfo);
DRCcd2c5d42011-08-11 11:18:34 +0000121
Pierre Ossman9144ae02011-09-07 11:35:04 +0000122 dest = new struct JPEG_DEST_MGR;
123 dest->pub.init_destination = JpegInitDestination;
124 dest->pub.empty_output_buffer = JpegEmptyOutputBuffer;
125 dest->pub.term_destination = JpegTermDestination;
126 dest->instance = this;
127 cinfo->dest = (struct jpeg_destination_mgr *)dest;
DRCcd2c5d42011-08-11 11:18:34 +0000128}
129
130JpegCompressor::~JpegCompressor(void)
131{
Pierre Ossman9144ae02011-09-07 11:35:04 +0000132 if(setjmp(err->jmpBuffer)) {
DRCcd2c5d42011-08-11 11:18:34 +0000133 // this will execute if libjpeg has an error
134 return;
135 }
136
Pierre Ossman9144ae02011-09-07 11:35:04 +0000137 jpeg_destroy_compress(cinfo);
138
139 delete err;
140 delete dest;
141
142 delete cinfo;
DRCcd2c5d42011-08-11 11:18:34 +0000143}
144
Pierre Ossmana10d8fe2014-01-22 11:28:05 +0100145void JpegCompressor::compress(const rdr::U8 *buf, int stride, const Rect& r,
Pierre Ossmanb948a912014-01-15 13:23:43 +0100146 const PixelFormat& pf, int quality, int subsamp)
DRCcd2c5d42011-08-11 11:18:34 +0000147{
148 int w = r.width();
149 int h = r.height();
150 int pixelsize;
151 rdr::U8 *srcBuf = NULL;
152 bool srcBufIsTemp = false;
153 JSAMPROW *rowPointer = NULL;
154
Pierre Ossman9144ae02011-09-07 11:35:04 +0000155 if(setjmp(err->jmpBuffer)) {
DRCcd2c5d42011-08-11 11:18:34 +0000156 // this will execute if libjpeg has an error
Pierre Ossman9144ae02011-09-07 11:35:04 +0000157 jpeg_abort_compress(cinfo);
DRCcd2c5d42011-08-11 11:18:34 +0000158 if (srcBufIsTemp && srcBuf) delete[] srcBuf;
159 if (rowPointer) delete[] rowPointer;
Pierre Ossman9144ae02011-09-07 11:35:04 +0000160 throw rdr::Exception(err->lastError);
DRCcd2c5d42011-08-11 11:18:34 +0000161 }
162
Pierre Ossman9144ae02011-09-07 11:35:04 +0000163 cinfo->image_width = w;
164 cinfo->image_height = h;
165 cinfo->in_color_space = JCS_RGB;
DRCcd2c5d42011-08-11 11:18:34 +0000166 pixelsize = 3;
167
168#ifdef JCS_EXTENSIONS
169 // Try to have libjpeg read directly from our native format
170 if(pf.is888()) {
171 int redShift, greenShift, blueShift;
172
173 if(pf.bigEndian) {
174 redShift = 24 - pf.redShift;
175 greenShift = 24 - pf.greenShift;
176 blueShift = 24 - pf.blueShift;
177 } else {
178 redShift = pf.redShift;
179 greenShift = pf.greenShift;
180 blueShift = pf.blueShift;
181 }
182
183 if(redShift == 0 && greenShift == 8 && blueShift == 16)
Pierre Ossman9144ae02011-09-07 11:35:04 +0000184 cinfo->in_color_space = JCS_EXT_RGBX;
DRCcd2c5d42011-08-11 11:18:34 +0000185 if(redShift == 16 && greenShift == 8 && blueShift == 0)
Pierre Ossman9144ae02011-09-07 11:35:04 +0000186 cinfo->in_color_space = JCS_EXT_BGRX;
DRCcd2c5d42011-08-11 11:18:34 +0000187 if(redShift == 24 && greenShift == 16 && blueShift == 8)
Pierre Ossman9144ae02011-09-07 11:35:04 +0000188 cinfo->in_color_space = JCS_EXT_XBGR;
DRCcd2c5d42011-08-11 11:18:34 +0000189 if(redShift == 8 && greenShift == 16 && blueShift == 24)
Pierre Ossman9144ae02011-09-07 11:35:04 +0000190 cinfo->in_color_space = JCS_EXT_XRGB;
DRCcd2c5d42011-08-11 11:18:34 +0000191
Pierre Ossman9144ae02011-09-07 11:35:04 +0000192 if (cinfo->in_color_space != JCS_RGB) {
DRCcd2c5d42011-08-11 11:18:34 +0000193 srcBuf = (rdr::U8 *)buf;
194 pixelsize = 4;
195 }
196 }
197#endif
198
Pierre Ossmana10d8fe2014-01-22 11:28:05 +0100199 if (stride == 0)
200 stride = w;
DRCffe09d62011-08-17 02:27:59 +0000201
Pierre Ossman9144ae02011-09-07 11:35:04 +0000202 if (cinfo->in_color_space == JCS_RGB) {
DRCcd2c5d42011-08-11 11:18:34 +0000203 srcBuf = new rdr::U8[w * h * pixelsize];
204 srcBufIsTemp = true;
Pierre Ossmana10d8fe2014-01-22 11:28:05 +0100205 pf.rgbFromBuffer(srcBuf, (const rdr::U8 *)buf, w, stride, h);
206 stride = w;
DRCcd2c5d42011-08-11 11:18:34 +0000207 }
208
Pierre Ossman9144ae02011-09-07 11:35:04 +0000209 cinfo->input_components = pixelsize;
DRCcd2c5d42011-08-11 11:18:34 +0000210
Pierre Ossman9144ae02011-09-07 11:35:04 +0000211 jpeg_set_defaults(cinfo);
Pierre Ossmanca519532014-03-21 12:38:59 +0100212
213 if (quality >= 1 && quality <= 100) {
214 jpeg_set_quality(cinfo, quality, TRUE);
215 if (quality >= 96)
216 cinfo->dct_method = JDCT_ISLOW;
217 else
218 cinfo->dct_method = JDCT_FASTEST;
219 }
DRCcd2c5d42011-08-11 11:18:34 +0000220
221 switch (subsamp) {
Pierre Ossmanb948a912014-01-15 13:23:43 +0100222 case subsample16X:
223 case subsample8X:
224 // FIXME (fall through)
225 case subsample4X:
Pierre Ossman9144ae02011-09-07 11:35:04 +0000226 cinfo->comp_info[0].h_samp_factor = 2;
227 cinfo->comp_info[0].v_samp_factor = 2;
DRCcd2c5d42011-08-11 11:18:34 +0000228 break;
Pierre Ossmanb948a912014-01-15 13:23:43 +0100229 case subsample2X:
Pierre Ossman9144ae02011-09-07 11:35:04 +0000230 cinfo->comp_info[0].h_samp_factor = 2;
231 cinfo->comp_info[0].v_samp_factor = 1;
DRCcd2c5d42011-08-11 11:18:34 +0000232 break;
Pierre Ossmanb948a912014-01-15 13:23:43 +0100233 case subsampleGray:
Pierre Ossman9144ae02011-09-07 11:35:04 +0000234 jpeg_set_colorspace(cinfo, JCS_GRAYSCALE);
DRCcd2c5d42011-08-11 11:18:34 +0000235 default:
Pierre Ossman9144ae02011-09-07 11:35:04 +0000236 cinfo->comp_info[0].h_samp_factor = 1;
237 cinfo->comp_info[0].v_samp_factor = 1;
DRCcd2c5d42011-08-11 11:18:34 +0000238 }
239
240 rowPointer = new JSAMPROW[h];
241 for (int dy = 0; dy < h; dy++)
Pierre Ossmana10d8fe2014-01-22 11:28:05 +0100242 rowPointer[dy] = (JSAMPROW)(&srcBuf[dy * stride * pixelsize]);
DRCcd2c5d42011-08-11 11:18:34 +0000243
Pierre Ossman9144ae02011-09-07 11:35:04 +0000244 jpeg_start_compress(cinfo, TRUE);
245 while (cinfo->next_scanline < cinfo->image_height)
246 jpeg_write_scanlines(cinfo, &rowPointer[cinfo->next_scanline],
247 cinfo->image_height - cinfo->next_scanline);
DRCcd2c5d42011-08-11 11:18:34 +0000248
Pierre Ossman9144ae02011-09-07 11:35:04 +0000249 jpeg_finish_compress(cinfo);
DRCcd2c5d42011-08-11 11:18:34 +0000250
251 if (srcBufIsTemp) delete[] srcBuf;
252 delete[] rowPointer;
253}
254
255void JpegCompressor::writeBytes(const void* data, int length)
256{
257 throw rdr::Exception("writeBytes() is not valid with a JpegCompressor instance. Use compress() instead.");
258}