blob: 503c030f6cc8ef17deccd04f7a7aaf3fb754d0ad [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,
142 rdr::U8 *buf, int pitch, const Rect& r, const PixelFormat& pf)
143{
144 int w = r.width();
145 int h = r.height();
146 int pixelsize;
147 int dstBufPitch;
148 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;
166 if (pitch == 0) pitch = w * pf.bpp / 8;
167 dstBufPitch = pitch;
168
169#ifdef JCS_EXTENSIONS
170 // Try to have libjpeg output directly to our native format
171 if (pf.is888()) {
172 int redShift, greenShift, blueShift;
173
174 if(pf.bigEndian) {
175 redShift = 24 - pf.redShift;
176 greenShift = 24 - pf.greenShift;
177 blueShift = 24 - pf.blueShift;
178 } else {
179 redShift = pf.redShift;
180 greenShift = pf.greenShift;
181 blueShift = pf.blueShift;
182 }
183
184 // libjpeg can only handle some "standard" formats
185 if(redShift == 0 && greenShift == 8 && blueShift == 16)
186 dinfo->out_color_space = JCS_EXT_RGBX;
187 if(redShift == 16 && greenShift == 8 && blueShift == 0)
188 dinfo->out_color_space = JCS_EXT_BGRX;
189 if(redShift == 24 && greenShift == 16 && blueShift == 8)
190 dinfo->out_color_space = JCS_EXT_XBGR;
191 if(redShift == 8 && greenShift == 16 && blueShift == 24)
192 dinfo->out_color_space = JCS_EXT_XRGB;
193
194 if (dinfo->out_color_space != JCS_RGB) {
195 dstBuf = (rdr::U8 *)buf;
196 pixelsize = 4;
197 }
198 }
199#endif
200
201 if (dinfo->out_color_space == JCS_RGB) {
202 dstBuf = new rdr::U8[w * h * pixelsize];
203 dstBufIsTemp = true;
204 dstBufPitch = w * pixelsize;
205 }
206
207 rowPointer = new JSAMPROW[h];
208 for (int dy = 0; dy < h; dy++)
209 rowPointer[dy] = (JSAMPROW)(&dstBuf[dy * dstBufPitch]);
210
211 jpeg_start_decompress(dinfo);
212
213 if (dinfo->output_width != (unsigned)r.width()
214 || dinfo->output_height != (unsigned)r.height()
215 || dinfo->output_components != pixelsize) {
216 jpeg_abort_decompress(dinfo);
217 if (dstBufIsTemp && dstBuf) delete[] dstBuf;
218 if (rowPointer) delete[] rowPointer;
219 throw rdr::Exception("Tight Decoding: Wrong JPEG data received.\n");
220 }
221
222 while (dinfo->output_scanline < dinfo->output_height) {
223 jpeg_read_scanlines(dinfo, &rowPointer[dinfo->output_scanline],
224 dinfo->output_height - dinfo->output_scanline);
225 }
226
227 if (dinfo->out_color_space == JCS_RGB)
228 pf.bufferFromRGB((rdr::U8*)buf, dstBuf, w, pitch, h);
229
230 jpeg_finish_decompress(dinfo);
231
232 if (dstBufIsTemp) delete [] dstBuf;
233 delete[] rowPointer;
234}