blob: 9d151b973aafb11fc19263eb80b0e8758829e94d [file] [log] [blame]
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. 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// -=- PixelBuffer.cxx
20//
21// The PixelBuffer class encapsulates the PixelFormat and dimensions
22// of a block of pixel data.
23
24#include <rfb/Exception.h>
25#include <rfb/LogWriter.h>
26#include <rfb/PixelBuffer.h>
27
28using namespace rfb;
29using namespace rdr;
30
31static LogWriter vlog("PixelBuffer");
32
33
34// -=- Generic pixel buffer class
35
36PixelBuffer::PixelBuffer(const PixelFormat& pf, int w, int h, ColourMap* cm)
37 : format(pf), width_(w), height_(h), colourmap(cm) {}
38PixelBuffer::PixelBuffer() : width_(0), height_(0), colourmap(0) {}
39
40PixelBuffer::~PixelBuffer() {}
41
42
43void PixelBuffer::setPF(const PixelFormat &pf) {format = pf;}
44const PixelFormat& PixelBuffer::getPF() const {return format;}
45ColourMap* PixelBuffer::getColourMap() const {return colourmap;}
46
47
48void
49PixelBuffer::getImage(void* imageBuf, const Rect& r, int outStride) {
50 int inStride;
Pierre Ossman945cdda2014-01-28 14:13:12 +010051 const U8* data = getBuffer(r, &inStride);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000052 // We assume that the specified rectangle is pre-clipped to the buffer
53 int bytesPerPixel = format.bpp/8;
54 int inBytesPerRow = inStride * bytesPerPixel;
55 if (!outStride) outStride = r.width();
56 int outBytesPerRow = outStride * bytesPerPixel;
57 int bytesPerMemCpy = r.width() * bytesPerPixel;
58 U8* imageBufPos = (U8*)imageBuf;
59 const U8* end = data + (inBytesPerRow * r.height());
60 while (data < end) {
61 memcpy(imageBufPos, data, bytesPerMemCpy);
62 imageBufPos += outBytesPerRow;
63 data += inBytesPerRow;
64 }
65}
66
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000067
DRC4f24c1a2011-11-03 23:56:10 +000068static void fillRect8(U8 *buf, int stride, const Rect& r, Pixel pix)
69{
70 U8* ptr = buf;
71 int w = r.width(), h = r.height();
72
73 while (h > 0) {
74 memset(ptr, pix, w);
75 ptr += stride;
76 h--;
77 }
78}
79
80static void fillRect16(U8 *buf, int stride, const Rect& r, Pixel pix)
81{
82 U16* ptr = (U16 *)buf;
83 int w = r.width(), h = r.height(), wBytes = w * 2;
84
85 while (w > 0) {
86 *ptr++ = pix; w--;
87 }
88 h--;
89
90 ptr = (U16 *)buf;
91
92 while (h > 0) {
93 U16 *oldptr = ptr;
94 memcpy(ptr += stride, oldptr, wBytes);
95 h--;
96 }
97}
98
99static void fillRect32(U8 *buf, int stride, const Rect& r, Pixel pix)
100{
101 U32* ptr = (U32 *)buf;
102 int w = r.width(), h = r.height(), wBytes = w * 4;
103
104 while (w > 0) {
105 *ptr++ = pix; w--;
106 }
107 h--;
108
109 ptr = (U32 *)buf;
110
111 while (h > 0) {
112 U32 *oldptr = ptr;
113 memcpy(ptr += stride, oldptr, wBytes);
114 h--;
115 }
116}
117
118
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000119FullFramePixelBuffer::FullFramePixelBuffer(const PixelFormat& pf, int w, int h,
120 rdr::U8* data_, ColourMap* cm)
121 : PixelBuffer(pf, w, h, cm), data(data_)
122{
Pierre Ossman1ed4d502014-01-07 15:28:45 +0000123 // Called again to configure the fill function
124 setPF(pf);
125}
126
127FullFramePixelBuffer::FullFramePixelBuffer() : data(0) {}
128
129FullFramePixelBuffer::~FullFramePixelBuffer() {}
130
131
132void FullFramePixelBuffer::setPF(const PixelFormat &pf) {
133 // We have this as a separate method for ManagedPixelBuffer's
134 // sake. Direct users of FullFramePixelBuffer aren't allowed
135 // to call it.
136
137 PixelBuffer::setPF(pf);
138
DRC4f24c1a2011-11-03 23:56:10 +0000139 switch(pf.bpp) {
140 case 8:
141 fillRectFn = fillRect8;
142 break;
143 case 16:
144 fillRectFn = fillRect16;
145 break;
146 case 32:
147 fillRectFn = fillRect32;
148 break;
149 default:
150 throw Exception("rfb::FullFramePixelBuffer - Unsupported pixel format");
151 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000152}
153
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000154
155int FullFramePixelBuffer::getStride() const { return width(); }
156
Pierre Ossman945cdda2014-01-28 14:13:12 +0100157rdr::U8* FullFramePixelBuffer::getBufferRW(const Rect& r, int* stride)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000158{
159 *stride = getStride();
160 return &data[(r.tl.x + (r.tl.y * *stride)) * format.bpp/8];
161}
162
163
164void FullFramePixelBuffer::fillRect(const Rect& r, Pixel pix) {
165 int stride;
Pierre Ossman945cdda2014-01-28 14:13:12 +0100166 U8 *buf = getBufferRW(r, &stride);
DRC4f24c1a2011-11-03 23:56:10 +0000167 fillRectFn(buf, stride, r, pix);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000168}
169
170void FullFramePixelBuffer::imageRect(const Rect& r, const void* pixels, int srcStride) {
171 int bytesPerPixel = getPF().bpp/8;
172 int destStride;
Pierre Ossman945cdda2014-01-28 14:13:12 +0100173 U8* dest = getBufferRW(r, &destStride);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000174 int bytesPerDestRow = bytesPerPixel * destStride;
175 if (!srcStride) srcStride = r.width();
176 int bytesPerSrcRow = bytesPerPixel * srcStride;
177 int bytesPerFill = bytesPerPixel * r.width();
178 const U8* src = (const U8*)pixels;
179 U8* end = dest + (bytesPerDestRow * r.height());
180 while (dest < end) {
181 memcpy(dest, src, bytesPerFill);
182 dest += bytesPerDestRow;
183 src += bytesPerSrcRow;
184 }
185}
186
187void FullFramePixelBuffer::maskRect(const Rect& r, const void* pixels, const void* mask_) {
188 Rect cr = getRect().intersect(r);
189 if (cr.is_empty()) return;
190 int stride;
Pierre Ossman945cdda2014-01-28 14:13:12 +0100191 U8* data = getBufferRW(cr, &stride);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000192 U8* mask = (U8*) mask_;
193 int w = cr.width();
194 int h = cr.height();
195 int bpp = getPF().bpp;
196 int pixelStride = r.width();
197 int maskStride = (r.width() + 7) / 8;
198
199 Point offset = Point(cr.tl.x-r.tl.x, cr.tl.y-r.tl.y);
200 mask += offset.y * maskStride;
201 for (int y = 0; y < h; y++) {
202 int cy = offset.y + y;
203 for (int x = 0; x < w; x++) {
204 int cx = offset.x + x;
205 U8* byte = mask + (cx / 8);
206 int bit = 7 - cx % 8;
207 if ((*byte) & (1 << bit)) {
208 switch (bpp) {
209 case 8:
210 ((U8*)data)[y * stride + x] = ((U8*)pixels)[cy * pixelStride + cx];
211 break;
212 case 16:
213 ((U16*)data)[y * stride + x] = ((U16*)pixels)[cy * pixelStride + cx];
214 break;
215 case 32:
216 ((U32*)data)[y * stride + x] = ((U32*)pixels)[cy * pixelStride + cx];
217 break;
218 }
219 }
220 }
221 mask += maskStride;
222 }
223}
224
225void FullFramePixelBuffer::maskRect(const Rect& r, Pixel pixel, const void* mask_) {
226 Rect cr = getRect().intersect(r);
227 if (cr.is_empty()) return;
228 int stride;
Pierre Ossman945cdda2014-01-28 14:13:12 +0100229 U8* data = getBufferRW(cr, &stride);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000230 U8* mask = (U8*) mask_;
231 int w = cr.width();
232 int h = cr.height();
233 int bpp = getPF().bpp;
234 int maskStride = (r.width() + 7) / 8;
235
236 Point offset = Point(cr.tl.x-r.tl.x, cr.tl.y-r.tl.y);
237 mask += offset.y * maskStride;
238 for (int y = 0; y < h; y++) {
239 for (int x = 0; x < w; x++) {
240 int cx = offset.x + x;
241 U8* byte = mask + (cx / 8);
242 int bit = 7 - cx % 8;
243 if ((*byte) & (1 << bit)) {
244 switch (bpp) {
245 case 8:
246 ((U8*)data)[y * stride + x] = pixel;
247 break;
248 case 16:
249 ((U16*)data)[y * stride + x] = pixel;
250 break;
251 case 32:
252 ((U32*)data)[y * stride + x] = pixel;
253 break;
254 }
255 }
256 }
257 mask += maskStride;
258 }
259}
260
261void FullFramePixelBuffer::copyRect(const Rect &rect, const Point &move_by_delta) {
262 int stride;
Pierre Ossman45de20d2010-12-21 15:53:42 +0000263 U8* data;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000264 unsigned int bytesPerPixel, bytesPerRow, bytesPerMemCpy;
Pierre Ossman45de20d2010-12-21 15:53:42 +0000265 Rect drect, srect = rect.translate(move_by_delta.negate());
266
267 drect = rect;
268 if (!drect.enclosed_by(getRect())) {
269 vlog.error("Destination rect %dx%d at %d,%d exceeds framebuffer %dx%d",
270 drect.width(), drect.height(), drect.tl.x, drect.tl.y, width_, height_);
271 drect = drect.intersect(getRect());
272 }
273
274 if (drect.is_empty())
275 return;
276
277 srect = drect.translate(move_by_delta.negate());
278 if (!srect.enclosed_by(getRect())) {
279 vlog.error("Source rect %dx%d at %d,%d exceeds framebuffer %dx%d",
280 srect.width(), srect.height(), srect.tl.x, srect.tl.y, width_, height_);
281 srect = srect.intersect(getRect());
282 // Need to readjust the destination now that the area has changed
283 drect = srect.translate(move_by_delta);
284 }
285
286 if (srect.is_empty())
287 return;
288
Pierre Ossman945cdda2014-01-28 14:13:12 +0100289 data = getBufferRW(getRect(), &stride);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000290 bytesPerPixel = getPF().bpp/8;
291 bytesPerRow = stride * bytesPerPixel;
Pierre Ossman45de20d2010-12-21 15:53:42 +0000292 bytesPerMemCpy = drect.width() * bytesPerPixel;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000293 if (move_by_delta.y <= 0) {
Pierre Ossman45de20d2010-12-21 15:53:42 +0000294 U8* dest = data + drect.tl.x*bytesPerPixel + drect.tl.y*bytesPerRow;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000295 U8* src = data + srect.tl.x*bytesPerPixel + srect.tl.y*bytesPerRow;
Pierre Ossman45de20d2010-12-21 15:53:42 +0000296 for (int i=drect.tl.y; i<drect.br.y; i++) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000297 memmove(dest, src, bytesPerMemCpy);
298 dest += bytesPerRow;
299 src += bytesPerRow;
300 }
301 } else {
Pierre Ossman45de20d2010-12-21 15:53:42 +0000302 U8* dest = data + drect.tl.x*bytesPerPixel + (drect.br.y-1)*bytesPerRow;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000303 U8* src = data + srect.tl.x*bytesPerPixel + (srect.br.y-1)*bytesPerRow;
Pierre Ossman45de20d2010-12-21 15:53:42 +0000304 for (int i=drect.tl.y; i<drect.br.y; i++) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000305 memmove(dest, src, bytesPerMemCpy);
306 dest -= bytesPerRow;
307 src -= bytesPerRow;
308 }
309 }
310}
311
312
313// -=- Managed pixel buffer class
314// Automatically allocates enough space for the specified format & area
315
316ManagedPixelBuffer::ManagedPixelBuffer()
317 : datasize(0), own_colourmap(false)
318{
319 checkDataSize();
320};
321
322ManagedPixelBuffer::ManagedPixelBuffer(const PixelFormat& pf, int w, int h)
323 : FullFramePixelBuffer(pf, w, h, 0, 0), datasize(0), own_colourmap(false)
324{
325 checkDataSize();
326};
327
328ManagedPixelBuffer::~ManagedPixelBuffer() {
329 if (data) delete [] data;
330 if (colourmap && own_colourmap) delete colourmap;
331};
332
333
334void
335ManagedPixelBuffer::setPF(const PixelFormat &pf) {
Pierre Ossman1ed4d502014-01-07 15:28:45 +0000336 FullFramePixelBuffer::setPF(pf); checkDataSize();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000337};
338void
339ManagedPixelBuffer::setSize(int w, int h) {
340 width_ = w; height_ = h; checkDataSize();
341};
342
343
344void
345ManagedPixelBuffer::setColourMap(ColourMap* cm, bool own_cm) {
346 if (colourmap && own_colourmap) delete colourmap;
347 colourmap = cm;
348 own_colourmap = own_cm;
349}
350
351inline void
352ManagedPixelBuffer::checkDataSize() {
353 unsigned long new_datasize = width_ * height_ * (format.bpp/8);
354 if (datasize < new_datasize) {
355 vlog.debug("reallocating managed buffer (%dx%d)", width_, height_);
356 if (data) {
357 delete [] data;
358 datasize = 0; data = 0;
359 }
360 if (new_datasize) {
361 data = new U8[new_datasize];
362 if (!data)
363 throw Exception("rfb::ManagedPixelBuffer unable to allocate buffer");
364 datasize = new_datasize;
365 }
366 }
367};