blob: 0914f850123c42b55ddfa8bf33f4420265c69b42 [file] [log] [blame]
Constantin Kaplinsky71a32f02007-07-24 12:10:16 +00001#include <stdlib.h>
2
3#include <rfb/JpegCompressor.h>
4
5using namespace rfb;
6
7const int StandardJpegCompressor::ALLOC_CHUNK_SIZE = 65536;
8const int StandardJpegCompressor::DEFAULT_QUALITY = 75;
9
10//
11// Extend jpeg_destination_mgr struct with a pointer to our object.
12//
13
14typedef struct {
15 struct jpeg_destination_mgr pub;
16 StandardJpegCompressor *_this;
17} my_destination_mgr;
18
19//
20// C-compatible interface to our destination manager. It just obtains
21// a pointer to the right object and calls a corresponding C++ member
22// function on that object.
23//
24
25static void
26init_destination(j_compress_ptr cinfo)
27{
28 my_destination_mgr *dest_ptr = (my_destination_mgr *)cinfo->dest;
29 dest_ptr->_this->initDestination();
30}
31
32static boolean
33empty_output_buffer (j_compress_ptr cinfo)
34{
35 my_destination_mgr *dest_ptr = (my_destination_mgr *)cinfo->dest;
36 return (boolean)dest_ptr->_this->emptyOutputBuffer();
37}
38
39static void
40term_destination (j_compress_ptr cinfo)
41{
42 my_destination_mgr *dest_ptr = (my_destination_mgr *)cinfo->dest;
43 dest_ptr->_this->termDestination();
44}
45
46//
47// Constructor and destructor.
48//
49
50StandardJpegCompressor::StandardJpegCompressor()
51 : m_cdata(0),
52 m_cdata_allocated(0),
53 m_cdata_ready(0)
54{
55 // Initialize JPEG compression structure.
56 m_cinfo.err = jpeg_std_error(&m_jerr);
57 jpeg_create_compress(&m_cinfo);
58
59 // Set up a destination manager.
60 my_destination_mgr *dest = new my_destination_mgr;
61 dest->pub.init_destination = init_destination;
62 dest->pub.empty_output_buffer = empty_output_buffer;
63 dest->pub.term_destination = term_destination;
64 dest->_this = this;
65 m_cinfo.dest = (jpeg_destination_mgr *)dest;
66
67 // Set up a destination manager.
68 m_cinfo.input_components = 3;
69 m_cinfo.in_color_space = JCS_RGB;
70 jpeg_set_defaults(&m_cinfo);
71 jpeg_set_quality(&m_cinfo, DEFAULT_QUALITY, true);
72
73 // We prefer speed over quality.
74 m_cinfo.dct_method = JDCT_FASTEST;
75}
76
77StandardJpegCompressor::~StandardJpegCompressor()
78{
79 // Free compressed data buffer.
80 if (m_cdata)
81 free(m_cdata);
82
83 // Clean up the destination manager.
84 delete m_cinfo.dest;
85 m_cinfo.dest = NULL;
86
87 // Release the JPEG compression structure.
88 jpeg_destroy_compress(&m_cinfo);
89}
90
91//
92// Our implementation of destination manager.
93//
94
95void
96StandardJpegCompressor::initDestination()
97{
98 if (!m_cdata) {
99 size_t new_size = ALLOC_CHUNK_SIZE;
100 m_cdata = (unsigned char *)malloc(new_size);
101 m_cdata_allocated = new_size;
102 }
103
104 m_cdata_ready = 0;
105 m_cinfo.dest->next_output_byte = m_cdata;
106 m_cinfo.dest->free_in_buffer = m_cdata_allocated;
107}
108
109bool
110StandardJpegCompressor::emptyOutputBuffer()
111{
112 size_t old_size = m_cdata_allocated;
113 size_t new_size = old_size + ALLOC_CHUNK_SIZE;
114
115 m_cdata = (unsigned char *)realloc(m_cdata, new_size);
116 m_cdata_allocated = new_size;
117
118 m_cinfo.dest->next_output_byte = &m_cdata[old_size];
119 m_cinfo.dest->free_in_buffer = new_size - old_size;
120
121 return true;
122}
123
124void
125StandardJpegCompressor::termDestination()
126{
127 m_cdata_ready = m_cdata_allocated - m_cinfo.dest->free_in_buffer;
128}
129
130//
131// Set JPEG quality level (0..100)
132//
133
134void
135StandardJpegCompressor::setQuality(int level)
136{
137 if (level < 0)
138 level = 0;
139 if (level > 100)
140 level = 100;
141
142 jpeg_set_quality(&m_cinfo, level, true);
143}
144
145//
146// Perform JPEG compression.
147//
148// FIXME: This function assumes that (fmt->bpp == 32 &&
149// fmt->depth == 24 && fmt->redMax == 255 &&
150// fmt->greenMax == 255 && fmt->blueMax == 255).
151//
152
153void
154StandardJpegCompressor::compress(const rdr::U32 *buf,
155 const PixelFormat *fmt,
156 int w, int h, int stride)
157{
158 m_cinfo.image_width = w;
159 m_cinfo.image_height = h;
160
161 jpeg_start_compress(&m_cinfo, TRUE);
162
163 const rdr::U32 *src = buf;
164
165 // We'll pass up to 8 rows to jpeg_write_scanlines().
166 JSAMPLE *rgb = new JSAMPLE[w * 3 * 8];
167 JSAMPROW row_pointer[8];
168 for (int i = 0; i < 8; i++)
169 row_pointer[i] = &rgb[w * 3 * i];
170
171 // Feed the pixels to the JPEG library.
172 while (m_cinfo.next_scanline < m_cinfo.image_height) {
173 int max_rows = m_cinfo.image_height - m_cinfo.next_scanline;
174 if (max_rows > 8) {
175 max_rows = 8;
176 }
177 for (int dy = 0; dy < max_rows; dy++) {
178 JSAMPLE *dst = row_pointer[dy];
179 for (int x = 0; x < w; x++) {
180 dst[x*3] = (JSAMPLE)(src[x] >> fmt->redShift);
181 dst[x*3+1] = (JSAMPLE)(src[x] >> fmt->greenShift);
182 dst[x*3+2] = (JSAMPLE)(src[x] >> fmt->blueShift);
183 }
184 src += stride;
185 }
186 jpeg_write_scanlines(&m_cinfo, row_pointer, max_rows);
187 }
188
189 delete[] rgb;
190
191 jpeg_finish_compress(&m_cinfo);
192}
193