blob: ea19d183d93ca2b16d8231e6e3e6e46682dba6b4 [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
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000063
64FullFramePixelBuffer::FullFramePixelBuffer(const PixelFormat& pf, int w, int h,
Pierre Ossman2e5a1062014-01-30 17:57:27 +010065 rdr::U8* data_, int stride_)
66 : PixelBuffer(pf, w, h), data(data_), stride(stride_)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000067{
Pierre Ossman1ed4d502014-01-07 15:28:45 +000068}
69
70FullFramePixelBuffer::FullFramePixelBuffer() : data(0) {}
71
72FullFramePixelBuffer::~FullFramePixelBuffer() {}
73
74
Pierre Ossman2e5a1062014-01-30 17:57:27 +010075rdr::U8* FullFramePixelBuffer::getBufferRW(const Rect& r, int* stride_)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000076{
Pierre Ossman2e5a1062014-01-30 17:57:27 +010077 *stride_ = stride;
78 return &data[(r.tl.x + (r.tl.y * stride)) * format.bpp/8];
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000079}
80
81
82void FullFramePixelBuffer::fillRect(const Rect& r, Pixel pix) {
83 int stride;
Pierre Ossman5c1a1532014-01-30 16:59:14 +010084 U8 *buf, pixbuf[4];
85 int w, h, b;
86
87 buf = getBufferRW(r, &stride);
88 w = r.width();
89 h = r.height();
90 b = format.bpp/8;
91
92 format.bufferFromPixel(pixbuf, pix);
93
94 while (h--) {
95 int w_ = w;
96 while (w_--) {
97 memcpy(buf, pixbuf, b);
98 buf += b;
99 }
100 buf += (stride - w) * b;
101 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000102}
103
104void FullFramePixelBuffer::imageRect(const Rect& r, const void* pixels, int srcStride) {
105 int bytesPerPixel = getPF().bpp/8;
106 int destStride;
Pierre Ossman945cdda2014-01-28 14:13:12 +0100107 U8* dest = getBufferRW(r, &destStride);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000108 int bytesPerDestRow = bytesPerPixel * destStride;
109 if (!srcStride) srcStride = r.width();
110 int bytesPerSrcRow = bytesPerPixel * srcStride;
111 int bytesPerFill = bytesPerPixel * r.width();
112 const U8* src = (const U8*)pixels;
113 U8* end = dest + (bytesPerDestRow * r.height());
114 while (dest < end) {
115 memcpy(dest, src, bytesPerFill);
116 dest += bytesPerDestRow;
117 src += bytesPerSrcRow;
118 }
119}
120
121void FullFramePixelBuffer::maskRect(const Rect& r, const void* pixels, const void* mask_) {
122 Rect cr = getRect().intersect(r);
123 if (cr.is_empty()) return;
124 int stride;
Pierre Ossman945cdda2014-01-28 14:13:12 +0100125 U8* data = getBufferRW(cr, &stride);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000126 U8* mask = (U8*) mask_;
127 int w = cr.width();
128 int h = cr.height();
129 int bpp = getPF().bpp;
130 int pixelStride = r.width();
131 int maskStride = (r.width() + 7) / 8;
132
133 Point offset = Point(cr.tl.x-r.tl.x, cr.tl.y-r.tl.y);
134 mask += offset.y * maskStride;
135 for (int y = 0; y < h; y++) {
136 int cy = offset.y + y;
137 for (int x = 0; x < w; x++) {
138 int cx = offset.x + x;
139 U8* byte = mask + (cx / 8);
140 int bit = 7 - cx % 8;
141 if ((*byte) & (1 << bit)) {
142 switch (bpp) {
143 case 8:
144 ((U8*)data)[y * stride + x] = ((U8*)pixels)[cy * pixelStride + cx];
145 break;
146 case 16:
147 ((U16*)data)[y * stride + x] = ((U16*)pixels)[cy * pixelStride + cx];
148 break;
149 case 32:
150 ((U32*)data)[y * stride + x] = ((U32*)pixels)[cy * pixelStride + cx];
151 break;
152 }
153 }
154 }
155 mask += maskStride;
156 }
157}
158
159void FullFramePixelBuffer::maskRect(const Rect& r, Pixel pixel, const void* mask_) {
160 Rect cr = getRect().intersect(r);
161 if (cr.is_empty()) return;
162 int stride;
Pierre Ossman945cdda2014-01-28 14:13:12 +0100163 U8* data = getBufferRW(cr, &stride);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000164 U8* mask = (U8*) mask_;
165 int w = cr.width();
166 int h = cr.height();
167 int bpp = getPF().bpp;
168 int maskStride = (r.width() + 7) / 8;
169
170 Point offset = Point(cr.tl.x-r.tl.x, cr.tl.y-r.tl.y);
171 mask += offset.y * maskStride;
172 for (int y = 0; y < h; y++) {
173 for (int x = 0; x < w; x++) {
174 int cx = offset.x + x;
175 U8* byte = mask + (cx / 8);
176 int bit = 7 - cx % 8;
177 if ((*byte) & (1 << bit)) {
178 switch (bpp) {
179 case 8:
180 ((U8*)data)[y * stride + x] = pixel;
181 break;
182 case 16:
183 ((U16*)data)[y * stride + x] = pixel;
184 break;
185 case 32:
186 ((U32*)data)[y * stride + x] = pixel;
187 break;
188 }
189 }
190 }
191 mask += maskStride;
192 }
193}
194
195void FullFramePixelBuffer::copyRect(const Rect &rect, const Point &move_by_delta) {
196 int stride;
Pierre Ossman45de20d2010-12-21 15:53:42 +0000197 U8* data;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000198 unsigned int bytesPerPixel, bytesPerRow, bytesPerMemCpy;
Pierre Ossman45de20d2010-12-21 15:53:42 +0000199 Rect drect, srect = rect.translate(move_by_delta.negate());
200
201 drect = rect;
202 if (!drect.enclosed_by(getRect())) {
203 vlog.error("Destination rect %dx%d at %d,%d exceeds framebuffer %dx%d",
204 drect.width(), drect.height(), drect.tl.x, drect.tl.y, width_, height_);
205 drect = drect.intersect(getRect());
206 }
207
208 if (drect.is_empty())
209 return;
210
211 srect = drect.translate(move_by_delta.negate());
212 if (!srect.enclosed_by(getRect())) {
213 vlog.error("Source rect %dx%d at %d,%d exceeds framebuffer %dx%d",
214 srect.width(), srect.height(), srect.tl.x, srect.tl.y, width_, height_);
215 srect = srect.intersect(getRect());
216 // Need to readjust the destination now that the area has changed
217 drect = srect.translate(move_by_delta);
218 }
219
220 if (srect.is_empty())
221 return;
222
Pierre Ossman945cdda2014-01-28 14:13:12 +0100223 data = getBufferRW(getRect(), &stride);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000224 bytesPerPixel = getPF().bpp/8;
225 bytesPerRow = stride * bytesPerPixel;
Pierre Ossman45de20d2010-12-21 15:53:42 +0000226 bytesPerMemCpy = drect.width() * bytesPerPixel;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000227 if (move_by_delta.y <= 0) {
Pierre Ossman45de20d2010-12-21 15:53:42 +0000228 U8* dest = data + drect.tl.x*bytesPerPixel + drect.tl.y*bytesPerRow;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000229 U8* src = data + srect.tl.x*bytesPerPixel + srect.tl.y*bytesPerRow;
Pierre Ossman45de20d2010-12-21 15:53:42 +0000230 for (int i=drect.tl.y; i<drect.br.y; i++) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000231 memmove(dest, src, bytesPerMemCpy);
232 dest += bytesPerRow;
233 src += bytesPerRow;
234 }
235 } else {
Pierre Ossman45de20d2010-12-21 15:53:42 +0000236 U8* dest = data + drect.tl.x*bytesPerPixel + (drect.br.y-1)*bytesPerRow;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000237 U8* src = data + srect.tl.x*bytesPerPixel + (srect.br.y-1)*bytesPerRow;
Pierre Ossman45de20d2010-12-21 15:53:42 +0000238 for (int i=drect.tl.y; i<drect.br.y; i++) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000239 memmove(dest, src, bytesPerMemCpy);
240 dest -= bytesPerRow;
241 src -= bytesPerRow;
242 }
243 }
244}
245
246
247// -=- Managed pixel buffer class
248// Automatically allocates enough space for the specified format & area
249
250ManagedPixelBuffer::ManagedPixelBuffer()
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100251 : datasize(0)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000252{
253 checkDataSize();
254};
255
256ManagedPixelBuffer::ManagedPixelBuffer(const PixelFormat& pf, int w, int h)
Pierre Ossman2e5a1062014-01-30 17:57:27 +0100257 : FullFramePixelBuffer(pf, w, h, NULL, w), datasize(0)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000258{
259 checkDataSize();
260};
261
262ManagedPixelBuffer::~ManagedPixelBuffer() {
263 if (data) delete [] data;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000264};
265
266
267void
268ManagedPixelBuffer::setPF(const PixelFormat &pf) {
Pierre Ossman5c1a1532014-01-30 16:59:14 +0100269 format = pf; checkDataSize();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000270};
271void
272ManagedPixelBuffer::setSize(int w, int h) {
Pierre Ossman2e5a1062014-01-30 17:57:27 +0100273 width_ = w; height_ = h; stride = w; checkDataSize();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000274};
275
276
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000277inline void
278ManagedPixelBuffer::checkDataSize() {
279 unsigned long new_datasize = width_ * height_ * (format.bpp/8);
280 if (datasize < new_datasize) {
281 vlog.debug("reallocating managed buffer (%dx%d)", width_, height_);
282 if (data) {
283 delete [] data;
284 datasize = 0; data = 0;
285 }
286 if (new_datasize) {
287 data = new U8[new_datasize];
288 if (!data)
289 throw Exception("rfb::ManagedPixelBuffer unable to allocate buffer");
290 datasize = new_datasize;
291 }
292 }
293};