KasmVNC/common/rfb/PixelBuffer.cxx
2020-09-20 12:16:44 +00:00

374 lines
9.9 KiB
C++

/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2014 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
// -=- PixelBuffer.cxx
//
// The PixelBuffer class encapsulates the PixelFormat and dimensions
// of a block of pixel data.
#include <rfb/Exception.h>
#include <rfb/LogWriter.h>
#include <rfb/PixelBuffer.h>
using namespace rfb;
using namespace rdr;
static LogWriter vlog("PixelBuffer");
// -=- Generic pixel buffer class
PixelBuffer::PixelBuffer(const PixelFormat& pf, int w, int h)
: format(pf), width_(w), height_(h) {}
PixelBuffer::PixelBuffer() : width_(0), height_(0) {}
PixelBuffer::~PixelBuffer() {}
void
PixelBuffer::getImage(void* imageBuf, const Rect& r, int outStride) const
{
int inStride;
const U8* data;
int bytesPerPixel, inBytesPerRow, outBytesPerRow, bytesPerMemCpy;
U8* imageBufPos;
const U8* end;
if (!r.enclosed_by(getRect()))
throw rfb::Exception("Source rect %dx%d at %d,%d exceeds framebuffer %dx%d",
r.width(), r.height(),
r.tl.x, r.tl.y, width_, height_);
data = getBuffer(r, &inStride);
bytesPerPixel = format.bpp/8;
inBytesPerRow = inStride * bytesPerPixel;
if (!outStride)
outStride = r.width();
outBytesPerRow = outStride * bytesPerPixel;
bytesPerMemCpy = r.width() * bytesPerPixel;
imageBufPos = (U8*)imageBuf;
end = data + (inBytesPerRow * r.height());
while (data < end) {
memcpy(imageBufPos, data, bytesPerMemCpy);
imageBufPos += outBytesPerRow;
data += inBytesPerRow;
}
}
void PixelBuffer::getImage(const PixelFormat& pf, void* imageBuf,
const Rect& r, int stride) const
{
const rdr::U8* srcBuffer;
int srcStride;
if (format.equal(pf)) {
getImage(imageBuf, r, stride);
return;
}
if (!r.enclosed_by(getRect()))
throw rfb::Exception("Source rect %dx%d at %d,%d exceeds framebuffer %dx%d",
r.width(), r.height(),
r.tl.x, r.tl.y, width_, height_);
if (stride == 0)
stride = r.width();
srcBuffer = getBuffer(r, &srcStride);
pf.bufferFromBuffer((U8*)imageBuf, format, srcBuffer, r.width(), r.height(),
stride, srcStride);
}
// -=- Modifiable generic pixel buffer class
ModifiablePixelBuffer::ModifiablePixelBuffer(const PixelFormat& pf,
int w, int h)
: PixelBuffer(pf, w, h)
{
}
ModifiablePixelBuffer::ModifiablePixelBuffer()
{
}
ModifiablePixelBuffer::~ModifiablePixelBuffer()
{
}
void ModifiablePixelBuffer::fillRect(const Rect& r, const void* pix)
{
int stride;
U8 *buf;
int w, h, b;
if (!r.enclosed_by(getRect()))
throw rfb::Exception("Destination rect %dx%d at %d,%d exceeds framebuffer %dx%d",
r.width(), r.height(), r.tl.x, r.tl.y, width_, height_);
w = r.width();
h = r.height();
b = format.bpp/8;
if (h == 0)
return;
buf = getBufferRW(r, &stride);
if (b == 1) {
while (h--) {
memset(buf, *(const U8*)pix, w);
buf += stride * b;
}
} else {
U8 *start;
int w1;
start = buf;
w1 = w;
while (w1--) {
memcpy(buf, pix, b);
buf += b;
}
buf += (stride - w) * b;
h--;
while (h--) {
memcpy(buf, start, w * b);
buf += stride * b;
}
}
commitBufferRW(r);
}
void ModifiablePixelBuffer::imageRect(const Rect& r,
const void* pixels, int srcStride)
{
U8* dest;
int destStride;
int bytesPerPixel, bytesPerDestRow, bytesPerSrcRow, bytesPerFill;
const U8* src;
U8* end;
if (!r.enclosed_by(getRect()))
throw rfb::Exception("Destination rect %dx%d at %d,%d exceeds framebuffer %dx%d",
r.width(), r.height(),
r.tl.x, r.tl.y, width_, height_);
bytesPerPixel = getPF().bpp/8;
dest = getBufferRW(r, &destStride);
bytesPerDestRow = bytesPerPixel * destStride;
if (!srcStride)
srcStride = r.width();
bytesPerSrcRow = bytesPerPixel * srcStride;
bytesPerFill = bytesPerPixel * r.width();
src = (const U8*)pixels;
end = dest + (bytesPerDestRow * r.height());
while (dest < end) {
memcpy(dest, src, bytesPerFill);
dest += bytesPerDestRow;
src += bytesPerSrcRow;
}
commitBufferRW(r);
}
void ModifiablePixelBuffer::copyRect(const Rect &rect,
const Point &move_by_delta)
{
int srcStride, dstStride;
int bytesPerPixel;
const U8* srcData;
U8* dstData;
Rect drect, srect;
drect = rect;
if (!drect.enclosed_by(getRect()))
throw rfb::Exception("Destination rect %dx%d at %d,%d exceeds framebuffer %dx%d",
drect.width(), drect.height(),
drect.tl.x, drect.tl.y, width_, height_);
srect = drect.translate(move_by_delta.negate());
if (!srect.enclosed_by(getRect()))
throw rfb::Exception("Source rect %dx%d at %d,%d exceeds framebuffer %dx%d",
srect.width(), srect.height(),
srect.tl.x, srect.tl.y, width_, height_);
bytesPerPixel = format.bpp/8;
srcData = getBuffer(srect, &srcStride);
dstData = getBufferRW(drect, &dstStride);
if (move_by_delta.y == 0) {
// Possible overlap. Be careful and use memmove().
int h = drect.height();
while (h--) {
memmove(dstData, srcData, drect.width() * bytesPerPixel);
dstData += dstStride * bytesPerPixel;
srcData += srcStride * bytesPerPixel;
}
} else if (move_by_delta.y < 0) {
// The data shifted upwards. Copy from top to bottom.
int h = drect.height();
while (h--) {
memcpy(dstData, srcData, drect.width() * bytesPerPixel);
dstData += dstStride * bytesPerPixel;
srcData += srcStride * bytesPerPixel;
}
} else {
// The data shifted downwards. Copy from bottom to top.
int h = drect.height();
dstData += (h-1) * dstStride * bytesPerPixel;
srcData += (h-1) * srcStride * bytesPerPixel;
while (h--) {
memcpy(dstData, srcData, drect.width() * bytesPerPixel);
dstData -= dstStride * bytesPerPixel;
srcData -= srcStride * bytesPerPixel;
}
}
commitBufferRW(drect);
}
void ModifiablePixelBuffer::fillRect(const PixelFormat& pf, const Rect &dest,
const void* pix)
{
rdr::U8 buf[4];
format.bufferFromBuffer(buf, pf, (const rdr::U8*)pix, 1);
fillRect(dest, buf);
}
void ModifiablePixelBuffer::imageRect(const PixelFormat& pf, const Rect &dest,
const void* pixels, int stride)
{
rdr::U8* dstBuffer;
int dstStride;
if (!dest.enclosed_by(getRect()))
throw rfb::Exception("Destination rect %dx%d at %d,%d exceeds framebuffer %dx%d",
dest.width(), dest.height(),
dest.tl.x, dest.tl.y, width_, height_);
if (stride == 0)
stride = dest.width();
dstBuffer = getBufferRW(dest, &dstStride);
format.bufferFromBuffer(dstBuffer, pf, (const rdr::U8*)pixels,
dest.width(), dest.height(),
dstStride, stride);
commitBufferRW(dest);
}
// -=- Simple pixel buffer with a continuous block of memory
FullFramePixelBuffer::FullFramePixelBuffer(const PixelFormat& pf, int w, int h,
rdr::U8* data_, int stride_)
: ModifiablePixelBuffer(pf, w, h), data(data_), stride(stride_)
{
}
FullFramePixelBuffer::FullFramePixelBuffer() : data(0) {}
FullFramePixelBuffer::~FullFramePixelBuffer() {}
rdr::U8* FullFramePixelBuffer::getBufferRW(const Rect& r, int* stride_)
{
if (!r.enclosed_by(getRect()))
throw rfb::Exception("Pixel buffer request %dx%d at %d,%d exceeds framebuffer %dx%d",
r.width(), r.height(),
r.tl.x, r.tl.y, width_, height_);
*stride_ = stride;
return &data[(r.tl.x + (r.tl.y * stride)) * (format.bpp/8)];
}
void FullFramePixelBuffer::commitBufferRW(const Rect& r)
{
}
const rdr::U8* FullFramePixelBuffer::getBuffer(const Rect& r, int* stride_) const
{
if (!r.enclosed_by(getRect()))
throw rfb::Exception("Pixel buffer request %dx%d at %d,%d exceeds framebuffer %dx%d",
r.width(), r.height(),
r.tl.x, r.tl.y, width_, height_);
*stride_ = stride;
return &data[(r.tl.x + (r.tl.y * stride)) * (format.bpp/8)];
}
// -=- Managed pixel buffer class
// Automatically allocates enough space for the specified format & area
ManagedPixelBuffer::ManagedPixelBuffer()
: datasize(0)
{
checkDataSize();
};
ManagedPixelBuffer::ManagedPixelBuffer(const PixelFormat& pf, int w, int h)
: FullFramePixelBuffer(pf, w, h, NULL, w), datasize(0)
{
checkDataSize();
};
ManagedPixelBuffer::~ManagedPixelBuffer() {
if (data) delete [] data;
};
void
ManagedPixelBuffer::setPF(const PixelFormat &pf) {
format = pf; checkDataSize();
};
void
ManagedPixelBuffer::setSize(int w, int h) {
width_ = w; height_ = h; stride = w; checkDataSize();
};
inline void
ManagedPixelBuffer::checkDataSize() {
unsigned long new_datasize = width_ * height_ * (format.bpp/8);
if (datasize < new_datasize) {
if (data) {
delete [] data;
datasize = 0; data = 0;
}
if (new_datasize) {
data = new U8[new_datasize];
if (!data)
throw Exception("rfb::ManagedPixelBuffer unable to allocate buffer");
datasize = new_datasize;
}
}
};