blob: 7f3df6cba883f6acbf8f26c541eaefd63f917a50 [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 Ossmand4f718d2014-02-13 14:37:25 +010045PixelBuffer::getImage(void* imageBuf, const Rect& r, int outStride) const {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000046 int inStride;
Pierre Ossman945cdda2014-01-28 14:13:12 +010047 const U8* data = getBuffer(r, &inStride);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000048 // We assume that the specified rectangle is pre-clipped to the buffer
49 int bytesPerPixel = format.bpp/8;
50 int inBytesPerRow = inStride * bytesPerPixel;
51 if (!outStride) outStride = r.width();
52 int outBytesPerRow = outStride * bytesPerPixel;
53 int bytesPerMemCpy = r.width() * bytesPerPixel;
54 U8* imageBufPos = (U8*)imageBuf;
55 const U8* end = data + (inBytesPerRow * r.height());
56 while (data < end) {
57 memcpy(imageBufPos, data, bytesPerMemCpy);
58 imageBufPos += outBytesPerRow;
59 data += inBytesPerRow;
60 }
61}
62
Pierre Ossman9cbdec62014-02-12 13:24:15 +010063void PixelBuffer::getImage(const PixelFormat& pf, void* imageBuf,
Pierre Ossmand4f718d2014-02-13 14:37:25 +010064 const Rect& r, int stride) const
Pierre Ossman9cbdec62014-02-12 13:24:15 +010065{
66 const rdr::U8* srcBuffer;
67 int srcStride;
68
69 if (format.equal(pf)) {
70 getImage(imageBuf, r, stride);
71 return;
72 }
73
74 if (stride == 0)
75 stride = r.width();
76
77 srcBuffer = getBuffer(r, &srcStride);
78
79 pf.bufferFromBuffer((U8*)imageBuf, format, srcBuffer, r.width(), r.height(),
80 stride, srcStride);
81}
82
Pierre Ossmana32040d2014-02-06 16:31:10 +010083// -=- Modifiable generic pixel buffer class
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000084
Pierre Ossmana32040d2014-02-06 16:31:10 +010085ModifiablePixelBuffer::ModifiablePixelBuffer(const PixelFormat& pf,
86 int w, int h)
87 : PixelBuffer(pf, w, h)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000088{
Pierre Ossman1ed4d502014-01-07 15:28:45 +000089}
90
Pierre Ossmana32040d2014-02-06 16:31:10 +010091ModifiablePixelBuffer::ModifiablePixelBuffer()
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000092{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000093}
94
Pierre Ossmana32040d2014-02-06 16:31:10 +010095ModifiablePixelBuffer::~ModifiablePixelBuffer()
96{
97}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000098
Pierre Ossman56f99d62015-06-05 12:57:02 +020099void ModifiablePixelBuffer::fillRect(const Rect& r, const void* pix)
Pierre Ossmana32040d2014-02-06 16:31:10 +0100100{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000101 int stride;
Pierre Ossmana43a3f42015-02-06 13:51:35 +0100102 U8 *buf;
Pierre Ossman5c1a1532014-01-30 16:59:14 +0100103 int w, h, b;
Michal Srb18c02012017-01-13 16:32:23 +0200104 Rect drect;
Pierre Ossman5c1a1532014-01-30 16:59:14 +0100105
Michal Srb18c02012017-01-13 16:32:23 +0200106 drect = r;
107 if (!drect.enclosed_by(getRect())) {
108 vlog.error("Destination rect %dx%d at %d,%d exceeds framebuffer %dx%d",
109 drect.width(), drect.height(), drect.tl.x, drect.tl.y, width_, height_);
110 drect = drect.intersect(getRect());
111 }
112
113 if (drect.is_empty())
114 return;
115
116 w = drect.width();
117 h = drect.height();
Pierre Ossman5c1a1532014-01-30 16:59:14 +0100118 b = format.bpp/8;
119
Pierre Ossmana43a3f42015-02-06 13:51:35 +0100120 if (h == 0)
121 return;
Pierre Ossman5c1a1532014-01-30 16:59:14 +0100122
Michal Srb18c02012017-01-13 16:32:23 +0200123 buf = getBufferRW(drect, &stride);
Pierre Ossmana43a3f42015-02-06 13:51:35 +0100124
125 if (b == 1) {
126 while (h--) {
Pierre Ossman56f99d62015-06-05 12:57:02 +0200127 memset(buf, *(const U8*)pix, w);
Pierre Ossmana43a3f42015-02-06 13:51:35 +0100128 buf += stride * b;
129 }
130 } else {
Pierre Ossman56f99d62015-06-05 12:57:02 +0200131 U8 *start;
Pierre Ossmana43a3f42015-02-06 13:51:35 +0100132 int w1;
133
134 start = buf;
135
Pierre Ossmana43a3f42015-02-06 13:51:35 +0100136 w1 = w;
137 while (w1--) {
Pierre Ossman56f99d62015-06-05 12:57:02 +0200138 memcpy(buf, pix, b);
Pierre Ossman5c1a1532014-01-30 16:59:14 +0100139 buf += b;
140 }
141 buf += (stride - w) * b;
Pierre Ossmana43a3f42015-02-06 13:51:35 +0100142 h--;
143
144 while (h--) {
145 memcpy(buf, start, w * b);
146 buf += stride * b;
147 }
Pierre Ossman5c1a1532014-01-30 16:59:14 +0100148 }
Pierre Ossmana32040d2014-02-06 16:31:10 +0100149
Michal Srb18c02012017-01-13 16:32:23 +0200150 commitBufferRW(drect);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000151}
152
Pierre Ossmana32040d2014-02-06 16:31:10 +0100153void ModifiablePixelBuffer::imageRect(const Rect& r,
154 const void* pixels, int srcStride)
155{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000156 int bytesPerPixel = getPF().bpp/8;
157 int destStride;
Pierre Ossman945cdda2014-01-28 14:13:12 +0100158 U8* dest = getBufferRW(r, &destStride);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000159 int bytesPerDestRow = bytesPerPixel * destStride;
160 if (!srcStride) srcStride = r.width();
161 int bytesPerSrcRow = bytesPerPixel * srcStride;
162 int bytesPerFill = bytesPerPixel * r.width();
163 const U8* src = (const U8*)pixels;
164 U8* end = dest + (bytesPerDestRow * r.height());
165 while (dest < end) {
166 memcpy(dest, src, bytesPerFill);
167 dest += bytesPerDestRow;
168 src += bytesPerSrcRow;
169 }
Pierre Ossmana32040d2014-02-06 16:31:10 +0100170 commitBufferRW(r);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000171}
172
Pierre Ossmana32040d2014-02-06 16:31:10 +0100173void ModifiablePixelBuffer::maskRect(const Rect& r,
174 const void* pixels, const void* mask_)
175{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000176 Rect cr = getRect().intersect(r);
177 if (cr.is_empty()) return;
178 int stride;
Pierre Ossman945cdda2014-01-28 14:13:12 +0100179 U8* data = getBufferRW(cr, &stride);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000180 U8* mask = (U8*) mask_;
181 int w = cr.width();
182 int h = cr.height();
183 int bpp = getPF().bpp;
184 int pixelStride = r.width();
185 int maskStride = (r.width() + 7) / 8;
186
187 Point offset = Point(cr.tl.x-r.tl.x, cr.tl.y-r.tl.y);
188 mask += offset.y * maskStride;
189 for (int y = 0; y < h; y++) {
190 int cy = offset.y + y;
191 for (int x = 0; x < w; x++) {
192 int cx = offset.x + x;
193 U8* byte = mask + (cx / 8);
194 int bit = 7 - cx % 8;
195 if ((*byte) & (1 << bit)) {
196 switch (bpp) {
197 case 8:
198 ((U8*)data)[y * stride + x] = ((U8*)pixels)[cy * pixelStride + cx];
199 break;
200 case 16:
201 ((U16*)data)[y * stride + x] = ((U16*)pixels)[cy * pixelStride + cx];
202 break;
203 case 32:
204 ((U32*)data)[y * stride + x] = ((U32*)pixels)[cy * pixelStride + cx];
205 break;
206 }
207 }
208 }
209 mask += maskStride;
210 }
Pierre Ossmana32040d2014-02-06 16:31:10 +0100211
212 commitBufferRW(cr);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000213}
214
Pierre Ossmana32040d2014-02-06 16:31:10 +0100215void ModifiablePixelBuffer::maskRect(const Rect& r,
216 Pixel pixel, const void* mask_)
217{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000218 Rect cr = getRect().intersect(r);
219 if (cr.is_empty()) return;
220 int stride;
Pierre Ossman945cdda2014-01-28 14:13:12 +0100221 U8* data = getBufferRW(cr, &stride);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000222 U8* mask = (U8*) mask_;
223 int w = cr.width();
224 int h = cr.height();
225 int bpp = getPF().bpp;
226 int maskStride = (r.width() + 7) / 8;
227
228 Point offset = Point(cr.tl.x-r.tl.x, cr.tl.y-r.tl.y);
229 mask += offset.y * maskStride;
230 for (int y = 0; y < h; y++) {
231 for (int x = 0; x < w; x++) {
232 int cx = offset.x + x;
233 U8* byte = mask + (cx / 8);
234 int bit = 7 - cx % 8;
235 if ((*byte) & (1 << bit)) {
236 switch (bpp) {
237 case 8:
238 ((U8*)data)[y * stride + x] = pixel;
239 break;
240 case 16:
241 ((U16*)data)[y * stride + x] = pixel;
242 break;
243 case 32:
244 ((U32*)data)[y * stride + x] = pixel;
245 break;
246 }
247 }
248 }
249 mask += maskStride;
250 }
Pierre Ossmana32040d2014-02-06 16:31:10 +0100251
252 commitBufferRW(cr);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000253}
254
Pierre Ossmana32040d2014-02-06 16:31:10 +0100255void ModifiablePixelBuffer::copyRect(const Rect &rect,
256 const Point &move_by_delta)
257{
Pierre Ossman8163f542014-02-13 09:42:48 +0100258 int srcStride, dstStride;
259 const U8* srcData;
260 U8* dstData;
261
262 Rect drect, srect;
Pierre Ossman45de20d2010-12-21 15:53:42 +0000263
264 drect = rect;
265 if (!drect.enclosed_by(getRect())) {
266 vlog.error("Destination rect %dx%d at %d,%d exceeds framebuffer %dx%d",
267 drect.width(), drect.height(), drect.tl.x, drect.tl.y, width_, height_);
268 drect = drect.intersect(getRect());
269 }
270
271 if (drect.is_empty())
272 return;
273
274 srect = drect.translate(move_by_delta.negate());
275 if (!srect.enclosed_by(getRect())) {
276 vlog.error("Source rect %dx%d at %d,%d exceeds framebuffer %dx%d",
277 srect.width(), srect.height(), srect.tl.x, srect.tl.y, width_, height_);
278 srect = srect.intersect(getRect());
279 // Need to readjust the destination now that the area has changed
280 drect = srect.translate(move_by_delta);
281 }
282
283 if (srect.is_empty())
284 return;
285
Pierre Ossman8163f542014-02-13 09:42:48 +0100286 srcData = getBuffer(srect, &srcStride);
287 dstData = getBufferRW(drect, &dstStride);
288
289 if (move_by_delta.y == 0) {
290 // Possible overlap. Be careful and use memmove().
291 int h = drect.height();
292 while (h--) {
293 memmove(dstData, srcData, drect.width() * format.bpp/8);
294 dstData += dstStride * format.bpp/8;
295 srcData += srcStride * format.bpp/8;
296 }
297 } else if (move_by_delta.y < 0) {
298 // The data shifted upwards. Copy from top to bottom.
299 int h = drect.height();
300 while (h--) {
301 memcpy(dstData, srcData, drect.width() * format.bpp/8);
302 dstData += dstStride * format.bpp/8;
303 srcData += srcStride * format.bpp/8;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000304 }
305 } else {
Pierre Ossman8163f542014-02-13 09:42:48 +0100306 // The data shifted downwards. Copy from bottom to top.
307 int h = drect.height();
308 dstData += (h-1) * dstStride * format.bpp/8;
309 srcData += (h-1) * srcStride * format.bpp/8;
310 while (h--) {
311 memcpy(dstData, srcData, drect.width() * format.bpp/8);
312 dstData -= dstStride * format.bpp/8;
313 srcData -= srcStride * format.bpp/8;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000314 }
315 }
Pierre Ossman8163f542014-02-13 09:42:48 +0100316
317 commitBufferRW(drect);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000318}
319
Pierre Ossman9da47f82014-02-13 10:38:48 +0100320void ModifiablePixelBuffer::fillRect(const PixelFormat& pf, const Rect &dest,
Pierre Ossman56f99d62015-06-05 12:57:02 +0200321 const void* pix)
Pierre Ossman9da47f82014-02-13 10:38:48 +0100322{
Pierre Ossman56f99d62015-06-05 12:57:02 +0200323 rdr::U8 buf[4];
324 format.bufferFromBuffer(buf, pf, (const rdr::U8*)pix, 1);
325 fillRect(dest, buf);
Pierre Ossman9da47f82014-02-13 10:38:48 +0100326}
327
328void ModifiablePixelBuffer::imageRect(const PixelFormat& pf, const Rect &dest,
329 const void* pixels, int stride)
330{
331 rdr::U8* dstBuffer;
332 int dstStride;
333
334 if (stride == 0)
335 stride = dest.width();
336
337 dstBuffer = getBufferRW(dest, &dstStride);
338 format.bufferFromBuffer(dstBuffer, pf, (const rdr::U8*)pixels,
339 dest.width(), dest.height(),
340 dstStride, stride);
341 commitBufferRW(dest);
342}
343
Pierre Ossmana32040d2014-02-06 16:31:10 +0100344// -=- Simple pixel buffer with a continuous block of memory
345
346FullFramePixelBuffer::FullFramePixelBuffer(const PixelFormat& pf, int w, int h,
347 rdr::U8* data_, int stride_)
348 : ModifiablePixelBuffer(pf, w, h), data(data_), stride(stride_)
349{
350}
351
352FullFramePixelBuffer::FullFramePixelBuffer() : data(0) {}
353
354FullFramePixelBuffer::~FullFramePixelBuffer() {}
355
356rdr::U8* FullFramePixelBuffer::getBufferRW(const Rect& r, int* stride_)
357{
358 *stride_ = stride;
359 return &data[(r.tl.x + (r.tl.y * stride)) * format.bpp/8];
360}
361
362void FullFramePixelBuffer::commitBufferRW(const Rect& r)
363{
364}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000365
Pierre Ossmand4f718d2014-02-13 14:37:25 +0100366const rdr::U8* FullFramePixelBuffer::getBuffer(const Rect& r, int* stride_) const
367{
368 *stride_ = stride;
369 return &data[(r.tl.x + (r.tl.y * stride)) * format.bpp/8];
370}
371
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000372// -=- Managed pixel buffer class
373// Automatically allocates enough space for the specified format & area
374
375ManagedPixelBuffer::ManagedPixelBuffer()
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100376 : datasize(0)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000377{
378 checkDataSize();
379};
380
381ManagedPixelBuffer::ManagedPixelBuffer(const PixelFormat& pf, int w, int h)
Pierre Ossman2e5a1062014-01-30 17:57:27 +0100382 : FullFramePixelBuffer(pf, w, h, NULL, w), datasize(0)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000383{
384 checkDataSize();
385};
386
387ManagedPixelBuffer::~ManagedPixelBuffer() {
388 if (data) delete [] data;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000389};
390
391
392void
393ManagedPixelBuffer::setPF(const PixelFormat &pf) {
Pierre Ossman5c1a1532014-01-30 16:59:14 +0100394 format = pf; checkDataSize();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000395};
396void
397ManagedPixelBuffer::setSize(int w, int h) {
Pierre Ossman2e5a1062014-01-30 17:57:27 +0100398 width_ = w; height_ = h; stride = w; checkDataSize();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000399};
400
401
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000402inline void
403ManagedPixelBuffer::checkDataSize() {
404 unsigned long new_datasize = width_ * height_ * (format.bpp/8);
405 if (datasize < new_datasize) {
406 vlog.debug("reallocating managed buffer (%dx%d)", width_, height_);
407 if (data) {
408 delete [] data;
409 datasize = 0; data = 0;
410 }
411 if (new_datasize) {
412 data = new U8[new_datasize];
413 if (!data)
414 throw Exception("rfb::ManagedPixelBuffer unable to allocate buffer");
415 datasize = new_datasize;
416 }
417 }
418};