blob: 187150d90537c4048b6a005ae40a56f6a40bdfc9 [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
45PixelBuffer::getImage(void* imageBuf, const Rect& r, int outStride) {
46 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 Ossmana32040d2014-02-06 16:31:10 +010063// -=- Modifiable generic pixel buffer class
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000064
Pierre Ossmana32040d2014-02-06 16:31:10 +010065ModifiablePixelBuffer::ModifiablePixelBuffer(const PixelFormat& pf,
66 int w, int h)
67 : PixelBuffer(pf, w, h)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000068{
Pierre Ossman1ed4d502014-01-07 15:28:45 +000069}
70
Pierre Ossmana32040d2014-02-06 16:31:10 +010071ModifiablePixelBuffer::ModifiablePixelBuffer()
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000072{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000073}
74
Pierre Ossmana32040d2014-02-06 16:31:10 +010075ModifiablePixelBuffer::~ModifiablePixelBuffer()
76{
77}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000078
Pierre Ossmana32040d2014-02-06 16:31:10 +010079void ModifiablePixelBuffer::fillRect(const Rect& r, Pixel pix)
80{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000081 int stride;
Pierre Ossman5c1a1532014-01-30 16:59:14 +010082 U8 *buf, pixbuf[4];
83 int w, h, b;
84
85 buf = getBufferRW(r, &stride);
86 w = r.width();
87 h = r.height();
88 b = format.bpp/8;
89
90 format.bufferFromPixel(pixbuf, pix);
91
92 while (h--) {
93 int w_ = w;
94 while (w_--) {
95 memcpy(buf, pixbuf, b);
96 buf += b;
97 }
98 buf += (stride - w) * b;
99 }
Pierre Ossmana32040d2014-02-06 16:31:10 +0100100
101 commitBufferRW(r);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000102}
103
Pierre Ossmana32040d2014-02-06 16:31:10 +0100104void ModifiablePixelBuffer::imageRect(const Rect& r,
105 const void* pixels, int srcStride)
106{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000107 int bytesPerPixel = getPF().bpp/8;
108 int destStride;
Pierre Ossman945cdda2014-01-28 14:13:12 +0100109 U8* dest = getBufferRW(r, &destStride);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000110 int bytesPerDestRow = bytesPerPixel * destStride;
111 if (!srcStride) srcStride = r.width();
112 int bytesPerSrcRow = bytesPerPixel * srcStride;
113 int bytesPerFill = bytesPerPixel * r.width();
114 const U8* src = (const U8*)pixels;
115 U8* end = dest + (bytesPerDestRow * r.height());
116 while (dest < end) {
117 memcpy(dest, src, bytesPerFill);
118 dest += bytesPerDestRow;
119 src += bytesPerSrcRow;
120 }
Pierre Ossmana32040d2014-02-06 16:31:10 +0100121 commitBufferRW(r);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000122}
123
Pierre Ossmana32040d2014-02-06 16:31:10 +0100124void ModifiablePixelBuffer::maskRect(const Rect& r,
125 const void* pixels, const void* mask_)
126{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000127 Rect cr = getRect().intersect(r);
128 if (cr.is_empty()) return;
129 int stride;
Pierre Ossman945cdda2014-01-28 14:13:12 +0100130 U8* data = getBufferRW(cr, &stride);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000131 U8* mask = (U8*) mask_;
132 int w = cr.width();
133 int h = cr.height();
134 int bpp = getPF().bpp;
135 int pixelStride = r.width();
136 int maskStride = (r.width() + 7) / 8;
137
138 Point offset = Point(cr.tl.x-r.tl.x, cr.tl.y-r.tl.y);
139 mask += offset.y * maskStride;
140 for (int y = 0; y < h; y++) {
141 int cy = offset.y + y;
142 for (int x = 0; x < w; x++) {
143 int cx = offset.x + x;
144 U8* byte = mask + (cx / 8);
145 int bit = 7 - cx % 8;
146 if ((*byte) & (1 << bit)) {
147 switch (bpp) {
148 case 8:
149 ((U8*)data)[y * stride + x] = ((U8*)pixels)[cy * pixelStride + cx];
150 break;
151 case 16:
152 ((U16*)data)[y * stride + x] = ((U16*)pixels)[cy * pixelStride + cx];
153 break;
154 case 32:
155 ((U32*)data)[y * stride + x] = ((U32*)pixels)[cy * pixelStride + cx];
156 break;
157 }
158 }
159 }
160 mask += maskStride;
161 }
Pierre Ossmana32040d2014-02-06 16:31:10 +0100162
163 commitBufferRW(cr);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000164}
165
Pierre Ossmana32040d2014-02-06 16:31:10 +0100166void ModifiablePixelBuffer::maskRect(const Rect& r,
167 Pixel pixel, const void* mask_)
168{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000169 Rect cr = getRect().intersect(r);
170 if (cr.is_empty()) return;
171 int stride;
Pierre Ossman945cdda2014-01-28 14:13:12 +0100172 U8* data = getBufferRW(cr, &stride);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000173 U8* mask = (U8*) mask_;
174 int w = cr.width();
175 int h = cr.height();
176 int bpp = getPF().bpp;
177 int maskStride = (r.width() + 7) / 8;
178
179 Point offset = Point(cr.tl.x-r.tl.x, cr.tl.y-r.tl.y);
180 mask += offset.y * maskStride;
181 for (int y = 0; y < h; y++) {
182 for (int x = 0; x < w; x++) {
183 int cx = offset.x + x;
184 U8* byte = mask + (cx / 8);
185 int bit = 7 - cx % 8;
186 if ((*byte) & (1 << bit)) {
187 switch (bpp) {
188 case 8:
189 ((U8*)data)[y * stride + x] = pixel;
190 break;
191 case 16:
192 ((U16*)data)[y * stride + x] = pixel;
193 break;
194 case 32:
195 ((U32*)data)[y * stride + x] = pixel;
196 break;
197 }
198 }
199 }
200 mask += maskStride;
201 }
Pierre Ossmana32040d2014-02-06 16:31:10 +0100202
203 commitBufferRW(cr);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000204}
205
Pierre Ossmana32040d2014-02-06 16:31:10 +0100206void ModifiablePixelBuffer::copyRect(const Rect &rect,
207 const Point &move_by_delta)
208{
Pierre Ossman8163f542014-02-13 09:42:48 +0100209 int srcStride, dstStride;
210 const U8* srcData;
211 U8* dstData;
212
213 Rect drect, srect;
Pierre Ossman45de20d2010-12-21 15:53:42 +0000214
215 drect = rect;
216 if (!drect.enclosed_by(getRect())) {
217 vlog.error("Destination rect %dx%d at %d,%d exceeds framebuffer %dx%d",
218 drect.width(), drect.height(), drect.tl.x, drect.tl.y, width_, height_);
219 drect = drect.intersect(getRect());
220 }
221
222 if (drect.is_empty())
223 return;
224
225 srect = drect.translate(move_by_delta.negate());
226 if (!srect.enclosed_by(getRect())) {
227 vlog.error("Source rect %dx%d at %d,%d exceeds framebuffer %dx%d",
228 srect.width(), srect.height(), srect.tl.x, srect.tl.y, width_, height_);
229 srect = srect.intersect(getRect());
230 // Need to readjust the destination now that the area has changed
231 drect = srect.translate(move_by_delta);
232 }
233
234 if (srect.is_empty())
235 return;
236
Pierre Ossman8163f542014-02-13 09:42:48 +0100237 srcData = getBuffer(srect, &srcStride);
238 dstData = getBufferRW(drect, &dstStride);
239
240 if (move_by_delta.y == 0) {
241 // Possible overlap. Be careful and use memmove().
242 int h = drect.height();
243 while (h--) {
244 memmove(dstData, srcData, drect.width() * format.bpp/8);
245 dstData += dstStride * format.bpp/8;
246 srcData += srcStride * format.bpp/8;
247 }
248 } else if (move_by_delta.y < 0) {
249 // The data shifted upwards. Copy from top to bottom.
250 int h = drect.height();
251 while (h--) {
252 memcpy(dstData, srcData, drect.width() * format.bpp/8);
253 dstData += dstStride * format.bpp/8;
254 srcData += srcStride * format.bpp/8;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000255 }
256 } else {
Pierre Ossman8163f542014-02-13 09:42:48 +0100257 // The data shifted downwards. Copy from bottom to top.
258 int h = drect.height();
259 dstData += (h-1) * dstStride * format.bpp/8;
260 srcData += (h-1) * srcStride * format.bpp/8;
261 while (h--) {
262 memcpy(dstData, srcData, drect.width() * format.bpp/8);
263 dstData -= dstStride * format.bpp/8;
264 srcData -= srcStride * format.bpp/8;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000265 }
266 }
Pierre Ossman8163f542014-02-13 09:42:48 +0100267
268 commitBufferRW(drect);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000269}
270
Pierre Ossmana32040d2014-02-06 16:31:10 +0100271// -=- Simple pixel buffer with a continuous block of memory
272
273FullFramePixelBuffer::FullFramePixelBuffer(const PixelFormat& pf, int w, int h,
274 rdr::U8* data_, int stride_)
275 : ModifiablePixelBuffer(pf, w, h), data(data_), stride(stride_)
276{
277}
278
279FullFramePixelBuffer::FullFramePixelBuffer() : data(0) {}
280
281FullFramePixelBuffer::~FullFramePixelBuffer() {}
282
283rdr::U8* FullFramePixelBuffer::getBufferRW(const Rect& r, int* stride_)
284{
285 *stride_ = stride;
286 return &data[(r.tl.x + (r.tl.y * stride)) * format.bpp/8];
287}
288
289void FullFramePixelBuffer::commitBufferRW(const Rect& r)
290{
291}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000292
293// -=- Managed pixel buffer class
294// Automatically allocates enough space for the specified format & area
295
296ManagedPixelBuffer::ManagedPixelBuffer()
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100297 : datasize(0)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000298{
299 checkDataSize();
300};
301
302ManagedPixelBuffer::ManagedPixelBuffer(const PixelFormat& pf, int w, int h)
Pierre Ossman2e5a1062014-01-30 17:57:27 +0100303 : FullFramePixelBuffer(pf, w, h, NULL, w), datasize(0)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000304{
305 checkDataSize();
306};
307
308ManagedPixelBuffer::~ManagedPixelBuffer() {
309 if (data) delete [] data;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000310};
311
312
313void
314ManagedPixelBuffer::setPF(const PixelFormat &pf) {
Pierre Ossman5c1a1532014-01-30 16:59:14 +0100315 format = pf; checkDataSize();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000316};
317void
318ManagedPixelBuffer::setSize(int w, int h) {
Pierre Ossman2e5a1062014-01-30 17:57:27 +0100319 width_ = w; height_ = h; stride = w; checkDataSize();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000320};
321
322
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000323inline void
324ManagedPixelBuffer::checkDataSize() {
325 unsigned long new_datasize = width_ * height_ * (format.bpp/8);
326 if (datasize < new_datasize) {
327 vlog.debug("reallocating managed buffer (%dx%d)", width_, height_);
328 if (data) {
329 delete [] data;
330 datasize = 0; data = 0;
331 }
332 if (new_datasize) {
333 data = new U8[new_datasize];
334 if (!data)
335 throw Exception("rfb::ManagedPixelBuffer unable to allocate buffer");
336 datasize = new_datasize;
337 }
338 }
339};