blob: a55fee7ed735081dfc21225737de0431df25a13e [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>
24
25using namespace rfb;
26
27//
28// Error manager implmentation for the JPEG library
29//
30
31static void
32JpegErrorExit(j_common_ptr cinfo)
33{
34 JPEG_ERROR_MGR *err = (JPEG_ERROR_MGR *)cinfo->err;
35
36 (*cinfo->err->output_message)(cinfo);
37 longjmp(err->jmpBuffer, 1);
38}
39
40static void
41JpegOutputMessage(j_common_ptr cinfo)
42{
43 JPEG_ERROR_MGR *err = (JPEG_ERROR_MGR *)cinfo->err;
44
45 (*cinfo->err->format_message)(cinfo, err->lastError);
46}
47
48//
49// Destination manager implementation for the JPEG library.
50//
51
52static void
53JpegInitDestination(j_compress_ptr cinfo)
54{
55 JPEG_DEST_MGR *dest = (JPEG_DEST_MGR *)cinfo->dest;
56 JpegCompressor *jc = dest->instance;
57
58 jc->clear();
59 dest->pub.next_output_byte = jc->getptr();
60 dest->pub.free_in_buffer = jc->getend() - jc->getptr();
61}
62
63static boolean
64JpegEmptyOutputBuffer(j_compress_ptr cinfo)
65{
66 JPEG_DEST_MGR *dest = (JPEG_DEST_MGR *)cinfo->dest;
67 JpegCompressor *jc = dest->instance;
68
69 jc->setptr(dest->pub.next_output_byte);
70 jc->overrun(jc->getend() - jc->getstart(), 1);
71 dest->pub.next_output_byte = jc->getptr();
72 dest->pub.free_in_buffer = jc->getend() - jc->getptr();
73
74 return TRUE;
75}
76
77static void
78JpegTermDestination(j_compress_ptr cinfo)
79{
80 JPEG_DEST_MGR *dest = (JPEG_DEST_MGR *)cinfo->dest;
81 JpegCompressor *jc = dest->instance;
82
83 jc->setptr(dest->pub.next_output_byte);
84}
85
86JpegCompressor::JpegCompressor(int bufferLen) : MemOutStream(bufferLen)
87{
88 cinfo.err = jpeg_std_error(&err.pub);
89 snprintf(err.lastError, JMSG_LENGTH_MAX, "No error");
90 err.pub.error_exit = JpegErrorExit;
91 err.pub.output_message = JpegOutputMessage;
92
93 if(setjmp(err.jmpBuffer)) {
94 // this will execute if libjpeg has an error
95 throw rdr::Exception(err.lastError);
96 }
97
98 jpeg_create_compress(&cinfo);
99
100 dest.pub.init_destination = JpegInitDestination;
101 dest.pub.empty_output_buffer = JpegEmptyOutputBuffer;
102 dest.pub.term_destination = JpegTermDestination;
103 dest.instance = this;
104 cinfo.dest = (struct jpeg_destination_mgr *)&dest;
105}
106
107JpegCompressor::~JpegCompressor(void)
108{
109 if(setjmp(err.jmpBuffer)) {
110 // this will execute if libjpeg has an error
111 return;
112 }
113
114 jpeg_destroy_compress(&cinfo);
115}
116
DRCffe09d62011-08-17 02:27:59 +0000117void JpegCompressor::compress(rdr::U8 *buf, int pitch, const Rect& r,
DRCcd2c5d42011-08-11 11:18:34 +0000118 const PixelFormat& pf, int quality, JPEG_SUBSAMP subsamp)
119{
120 int w = r.width();
121 int h = r.height();
122 int pixelsize;
123 rdr::U8 *srcBuf = NULL;
124 bool srcBufIsTemp = false;
125 JSAMPROW *rowPointer = NULL;
126
127 if(setjmp(err.jmpBuffer)) {
128 // this will execute if libjpeg has an error
129 jpeg_abort_compress(&cinfo);
130 if (srcBufIsTemp && srcBuf) delete[] srcBuf;
131 if (rowPointer) delete[] rowPointer;
132 throw rdr::Exception(err.lastError);
133 }
134
135 cinfo.image_width = w;
136 cinfo.image_height = h;
137 cinfo.in_color_space = JCS_RGB;
138 pixelsize = 3;
139
140#ifdef JCS_EXTENSIONS
141 // Try to have libjpeg read directly from our native format
142 if(pf.is888()) {
143 int redShift, greenShift, blueShift;
144
145 if(pf.bigEndian) {
146 redShift = 24 - pf.redShift;
147 greenShift = 24 - pf.greenShift;
148 blueShift = 24 - pf.blueShift;
149 } else {
150 redShift = pf.redShift;
151 greenShift = pf.greenShift;
152 blueShift = pf.blueShift;
153 }
154
155 if(redShift == 0 && greenShift == 8 && blueShift == 16)
156 cinfo.in_color_space = JCS_EXT_RGBX;
157 if(redShift == 16 && greenShift == 8 && blueShift == 0)
158 cinfo.in_color_space = JCS_EXT_BGRX;
159 if(redShift == 24 && greenShift == 16 && blueShift == 8)
160 cinfo.in_color_space = JCS_EXT_XBGR;
161 if(redShift == 8 && greenShift == 16 && blueShift == 24)
162 cinfo.in_color_space = JCS_EXT_XRGB;
163
164 if (cinfo.in_color_space != JCS_RGB) {
165 srcBuf = (rdr::U8 *)buf;
166 pixelsize = 4;
167 }
168 }
169#endif
170
DRCffe09d62011-08-17 02:27:59 +0000171 if (pitch == 0) pitch = w * pixelsize;
172
DRCcd2c5d42011-08-11 11:18:34 +0000173 if (cinfo.in_color_space == JCS_RGB) {
174 srcBuf = new rdr::U8[w * h * pixelsize];
175 srcBufIsTemp = true;
DRCffe09d62011-08-17 02:27:59 +0000176 pf.rgbFromBuffer(srcBuf, (const rdr::U8 *)buf, w, pitch, h);
177 pitch = w * pixelsize;
DRCcd2c5d42011-08-11 11:18:34 +0000178 }
179
180 cinfo.input_components = pixelsize;
181
182 jpeg_set_defaults(&cinfo);
183 jpeg_set_quality(&cinfo, quality, TRUE);
184 if(quality >= 96) cinfo.dct_method = JDCT_ISLOW;
185 else cinfo.dct_method = JDCT_FASTEST;
186
187 switch (subsamp) {
188 case SUBSAMP_420:
189 cinfo.comp_info[0].h_samp_factor = 2;
190 cinfo.comp_info[0].v_samp_factor = 2;
191 break;
192 case SUBSAMP_422:
193 cinfo.comp_info[0].h_samp_factor = 2;
194 cinfo.comp_info[0].v_samp_factor = 1;
195 break;
196 default:
197 cinfo.comp_info[0].h_samp_factor = 1;
198 cinfo.comp_info[0].v_samp_factor = 1;
199 }
200
201 rowPointer = new JSAMPROW[h];
202 for (int dy = 0; dy < h; dy++)
DRCffe09d62011-08-17 02:27:59 +0000203 rowPointer[dy] = (JSAMPROW)(&srcBuf[dy * pitch]);
DRCcd2c5d42011-08-11 11:18:34 +0000204
205 jpeg_start_compress(&cinfo, TRUE);
206 while (cinfo.next_scanline < cinfo.image_height)
207 jpeg_write_scanlines(&cinfo, &rowPointer[cinfo.next_scanline],
208 cinfo.image_height - cinfo.next_scanline);
209
210 jpeg_finish_compress(&cinfo);
211
212 if (srcBufIsTemp) delete[] srcBuf;
213 delete[] rowPointer;
214}
215
216void JpegCompressor::writeBytes(const void* data, int length)
217{
218 throw rdr::Exception("writeBytes() is not valid with a JpegCompressor instance. Use compress() instead.");
219}