blob: 60957a25dbed854f15591ce3a880d5402ddffef1 [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;
51 const U8* data = getPixelsR(r, &inStride);
52 // 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
67/* ***
68Pixel PixelBuffer::getPixel(const Point& p) {
69 int stride;
70 Rect r = Rect(p.x, p.y, p.x+1, p.y+1);
71 switch(format.bpp) {
72 case 8: return *((rdr::U8*)getDataAt(r, &stride));
73 case 16: return *((rdr::U16*)getDataAt(r, &stride));
74 case 32: return *((rdr::U32*)getDataAt(r, &stride));
75 default: return 0;
76 };
77}
78*/
79
80
DRC4f24c1a2011-11-03 23:56:10 +000081static void fillRect8(U8 *buf, int stride, const Rect& r, Pixel pix)
82{
83 U8* ptr = buf;
84 int w = r.width(), h = r.height();
85
86 while (h > 0) {
87 memset(ptr, pix, w);
88 ptr += stride;
89 h--;
90 }
91}
92
93static void fillRect16(U8 *buf, int stride, const Rect& r, Pixel pix)
94{
95 U16* ptr = (U16 *)buf;
96 int w = r.width(), h = r.height(), wBytes = w * 2;
97
98 while (w > 0) {
99 *ptr++ = pix; w--;
100 }
101 h--;
102
103 ptr = (U16 *)buf;
104
105 while (h > 0) {
106 U16 *oldptr = ptr;
107 memcpy(ptr += stride, oldptr, wBytes);
108 h--;
109 }
110}
111
112static void fillRect32(U8 *buf, int stride, const Rect& r, Pixel pix)
113{
114 U32* ptr = (U32 *)buf;
115 int w = r.width(), h = r.height(), wBytes = w * 4;
116
117 while (w > 0) {
118 *ptr++ = pix; w--;
119 }
120 h--;
121
122 ptr = (U32 *)buf;
123
124 while (h > 0) {
125 U32 *oldptr = ptr;
126 memcpy(ptr += stride, oldptr, wBytes);
127 h--;
128 }
129}
130
131
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000132FullFramePixelBuffer::FullFramePixelBuffer(const PixelFormat& pf, int w, int h,
133 rdr::U8* data_, ColourMap* cm)
134 : PixelBuffer(pf, w, h, cm), data(data_)
135{
Pierre Ossman1ed4d502014-01-07 15:28:45 +0000136 // Called again to configure the fill function
137 setPF(pf);
138}
139
140FullFramePixelBuffer::FullFramePixelBuffer() : data(0) {}
141
142FullFramePixelBuffer::~FullFramePixelBuffer() {}
143
144
145void FullFramePixelBuffer::setPF(const PixelFormat &pf) {
146 // We have this as a separate method for ManagedPixelBuffer's
147 // sake. Direct users of FullFramePixelBuffer aren't allowed
148 // to call it.
149
150 PixelBuffer::setPF(pf);
151
DRC4f24c1a2011-11-03 23:56:10 +0000152 switch(pf.bpp) {
153 case 8:
154 fillRectFn = fillRect8;
155 break;
156 case 16:
157 fillRectFn = fillRect16;
158 break;
159 case 32:
160 fillRectFn = fillRect32;
161 break;
162 default:
163 throw Exception("rfb::FullFramePixelBuffer - Unsupported pixel format");
164 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000165}
166
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000167
168int FullFramePixelBuffer::getStride() const { return width(); }
169
170rdr::U8* FullFramePixelBuffer::getPixelsRW(const Rect& r, int* stride)
171{
172 *stride = getStride();
173 return &data[(r.tl.x + (r.tl.y * *stride)) * format.bpp/8];
174}
175
176
177void FullFramePixelBuffer::fillRect(const Rect& r, Pixel pix) {
178 int stride;
DRC4f24c1a2011-11-03 23:56:10 +0000179 U8 *buf = getPixelsRW(r, &stride);
180 fillRectFn(buf, stride, r, pix);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000181}
182
183void FullFramePixelBuffer::imageRect(const Rect& r, const void* pixels, int srcStride) {
184 int bytesPerPixel = getPF().bpp/8;
185 int destStride;
186 U8* dest = getPixelsRW(r, &destStride);
187 int bytesPerDestRow = bytesPerPixel * destStride;
188 if (!srcStride) srcStride = r.width();
189 int bytesPerSrcRow = bytesPerPixel * srcStride;
190 int bytesPerFill = bytesPerPixel * r.width();
191 const U8* src = (const U8*)pixels;
192 U8* end = dest + (bytesPerDestRow * r.height());
193 while (dest < end) {
194 memcpy(dest, src, bytesPerFill);
195 dest += bytesPerDestRow;
196 src += bytesPerSrcRow;
197 }
198}
199
200void FullFramePixelBuffer::maskRect(const Rect& r, const void* pixels, const void* mask_) {
201 Rect cr = getRect().intersect(r);
202 if (cr.is_empty()) return;
203 int stride;
204 U8* data = getPixelsRW(cr, &stride);
205 U8* mask = (U8*) mask_;
206 int w = cr.width();
207 int h = cr.height();
208 int bpp = getPF().bpp;
209 int pixelStride = r.width();
210 int maskStride = (r.width() + 7) / 8;
211
212 Point offset = Point(cr.tl.x-r.tl.x, cr.tl.y-r.tl.y);
213 mask += offset.y * maskStride;
214 for (int y = 0; y < h; y++) {
215 int cy = offset.y + y;
216 for (int x = 0; x < w; x++) {
217 int cx = offset.x + x;
218 U8* byte = mask + (cx / 8);
219 int bit = 7 - cx % 8;
220 if ((*byte) & (1 << bit)) {
221 switch (bpp) {
222 case 8:
223 ((U8*)data)[y * stride + x] = ((U8*)pixels)[cy * pixelStride + cx];
224 break;
225 case 16:
226 ((U16*)data)[y * stride + x] = ((U16*)pixels)[cy * pixelStride + cx];
227 break;
228 case 32:
229 ((U32*)data)[y * stride + x] = ((U32*)pixels)[cy * pixelStride + cx];
230 break;
231 }
232 }
233 }
234 mask += maskStride;
235 }
236}
237
238void FullFramePixelBuffer::maskRect(const Rect& r, Pixel pixel, const void* mask_) {
239 Rect cr = getRect().intersect(r);
240 if (cr.is_empty()) return;
241 int stride;
242 U8* data = getPixelsRW(cr, &stride);
243 U8* mask = (U8*) mask_;
244 int w = cr.width();
245 int h = cr.height();
246 int bpp = getPF().bpp;
247 int maskStride = (r.width() + 7) / 8;
248
249 Point offset = Point(cr.tl.x-r.tl.x, cr.tl.y-r.tl.y);
250 mask += offset.y * maskStride;
251 for (int y = 0; y < h; y++) {
252 for (int x = 0; x < w; x++) {
253 int cx = offset.x + x;
254 U8* byte = mask + (cx / 8);
255 int bit = 7 - cx % 8;
256 if ((*byte) & (1 << bit)) {
257 switch (bpp) {
258 case 8:
259 ((U8*)data)[y * stride + x] = pixel;
260 break;
261 case 16:
262 ((U16*)data)[y * stride + x] = pixel;
263 break;
264 case 32:
265 ((U32*)data)[y * stride + x] = pixel;
266 break;
267 }
268 }
269 }
270 mask += maskStride;
271 }
272}
273
274void FullFramePixelBuffer::copyRect(const Rect &rect, const Point &move_by_delta) {
275 int stride;
Pierre Ossman45de20d2010-12-21 15:53:42 +0000276 U8* data;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000277 unsigned int bytesPerPixel, bytesPerRow, bytesPerMemCpy;
Pierre Ossman45de20d2010-12-21 15:53:42 +0000278 Rect drect, srect = rect.translate(move_by_delta.negate());
279
280 drect = rect;
281 if (!drect.enclosed_by(getRect())) {
282 vlog.error("Destination rect %dx%d at %d,%d exceeds framebuffer %dx%d",
283 drect.width(), drect.height(), drect.tl.x, drect.tl.y, width_, height_);
284 drect = drect.intersect(getRect());
285 }
286
287 if (drect.is_empty())
288 return;
289
290 srect = drect.translate(move_by_delta.negate());
291 if (!srect.enclosed_by(getRect())) {
292 vlog.error("Source rect %dx%d at %d,%d exceeds framebuffer %dx%d",
293 srect.width(), srect.height(), srect.tl.x, srect.tl.y, width_, height_);
294 srect = srect.intersect(getRect());
295 // Need to readjust the destination now that the area has changed
296 drect = srect.translate(move_by_delta);
297 }
298
299 if (srect.is_empty())
300 return;
301
302 data = getPixelsRW(getRect(), &stride);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000303 bytesPerPixel = getPF().bpp/8;
304 bytesPerRow = stride * bytesPerPixel;
Pierre Ossman45de20d2010-12-21 15:53:42 +0000305 bytesPerMemCpy = drect.width() * bytesPerPixel;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000306 if (move_by_delta.y <= 0) {
Pierre Ossman45de20d2010-12-21 15:53:42 +0000307 U8* dest = data + drect.tl.x*bytesPerPixel + drect.tl.y*bytesPerRow;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000308 U8* src = data + srect.tl.x*bytesPerPixel + srect.tl.y*bytesPerRow;
Pierre Ossman45de20d2010-12-21 15:53:42 +0000309 for (int i=drect.tl.y; i<drect.br.y; i++) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000310 memmove(dest, src, bytesPerMemCpy);
311 dest += bytesPerRow;
312 src += bytesPerRow;
313 }
314 } else {
Pierre Ossman45de20d2010-12-21 15:53:42 +0000315 U8* dest = data + drect.tl.x*bytesPerPixel + (drect.br.y-1)*bytesPerRow;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000316 U8* src = data + srect.tl.x*bytesPerPixel + (srect.br.y-1)*bytesPerRow;
Pierre Ossman45de20d2010-12-21 15:53:42 +0000317 for (int i=drect.tl.y; i<drect.br.y; i++) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000318 memmove(dest, src, bytesPerMemCpy);
319 dest -= bytesPerRow;
320 src -= bytesPerRow;
321 }
322 }
323}
324
325
326// -=- Managed pixel buffer class
327// Automatically allocates enough space for the specified format & area
328
329ManagedPixelBuffer::ManagedPixelBuffer()
330 : datasize(0), own_colourmap(false)
331{
332 checkDataSize();
333};
334
335ManagedPixelBuffer::ManagedPixelBuffer(const PixelFormat& pf, int w, int h)
336 : FullFramePixelBuffer(pf, w, h, 0, 0), datasize(0), own_colourmap(false)
337{
338 checkDataSize();
339};
340
341ManagedPixelBuffer::~ManagedPixelBuffer() {
342 if (data) delete [] data;
343 if (colourmap && own_colourmap) delete colourmap;
344};
345
346
347void
348ManagedPixelBuffer::setPF(const PixelFormat &pf) {
Pierre Ossman1ed4d502014-01-07 15:28:45 +0000349 FullFramePixelBuffer::setPF(pf); checkDataSize();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000350};
351void
352ManagedPixelBuffer::setSize(int w, int h) {
353 width_ = w; height_ = h; checkDataSize();
354};
355
356
357void
358ManagedPixelBuffer::setColourMap(ColourMap* cm, bool own_cm) {
359 if (colourmap && own_colourmap) delete colourmap;
360 colourmap = cm;
361 own_colourmap = own_cm;
362}
363
364inline void
365ManagedPixelBuffer::checkDataSize() {
366 unsigned long new_datasize = width_ * height_ * (format.bpp/8);
367 if (datasize < new_datasize) {
368 vlog.debug("reallocating managed buffer (%dx%d)", width_, height_);
369 if (data) {
370 delete [] data;
371 datasize = 0; data = 0;
372 }
373 if (new_datasize) {
374 data = new U8[new_datasize];
375 if (!data)
376 throw Exception("rfb::ManagedPixelBuffer unable to allocate buffer");
377 datasize = new_datasize;
378 }
379 }
380};