blob: f4b6ed1ceee99a5869235963fed70072ab149db5 [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
Pierre Ossman9144ae02011-09-07 11:35:04 +000026#include <stdio.h>
27extern "C" {
28#include <jpeglib.h>
29}
30#include <setjmp.h>
31
DRCcd2c5d42011-08-11 11:18:34 +000032using namespace rfb;
33
34//
35// Error manager implmentation for the JPEG library
36//
37
Pierre Ossman9144ae02011-09-07 11:35:04 +000038struct JPEG_ERROR_MGR {
39 struct jpeg_error_mgr pub;
40 jmp_buf jmpBuffer;
41 char lastError[JMSG_LENGTH_MAX];
42};
43
DRCcd2c5d42011-08-11 11:18:34 +000044static void
45JpegErrorExit(j_common_ptr cinfo)
46{
47 JPEG_ERROR_MGR *err = (JPEG_ERROR_MGR *)cinfo->err;
48
49 (*cinfo->err->output_message)(cinfo);
50 longjmp(err->jmpBuffer, 1);
51}
52
53static void
54JpegOutputMessage(j_common_ptr cinfo)
55{
56 JPEG_ERROR_MGR *err = (JPEG_ERROR_MGR *)cinfo->err;
57
58 (*cinfo->err->format_message)(cinfo, err->lastError);
59}
60
61//
62// Destination manager implementation for the JPEG library.
63//
64
Pierre Ossman9144ae02011-09-07 11:35:04 +000065struct JPEG_DEST_MGR {
66 struct jpeg_destination_mgr pub;
67 JpegCompressor *instance;
68};
69
DRCcd2c5d42011-08-11 11:18:34 +000070static void
71JpegInitDestination(j_compress_ptr cinfo)
72{
73 JPEG_DEST_MGR *dest = (JPEG_DEST_MGR *)cinfo->dest;
74 JpegCompressor *jc = dest->instance;
75
76 jc->clear();
77 dest->pub.next_output_byte = jc->getptr();
78 dest->pub.free_in_buffer = jc->getend() - jc->getptr();
79}
80
81static boolean
82JpegEmptyOutputBuffer(j_compress_ptr cinfo)
83{
84 JPEG_DEST_MGR *dest = (JPEG_DEST_MGR *)cinfo->dest;
85 JpegCompressor *jc = dest->instance;
86
87 jc->setptr(dest->pub.next_output_byte);
88 jc->overrun(jc->getend() - jc->getstart(), 1);
89 dest->pub.next_output_byte = jc->getptr();
90 dest->pub.free_in_buffer = jc->getend() - jc->getptr();
91
92 return TRUE;
93}
94
95static void
96JpegTermDestination(j_compress_ptr cinfo)
97{
98 JPEG_DEST_MGR *dest = (JPEG_DEST_MGR *)cinfo->dest;
99 JpegCompressor *jc = dest->instance;
100
101 jc->setptr(dest->pub.next_output_byte);
102}
103
104JpegCompressor::JpegCompressor(int bufferLen) : MemOutStream(bufferLen)
105{
Pierre Ossman9144ae02011-09-07 11:35:04 +0000106 cinfo = new jpeg_compress_struct;
DRCcd2c5d42011-08-11 11:18:34 +0000107
Pierre Ossman9144ae02011-09-07 11:35:04 +0000108 err = new struct JPEG_ERROR_MGR;
109 cinfo->err = jpeg_std_error(&err->pub);
110 snprintf(err->lastError, JMSG_LENGTH_MAX, "No error");
111 err->pub.error_exit = JpegErrorExit;
112 err->pub.output_message = JpegOutputMessage;
113
114 if(setjmp(err->jmpBuffer)) {
DRCcd2c5d42011-08-11 11:18:34 +0000115 // this will execute if libjpeg has an error
Pierre Ossman9144ae02011-09-07 11:35:04 +0000116 throw rdr::Exception(err->lastError);
DRCcd2c5d42011-08-11 11:18:34 +0000117 }
118
Pierre Ossman9144ae02011-09-07 11:35:04 +0000119 jpeg_create_compress(cinfo);
DRCcd2c5d42011-08-11 11:18:34 +0000120
Pierre Ossman9144ae02011-09-07 11:35:04 +0000121 dest = new struct JPEG_DEST_MGR;
122 dest->pub.init_destination = JpegInitDestination;
123 dest->pub.empty_output_buffer = JpegEmptyOutputBuffer;
124 dest->pub.term_destination = JpegTermDestination;
125 dest->instance = this;
126 cinfo->dest = (struct jpeg_destination_mgr *)dest;
DRCcd2c5d42011-08-11 11:18:34 +0000127}
128
129JpegCompressor::~JpegCompressor(void)
130{
Pierre Ossman9144ae02011-09-07 11:35:04 +0000131 if(setjmp(err->jmpBuffer)) {
DRCcd2c5d42011-08-11 11:18:34 +0000132 // this will execute if libjpeg has an error
133 return;
134 }
135
Pierre Ossman9144ae02011-09-07 11:35:04 +0000136 jpeg_destroy_compress(cinfo);
137
138 delete err;
139 delete dest;
140
141 delete cinfo;
DRCcd2c5d42011-08-11 11:18:34 +0000142}
143
DRCffe09d62011-08-17 02:27:59 +0000144void JpegCompressor::compress(rdr::U8 *buf, int pitch, const Rect& r,
DRCcd2c5d42011-08-11 11:18:34 +0000145 const PixelFormat& pf, int quality, JPEG_SUBSAMP subsamp)
146{
147 int w = r.width();
148 int h = r.height();
149 int pixelsize;
150 rdr::U8 *srcBuf = NULL;
151 bool srcBufIsTemp = false;
152 JSAMPROW *rowPointer = NULL;
153
Pierre Ossman9144ae02011-09-07 11:35:04 +0000154 if(setjmp(err->jmpBuffer)) {
DRCcd2c5d42011-08-11 11:18:34 +0000155 // this will execute if libjpeg has an error
Pierre Ossman9144ae02011-09-07 11:35:04 +0000156 jpeg_abort_compress(cinfo);
DRCcd2c5d42011-08-11 11:18:34 +0000157 if (srcBufIsTemp && srcBuf) delete[] srcBuf;
158 if (rowPointer) delete[] rowPointer;
Pierre Ossman9144ae02011-09-07 11:35:04 +0000159 throw rdr::Exception(err->lastError);
DRCcd2c5d42011-08-11 11:18:34 +0000160 }
161
Pierre Ossman9144ae02011-09-07 11:35:04 +0000162 cinfo->image_width = w;
163 cinfo->image_height = h;
164 cinfo->in_color_space = JCS_RGB;
DRCcd2c5d42011-08-11 11:18:34 +0000165 pixelsize = 3;
166
167#ifdef JCS_EXTENSIONS
168 // Try to have libjpeg read directly from our native format
169 if(pf.is888()) {
170 int redShift, greenShift, blueShift;
171
172 if(pf.bigEndian) {
173 redShift = 24 - pf.redShift;
174 greenShift = 24 - pf.greenShift;
175 blueShift = 24 - pf.blueShift;
176 } else {
177 redShift = pf.redShift;
178 greenShift = pf.greenShift;
179 blueShift = pf.blueShift;
180 }
181
182 if(redShift == 0 && greenShift == 8 && blueShift == 16)
Pierre Ossman9144ae02011-09-07 11:35:04 +0000183 cinfo->in_color_space = JCS_EXT_RGBX;
DRCcd2c5d42011-08-11 11:18:34 +0000184 if(redShift == 16 && greenShift == 8 && blueShift == 0)
Pierre Ossman9144ae02011-09-07 11:35:04 +0000185 cinfo->in_color_space = JCS_EXT_BGRX;
DRCcd2c5d42011-08-11 11:18:34 +0000186 if(redShift == 24 && greenShift == 16 && blueShift == 8)
Pierre Ossman9144ae02011-09-07 11:35:04 +0000187 cinfo->in_color_space = JCS_EXT_XBGR;
DRCcd2c5d42011-08-11 11:18:34 +0000188 if(redShift == 8 && greenShift == 16 && blueShift == 24)
Pierre Ossman9144ae02011-09-07 11:35:04 +0000189 cinfo->in_color_space = JCS_EXT_XRGB;
DRCcd2c5d42011-08-11 11:18:34 +0000190
Pierre Ossman9144ae02011-09-07 11:35:04 +0000191 if (cinfo->in_color_space != JCS_RGB) {
DRCcd2c5d42011-08-11 11:18:34 +0000192 srcBuf = (rdr::U8 *)buf;
193 pixelsize = 4;
194 }
195 }
196#endif
197
DRC51bdb2f2011-08-19 13:45:22 +0000198 if (pitch == 0) pitch = w * pf.bpp / 8;
DRCffe09d62011-08-17 02:27:59 +0000199
Pierre Ossman9144ae02011-09-07 11:35:04 +0000200 if (cinfo->in_color_space == JCS_RGB) {
DRCcd2c5d42011-08-11 11:18:34 +0000201 srcBuf = new rdr::U8[w * h * pixelsize];
202 srcBufIsTemp = true;
DRCffe09d62011-08-17 02:27:59 +0000203 pf.rgbFromBuffer(srcBuf, (const rdr::U8 *)buf, w, pitch, h);
204 pitch = w * pixelsize;
DRCcd2c5d42011-08-11 11:18:34 +0000205 }
206
Pierre Ossman9144ae02011-09-07 11:35:04 +0000207 cinfo->input_components = pixelsize;
DRCcd2c5d42011-08-11 11:18:34 +0000208
Pierre Ossman9144ae02011-09-07 11:35:04 +0000209 jpeg_set_defaults(cinfo);
210 jpeg_set_quality(cinfo, quality, TRUE);
211 if(quality >= 96) cinfo->dct_method = JDCT_ISLOW;
212 else cinfo->dct_method = JDCT_FASTEST;
DRCcd2c5d42011-08-11 11:18:34 +0000213
214 switch (subsamp) {
215 case SUBSAMP_420:
Pierre Ossman9144ae02011-09-07 11:35:04 +0000216 cinfo->comp_info[0].h_samp_factor = 2;
217 cinfo->comp_info[0].v_samp_factor = 2;
DRCcd2c5d42011-08-11 11:18:34 +0000218 break;
219 case SUBSAMP_422:
Pierre Ossman9144ae02011-09-07 11:35:04 +0000220 cinfo->comp_info[0].h_samp_factor = 2;
221 cinfo->comp_info[0].v_samp_factor = 1;
DRCcd2c5d42011-08-11 11:18:34 +0000222 break;
DRCb4a83232011-08-19 04:57:18 +0000223 case SUBSAMP_GRAY:
Pierre Ossman9144ae02011-09-07 11:35:04 +0000224 jpeg_set_colorspace(cinfo, JCS_GRAYSCALE);
DRCcd2c5d42011-08-11 11:18:34 +0000225 default:
Pierre Ossman9144ae02011-09-07 11:35:04 +0000226 cinfo->comp_info[0].h_samp_factor = 1;
227 cinfo->comp_info[0].v_samp_factor = 1;
DRCcd2c5d42011-08-11 11:18:34 +0000228 }
229
230 rowPointer = new JSAMPROW[h];
231 for (int dy = 0; dy < h; dy++)
DRCffe09d62011-08-17 02:27:59 +0000232 rowPointer[dy] = (JSAMPROW)(&srcBuf[dy * pitch]);
DRCcd2c5d42011-08-11 11:18:34 +0000233
Pierre Ossman9144ae02011-09-07 11:35:04 +0000234 jpeg_start_compress(cinfo, TRUE);
235 while (cinfo->next_scanline < cinfo->image_height)
236 jpeg_write_scanlines(cinfo, &rowPointer[cinfo->next_scanline],
237 cinfo->image_height - cinfo->next_scanline);
DRCcd2c5d42011-08-11 11:18:34 +0000238
Pierre Ossman9144ae02011-09-07 11:35:04 +0000239 jpeg_finish_compress(cinfo);
DRCcd2c5d42011-08-11 11:18:34 +0000240
241 if (srcBufIsTemp) delete[] srcBuf;
242 delete[] rowPointer;
243}
244
245void JpegCompressor::writeBytes(const void* data, int length)
246{
247 throw rdr::Exception("writeBytes() is not valid with a JpegCompressor instance. Use compress() instead.");
248}