blob: ace0934d070ea0d10411739f6b3a6c2d2f4d8dd3 [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
63FullFramePixelBuffer::FullFramePixelBuffer(const PixelFormat& pf, int w, int h,
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +010064 rdr::U8* data_)
65 : PixelBuffer(pf, w, h), data(data_)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000066{
Pierre Ossman1ed4d502014-01-07 15:28:45 +000067}
68
69FullFramePixelBuffer::FullFramePixelBuffer() : data(0) {}
70
71FullFramePixelBuffer::~FullFramePixelBuffer() {}
72
73
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000074int FullFramePixelBuffer::getStride() const { return width(); }
75
Pierre Ossman945cdda2014-01-28 14:13:12 +010076rdr::U8* FullFramePixelBuffer::getBufferRW(const Rect& r, int* stride)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000077{
78 *stride = getStride();
79 return &data[(r.tl.x + (r.tl.y * *stride)) * format.bpp/8];
80}
81
82
83void FullFramePixelBuffer::fillRect(const Rect& r, Pixel pix) {
84 int stride;
Pierre Ossman5c1a1532014-01-30 16:59:14 +010085 U8 *buf, pixbuf[4];
86 int w, h, b;
87
88 buf = getBufferRW(r, &stride);
89 w = r.width();
90 h = r.height();
91 b = format.bpp/8;
92
93 format.bufferFromPixel(pixbuf, pix);
94
95 while (h--) {
96 int w_ = w;
97 while (w_--) {
98 memcpy(buf, pixbuf, b);
99 buf += b;
100 }
101 buf += (stride - w) * b;
102 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000103}
104
105void FullFramePixelBuffer::imageRect(const Rect& r, const void* pixels, int srcStride) {
106 int bytesPerPixel = getPF().bpp/8;
107 int destStride;
Pierre Ossman945cdda2014-01-28 14:13:12 +0100108 U8* dest = getBufferRW(r, &destStride);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000109 int bytesPerDestRow = bytesPerPixel * destStride;
110 if (!srcStride) srcStride = r.width();
111 int bytesPerSrcRow = bytesPerPixel * srcStride;
112 int bytesPerFill = bytesPerPixel * r.width();
113 const U8* src = (const U8*)pixels;
114 U8* end = dest + (bytesPerDestRow * r.height());
115 while (dest < end) {
116 memcpy(dest, src, bytesPerFill);
117 dest += bytesPerDestRow;
118 src += bytesPerSrcRow;
119 }
120}
121
122void FullFramePixelBuffer::maskRect(const Rect& r, const void* pixels, const void* mask_) {
123 Rect cr = getRect().intersect(r);
124 if (cr.is_empty()) return;
125 int stride;
Pierre Ossman945cdda2014-01-28 14:13:12 +0100126 U8* data = getBufferRW(cr, &stride);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000127 U8* mask = (U8*) mask_;
128 int w = cr.width();
129 int h = cr.height();
130 int bpp = getPF().bpp;
131 int pixelStride = r.width();
132 int maskStride = (r.width() + 7) / 8;
133
134 Point offset = Point(cr.tl.x-r.tl.x, cr.tl.y-r.tl.y);
135 mask += offset.y * maskStride;
136 for (int y = 0; y < h; y++) {
137 int cy = offset.y + y;
138 for (int x = 0; x < w; x++) {
139 int cx = offset.x + x;
140 U8* byte = mask + (cx / 8);
141 int bit = 7 - cx % 8;
142 if ((*byte) & (1 << bit)) {
143 switch (bpp) {
144 case 8:
145 ((U8*)data)[y * stride + x] = ((U8*)pixels)[cy * pixelStride + cx];
146 break;
147 case 16:
148 ((U16*)data)[y * stride + x] = ((U16*)pixels)[cy * pixelStride + cx];
149 break;
150 case 32:
151 ((U32*)data)[y * stride + x] = ((U32*)pixels)[cy * pixelStride + cx];
152 break;
153 }
154 }
155 }
156 mask += maskStride;
157 }
158}
159
160void FullFramePixelBuffer::maskRect(const Rect& r, Pixel pixel, const void* mask_) {
161 Rect cr = getRect().intersect(r);
162 if (cr.is_empty()) return;
163 int stride;
Pierre Ossman945cdda2014-01-28 14:13:12 +0100164 U8* data = getBufferRW(cr, &stride);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000165 U8* mask = (U8*) mask_;
166 int w = cr.width();
167 int h = cr.height();
168 int bpp = getPF().bpp;
169 int maskStride = (r.width() + 7) / 8;
170
171 Point offset = Point(cr.tl.x-r.tl.x, cr.tl.y-r.tl.y);
172 mask += offset.y * maskStride;
173 for (int y = 0; y < h; y++) {
174 for (int x = 0; x < w; x++) {
175 int cx = offset.x + x;
176 U8* byte = mask + (cx / 8);
177 int bit = 7 - cx % 8;
178 if ((*byte) & (1 << bit)) {
179 switch (bpp) {
180 case 8:
181 ((U8*)data)[y * stride + x] = pixel;
182 break;
183 case 16:
184 ((U16*)data)[y * stride + x] = pixel;
185 break;
186 case 32:
187 ((U32*)data)[y * stride + x] = pixel;
188 break;
189 }
190 }
191 }
192 mask += maskStride;
193 }
194}
195
196void FullFramePixelBuffer::copyRect(const Rect &rect, const Point &move_by_delta) {
197 int stride;
Pierre Ossman45de20d2010-12-21 15:53:42 +0000198 U8* data;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000199 unsigned int bytesPerPixel, bytesPerRow, bytesPerMemCpy;
Pierre Ossman45de20d2010-12-21 15:53:42 +0000200 Rect drect, srect = rect.translate(move_by_delta.negate());
201
202 drect = rect;
203 if (!drect.enclosed_by(getRect())) {
204 vlog.error("Destination rect %dx%d at %d,%d exceeds framebuffer %dx%d",
205 drect.width(), drect.height(), drect.tl.x, drect.tl.y, width_, height_);
206 drect = drect.intersect(getRect());
207 }
208
209 if (drect.is_empty())
210 return;
211
212 srect = drect.translate(move_by_delta.negate());
213 if (!srect.enclosed_by(getRect())) {
214 vlog.error("Source rect %dx%d at %d,%d exceeds framebuffer %dx%d",
215 srect.width(), srect.height(), srect.tl.x, srect.tl.y, width_, height_);
216 srect = srect.intersect(getRect());
217 // Need to readjust the destination now that the area has changed
218 drect = srect.translate(move_by_delta);
219 }
220
221 if (srect.is_empty())
222 return;
223
Pierre Ossman945cdda2014-01-28 14:13:12 +0100224 data = getBufferRW(getRect(), &stride);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000225 bytesPerPixel = getPF().bpp/8;
226 bytesPerRow = stride * bytesPerPixel;
Pierre Ossman45de20d2010-12-21 15:53:42 +0000227 bytesPerMemCpy = drect.width() * bytesPerPixel;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000228 if (move_by_delta.y <= 0) {
Pierre Ossman45de20d2010-12-21 15:53:42 +0000229 U8* dest = data + drect.tl.x*bytesPerPixel + drect.tl.y*bytesPerRow;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000230 U8* src = data + srect.tl.x*bytesPerPixel + srect.tl.y*bytesPerRow;
Pierre Ossman45de20d2010-12-21 15:53:42 +0000231 for (int i=drect.tl.y; i<drect.br.y; i++) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000232 memmove(dest, src, bytesPerMemCpy);
233 dest += bytesPerRow;
234 src += bytesPerRow;
235 }
236 } else {
Pierre Ossman45de20d2010-12-21 15:53:42 +0000237 U8* dest = data + drect.tl.x*bytesPerPixel + (drect.br.y-1)*bytesPerRow;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000238 U8* src = data + srect.tl.x*bytesPerPixel + (srect.br.y-1)*bytesPerRow;
Pierre Ossman45de20d2010-12-21 15:53:42 +0000239 for (int i=drect.tl.y; i<drect.br.y; i++) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000240 memmove(dest, src, bytesPerMemCpy);
241 dest -= bytesPerRow;
242 src -= bytesPerRow;
243 }
244 }
245}
246
247
248// -=- Managed pixel buffer class
249// Automatically allocates enough space for the specified format & area
250
251ManagedPixelBuffer::ManagedPixelBuffer()
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100252 : datasize(0)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000253{
254 checkDataSize();
255};
256
257ManagedPixelBuffer::ManagedPixelBuffer(const PixelFormat& pf, int w, int h)
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100258 : FullFramePixelBuffer(pf, w, h, 0), datasize(0)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000259{
260 checkDataSize();
261};
262
263ManagedPixelBuffer::~ManagedPixelBuffer() {
264 if (data) delete [] data;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000265};
266
267
268void
269ManagedPixelBuffer::setPF(const PixelFormat &pf) {
Pierre Ossman5c1a1532014-01-30 16:59:14 +0100270 format = pf; checkDataSize();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000271};
272void
273ManagedPixelBuffer::setSize(int w, int h) {
274 width_ = w; height_ = h; checkDataSize();
275};
276
277
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000278inline void
279ManagedPixelBuffer::checkDataSize() {
280 unsigned long new_datasize = width_ * height_ * (format.bpp/8);
281 if (datasize < new_datasize) {
282 vlog.debug("reallocating managed buffer (%dx%d)", width_, height_);
283 if (data) {
284 delete [] data;
285 datasize = 0; data = 0;
286 }
287 if (new_datasize) {
288 data = new U8[new_datasize];
289 if (!data)
290 throw Exception("rfb::ManagedPixelBuffer unable to allocate buffer");
291 datasize = new_datasize;
292 }
293 }
294};