blob: 023ff3e3d98aa1d9676d4f00f54869af2d7156aa [file] [log] [blame]
Peter Åstrand462753d2004-11-16 15:23:25 +00001/* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved.
2 *
3 * This is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This software is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
16 * USA.
17 */
18
19//
20// Tight decoding functions.
21//
22// This file is #included after having set the following macros:
23// BPP - 8, 16 or 32
24// EXTRA_ARGS - optional extra arguments
25// FILL_RECT - fill a rectangle with a single colour
26// IMAGE_RECT - draw a rectangle of pixel data from a buffer
27
28#include <rdr/InStream.h>
29#include <rdr/ZlibInStream.h>
30#include <rfb/Exception.h>
31#include <assert.h>
32
33namespace rfb {
34
35// CONCAT2E concatenates its arguments, expanding them if they are macros
36
37#ifndef CONCAT2E
38#define CONCAT2(a,b) a##b
39#define CONCAT2E(a,b) CONCAT2(a,b)
40#endif
41
42#define PIXEL_T rdr::CONCAT2E(U,BPP)
43#define READ_PIXEL CONCAT2E(readOpaque,BPP)
44#define TIGHT_DECODE CONCAT2E(tightDecode,BPP)
45
46#define TIGHT_MIN_TO_COMPRESS 12
Peter Åstranda6bb7702004-12-07 08:22:42 +000047static bool DecompressJpegRect(const Rect& r, rdr::InStream* is,
48 PIXEL_T* buf, CMsgHandler* handler);
Peter Åstrand462753d2004-11-16 15:23:25 +000049
50// Main function implementing Tight decoder
51
52void TIGHT_DECODE (const Rect& r, rdr::InStream* is,
53 rdr::ZlibInStream zis[], PIXEL_T* buf
54#ifdef EXTRA_ARGS
55 , EXTRA_ARGS
56#endif
57 )
58{
59 rdr::U8 comp_ctl = is->readU8();
60
61 // Flush zlib streams if we are told by the server to do so.
62 for (int i = 0; i < 4; i++) {
Peter Åstrandef5dd312004-11-17 08:50:05 +000063 if (comp_ctl & 1) {
64 zis[i].reset();
Peter Åstrand462753d2004-11-16 15:23:25 +000065 }
Peter Åstrand462753d2004-11-16 15:23:25 +000066 comp_ctl >>= 1;
67 }
68
69 // "Fill" compression type.
Peter Åstrand5bbe3482004-11-17 09:45:44 +000070 if (comp_ctl == rfbTightFill) {
Peter Åstrand462753d2004-11-16 15:23:25 +000071 PIXEL_T pix = is->READ_PIXEL();
72 FILL_RECT(r, pix);
73 return;
74 }
75
76 // "JPEG" compression type.
Peter Åstrand5bbe3482004-11-17 09:45:44 +000077 if (comp_ctl == rfbTightJpeg) {
Peter Åstranda6bb7702004-12-07 08:22:42 +000078 DecompressJpegRect(r, is, buf, handler);
79 return;
Peter Åstrand462753d2004-11-16 15:23:25 +000080 }
81
82 // Quit on unsupported compression type.
Peter Åstrand5bbe3482004-11-17 09:45:44 +000083 if (comp_ctl > rfbTightMaxSubencoding) {
Peter Åstrand462753d2004-11-16 15:23:25 +000084 throw Exception("TightDecoder: bad subencoding value received");
85 return;
86 }
87
88 // "Basic" compression type.
89 int palSize = 0;
90 PIXEL_T palette[256];
91 bool useGradient = false;
92
Peter Åstrand5bbe3482004-11-17 09:45:44 +000093 if ((comp_ctl & rfbTightExplicitFilter) != 0) {
Peter Åstrand462753d2004-11-16 15:23:25 +000094 rdr::U8 filterId = is->readU8();
95
96 switch (filterId) {
Peter Åstrand5bbe3482004-11-17 09:45:44 +000097 case rfbTightFilterPalette:
Peter Åstrand462753d2004-11-16 15:23:25 +000098 palSize = is->readU8() + 1;
99 {
100 for (int i = 0; i < palSize; i++)
101 palette[i] = is->READ_PIXEL();
102 }
103 break;
Peter Åstrand5bbe3482004-11-17 09:45:44 +0000104 case rfbTightFilterGradient:
Peter Åstrand462753d2004-11-16 15:23:25 +0000105 useGradient = true;
106 break;
Peter Åstrand5bbe3482004-11-17 09:45:44 +0000107 case rfbTightFilterCopy:
Peter Åstrand462753d2004-11-16 15:23:25 +0000108 break;
109 default:
110 throw Exception("TightDecoder: unknown filter code received");
111 return;
112 }
113 }
114
115 int bppp = BPP;
116 if (palSize != 0) {
117 bppp = (palSize <= 2) ? 1 : 8;
118 }
119
120 // Determine if the data should be decompressed or just copied.
121 int rowSize = (r.width() * bppp + 7) / 8;
122 int dataSize = r.height() * rowSize;
123 rdr::InStream *input;
124 if (dataSize < TIGHT_MIN_TO_COMPRESS) {
125 input = is;
126 } else {
127 int length = is->readCompactLength();
128 int streamId = comp_ctl & 0x03;
129 zis[streamId].setUnderlying(is, length);
130 input = &zis[streamId];
131 }
132
133 if (palSize == 0) {
134 // Truecolor data
135 input->readBytes(buf, dataSize);
136 if (useGradient) {
137 // FIXME: Implement the "gradient" filter.
138 }
139 } else {
140 int x, y, b, w;
141 PIXEL_T *ptr = buf;
142 rdr::U8 bits;
143 if (palSize <= 2) {
144 // 2-color palette
145 w = (r.width() + 7) / 8;
146 for (y = 0; y < r.height(); y++) {
147 for (x = 0; x < r.width() / 8; x++) {
148 bits = input->readU8();
149 for (b = 7; b >= 0; b--)
150 *ptr++ = palette[bits >> b & 1];
151 }
152 if (r.width() % 8 != 0) {
153 bits = input->readU8();
154 for (b = 7; b >= 8 - r.width() % 8; b--) {
155 *ptr++ = palette[bits >> b & 1];
156 }
157 }
158 }
159 } else {
160 // 256-color palette
161 for (y = 0; y < r.height(); y++)
162 for (x = 0; x < r.width(); x++)
163 *ptr++ = palette[input->readU8()];
164 }
165 }
166
167 IMAGE_RECT(r, buf);
Peter Åstrand462753d2004-11-16 15:23:25 +0000168}
169
Peter Åstranda6bb7702004-12-07 08:22:42 +0000170static bool
171DecompressJpegRect(const Rect& r, rdr::InStream* is,
172 PIXEL_T* buf, CMsgHandler* handler)
173{
174 struct jpeg_decompress_struct cinfo;
175 struct jpeg_error_mgr jerr;
176 PIXEL_T *pixelPtr;
177 JSAMPROW scanline;
178
179 // Read length
180 int compressedLen = is->readCompactLength();
181 if (compressedLen <= 0) {
182 throw Exception("Incorrect data received from the server.\n");
183 }
184
185 // Allocate netbuf and read in data
186 rdr::U8* netbuf = new rdr::U8[compressedLen];
187 if (!netbuf)
188 throw Exception("rfb::tightDecode unable to allocate buffer");
189 is->readBytes(netbuf, compressedLen);
190
191 // Set up JPEG decompression
192 cinfo.err = jpeg_std_error(&jerr);
193 jpeg_create_decompress(&cinfo);
194 JpegSetSrcManager(&cinfo, (char*)netbuf, compressedLen);
195 jpeg_read_header(&cinfo, TRUE);
196 cinfo.out_color_space = JCS_RGB;
197
198 jpeg_start_decompress(&cinfo);
199 if (cinfo.output_width != (unsigned)r.width() || cinfo.output_height != (unsigned)r.height() ||
200 cinfo.output_components != 3) {
201 jpeg_destroy_decompress(&cinfo);
202 throw Exception("Tight Encoding: Wrong JPEG data received.\n");
203 }
204
205 // Decompress
206 scanline = (JSAMPROW)buf;
207 const rfb::PixelFormat& myFormat = handler->cp.pf();
208 int bytesPerRow = cinfo.output_width * myFormat.bpp/8;
209 while (cinfo.output_scanline < cinfo.output_height) {
210 jpeg_read_scanlines(&cinfo, &scanline, 1);
211 if (jpegError) {
212 break;
213 }
214
215 pixelPtr = (PIXEL_T*)(scanline);
216 for (int dx = 0; dx < r.width(); dx++) {
217 *pixelPtr++ =
218 RGB24_TO_PIXEL(BPP, scanline[dx*3], scanline[dx*3+1], scanline[dx*3+2]);
219 }
220 scanline += bytesPerRow;
221 }
222
223 IMAGE_RECT(r, buf);
224
225 if (!jpegError)
226 jpeg_finish_decompress(&cinfo);
227
228 jpeg_destroy_decompress(&cinfo);
229
230 delete [] netbuf;
231
232 return !jpegError;
233}
234
235
Peter Åstrand462753d2004-11-16 15:23:25 +0000236#undef TIGHT_DECODE
237#undef READ_PIXEL
238#undef PIXEL_T
239}