blob: 7f4c34406ba6cf2bdfe2faf0815a9c165ca42172 [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
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +010036PixelBuffer::PixelBuffer(const PixelFormat& pf, int w, int h)
37 : format(pf), width_(w), height_(h) {}
38PixelBuffer::PixelBuffer() : width_(0), height_(0) {}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000039
40PixelBuffer::~PixelBuffer() {}
41
42
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000043void
44PixelBuffer::getImage(void* imageBuf, const Rect& r, int outStride) {
45 int inStride;
Pierre Ossman945cdda2014-01-28 14:13:12 +010046 const U8* data = getBuffer(r, &inStride);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000047 // We assume that the specified rectangle is pre-clipped to the buffer
48 int bytesPerPixel = format.bpp/8;
49 int inBytesPerRow = inStride * bytesPerPixel;
50 if (!outStride) outStride = r.width();
51 int outBytesPerRow = outStride * bytesPerPixel;
52 int bytesPerMemCpy = r.width() * bytesPerPixel;
53 U8* imageBufPos = (U8*)imageBuf;
54 const U8* end = data + (inBytesPerRow * r.height());
55 while (data < end) {
56 memcpy(imageBufPos, data, bytesPerMemCpy);
57 imageBufPos += outBytesPerRow;
58 data += inBytesPerRow;
59 }
60}
61
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000062
DRC4f24c1a2011-11-03 23:56:10 +000063static void fillRect8(U8 *buf, int stride, const Rect& r, Pixel pix)
64{
65 U8* ptr = buf;
66 int w = r.width(), h = r.height();
67
68 while (h > 0) {
69 memset(ptr, pix, w);
70 ptr += stride;
71 h--;
72 }
73}
74
75static void fillRect16(U8 *buf, int stride, const Rect& r, Pixel pix)
76{
77 U16* ptr = (U16 *)buf;
78 int w = r.width(), h = r.height(), wBytes = w * 2;
79
80 while (w > 0) {
81 *ptr++ = pix; w--;
82 }
83 h--;
84
85 ptr = (U16 *)buf;
86
87 while (h > 0) {
88 U16 *oldptr = ptr;
89 memcpy(ptr += stride, oldptr, wBytes);
90 h--;
91 }
92}
93
94static void fillRect32(U8 *buf, int stride, const Rect& r, Pixel pix)
95{
96 U32* ptr = (U32 *)buf;
97 int w = r.width(), h = r.height(), wBytes = w * 4;
98
99 while (w > 0) {
100 *ptr++ = pix; w--;
101 }
102 h--;
103
104 ptr = (U32 *)buf;
105
106 while (h > 0) {
107 U32 *oldptr = ptr;
108 memcpy(ptr += stride, oldptr, wBytes);
109 h--;
110 }
111}
112
113
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000114FullFramePixelBuffer::FullFramePixelBuffer(const PixelFormat& pf, int w, int h,
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100115 rdr::U8* data_)
116 : PixelBuffer(pf, w, h), data(data_)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000117{
Pierre Ossman1ed4d502014-01-07 15:28:45 +0000118 // Called again to configure the fill function
119 setPF(pf);
120}
121
122FullFramePixelBuffer::FullFramePixelBuffer() : data(0) {}
123
124FullFramePixelBuffer::~FullFramePixelBuffer() {}
125
126
127void FullFramePixelBuffer::setPF(const PixelFormat &pf) {
128 // We have this as a separate method for ManagedPixelBuffer's
129 // sake. Direct users of FullFramePixelBuffer aren't allowed
130 // to call it.
131
132 PixelBuffer::setPF(pf);
133
DRC4f24c1a2011-11-03 23:56:10 +0000134 switch(pf.bpp) {
135 case 8:
136 fillRectFn = fillRect8;
137 break;
138 case 16:
139 fillRectFn = fillRect16;
140 break;
141 case 32:
142 fillRectFn = fillRect32;
143 break;
144 default:
145 throw Exception("rfb::FullFramePixelBuffer - Unsupported pixel format");
146 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000147}
148
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000149
150int FullFramePixelBuffer::getStride() const { return width(); }
151
Pierre Ossman945cdda2014-01-28 14:13:12 +0100152rdr::U8* FullFramePixelBuffer::getBufferRW(const Rect& r, int* stride)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000153{
154 *stride = getStride();
155 return &data[(r.tl.x + (r.tl.y * *stride)) * format.bpp/8];
156}
157
158
159void FullFramePixelBuffer::fillRect(const Rect& r, Pixel pix) {
160 int stride;
Pierre Ossman945cdda2014-01-28 14:13:12 +0100161 U8 *buf = getBufferRW(r, &stride);
DRC4f24c1a2011-11-03 23:56:10 +0000162 fillRectFn(buf, stride, r, pix);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000163}
164
165void FullFramePixelBuffer::imageRect(const Rect& r, const void* pixels, int srcStride) {
166 int bytesPerPixel = getPF().bpp/8;
167 int destStride;
Pierre Ossman945cdda2014-01-28 14:13:12 +0100168 U8* dest = getBufferRW(r, &destStride);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000169 int bytesPerDestRow = bytesPerPixel * destStride;
170 if (!srcStride) srcStride = r.width();
171 int bytesPerSrcRow = bytesPerPixel * srcStride;
172 int bytesPerFill = bytesPerPixel * r.width();
173 const U8* src = (const U8*)pixels;
174 U8* end = dest + (bytesPerDestRow * r.height());
175 while (dest < end) {
176 memcpy(dest, src, bytesPerFill);
177 dest += bytesPerDestRow;
178 src += bytesPerSrcRow;
179 }
180}
181
182void FullFramePixelBuffer::maskRect(const Rect& r, const void* pixels, const void* mask_) {
183 Rect cr = getRect().intersect(r);
184 if (cr.is_empty()) return;
185 int stride;
Pierre Ossman945cdda2014-01-28 14:13:12 +0100186 U8* data = getBufferRW(cr, &stride);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000187 U8* mask = (U8*) mask_;
188 int w = cr.width();
189 int h = cr.height();
190 int bpp = getPF().bpp;
191 int pixelStride = r.width();
192 int maskStride = (r.width() + 7) / 8;
193
194 Point offset = Point(cr.tl.x-r.tl.x, cr.tl.y-r.tl.y);
195 mask += offset.y * maskStride;
196 for (int y = 0; y < h; y++) {
197 int cy = offset.y + y;
198 for (int x = 0; x < w; x++) {
199 int cx = offset.x + x;
200 U8* byte = mask + (cx / 8);
201 int bit = 7 - cx % 8;
202 if ((*byte) & (1 << bit)) {
203 switch (bpp) {
204 case 8:
205 ((U8*)data)[y * stride + x] = ((U8*)pixels)[cy * pixelStride + cx];
206 break;
207 case 16:
208 ((U16*)data)[y * stride + x] = ((U16*)pixels)[cy * pixelStride + cx];
209 break;
210 case 32:
211 ((U32*)data)[y * stride + x] = ((U32*)pixels)[cy * pixelStride + cx];
212 break;
213 }
214 }
215 }
216 mask += maskStride;
217 }
218}
219
220void FullFramePixelBuffer::maskRect(const Rect& r, Pixel pixel, const void* mask_) {
221 Rect cr = getRect().intersect(r);
222 if (cr.is_empty()) return;
223 int stride;
Pierre Ossman945cdda2014-01-28 14:13:12 +0100224 U8* data = getBufferRW(cr, &stride);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000225 U8* mask = (U8*) mask_;
226 int w = cr.width();
227 int h = cr.height();
228 int bpp = getPF().bpp;
229 int maskStride = (r.width() + 7) / 8;
230
231 Point offset = Point(cr.tl.x-r.tl.x, cr.tl.y-r.tl.y);
232 mask += offset.y * maskStride;
233 for (int y = 0; y < h; y++) {
234 for (int x = 0; x < w; x++) {
235 int cx = offset.x + x;
236 U8* byte = mask + (cx / 8);
237 int bit = 7 - cx % 8;
238 if ((*byte) & (1 << bit)) {
239 switch (bpp) {
240 case 8:
241 ((U8*)data)[y * stride + x] = pixel;
242 break;
243 case 16:
244 ((U16*)data)[y * stride + x] = pixel;
245 break;
246 case 32:
247 ((U32*)data)[y * stride + x] = pixel;
248 break;
249 }
250 }
251 }
252 mask += maskStride;
253 }
254}
255
256void FullFramePixelBuffer::copyRect(const Rect &rect, const Point &move_by_delta) {
257 int stride;
Pierre Ossman45de20d2010-12-21 15:53:42 +0000258 U8* data;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000259 unsigned int bytesPerPixel, bytesPerRow, bytesPerMemCpy;
Pierre Ossman45de20d2010-12-21 15:53:42 +0000260 Rect drect, srect = rect.translate(move_by_delta.negate());
261
262 drect = rect;
263 if (!drect.enclosed_by(getRect())) {
264 vlog.error("Destination rect %dx%d at %d,%d exceeds framebuffer %dx%d",
265 drect.width(), drect.height(), drect.tl.x, drect.tl.y, width_, height_);
266 drect = drect.intersect(getRect());
267 }
268
269 if (drect.is_empty())
270 return;
271
272 srect = drect.translate(move_by_delta.negate());
273 if (!srect.enclosed_by(getRect())) {
274 vlog.error("Source rect %dx%d at %d,%d exceeds framebuffer %dx%d",
275 srect.width(), srect.height(), srect.tl.x, srect.tl.y, width_, height_);
276 srect = srect.intersect(getRect());
277 // Need to readjust the destination now that the area has changed
278 drect = srect.translate(move_by_delta);
279 }
280
281 if (srect.is_empty())
282 return;
283
Pierre Ossman945cdda2014-01-28 14:13:12 +0100284 data = getBufferRW(getRect(), &stride);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000285 bytesPerPixel = getPF().bpp/8;
286 bytesPerRow = stride * bytesPerPixel;
Pierre Ossman45de20d2010-12-21 15:53:42 +0000287 bytesPerMemCpy = drect.width() * bytesPerPixel;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000288 if (move_by_delta.y <= 0) {
Pierre Ossman45de20d2010-12-21 15:53:42 +0000289 U8* dest = data + drect.tl.x*bytesPerPixel + drect.tl.y*bytesPerRow;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000290 U8* src = data + srect.tl.x*bytesPerPixel + srect.tl.y*bytesPerRow;
Pierre Ossman45de20d2010-12-21 15:53:42 +0000291 for (int i=drect.tl.y; i<drect.br.y; i++) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000292 memmove(dest, src, bytesPerMemCpy);
293 dest += bytesPerRow;
294 src += bytesPerRow;
295 }
296 } else {
Pierre Ossman45de20d2010-12-21 15:53:42 +0000297 U8* dest = data + drect.tl.x*bytesPerPixel + (drect.br.y-1)*bytesPerRow;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000298 U8* src = data + srect.tl.x*bytesPerPixel + (srect.br.y-1)*bytesPerRow;
Pierre Ossman45de20d2010-12-21 15:53:42 +0000299 for (int i=drect.tl.y; i<drect.br.y; i++) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000300 memmove(dest, src, bytesPerMemCpy);
301 dest -= bytesPerRow;
302 src -= bytesPerRow;
303 }
304 }
305}
306
307
308// -=- Managed pixel buffer class
309// Automatically allocates enough space for the specified format & area
310
311ManagedPixelBuffer::ManagedPixelBuffer()
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100312 : datasize(0)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000313{
314 checkDataSize();
315};
316
317ManagedPixelBuffer::ManagedPixelBuffer(const PixelFormat& pf, int w, int h)
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100318 : FullFramePixelBuffer(pf, w, h, 0), datasize(0)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000319{
320 checkDataSize();
321};
322
323ManagedPixelBuffer::~ManagedPixelBuffer() {
324 if (data) delete [] data;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000325};
326
327
328void
329ManagedPixelBuffer::setPF(const PixelFormat &pf) {
Pierre Ossman1ed4d502014-01-07 15:28:45 +0000330 FullFramePixelBuffer::setPF(pf); checkDataSize();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000331};
332void
333ManagedPixelBuffer::setSize(int w, int h) {
334 width_ = w; height_ = h; checkDataSize();
335};
336
337
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000338inline void
339ManagedPixelBuffer::checkDataSize() {
340 unsigned long new_datasize = width_ * height_ * (format.bpp/8);
341 if (datasize < new_datasize) {
342 vlog.debug("reallocating managed buffer (%dx%d)", width_, height_);
343 if (data) {
344 delete [] data;
345 datasize = 0; data = 0;
346 }
347 if (new_datasize) {
348 data = new U8[new_datasize];
349 if (!data)
350 throw Exception("rfb::ManagedPixelBuffer unable to allocate buffer");
351 datasize = new_datasize;
352 }
353 }
354};