blob: 3ed3a92400b7e8718917e117826792faaa87efd4 [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
43void PixelBuffer::setPF(const PixelFormat &pf) {format = pf;}
44const PixelFormat& PixelBuffer::getPF() const {return format;}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000045
46
47void
48PixelBuffer::getImage(void* imageBuf, const Rect& r, int outStride) {
49 int inStride;
Pierre Ossman945cdda2014-01-28 14:13:12 +010050 const U8* data = getBuffer(r, &inStride);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000051 // We assume that the specified rectangle is pre-clipped to the buffer
52 int bytesPerPixel = format.bpp/8;
53 int inBytesPerRow = inStride * bytesPerPixel;
54 if (!outStride) outStride = r.width();
55 int outBytesPerRow = outStride * bytesPerPixel;
56 int bytesPerMemCpy = r.width() * bytesPerPixel;
57 U8* imageBufPos = (U8*)imageBuf;
58 const U8* end = data + (inBytesPerRow * r.height());
59 while (data < end) {
60 memcpy(imageBufPos, data, bytesPerMemCpy);
61 imageBufPos += outBytesPerRow;
62 data += inBytesPerRow;
63 }
64}
65
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000066
DRC4f24c1a2011-11-03 23:56:10 +000067static void fillRect8(U8 *buf, int stride, const Rect& r, Pixel pix)
68{
69 U8* ptr = buf;
70 int w = r.width(), h = r.height();
71
72 while (h > 0) {
73 memset(ptr, pix, w);
74 ptr += stride;
75 h--;
76 }
77}
78
79static void fillRect16(U8 *buf, int stride, const Rect& r, Pixel pix)
80{
81 U16* ptr = (U16 *)buf;
82 int w = r.width(), h = r.height(), wBytes = w * 2;
83
84 while (w > 0) {
85 *ptr++ = pix; w--;
86 }
87 h--;
88
89 ptr = (U16 *)buf;
90
91 while (h > 0) {
92 U16 *oldptr = ptr;
93 memcpy(ptr += stride, oldptr, wBytes);
94 h--;
95 }
96}
97
98static void fillRect32(U8 *buf, int stride, const Rect& r, Pixel pix)
99{
100 U32* ptr = (U32 *)buf;
101 int w = r.width(), h = r.height(), wBytes = w * 4;
102
103 while (w > 0) {
104 *ptr++ = pix; w--;
105 }
106 h--;
107
108 ptr = (U32 *)buf;
109
110 while (h > 0) {
111 U32 *oldptr = ptr;
112 memcpy(ptr += stride, oldptr, wBytes);
113 h--;
114 }
115}
116
117
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000118FullFramePixelBuffer::FullFramePixelBuffer(const PixelFormat& pf, int w, int h,
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100119 rdr::U8* data_)
120 : PixelBuffer(pf, w, h), data(data_)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000121{
Pierre Ossman1ed4d502014-01-07 15:28:45 +0000122 // Called again to configure the fill function
123 setPF(pf);
124}
125
126FullFramePixelBuffer::FullFramePixelBuffer() : data(0) {}
127
128FullFramePixelBuffer::~FullFramePixelBuffer() {}
129
130
131void FullFramePixelBuffer::setPF(const PixelFormat &pf) {
132 // We have this as a separate method for ManagedPixelBuffer's
133 // sake. Direct users of FullFramePixelBuffer aren't allowed
134 // to call it.
135
136 PixelBuffer::setPF(pf);
137
DRC4f24c1a2011-11-03 23:56:10 +0000138 switch(pf.bpp) {
139 case 8:
140 fillRectFn = fillRect8;
141 break;
142 case 16:
143 fillRectFn = fillRect16;
144 break;
145 case 32:
146 fillRectFn = fillRect32;
147 break;
148 default:
149 throw Exception("rfb::FullFramePixelBuffer - Unsupported pixel format");
150 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000151}
152
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000153
154int FullFramePixelBuffer::getStride() const { return width(); }
155
Pierre Ossman945cdda2014-01-28 14:13:12 +0100156rdr::U8* FullFramePixelBuffer::getBufferRW(const Rect& r, int* stride)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000157{
158 *stride = getStride();
159 return &data[(r.tl.x + (r.tl.y * *stride)) * format.bpp/8];
160}
161
162
163void FullFramePixelBuffer::fillRect(const Rect& r, Pixel pix) {
164 int stride;
Pierre Ossman945cdda2014-01-28 14:13:12 +0100165 U8 *buf = getBufferRW(r, &stride);
DRC4f24c1a2011-11-03 23:56:10 +0000166 fillRectFn(buf, stride, r, pix);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000167}
168
169void FullFramePixelBuffer::imageRect(const Rect& r, const void* pixels, int srcStride) {
170 int bytesPerPixel = getPF().bpp/8;
171 int destStride;
Pierre Ossman945cdda2014-01-28 14:13:12 +0100172 U8* dest = getBufferRW(r, &destStride);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000173 int bytesPerDestRow = bytesPerPixel * destStride;
174 if (!srcStride) srcStride = r.width();
175 int bytesPerSrcRow = bytesPerPixel * srcStride;
176 int bytesPerFill = bytesPerPixel * r.width();
177 const U8* src = (const U8*)pixels;
178 U8* end = dest + (bytesPerDestRow * r.height());
179 while (dest < end) {
180 memcpy(dest, src, bytesPerFill);
181 dest += bytesPerDestRow;
182 src += bytesPerSrcRow;
183 }
184}
185
186void FullFramePixelBuffer::maskRect(const Rect& r, const void* pixels, const void* mask_) {
187 Rect cr = getRect().intersect(r);
188 if (cr.is_empty()) return;
189 int stride;
Pierre Ossman945cdda2014-01-28 14:13:12 +0100190 U8* data = getBufferRW(cr, &stride);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000191 U8* mask = (U8*) mask_;
192 int w = cr.width();
193 int h = cr.height();
194 int bpp = getPF().bpp;
195 int pixelStride = r.width();
196 int maskStride = (r.width() + 7) / 8;
197
198 Point offset = Point(cr.tl.x-r.tl.x, cr.tl.y-r.tl.y);
199 mask += offset.y * maskStride;
200 for (int y = 0; y < h; y++) {
201 int cy = offset.y + y;
202 for (int x = 0; x < w; x++) {
203 int cx = offset.x + x;
204 U8* byte = mask + (cx / 8);
205 int bit = 7 - cx % 8;
206 if ((*byte) & (1 << bit)) {
207 switch (bpp) {
208 case 8:
209 ((U8*)data)[y * stride + x] = ((U8*)pixels)[cy * pixelStride + cx];
210 break;
211 case 16:
212 ((U16*)data)[y * stride + x] = ((U16*)pixels)[cy * pixelStride + cx];
213 break;
214 case 32:
215 ((U32*)data)[y * stride + x] = ((U32*)pixels)[cy * pixelStride + cx];
216 break;
217 }
218 }
219 }
220 mask += maskStride;
221 }
222}
223
224void FullFramePixelBuffer::maskRect(const Rect& r, Pixel pixel, const void* mask_) {
225 Rect cr = getRect().intersect(r);
226 if (cr.is_empty()) return;
227 int stride;
Pierre Ossman945cdda2014-01-28 14:13:12 +0100228 U8* data = getBufferRW(cr, &stride);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000229 U8* mask = (U8*) mask_;
230 int w = cr.width();
231 int h = cr.height();
232 int bpp = getPF().bpp;
233 int maskStride = (r.width() + 7) / 8;
234
235 Point offset = Point(cr.tl.x-r.tl.x, cr.tl.y-r.tl.y);
236 mask += offset.y * maskStride;
237 for (int y = 0; y < h; y++) {
238 for (int x = 0; x < w; x++) {
239 int cx = offset.x + x;
240 U8* byte = mask + (cx / 8);
241 int bit = 7 - cx % 8;
242 if ((*byte) & (1 << bit)) {
243 switch (bpp) {
244 case 8:
245 ((U8*)data)[y * stride + x] = pixel;
246 break;
247 case 16:
248 ((U16*)data)[y * stride + x] = pixel;
249 break;
250 case 32:
251 ((U32*)data)[y * stride + x] = pixel;
252 break;
253 }
254 }
255 }
256 mask += maskStride;
257 }
258}
259
260void FullFramePixelBuffer::copyRect(const Rect &rect, const Point &move_by_delta) {
261 int stride;
Pierre Ossman45de20d2010-12-21 15:53:42 +0000262 U8* data;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000263 unsigned int bytesPerPixel, bytesPerRow, bytesPerMemCpy;
Pierre Ossman45de20d2010-12-21 15:53:42 +0000264 Rect drect, srect = rect.translate(move_by_delta.negate());
265
266 drect = rect;
267 if (!drect.enclosed_by(getRect())) {
268 vlog.error("Destination rect %dx%d at %d,%d exceeds framebuffer %dx%d",
269 drect.width(), drect.height(), drect.tl.x, drect.tl.y, width_, height_);
270 drect = drect.intersect(getRect());
271 }
272
273 if (drect.is_empty())
274 return;
275
276 srect = drect.translate(move_by_delta.negate());
277 if (!srect.enclosed_by(getRect())) {
278 vlog.error("Source rect %dx%d at %d,%d exceeds framebuffer %dx%d",
279 srect.width(), srect.height(), srect.tl.x, srect.tl.y, width_, height_);
280 srect = srect.intersect(getRect());
281 // Need to readjust the destination now that the area has changed
282 drect = srect.translate(move_by_delta);
283 }
284
285 if (srect.is_empty())
286 return;
287
Pierre Ossman945cdda2014-01-28 14:13:12 +0100288 data = getBufferRW(getRect(), &stride);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000289 bytesPerPixel = getPF().bpp/8;
290 bytesPerRow = stride * bytesPerPixel;
Pierre Ossman45de20d2010-12-21 15:53:42 +0000291 bytesPerMemCpy = drect.width() * bytesPerPixel;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000292 if (move_by_delta.y <= 0) {
Pierre Ossman45de20d2010-12-21 15:53:42 +0000293 U8* dest = data + drect.tl.x*bytesPerPixel + drect.tl.y*bytesPerRow;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000294 U8* src = data + srect.tl.x*bytesPerPixel + srect.tl.y*bytesPerRow;
Pierre Ossman45de20d2010-12-21 15:53:42 +0000295 for (int i=drect.tl.y; i<drect.br.y; i++) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000296 memmove(dest, src, bytesPerMemCpy);
297 dest += bytesPerRow;
298 src += bytesPerRow;
299 }
300 } else {
Pierre Ossman45de20d2010-12-21 15:53:42 +0000301 U8* dest = data + drect.tl.x*bytesPerPixel + (drect.br.y-1)*bytesPerRow;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000302 U8* src = data + srect.tl.x*bytesPerPixel + (srect.br.y-1)*bytesPerRow;
Pierre Ossman45de20d2010-12-21 15:53:42 +0000303 for (int i=drect.tl.y; i<drect.br.y; i++) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000304 memmove(dest, src, bytesPerMemCpy);
305 dest -= bytesPerRow;
306 src -= bytesPerRow;
307 }
308 }
309}
310
311
312// -=- Managed pixel buffer class
313// Automatically allocates enough space for the specified format & area
314
315ManagedPixelBuffer::ManagedPixelBuffer()
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100316 : datasize(0)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000317{
318 checkDataSize();
319};
320
321ManagedPixelBuffer::ManagedPixelBuffer(const PixelFormat& pf, int w, int h)
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100322 : FullFramePixelBuffer(pf, w, h, 0), datasize(0)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000323{
324 checkDataSize();
325};
326
327ManagedPixelBuffer::~ManagedPixelBuffer() {
328 if (data) delete [] data;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000329};
330
331
332void
333ManagedPixelBuffer::setPF(const PixelFormat &pf) {
Pierre Ossman1ed4d502014-01-07 15:28:45 +0000334 FullFramePixelBuffer::setPF(pf); checkDataSize();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000335};
336void
337ManagedPixelBuffer::setSize(int w, int h) {
338 width_ = w; height_ = h; checkDataSize();
339};
340
341
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000342inline void
343ManagedPixelBuffer::checkDataSize() {
344 unsigned long new_datasize = width_ * height_ * (format.bpp/8);
345 if (datasize < new_datasize) {
346 vlog.debug("reallocating managed buffer (%dx%d)", width_, height_);
347 if (data) {
348 delete [] data;
349 datasize = 0; data = 0;
350 }
351 if (new_datasize) {
352 data = new U8[new_datasize];
353 if (!data)
354 throw Exception("rfb::ManagedPixelBuffer unable to allocate buffer");
355 datasize = new_datasize;
356 }
357 }
358};