blob: 4f94faa8e9b8da0c88f0f4afb2df1c6782914601 [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.
Pierre Ossman4d0bc6e2014-02-12 13:12:31 +01004 * Copyright 2014 Pierre Ossman for Cendio AB
DRC7c2a39c2011-11-03 18:51:00 +00005 *
6 * This is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This software is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this software; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
19 * USA.
20 */
21
22#include <rfb/JpegDecompressor.h>
23#include <rdr/Exception.h>
24#include <rfb/Rect.h>
25#include <rfb/PixelFormat.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
Pierre Ossman4d0bc6e2014-02-12 13:12:31 +010036//
37// Special formats that libjpeg can have optimised code paths for
38//
39
40static const PixelFormat pfRGBX(32, 24, false, true, 255, 255, 255, 0, 8, 16);
41static const PixelFormat pfBGRX(32, 24, false, true, 255, 255, 255, 16, 8, 0);
42static const PixelFormat pfXRGB(32, 24, false, true, 255, 255, 255, 8, 16, 24);
43static const PixelFormat pfXBGR(32, 24, false, true, 255, 255, 255, 24, 16, 8);
DRC7c2a39c2011-11-03 18:51:00 +000044
45//
klemens0536d092017-01-28 20:56:56 +010046// Error manager implementation for the JPEG library
DRC7c2a39c2011-11-03 18:51:00 +000047//
48
49struct JPEG_ERROR_MGR {
50 struct jpeg_error_mgr pub;
51 jmp_buf jmpBuffer;
52 char lastError[JMSG_LENGTH_MAX];
53};
54
55static void
56JpegErrorExit(j_common_ptr dinfo)
57{
58 JPEG_ERROR_MGR *err = (JPEG_ERROR_MGR *)dinfo->err;
59
60 (*dinfo->err->output_message)(dinfo);
61 longjmp(err->jmpBuffer, 1);
62}
63
64static void
65JpegOutputMessage(j_common_ptr dinfo)
66{
67 JPEG_ERROR_MGR *err = (JPEG_ERROR_MGR *)dinfo->err;
68
69 (*dinfo->err->format_message)(dinfo, err->lastError);
70}
71
72
73//
74// Source manager implementation for the JPEG library.
75//
76
77struct JPEG_SRC_MGR {
78 struct jpeg_source_mgr pub;
79 JpegDecompressor *instance;
80};
81
82static void
83JpegNoOp(j_decompress_ptr dinfo)
84{
85}
86
87static boolean
88JpegFillInputBuffer(j_decompress_ptr dinfo)
89{
90 ERREXIT(dinfo, JERR_BUFFER_SIZE);
91 return TRUE;
92}
93
94static void
95JpegSkipInputData(j_decompress_ptr dinfo, long num_bytes)
96{
97 JPEG_SRC_MGR *src = (JPEG_SRC_MGR *)dinfo->src;
98
99 if (num_bytes < 0 || (size_t)num_bytes > src->pub.bytes_in_buffer) {
100 ERREXIT(dinfo, JERR_BUFFER_SIZE);
101 } else {
102 src->pub.next_input_byte += (size_t) num_bytes;
103 src->pub.bytes_in_buffer -= (size_t) num_bytes;
104 }
105}
106
107JpegDecompressor::JpegDecompressor(void)
108{
109 dinfo = new jpeg_decompress_struct;
110
111 err = new struct JPEG_ERROR_MGR;
112 dinfo->err = jpeg_std_error(&err->pub);
113 snprintf(err->lastError, JMSG_LENGTH_MAX, "No error");
114 err->pub.error_exit = JpegErrorExit;
115 err->pub.output_message = JpegOutputMessage;
116
117 if(setjmp(err->jmpBuffer)) {
118 // this will execute if libjpeg has an error
Pierre Ossmana7bbe9c2015-03-03 16:17:51 +0100119 throw rdr::Exception("%s", err->lastError);
DRC7c2a39c2011-11-03 18:51:00 +0000120 }
121
122 jpeg_create_decompress(dinfo);
123
124 src = new struct JPEG_SRC_MGR;
125 src->pub.init_source = JpegNoOp;
126 src->pub.fill_input_buffer = JpegFillInputBuffer;
127 src->pub.skip_input_data = JpegSkipInputData;
128 src->pub.resync_to_restart = jpeg_resync_to_restart;
129 src->pub.term_source = JpegNoOp;
130 src->instance = this;
131 dinfo->src = (struct jpeg_source_mgr *)src;
132}
133
134JpegDecompressor::~JpegDecompressor(void)
135{
136 if(setjmp(err->jmpBuffer)) {
137 // this will execute if libjpeg has an error
138 return;
139 }
140
141 jpeg_destroy_decompress(dinfo);
142
143 delete err;
144 delete src;
145
146 delete dinfo;
147}
148
149void JpegDecompressor::decompress(const rdr::U8 *jpegBuf, int jpegBufLen,
Pierre Ossmana10d8fe2014-01-22 11:28:05 +0100150 rdr::U8 *buf, int stride, const Rect& r, const PixelFormat& pf)
DRC7c2a39c2011-11-03 18:51:00 +0000151{
152 int w = r.width();
153 int h = r.height();
154 int pixelsize;
Pierre Ossmana10d8fe2014-01-22 11:28:05 +0100155 int dstBufStride;
DRC7c2a39c2011-11-03 18:51:00 +0000156 rdr::U8 *dstBuf = NULL;
157 bool dstBufIsTemp = false;
158 JSAMPROW *rowPointer = NULL;
159
160 if(setjmp(err->jmpBuffer)) {
161 // this will execute if libjpeg has an error
162 jpeg_abort_decompress(dinfo);
163 if (dstBufIsTemp && dstBuf) delete[] dstBuf;
164 if (rowPointer) delete[] rowPointer;
Pierre Ossmana7bbe9c2015-03-03 16:17:51 +0100165 throw rdr::Exception("%s", err->lastError);
DRC7c2a39c2011-11-03 18:51:00 +0000166 }
167
168 src->pub.next_input_byte = jpegBuf;
169 src->pub.bytes_in_buffer = jpegBufLen;
170
171 jpeg_read_header(dinfo, TRUE);
172 dinfo->out_color_space = JCS_RGB;
173 pixelsize = 3;
Pierre Ossmana10d8fe2014-01-22 11:28:05 +0100174 if (stride == 0)
175 stride = w;
176 dstBufStride = stride;
DRC7c2a39c2011-11-03 18:51:00 +0000177
178#ifdef JCS_EXTENSIONS
179 // Try to have libjpeg output directly to our native format
Pierre Ossman4d0bc6e2014-02-12 13:12:31 +0100180 // libjpeg can only handle some "standard" formats
181 if (pfRGBX.equal(pf))
182 dinfo->out_color_space = JCS_EXT_RGBX;
183 else if (pfBGRX.equal(pf))
184 dinfo->out_color_space = JCS_EXT_BGRX;
185 else if (pfXRGB.equal(pf))
186 dinfo->out_color_space = JCS_EXT_XRGB;
187 else if (pfXBGR.equal(pf))
188 dinfo->out_color_space = JCS_EXT_XBGR;
DRC7c2a39c2011-11-03 18:51:00 +0000189
Pierre Ossman4d0bc6e2014-02-12 13:12:31 +0100190 if (dinfo->out_color_space != JCS_RGB) {
191 dstBuf = (rdr::U8 *)buf;
192 pixelsize = 4;
DRC7c2a39c2011-11-03 18:51:00 +0000193 }
194#endif
195
196 if (dinfo->out_color_space == JCS_RGB) {
197 dstBuf = new rdr::U8[w * h * pixelsize];
198 dstBufIsTemp = true;
Pierre Ossmana10d8fe2014-01-22 11:28:05 +0100199 dstBufStride = w;
DRC7c2a39c2011-11-03 18:51:00 +0000200 }
201
202 rowPointer = new JSAMPROW[h];
203 for (int dy = 0; dy < h; dy++)
Pierre Ossmana10d8fe2014-01-22 11:28:05 +0100204 rowPointer[dy] = (JSAMPROW)(&dstBuf[dy * dstBufStride * pixelsize]);
DRC7c2a39c2011-11-03 18:51:00 +0000205
206 jpeg_start_decompress(dinfo);
207
208 if (dinfo->output_width != (unsigned)r.width()
209 || dinfo->output_height != (unsigned)r.height()
210 || dinfo->output_components != pixelsize) {
211 jpeg_abort_decompress(dinfo);
212 if (dstBufIsTemp && dstBuf) delete[] dstBuf;
213 if (rowPointer) delete[] rowPointer;
214 throw rdr::Exception("Tight Decoding: Wrong JPEG data received.\n");
215 }
216
217 while (dinfo->output_scanline < dinfo->output_height) {
218 jpeg_read_scanlines(dinfo, &rowPointer[dinfo->output_scanline],
219 dinfo->output_height - dinfo->output_scanline);
220 }
221
222 if (dinfo->out_color_space == JCS_RGB)
Pierre Ossmana10d8fe2014-01-22 11:28:05 +0100223 pf.bufferFromRGB((rdr::U8*)buf, dstBuf, w, stride, h);
DRC7c2a39c2011-11-03 18:51:00 +0000224
225 jpeg_finish_decompress(dinfo);
226
227 if (dstBufIsTemp) delete [] dstBuf;
228 delete[] rowPointer;
229}