blob: 9cd891a5a67b0286be2693b0dc017e60253f988e [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>
25
26#include <stdio.h>
27extern "C" {
28#include <jpeglib.h>
29}
30#include <jerror.h>
31#include <setjmp.h>
32
33using namespace rfb;
34
35
36//
37// Error manager implmentation for the JPEG library
38//
39
40struct JPEG_ERROR_MGR {
41 struct jpeg_error_mgr pub;
42 jmp_buf jmpBuffer;
43 char lastError[JMSG_LENGTH_MAX];
44};
45
46static void
47JpegErrorExit(j_common_ptr dinfo)
48{
49 JPEG_ERROR_MGR *err = (JPEG_ERROR_MGR *)dinfo->err;
50
51 (*dinfo->err->output_message)(dinfo);
52 longjmp(err->jmpBuffer, 1);
53}
54
55static void
56JpegOutputMessage(j_common_ptr dinfo)
57{
58 JPEG_ERROR_MGR *err = (JPEG_ERROR_MGR *)dinfo->err;
59
60 (*dinfo->err->format_message)(dinfo, err->lastError);
61}
62
63
64//
65// Source manager implementation for the JPEG library.
66//
67
68struct JPEG_SRC_MGR {
69 struct jpeg_source_mgr pub;
70 JpegDecompressor *instance;
71};
72
73static void
74JpegNoOp(j_decompress_ptr dinfo)
75{
76}
77
78static boolean
79JpegFillInputBuffer(j_decompress_ptr dinfo)
80{
81 ERREXIT(dinfo, JERR_BUFFER_SIZE);
82 return TRUE;
83}
84
85static void
86JpegSkipInputData(j_decompress_ptr dinfo, long num_bytes)
87{
88 JPEG_SRC_MGR *src = (JPEG_SRC_MGR *)dinfo->src;
89
90 if (num_bytes < 0 || (size_t)num_bytes > src->pub.bytes_in_buffer) {
91 ERREXIT(dinfo, JERR_BUFFER_SIZE);
92 } else {
93 src->pub.next_input_byte += (size_t) num_bytes;
94 src->pub.bytes_in_buffer -= (size_t) num_bytes;
95 }
96}
97
98JpegDecompressor::JpegDecompressor(void)
99{
100 dinfo = new jpeg_decompress_struct;
101
102 err = new struct JPEG_ERROR_MGR;
103 dinfo->err = jpeg_std_error(&err->pub);
104 snprintf(err->lastError, JMSG_LENGTH_MAX, "No error");
105 err->pub.error_exit = JpegErrorExit;
106 err->pub.output_message = JpegOutputMessage;
107
108 if(setjmp(err->jmpBuffer)) {
109 // this will execute if libjpeg has an error
110 throw rdr::Exception(err->lastError);
111 }
112
113 jpeg_create_decompress(dinfo);
114
115 src = new struct JPEG_SRC_MGR;
116 src->pub.init_source = JpegNoOp;
117 src->pub.fill_input_buffer = JpegFillInputBuffer;
118 src->pub.skip_input_data = JpegSkipInputData;
119 src->pub.resync_to_restart = jpeg_resync_to_restart;
120 src->pub.term_source = JpegNoOp;
121 src->instance = this;
122 dinfo->src = (struct jpeg_source_mgr *)src;
123}
124
125JpegDecompressor::~JpegDecompressor(void)
126{
127 if(setjmp(err->jmpBuffer)) {
128 // this will execute if libjpeg has an error
129 return;
130 }
131
132 jpeg_destroy_decompress(dinfo);
133
134 delete err;
135 delete src;
136
137 delete dinfo;
138}
139
140void JpegDecompressor::decompress(const rdr::U8 *jpegBuf, int jpegBufLen,
141 rdr::U8 *buf, int pitch, const Rect& r, const PixelFormat& pf)
142{
143 int w = r.width();
144 int h = r.height();
145 int pixelsize;
146 int dstBufPitch;
147 rdr::U8 *dstBuf = NULL;
148 bool dstBufIsTemp = false;
149 JSAMPROW *rowPointer = NULL;
150
151 if(setjmp(err->jmpBuffer)) {
152 // this will execute if libjpeg has an error
153 jpeg_abort_decompress(dinfo);
154 if (dstBufIsTemp && dstBuf) delete[] dstBuf;
155 if (rowPointer) delete[] rowPointer;
156 throw rdr::Exception(err->lastError);
157 }
158
159 src->pub.next_input_byte = jpegBuf;
160 src->pub.bytes_in_buffer = jpegBufLen;
161
162 jpeg_read_header(dinfo, TRUE);
163 dinfo->out_color_space = JCS_RGB;
164 pixelsize = 3;
165 if (pitch == 0) pitch = w * pf.bpp / 8;
166 dstBufPitch = pitch;
167
168#ifdef JCS_EXTENSIONS
169 // Try to have libjpeg output directly to 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 // libjpeg can only handle some "standard" formats
184 if(redShift == 0 && greenShift == 8 && blueShift == 16)
185 dinfo->out_color_space = JCS_EXT_RGBX;
186 if(redShift == 16 && greenShift == 8 && blueShift == 0)
187 dinfo->out_color_space = JCS_EXT_BGRX;
188 if(redShift == 24 && greenShift == 16 && blueShift == 8)
189 dinfo->out_color_space = JCS_EXT_XBGR;
190 if(redShift == 8 && greenShift == 16 && blueShift == 24)
191 dinfo->out_color_space = JCS_EXT_XRGB;
192
193 if (dinfo->out_color_space != JCS_RGB) {
194 dstBuf = (rdr::U8 *)buf;
195 pixelsize = 4;
196 }
197 }
198#endif
199
200 if (dinfo->out_color_space == JCS_RGB) {
201 dstBuf = new rdr::U8[w * h * pixelsize];
202 dstBufIsTemp = true;
203 dstBufPitch = w * pixelsize;
204 }
205
206 rowPointer = new JSAMPROW[h];
207 for (int dy = 0; dy < h; dy++)
208 rowPointer[dy] = (JSAMPROW)(&dstBuf[dy * dstBufPitch]);
209
210 jpeg_start_decompress(dinfo);
211
212 if (dinfo->output_width != (unsigned)r.width()
213 || dinfo->output_height != (unsigned)r.height()
214 || dinfo->output_components != pixelsize) {
215 jpeg_abort_decompress(dinfo);
216 if (dstBufIsTemp && dstBuf) delete[] dstBuf;
217 if (rowPointer) delete[] rowPointer;
218 throw rdr::Exception("Tight Decoding: Wrong JPEG data received.\n");
219 }
220
221 while (dinfo->output_scanline < dinfo->output_height) {
222 jpeg_read_scanlines(dinfo, &rowPointer[dinfo->output_scanline],
223 dinfo->output_height - dinfo->output_scanline);
224 }
225
226 if (dinfo->out_color_space == JCS_RGB)
227 pf.bufferFromRGB((rdr::U8*)buf, dstBuf, w, pitch, h);
228
229 jpeg_finish_decompress(dinfo);
230
231 if (dstBufIsTemp) delete [] dstBuf;
232 delete[] rowPointer;
233}