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