blob: f444aa02a70bb831b980f43527e3fdf32dcc1786 [file] [log] [blame]
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
Pierre Ossman2e5a1062014-01-30 17:57:27 +01002 * Copyright 2014 Pierre Ossman for Cendio AB
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00003 *
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// -=- PixelBuffer.cxx
21//
22// The PixelBuffer class encapsulates the PixelFormat and dimensions
23// of a block of pixel data.
24
25#include <rfb/Exception.h>
26#include <rfb/LogWriter.h>
27#include <rfb/PixelBuffer.h>
28
29using namespace rfb;
30using namespace rdr;
31
32static LogWriter vlog("PixelBuffer");
33
34
35// -=- Generic pixel buffer class
36
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +010037PixelBuffer::PixelBuffer(const PixelFormat& pf, int w, int h)
38 : format(pf), width_(w), height_(h) {}
39PixelBuffer::PixelBuffer() : width_(0), height_(0) {}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000040
41PixelBuffer::~PixelBuffer() {}
42
43
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000044void
Pierre Ossmand5ab3e32017-01-18 13:34:13 +010045PixelBuffer::getImage(void* imageBuf, const Rect& r, int outStride) const
46{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000047 int inStride;
Pierre Ossmand5ab3e32017-01-18 13:34:13 +010048 const U8* data;
49 int bytesPerPixel, inBytesPerRow, outBytesPerRow, bytesPerMemCpy;
50 U8* imageBufPos;
51 const U8* end;
52
53 if (!r.enclosed_by(getRect()))
54 throw rfb::Exception("Source rect %dx%d at %d,%d exceeds framebuffer %dx%d",
55 r.width(), r.height(),
56 r.tl.x, r.tl.y, width_, height_);
57
58 data = getBuffer(r, &inStride);
59
60 bytesPerPixel = format.bpp/8;
61 inBytesPerRow = inStride * bytesPerPixel;
62
63 if (!outStride)
64 outStride = r.width();
65 outBytesPerRow = outStride * bytesPerPixel;
66 bytesPerMemCpy = r.width() * bytesPerPixel;
67
68 imageBufPos = (U8*)imageBuf;
69 end = data + (inBytesPerRow * r.height());
70
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000071 while (data < end) {
72 memcpy(imageBufPos, data, bytesPerMemCpy);
73 imageBufPos += outBytesPerRow;
74 data += inBytesPerRow;
75 }
76}
77
Pierre Ossman9cbdec62014-02-12 13:24:15 +010078void PixelBuffer::getImage(const PixelFormat& pf, void* imageBuf,
Pierre Ossmand4f718d2014-02-13 14:37:25 +010079 const Rect& r, int stride) const
Pierre Ossman9cbdec62014-02-12 13:24:15 +010080{
81 const rdr::U8* srcBuffer;
82 int srcStride;
83
84 if (format.equal(pf)) {
85 getImage(imageBuf, r, stride);
86 return;
87 }
88
Pierre Ossmand5ab3e32017-01-18 13:34:13 +010089 if (!r.enclosed_by(getRect()))
90 throw rfb::Exception("Source rect %dx%d at %d,%d exceeds framebuffer %dx%d",
91 r.width(), r.height(),
92 r.tl.x, r.tl.y, width_, height_);
93
Pierre Ossman9cbdec62014-02-12 13:24:15 +010094 if (stride == 0)
95 stride = r.width();
96
97 srcBuffer = getBuffer(r, &srcStride);
98
99 pf.bufferFromBuffer((U8*)imageBuf, format, srcBuffer, r.width(), r.height(),
100 stride, srcStride);
101}
102
Pierre Ossmana32040d2014-02-06 16:31:10 +0100103// -=- Modifiable generic pixel buffer class
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000104
Pierre Ossmana32040d2014-02-06 16:31:10 +0100105ModifiablePixelBuffer::ModifiablePixelBuffer(const PixelFormat& pf,
106 int w, int h)
107 : PixelBuffer(pf, w, h)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000108{
Pierre Ossman1ed4d502014-01-07 15:28:45 +0000109}
110
Pierre Ossmana32040d2014-02-06 16:31:10 +0100111ModifiablePixelBuffer::ModifiablePixelBuffer()
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000112{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000113}
114
Pierre Ossmana32040d2014-02-06 16:31:10 +0100115ModifiablePixelBuffer::~ModifiablePixelBuffer()
116{
117}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000118
Pierre Ossman56f99d62015-06-05 12:57:02 +0200119void ModifiablePixelBuffer::fillRect(const Rect& r, const void* pix)
Pierre Ossmana32040d2014-02-06 16:31:10 +0100120{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000121 int stride;
Pierre Ossmana43a3f42015-02-06 13:51:35 +0100122 U8 *buf;
Pierre Ossman5c1a1532014-01-30 16:59:14 +0100123 int w, h, b;
124
Pierre Ossmand5ab3e32017-01-18 13:34:13 +0100125 if (!r.enclosed_by(getRect()))
126 throw rfb::Exception("Destination rect %dx%d at %d,%d exceeds framebuffer %dx%d",
127 r.width(), r.height(), r.tl.x, r.tl.y, width_, height_);
Michal Srb18c02012017-01-13 16:32:23 +0200128
Pierre Ossmand5ab3e32017-01-18 13:34:13 +0100129 w = r.width();
130 h = r.height();
Pierre Ossman5c1a1532014-01-30 16:59:14 +0100131 b = format.bpp/8;
132
Pierre Ossmana43a3f42015-02-06 13:51:35 +0100133 if (h == 0)
134 return;
Pierre Ossman5c1a1532014-01-30 16:59:14 +0100135
Pierre Ossmand5ab3e32017-01-18 13:34:13 +0100136 buf = getBufferRW(r, &stride);
Pierre Ossmana43a3f42015-02-06 13:51:35 +0100137
138 if (b == 1) {
139 while (h--) {
Pierre Ossman56f99d62015-06-05 12:57:02 +0200140 memset(buf, *(const U8*)pix, w);
Pierre Ossmana43a3f42015-02-06 13:51:35 +0100141 buf += stride * b;
142 }
143 } else {
Pierre Ossman56f99d62015-06-05 12:57:02 +0200144 U8 *start;
Pierre Ossmana43a3f42015-02-06 13:51:35 +0100145 int w1;
146
147 start = buf;
148
Pierre Ossmana43a3f42015-02-06 13:51:35 +0100149 w1 = w;
150 while (w1--) {
Pierre Ossman56f99d62015-06-05 12:57:02 +0200151 memcpy(buf, pix, b);
Pierre Ossman5c1a1532014-01-30 16:59:14 +0100152 buf += b;
153 }
154 buf += (stride - w) * b;
Pierre Ossmana43a3f42015-02-06 13:51:35 +0100155 h--;
156
157 while (h--) {
158 memcpy(buf, start, w * b);
159 buf += stride * b;
160 }
Pierre Ossman5c1a1532014-01-30 16:59:14 +0100161 }
Pierre Ossmana32040d2014-02-06 16:31:10 +0100162
Pierre Ossmand5ab3e32017-01-18 13:34:13 +0100163 commitBufferRW(r);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000164}
165
Pierre Ossmana32040d2014-02-06 16:31:10 +0100166void ModifiablePixelBuffer::imageRect(const Rect& r,
167 const void* pixels, int srcStride)
168{
Pierre Ossmand5ab3e32017-01-18 13:34:13 +0100169 U8* dest;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000170 int destStride;
Pierre Ossmand5ab3e32017-01-18 13:34:13 +0100171 int bytesPerPixel, bytesPerDestRow, bytesPerSrcRow, bytesPerFill;
172 const U8* src;
173 U8* end;
174
175 if (!r.enclosed_by(getRect()))
176 throw rfb::Exception("Destination rect %dx%d at %d,%d exceeds framebuffer %dx%d",
177 r.width(), r.height(),
178 r.tl.x, r.tl.y, width_, height_);
179
180 bytesPerPixel = getPF().bpp/8;
181
182 dest = getBufferRW(r, &destStride);
183
184 bytesPerDestRow = bytesPerPixel * destStride;
185
186 if (!srcStride)
187 srcStride = r.width();
188 bytesPerSrcRow = bytesPerPixel * srcStride;
189 bytesPerFill = bytesPerPixel * r.width();
190
191 src = (const U8*)pixels;
192 end = dest + (bytesPerDestRow * r.height());
193
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000194 while (dest < end) {
195 memcpy(dest, src, bytesPerFill);
196 dest += bytesPerDestRow;
197 src += bytesPerSrcRow;
198 }
Pierre Ossmand5ab3e32017-01-18 13:34:13 +0100199
Pierre Ossmana32040d2014-02-06 16:31:10 +0100200 commitBufferRW(r);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000201}
202
Pierre Ossmana32040d2014-02-06 16:31:10 +0100203void ModifiablePixelBuffer::maskRect(const Rect& r,
204 const void* pixels, const void* mask_)
205{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000206 int stride;
Pierre Ossmand5ab3e32017-01-18 13:34:13 +0100207 U8* data;
208 U8* mask;
209 int w, h, bpp, pixelStride, maskStride;
210 Point offset;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000211
Pierre Ossmand5ab3e32017-01-18 13:34:13 +0100212 if (!r.enclosed_by(getRect()))
213 throw rfb::Exception("Destination rect %dx%d at %d,%d exceeds framebuffer %dx%d",
214 r.width(), r.height(),
215 r.tl.x, r.tl.y, width_, height_);
216
217 data = getBufferRW(r, &stride);
218 mask = (U8*) mask_;
219
220 w = r.width();
221 h = r.height();
222 bpp = getPF().bpp;
223 pixelStride = r.width();
224 maskStride = (r.width() + 7) / 8;
225
226 offset = Point(r.tl.x-r.tl.x, r.tl.y-r.tl.y);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000227 mask += offset.y * maskStride;
228 for (int y = 0; y < h; y++) {
229 int cy = offset.y + y;
230 for (int x = 0; x < w; x++) {
231 int cx = offset.x + x;
232 U8* byte = mask + (cx / 8);
233 int bit = 7 - cx % 8;
234 if ((*byte) & (1 << bit)) {
235 switch (bpp) {
236 case 8:
237 ((U8*)data)[y * stride + x] = ((U8*)pixels)[cy * pixelStride + cx];
238 break;
239 case 16:
240 ((U16*)data)[y * stride + x] = ((U16*)pixels)[cy * pixelStride + cx];
241 break;
242 case 32:
243 ((U32*)data)[y * stride + x] = ((U32*)pixels)[cy * pixelStride + cx];
244 break;
245 }
246 }
247 }
248 mask += maskStride;
249 }
Pierre Ossmana32040d2014-02-06 16:31:10 +0100250
Pierre Ossmand5ab3e32017-01-18 13:34:13 +0100251 commitBufferRW(r);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000252}
253
Pierre Ossmana32040d2014-02-06 16:31:10 +0100254void ModifiablePixelBuffer::maskRect(const Rect& r,
255 Pixel pixel, const void* mask_)
256{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000257 int stride;
Pierre Ossmand5ab3e32017-01-18 13:34:13 +0100258 U8* data;
259 U8* mask;
260 int w, h, bpp, maskStride;
261 Point offset;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000262
Pierre Ossmand5ab3e32017-01-18 13:34:13 +0100263 if (!r.enclosed_by(getRect()))
264 throw rfb::Exception("Destination rect %dx%d at %d,%d exceeds framebuffer %dx%d",
265 r.width(), r.height(),
266 r.tl.x, r.tl.y, width_, height_);
267
268 data = getBufferRW(r, &stride);
269 mask = (U8*) mask_;
270
271 w = r.width();
272 h = r.height();
273 bpp = getPF().bpp;
274 maskStride = (r.width() + 7) / 8;
275
276 offset = Point(r.tl.x-r.tl.x, r.tl.y-r.tl.y);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000277 mask += offset.y * maskStride;
278 for (int y = 0; y < h; y++) {
279 for (int x = 0; x < w; x++) {
280 int cx = offset.x + x;
281 U8* byte = mask + (cx / 8);
282 int bit = 7 - cx % 8;
283 if ((*byte) & (1 << bit)) {
284 switch (bpp) {
285 case 8:
286 ((U8*)data)[y * stride + x] = pixel;
287 break;
288 case 16:
289 ((U16*)data)[y * stride + x] = pixel;
290 break;
291 case 32:
292 ((U32*)data)[y * stride + x] = pixel;
293 break;
294 }
295 }
296 }
297 mask += maskStride;
298 }
Pierre Ossmana32040d2014-02-06 16:31:10 +0100299
Pierre Ossmand5ab3e32017-01-18 13:34:13 +0100300 commitBufferRW(r);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000301}
302
Pierre Ossmana32040d2014-02-06 16:31:10 +0100303void ModifiablePixelBuffer::copyRect(const Rect &rect,
304 const Point &move_by_delta)
305{
Pierre Ossman8163f542014-02-13 09:42:48 +0100306 int srcStride, dstStride;
307 const U8* srcData;
308 U8* dstData;
309
310 Rect drect, srect;
Pierre Ossman45de20d2010-12-21 15:53:42 +0000311
312 drect = rect;
Pierre Ossmand5ab3e32017-01-18 13:34:13 +0100313 if (!drect.enclosed_by(getRect()))
314 throw rfb::Exception("Destination rect %dx%d at %d,%d exceeds framebuffer %dx%d",
315 drect.width(), drect.height(),
316 drect.tl.x, drect.tl.y, width_, height_);
Pierre Ossman45de20d2010-12-21 15:53:42 +0000317
318 srect = drect.translate(move_by_delta.negate());
Pierre Ossmand5ab3e32017-01-18 13:34:13 +0100319 if (!srect.enclosed_by(getRect()))
320 throw rfb::Exception("Source rect %dx%d at %d,%d exceeds framebuffer %dx%d",
321 srect.width(), srect.height(),
322 srect.tl.x, srect.tl.y, width_, height_);
Pierre Ossman45de20d2010-12-21 15:53:42 +0000323
Pierre Ossman8163f542014-02-13 09:42:48 +0100324 srcData = getBuffer(srect, &srcStride);
325 dstData = getBufferRW(drect, &dstStride);
326
327 if (move_by_delta.y == 0) {
328 // Possible overlap. Be careful and use memmove().
329 int h = drect.height();
330 while (h--) {
331 memmove(dstData, srcData, drect.width() * format.bpp/8);
332 dstData += dstStride * format.bpp/8;
333 srcData += srcStride * format.bpp/8;
334 }
335 } else if (move_by_delta.y < 0) {
336 // The data shifted upwards. Copy from top to bottom.
337 int h = drect.height();
338 while (h--) {
339 memcpy(dstData, srcData, drect.width() * format.bpp/8);
340 dstData += dstStride * format.bpp/8;
341 srcData += srcStride * format.bpp/8;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000342 }
343 } else {
Pierre Ossman8163f542014-02-13 09:42:48 +0100344 // The data shifted downwards. Copy from bottom to top.
345 int h = drect.height();
346 dstData += (h-1) * dstStride * format.bpp/8;
347 srcData += (h-1) * srcStride * format.bpp/8;
348 while (h--) {
349 memcpy(dstData, srcData, drect.width() * format.bpp/8);
350 dstData -= dstStride * format.bpp/8;
351 srcData -= srcStride * format.bpp/8;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000352 }
353 }
Pierre Ossman8163f542014-02-13 09:42:48 +0100354
355 commitBufferRW(drect);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000356}
357
Pierre Ossman9da47f82014-02-13 10:38:48 +0100358void ModifiablePixelBuffer::fillRect(const PixelFormat& pf, const Rect &dest,
Pierre Ossman56f99d62015-06-05 12:57:02 +0200359 const void* pix)
Pierre Ossman9da47f82014-02-13 10:38:48 +0100360{
Pierre Ossman56f99d62015-06-05 12:57:02 +0200361 rdr::U8 buf[4];
362 format.bufferFromBuffer(buf, pf, (const rdr::U8*)pix, 1);
363 fillRect(dest, buf);
Pierre Ossman9da47f82014-02-13 10:38:48 +0100364}
365
366void ModifiablePixelBuffer::imageRect(const PixelFormat& pf, const Rect &dest,
367 const void* pixels, int stride)
368{
369 rdr::U8* dstBuffer;
370 int dstStride;
371
Pierre Ossmand5ab3e32017-01-18 13:34:13 +0100372 if (!dest.enclosed_by(getRect()))
373 throw rfb::Exception("Destination rect %dx%d at %d,%d exceeds framebuffer %dx%d",
374 dest.width(), dest.height(),
375 dest.tl.x, dest.tl.y, width_, height_);
376
Pierre Ossman9da47f82014-02-13 10:38:48 +0100377 if (stride == 0)
378 stride = dest.width();
379
380 dstBuffer = getBufferRW(dest, &dstStride);
381 format.bufferFromBuffer(dstBuffer, pf, (const rdr::U8*)pixels,
382 dest.width(), dest.height(),
383 dstStride, stride);
384 commitBufferRW(dest);
385}
386
Pierre Ossmana32040d2014-02-06 16:31:10 +0100387// -=- Simple pixel buffer with a continuous block of memory
388
389FullFramePixelBuffer::FullFramePixelBuffer(const PixelFormat& pf, int w, int h,
390 rdr::U8* data_, int stride_)
391 : ModifiablePixelBuffer(pf, w, h), data(data_), stride(stride_)
392{
393}
394
395FullFramePixelBuffer::FullFramePixelBuffer() : data(0) {}
396
397FullFramePixelBuffer::~FullFramePixelBuffer() {}
398
399rdr::U8* FullFramePixelBuffer::getBufferRW(const Rect& r, int* stride_)
400{
Pierre Ossmand5ab3e32017-01-18 13:34:13 +0100401 if (!r.enclosed_by(getRect()))
402 throw rfb::Exception("Pixel buffer request %dx%d at %d,%d exceeds framebuffer %dx%d",
403 r.width(), r.height(),
404 r.tl.x, r.tl.y, width_, height_);
405
Pierre Ossmana32040d2014-02-06 16:31:10 +0100406 *stride_ = stride;
407 return &data[(r.tl.x + (r.tl.y * stride)) * format.bpp/8];
408}
409
410void FullFramePixelBuffer::commitBufferRW(const Rect& r)
411{
412}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000413
Pierre Ossmand4f718d2014-02-13 14:37:25 +0100414const rdr::U8* FullFramePixelBuffer::getBuffer(const Rect& r, int* stride_) const
415{
Pierre Ossmand5ab3e32017-01-18 13:34:13 +0100416 if (!r.enclosed_by(getRect()))
417 throw rfb::Exception("Pixel buffer request %dx%d at %d,%d exceeds framebuffer %dx%d",
418 r.width(), r.height(),
419 r.tl.x, r.tl.y, width_, height_);
420
Pierre Ossmand4f718d2014-02-13 14:37:25 +0100421 *stride_ = stride;
422 return &data[(r.tl.x + (r.tl.y * stride)) * format.bpp/8];
423}
424
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000425// -=- Managed pixel buffer class
426// Automatically allocates enough space for the specified format & area
427
428ManagedPixelBuffer::ManagedPixelBuffer()
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100429 : datasize(0)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000430{
431 checkDataSize();
432};
433
434ManagedPixelBuffer::ManagedPixelBuffer(const PixelFormat& pf, int w, int h)
Pierre Ossman2e5a1062014-01-30 17:57:27 +0100435 : FullFramePixelBuffer(pf, w, h, NULL, w), datasize(0)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000436{
437 checkDataSize();
438};
439
440ManagedPixelBuffer::~ManagedPixelBuffer() {
441 if (data) delete [] data;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000442};
443
444
445void
446ManagedPixelBuffer::setPF(const PixelFormat &pf) {
Pierre Ossman5c1a1532014-01-30 16:59:14 +0100447 format = pf; checkDataSize();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000448};
449void
450ManagedPixelBuffer::setSize(int w, int h) {
Pierre Ossman2e5a1062014-01-30 17:57:27 +0100451 width_ = w; height_ = h; stride = w; checkDataSize();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000452};
453
454
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000455inline void
456ManagedPixelBuffer::checkDataSize() {
457 unsigned long new_datasize = width_ * height_ * (format.bpp/8);
458 if (datasize < new_datasize) {
459 vlog.debug("reallocating managed buffer (%dx%d)", width_, height_);
460 if (data) {
461 delete [] data;
462 datasize = 0; data = 0;
463 }
464 if (new_datasize) {
465 data = new U8[new_datasize];
466 if (!data)
467 throw Exception("rfb::ManagedPixelBuffer unable to allocate buffer");
468 datasize = new_datasize;
469 }
470 }
471};