blob: 4d230eb865c8b6f86508ff374cc046896e5a9586 [file] [log] [blame]
DRC7c2a39c2011-11-03 18:51:00 +00001/* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved.
2 * Copyright (C) 2004-2005 Cendio AB. All rights reserved.
3 * Copyright (C) 2011 D. R. Commander. All Rights Reserved.
4 *
5 * This is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This software is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this software; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
18 * USA.
19 */
20
21#include <rfb/JpegDecompressor.h>
22#include <rdr/Exception.h>
23#include <rfb/Rect.h>
24#include <rfb/PixelFormat.h>
DRC374b4d52011-11-03 19:26:14 +000025#include <os/print.h>
DRC7c2a39c2011-11-03 18:51:00 +000026
27#include <stdio.h>
28extern "C" {
29#include <jpeglib.h>
30}
31#include <jerror.h>
32#include <setjmp.h>
33
34using namespace rfb;
35
36
37//
38// Error manager implmentation for the JPEG library
39//
40
41struct JPEG_ERROR_MGR {
42 struct jpeg_error_mgr pub;
43 jmp_buf jmpBuffer;
44 char lastError[JMSG_LENGTH_MAX];
45};
46
47static void
48JpegErrorExit(j_common_ptr dinfo)
49{
50 JPEG_ERROR_MGR *err = (JPEG_ERROR_MGR *)dinfo->err;
51
52 (*dinfo->err->output_message)(dinfo);
53 longjmp(err->jmpBuffer, 1);
54}
55
56static void
57JpegOutputMessage(j_common_ptr dinfo)
58{
59 JPEG_ERROR_MGR *err = (JPEG_ERROR_MGR *)dinfo->err;
60
61 (*dinfo->err->format_message)(dinfo, err->lastError);
62}
63
64
65//
66// Source manager implementation for the JPEG library.
67//
68
69struct JPEG_SRC_MGR {
70 struct jpeg_source_mgr pub;
71 JpegDecompressor *instance;
72};
73
74static void
75JpegNoOp(j_decompress_ptr dinfo)
76{
77}
78
79static boolean
80JpegFillInputBuffer(j_decompress_ptr dinfo)
81{
82 ERREXIT(dinfo, JERR_BUFFER_SIZE);
83 return TRUE;
84}
85
86static void
87JpegSkipInputData(j_decompress_ptr dinfo, long num_bytes)
88{
89 JPEG_SRC_MGR *src = (JPEG_SRC_MGR *)dinfo->src;
90
91 if (num_bytes < 0 || (size_t)num_bytes > src->pub.bytes_in_buffer) {
92 ERREXIT(dinfo, JERR_BUFFER_SIZE);
93 } else {
94 src->pub.next_input_byte += (size_t) num_bytes;
95 src->pub.bytes_in_buffer -= (size_t) num_bytes;
96 }
97}
98
99JpegDecompressor::JpegDecompressor(void)
100{
101 dinfo = new jpeg_decompress_struct;
102
103 err = new struct JPEG_ERROR_MGR;
104 dinfo->err = jpeg_std_error(&err->pub);
105 snprintf(err->lastError, JMSG_LENGTH_MAX, "No error");
106 err->pub.error_exit = JpegErrorExit;
107 err->pub.output_message = JpegOutputMessage;
108
109 if(setjmp(err->jmpBuffer)) {
110 // this will execute if libjpeg has an error
111 throw rdr::Exception(err->lastError);
112 }
113
114 jpeg_create_decompress(dinfo);
115
116 src = new struct JPEG_SRC_MGR;
117 src->pub.init_source = JpegNoOp;
118 src->pub.fill_input_buffer = JpegFillInputBuffer;
119 src->pub.skip_input_data = JpegSkipInputData;
120 src->pub.resync_to_restart = jpeg_resync_to_restart;
121 src->pub.term_source = JpegNoOp;
122 src->instance = this;
123 dinfo->src = (struct jpeg_source_mgr *)src;
124}
125
126JpegDecompressor::~JpegDecompressor(void)
127{
128 if(setjmp(err->jmpBuffer)) {
129 // this will execute if libjpeg has an error
130 return;
131 }
132
133 jpeg_destroy_decompress(dinfo);
134
135 delete err;
136 delete src;
137
138 delete dinfo;
139}
140
141void JpegDecompressor::decompress(const rdr::U8 *jpegBuf, int jpegBufLen,
Pierre Ossmana10d8fe2014-01-22 11:28:05 +0100142 rdr::U8 *buf, int stride, const Rect& r, const PixelFormat& pf)
DRC7c2a39c2011-11-03 18:51:00 +0000143{
144 int w = r.width();
145 int h = r.height();
146 int pixelsize;
Pierre Ossmana10d8fe2014-01-22 11:28:05 +0100147 int dstBufStride;
DRC7c2a39c2011-11-03 18:51:00 +0000148 rdr::U8 *dstBuf = NULL;
149 bool dstBufIsTemp = false;
150 JSAMPROW *rowPointer = NULL;
151
152 if(setjmp(err->jmpBuffer)) {
153 // this will execute if libjpeg has an error
154 jpeg_abort_decompress(dinfo);
155 if (dstBufIsTemp && dstBuf) delete[] dstBuf;
156 if (rowPointer) delete[] rowPointer;
157 throw rdr::Exception(err->lastError);
158 }
159
160 src->pub.next_input_byte = jpegBuf;
161 src->pub.bytes_in_buffer = jpegBufLen;
162
163 jpeg_read_header(dinfo, TRUE);
164 dinfo->out_color_space = JCS_RGB;
165 pixelsize = 3;
Pierre Ossmana10d8fe2014-01-22 11:28:05 +0100166 if (stride == 0)
167 stride = w;
168 dstBufStride = stride;
DRC7c2a39c2011-11-03 18:51:00 +0000169
170#ifdef JCS_EXTENSIONS
171 // Try to have libjpeg output directly to our native format
172 if (pf.is888()) {
173 int redShift, greenShift, blueShift;
174
175 if(pf.bigEndian) {
176 redShift = 24 - pf.redShift;
177 greenShift = 24 - pf.greenShift;
178 blueShift = 24 - pf.blueShift;
179 } else {
180 redShift = pf.redShift;
181 greenShift = pf.greenShift;
182 blueShift = pf.blueShift;
183 }
184
185 // libjpeg can only handle some "standard" formats
186 if(redShift == 0 && greenShift == 8 && blueShift == 16)
187 dinfo->out_color_space = JCS_EXT_RGBX;
188 if(redShift == 16 && greenShift == 8 && blueShift == 0)
189 dinfo->out_color_space = JCS_EXT_BGRX;
190 if(redShift == 24 && greenShift == 16 && blueShift == 8)
191 dinfo->out_color_space = JCS_EXT_XBGR;
192 if(redShift == 8 && greenShift == 16 && blueShift == 24)
193 dinfo->out_color_space = JCS_EXT_XRGB;
194
195 if (dinfo->out_color_space != JCS_RGB) {
196 dstBuf = (rdr::U8 *)buf;
197 pixelsize = 4;
198 }
199 }
200#endif
201
202 if (dinfo->out_color_space == JCS_RGB) {
203 dstBuf = new rdr::U8[w * h * pixelsize];
204 dstBufIsTemp = true;
Pierre Ossmana10d8fe2014-01-22 11:28:05 +0100205 dstBufStride = w;
DRC7c2a39c2011-11-03 18:51:00 +0000206 }
207
208 rowPointer = new JSAMPROW[h];
209 for (int dy = 0; dy < h; dy++)
Pierre Ossmana10d8fe2014-01-22 11:28:05 +0100210 rowPointer[dy] = (JSAMPROW)(&dstBuf[dy * dstBufStride * pixelsize]);
DRC7c2a39c2011-11-03 18:51:00 +0000211
212 jpeg_start_decompress(dinfo);
213
214 if (dinfo->output_width != (unsigned)r.width()
215 || dinfo->output_height != (unsigned)r.height()
216 || dinfo->output_components != pixelsize) {
217 jpeg_abort_decompress(dinfo);
218 if (dstBufIsTemp && dstBuf) delete[] dstBuf;
219 if (rowPointer) delete[] rowPointer;
220 throw rdr::Exception("Tight Decoding: Wrong JPEG data received.\n");
221 }
222
223 while (dinfo->output_scanline < dinfo->output_height) {
224 jpeg_read_scanlines(dinfo, &rowPointer[dinfo->output_scanline],
225 dinfo->output_height - dinfo->output_scanline);
226 }
227
228 if (dinfo->out_color_space == JCS_RGB)
Pierre Ossmana10d8fe2014-01-22 11:28:05 +0100229 pf.bufferFromRGB((rdr::U8*)buf, dstBuf, w, stride, h);
DRC7c2a39c2011-11-03 18:51:00 +0000230
231 jpeg_finish_decompress(dinfo);
232
233 if (dstBufIsTemp) delete [] dstBuf;
234 delete[] rowPointer;
235}