int w, int h,
const ScreenSet& layout);
virtual void setCursor(int width, int height, const Point& hotspot,
- void* data, void* mask) = 0;
+ const rdr::U8* data) = 0;
virtual void setPixelFormat(const PixelFormat& pf);
virtual void setName(const char* name);
virtual void fence(rdr::U32 flags, unsigned len, const char data[]);
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2009-2014 Pierre Ossman for Cendio AB
+ * Copyright 2009-2017 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
rdr::U8Array data(data_len);
rdr::U8Array mask(mask_len);
+ int x, y;
+ rdr::U8 buf[width*height*4];
+ rdr::U8* in;
+ rdr::U8* out;
+
is->readBytes(data.buf, data_len);
is->readBytes(mask.buf, mask_len);
- handler->setCursor(width, height, hotspot, data.buf, mask.buf);
+ int maskBytesPerRow = (width+7)/8;
+ in = data.buf;
+ out = buf;
+ for (y = 0;y < height;y++) {
+ for (x = 0;x < width;x++) {
+ int byte = y * maskBytesPerRow + x / 8;
+ int bit = 7 - x % 8;
+
+ handler->cp.pf().rgbFromBuffer(out, in, 1);
+
+ if (mask.buf[byte] & (1 << bit))
+ out[3] = 255;
+ else
+ out[3] = 0;
+
+ in += handler->cp.pf().bpp/8;
+ out += 4;
+ }
+ }
+
+ handler->setCursor(width, height, hotspot, buf);
}
void CMsgReader::readSetDesktopName(int x, int y, int w, int h)
subsampling(subsampleUndefined), name_(0), verStrPos(0)
{
setName("");
+ cursor_ = new Cursor(0, 0, Point(), NULL);
}
ConnParams::~ConnParams()
{
delete [] name_;
+ delete cursor_;
}
bool ConnParams::readVersion(rdr::InStream* is, bool* done)
void ConnParams::setCursor(const Cursor& other)
{
- const rdr::U8* data;
- int stride;
-
- cursor_.hotspot = other.hotspot;
- cursor_.setPF(other.getPF());
- cursor_.setSize(other.width(), other.height());
-
- data = other.getBuffer(other.getRect(), &stride);
- cursor_.imageRect(cursor_.getRect(), data, stride);
-
- memcpy(cursor_.mask.buf, other.mask.buf, cursor_.maskLen());
+ delete cursor_;
+ cursor_ = new Cursor(other);
}
bool ConnParams::supportsEncoding(rdr::S32 encoding) const
const char* name() const { return name_; }
void setName(const char* name);
- const Cursor& cursor() const { return cursor_; }
+ const Cursor& cursor() const { return *cursor_; }
void setCursor(const Cursor& cursor);
bool supportsEncoding(rdr::S32 encoding) const;
PixelFormat pf_;
char* name_;
- Cursor cursor_;
+ Cursor* cursor_;
std::set<rdr::S32> encodings_;
char verStr[13];
int verStrPos;
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2014 Pierre Ossman for Cendio AB
+ * Copyright 2014-2017 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
static LogWriter vlog("Cursor");
-void Cursor::setSize(int w, int h) {
- int oldMaskLen = maskLen();
- ManagedPixelBuffer::setSize(w, h);
- if (maskLen() > oldMaskLen) {
- delete [] mask.buf;
- mask.buf = new rdr::U8[maskLen()];
- }
+Cursor::Cursor(int width, int height, const Point& hotspot,
+ const rdr::U8* data) :
+ width_(width), height_(height), hotspot_(hotspot)
+{
+ this->data = new rdr::U8[width_*height_*4];
+ memcpy(this->data, data, width_*height_*4);
}
-void Cursor::drawOutline(const Pixel& c)
+Cursor::Cursor(const Cursor& other) :
+ width_(other.width_), height_(other.height_),
+ hotspot_(other.hotspot_)
{
- Cursor outlined;
- rdr::U8 cbuf[4];
-
- // Create a mirror of the existing cursor
- outlined.setPF(getPF());
- outlined.setSize(width(), height());
- outlined.hotspot = hotspot;
+ data = new rdr::U8[width_*height_*4];
+ memcpy(data, other.data, width_*height_*4);
+}
- // Clear the mirror's background to the outline colour
- outlined.getPF().bufferFromPixel(cbuf, c);
- outlined.fillRect(getRect(), cbuf);
+Cursor::~Cursor()
+{
+ delete [] data;
+}
- // Blit the existing cursor, using its mask
- outlined.maskRect(getRect(), data, mask.buf);
+rdr::U8* Cursor::getBitmap() const
+{
+ rdr::U8Array source((width()+7)/8*height());
+ memset(source.buf, 0, (width()+7)/8*height());
- // Now just adjust the mask to add the outline. The outline pixels
- // will already be the right colour. :)
int maskBytesPerRow = (width() + 7) / 8;
+ const rdr::U8 *data_ptr = data;
for (int y = 0; y < height(); y++) {
- for (int byte=0; byte<maskBytesPerRow; byte++) {
- rdr::U8 m8 = mask.buf[y*maskBytesPerRow + byte];
-
- // Handle above & below outline
- if (y > 0) m8 |= mask.buf[(y-1)*maskBytesPerRow + byte];
- if (y < height()-1) m8 |= mask.buf[(y+1)*maskBytesPerRow + byte];
-
- // Left outline
- m8 |= mask.buf[y*maskBytesPerRow + byte] << 1;
- if (byte < maskBytesPerRow-1)
- m8 |= (mask.buf[y*maskBytesPerRow + byte + 1] >> 7) & 1;
-
- // Right outline
- m8 |= mask.buf[y*maskBytesPerRow + byte] >> 1;
- if (byte > 0)
- m8 |= (mask.buf[y*maskBytesPerRow + byte - 1] << 7) & 128;
-
- outlined.mask.buf[y*maskBytesPerRow + byte] = m8;
+ for (int x = 0; x < width(); x++) {
+ int byte = y * maskBytesPerRow + x / 8;
+ int bit = 7 - x % 8;
+ if (data_ptr[3] >= 0x80) {
+ // Use Luma with BT.709 coefficients for grayscale
+ unsigned luma;
+
+ luma = 0;
+ luma += (unsigned)data_ptr[0] * 13933; // 0.2126
+ luma += (unsigned)data_ptr[1] * 46871; // 0.7152
+ luma += (unsigned)data_ptr[2] * 4732; // 0.0722
+ luma /= 65536;
+
+ // Gamma compensated half intensity gray
+ if (luma > 187)
+ source.buf[byte] |= (1 << bit);
+ }
+ data_ptr += 4;
}
}
- // Replace the existing cursor & mask with the new one
- delete [] data;
- delete [] mask.buf;
- data = outlined.data; outlined.data = 0;
- mask.buf = outlined.mask.buf; outlined.mask.buf = 0;
+ return source.takeBuf();
}
-rdr::U8* Cursor::getBitmap(Pixel* pix0, Pixel* pix1) const
+rdr::U8* Cursor::getMask() const
{
- bool gotPix0 = false;
- bool gotPix1 = false;
- *pix0 = *pix1 = 0;
- rdr::U8Array source(maskLen());
- memset(source.buf, 0, maskLen());
+ rdr::U8Array mask((width()+7)/8*height());
+ memset(mask.buf, 0, (width()+7)/8*height());
int maskBytesPerRow = (width() + 7) / 8;
const rdr::U8 *data_ptr = data;
for (int x = 0; x < width(); x++) {
int byte = y * maskBytesPerRow + x / 8;
int bit = 7 - x % 8;
- if (mask.buf[byte] & (1 << bit)) {
- Pixel pix = getPF().pixelFromBuffer(data_ptr);
- if (!gotPix0 || pix == *pix0) {
- gotPix0 = true;
- *pix0 = pix;
- } else if (!gotPix1 || pix == *pix1) {
- gotPix1 = true;
- *pix1 = pix;
- source.buf[byte] |= (1 << bit);
- } else {
- // not a bitmap
- return 0;
- }
- }
- data_ptr += getPF().bpp/8;
+ if (data_ptr[3] >= 0x80)
+ mask.buf[byte] |= (1 << bit);
+ data_ptr += 4;
}
}
- return source.takeBuf();
+
+ return mask.takeBuf();
}
// crop() determines the "busy" rectangle for the cursor - the minimum bounding
void Cursor::crop()
{
- Rect busy = getRect().intersect(Rect(hotspot.x, hotspot.y,
- hotspot.x+1, hotspot.y+1));
- int maskBytesPerRow = (width() + 7) / 8;
+ Rect busy = Rect(0, 0, width_, height_);
+ busy = busy.intersect(Rect(hotspot_.x, hotspot_.y,
+ hotspot_.x+1, hotspot_.y+1));
int x, y;
+ rdr::U8 *data_ptr = data;
for (y = 0; y < height(); y++) {
for (x = 0; x < width(); x++) {
- int byte = y * maskBytesPerRow + x / 8;
- int bit = 7 - x % 8;
- if (mask.buf[byte] & (1 << bit)) {
+ if (data_ptr[3] > 0) {
if (x < busy.tl.x) busy.tl.x = x;
if (x+1 > busy.br.x) busy.br.x = x+1;
if (y < busy.tl.y) busy.tl.y = y;
if (y+1 > busy.br.y) busy.br.y = y+1;
}
+ data_ptr += 4;
}
}
if (width() == busy.width() && height() == busy.height()) return;
- vlog.debug("cropping %dx%d to %dx%d", width(), height(),
- busy.width(), busy.height());
-
// Copy the pixel data
- int newDataLen = busy.area() * (getPF().bpp/8);
+ int newDataLen = busy.area() * 4;
rdr::U8* newData = new rdr::U8[newDataLen];
- getImage(newData, busy);
-
- // Copy the mask
- int newMaskBytesPerRow = (busy.width()+7)/8;
- int newMaskLen = newMaskBytesPerRow * busy.height();
- rdr::U8* newMask = new rdr::U8[newMaskLen];
- memset(newMask, 0, newMaskLen);
- for (y = 0; y < busy.height(); y++) {
- int newByte, newBit;
- for (x = 0; x < busy.width(); x++) {
- int oldByte = (y+busy.tl.y) * maskBytesPerRow + (x+busy.tl.x) / 8;
- int oldBit = 7 - (x+busy.tl.x) % 8;
- newByte = y * newMaskBytesPerRow + x / 8;
- newBit = 7 - x % 8;
- if (mask.buf[oldByte] & (1 << oldBit))
- newMask[newByte] |= (1 << newBit);
- }
+ data_ptr = newData;
+ for (y = busy.tl.y; y < busy.br.y; y++) {
+ memcpy(data_ptr, data + y*width()*4 + busy.tl.x*4, busy.width()*4);
+ data_ptr += busy.width()*4;
}
// Set the size and data to the new, cropped cursor.
- setSize(busy.width(), busy.height());
- hotspot = hotspot.subtract(busy.tl);
+ width_ = busy.width();
+ height_ = busy.height();
+ hotspot_ = hotspot_.subtract(busy.tl);
delete [] data;
- delete [] mask.buf;
- datasize = newDataLen;
data = newData;
- mask.buf = newMask;
}
RenderedCursor::RenderedCursor()
assert(framebuffer);
assert(cursor);
- if (!framebuffer->getPF().equal(cursor->getPF()))
- throw Exception("RenderedCursor: Trying to render cursor on incompatible frame buffer");
-
format = framebuffer->getPF();
width_ = framebuffer->width();
height_ = framebuffer->height();
- rawOffset = pos.subtract(cursor->hotspot);
- clippedRect = cursor->getRect(rawOffset).intersect(framebuffer->getRect());
+ rawOffset = pos.subtract(cursor->hotspot());
+ clippedRect = Rect(0, 0, cursor->width(), cursor->height())
+ .translate(rawOffset)
+ .intersect(framebuffer->getRect());
offset = clippedRect.tl;
- buffer.setPF(cursor->getPF());
+ buffer.setPF(format);
buffer.setSize(clippedRect.width(), clippedRect.height());
data = framebuffer->getBuffer(buffer.getRect(offset), &stride);
buffer.imageRect(buffer.getRect(), data, stride);
diff = offset.subtract(rawOffset);
- data = cursor->getBuffer(buffer.getRect(diff), &stride);
+ for (int y = 0;y < buffer.height();y++) {
+ for (int x = 0;x < buffer.width();x++) {
+ size_t idx;
+ rdr::U8 bg[4], fg[4];
+ rdr::U8 rgb[3];
+
+ idx = (y+diff.y)*cursor->width() + (x+diff.x);
+ memcpy(fg, cursor->getBuffer() + idx*4, 4);
+
+ if (fg[3] == 0x00)
+ continue;
+ else if (fg[3] == 0xff) {
+ memcpy(rgb, fg, 3);
+ } else {
+ buffer.getImage(bg, Rect(x, y, x+1, y+1));
+ format.rgbFromBuffer(rgb, bg, 1);
+ // FIXME: Gamma aware blending
+ for (int i = 0;i < 3;i++) {
+ rgb[i] = (unsigned)rgb[i]*(255-fg[3])/255 +
+ (unsigned)fg[i]*fg[3]/255;
+ }
+ }
- buffer.maskRect(buffer.getRect(), data, cursor->mask.buf, diff,
- stride, (cursor->width() + 7) / 8);
+ format.bufferFromRGB(bg, rgb, 1);
+ buffer.imageRect(Rect(x, y, x+1, y+1), bg);
+ }
+ }
}
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2014 Pierre Ossman for Cendio AB
+ * Copyright 2014-2017 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
namespace rfb {
- class Cursor : public ManagedPixelBuffer {
+ class Cursor {
public:
- Cursor() {}
- rdr::U8Array mask;
- Point hotspot;
+ Cursor(int width, int height, const Point& hotspot, const rdr::U8* data);
+ Cursor(const Cursor& other);
+ ~Cursor();
- int maskLen() const { return (width() + 7) / 8 * height(); }
+ int width() const { return width_; };
+ int height() const { return height_; };
+ const Point& hotspot() const { return hotspot_; };
+ const rdr::U8* getBuffer() const { return data; };
- // setSize() resizes the cursor. The contents of the data and mask are
- // undefined after this call.
- virtual void setSize(int w, int h);
-
- // drawOutline() adds an outline to the cursor in the given colour.
- void drawOutline(const Pixel& c);
-
- // getBitmap() tests whether the cursor is monochrome, and if so returns a
- // bitmap together with background and foreground colours. The size and
- // layout of the bitmap are the same as the mask.
- rdr::U8* getBitmap(Pixel* pix0, Pixel* pix1) const;
+ // getBitmap() returns a monochrome version of the cursor
+ rdr::U8* getBitmap() const;
+ // getMask() returns a simple mask version of the alpha channel
+ rdr::U8* getMask() const;
// crop() crops the cursor down to the smallest possible size, based on the
// mask.
void crop();
+
+ protected:
+ int width_, height_;
+ Point hotspot_;
+ rdr::U8* data;
};
class RenderedCursor : public PixelBuffer {
commitBufferRW(r);
}
-void ModifiablePixelBuffer::maskRect(const Rect& r,
- const void* pixels,
- const void* mask_,
- const Point& maskPos,
- int pStride, int mStride)
-{
- int stride;
- U8* data;
- U8* mask;
- int w, h, bpp;
-
- 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_);
-
- data = getBufferRW(r, &stride);
- mask = (U8*) mask_;
-
- w = r.width();
- h = r.height();
- bpp = getPF().bpp;
- if (pStride == 0)
- pStride = r.width();
- if (mStride == 0)
- mStride = (r.width() + 7) / 8;
-
- mask += maskPos.y * mStride;
- for (int y = 0; y < h; y++) {
- for (int x = 0; x < w; x++) {
- int cx = maskPos.x + x;
- U8* byte = mask + (cx / 8);
- int bit = 7 - cx % 8;
- if ((*byte) & (1 << bit)) {
- switch (bpp) {
- case 8:
- ((U8*)data)[y * stride + x] = ((U8*)pixels)[y * pStride + x];
- break;
- case 16:
- ((U16*)data)[y * stride + x] = ((U16*)pixels)[y * pStride + x];
- break;
- case 32:
- ((U32*)data)[y * stride + x] = ((U32*)pixels)[y * pStride + x];
- break;
- }
- }
- }
- mask += mStride;
- }
-
- commitBufferRW(r);
-}
-
-void ModifiablePixelBuffer::maskRect(const Rect& r,
- Pixel pixel,
- const void* mask_,
- const Point& maskPos,
- int mStride)
-{
- int stride;
- U8* data;
- U8* mask;
- int w, h, bpp;
-
- 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_);
-
- data = getBufferRW(r, &stride);
- mask = (U8*) mask_;
-
- w = r.width();
- h = r.height();
- bpp = getPF().bpp;
- if (mStride == 0)
- mStride = (r.width() + 7) / 8;
-
- mask += maskPos.y * mStride;
- for (int y = 0; y < h; y++) {
- for (int x = 0; x < w; x++) {
- int cx = maskPos.x + x;
- U8* byte = mask + (cx / 8);
- int bit = 7 - cx % 8;
- if ((*byte) & (1 << bit)) {
- switch (bpp) {
- case 8:
- ((U8*)data)[y * stride + x] = pixel;
- break;
- case 16:
- ((U16*)data)[y * stride + x] = pixel;
- break;
- case 32:
- ((U32*)data)[y * stride + x] = pixel;
- break;
- }
- }
- }
- mask += mStride;
- }
-
- commitBufferRW(r);
-}
-
void ModifiablePixelBuffer::copyRect(const Rect &rect,
const Point &move_by_delta)
{
// Copy pixel data from one PixelBuffer location to another
void copyRect(const Rect &dest, const Point& move_by_delta);
- // Copy pixel data to the buffer through a mask
- // pixels is a pointer to the pixel to be copied to r.tl.
- // maskPos specifies the pixel offset in the mask to start from.
- // mask_ is a pointer to the mask bits at (0,0).
- // pStride and mStride are the strides of the pixel and mask buffers.
- void maskRect(const Rect& r, const void* pixels, const void* mask_,
- const Point& maskPos=Point(0, 0),
- int pStride=0, int mStride=0);
-
- // pixel is the Pixel value to be used where mask_ is set
- void maskRect(const Rect& r, Pixel pixel, const void* mask_,
- const Point& maskPos=Point(0, 0),
- int mStride=0);
-
// Render in a specific format
// Does the exact same thing as the above methods, but the given
// pixel values are defined by the given PixelFormat.
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright (C) 2011 D. R. Commander. All Rights Reserved.
- * Copyright 2009-2014 Pierre Ossman for Cendio AB
+ * Copyright 2009-2017 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
void SMsgWriter::writePseudoRects()
{
if (needSetCursor) {
- rdr::U8* data;
-
const Cursor& cursor = cp->cursor();
- data = new rdr::U8[cursor.area() * cp->pf().bpp/8];
- cursor.getImage(cp->pf(), data, cursor.getRect());
+ rdr::U8Array data(cursor.width()*cursor.height() * cp->pf().bpp/8);
+ rdr::U8Array mask(cursor.getMask());
+
+ const rdr::U8* in;
+ rdr::U8* out;
+
+ in = cursor.getBuffer();
+ out = data.buf;
+ for (int i = 0;i < cursor.width()*cursor.height();i++) {
+ cp->pf().bufferFromRGB(out, in, 1);
+ in += 4;
+ out += cp->pf().bpp/8;
+ }
writeSetCursorRect(cursor.width(), cursor.height(),
- cursor.hotspot.x, cursor.hotspot.y,
- data, cursor.mask.buf);
+ cursor.hotspot().x, cursor.hotspot().y,
+ data.buf, mask.buf);
needSetCursor = false;
-
- delete [] data;
}
if (needSetXCursor) {
const Cursor& cursor = cp->cursor();
- Pixel pix0, pix1;
- rdr::U8 rgb0[3], rgb1[3];
- rdr::U8Array bitmap(cursor.getBitmap(&pix0, &pix1));
-
- if (!bitmap.buf) {
- // FIXME: We could reduce to two colors.
- throw Exception("SMsgWriter::writePseudoRects: Unable to send multicolor cursor: RichCursor not supported by client");
- }
-
- cp->pf().rgbFromPixel(pix0, &rgb0[0], &rgb0[1], &rgb0[2]);
- cp->pf().rgbFromPixel(pix1, &rgb1[0], &rgb1[1], &rgb1[2]);
+ rdr::U8Array bitmap(cursor.getBitmap());
+ rdr::U8Array mask(cursor.getMask());
writeSetXCursorRect(cursor.width(), cursor.height(),
- cursor.hotspot.x, cursor.hotspot.y,
- rgb0, rgb1, bitmap.buf, cursor.mask.buf);
+ cursor.hotspot().x, cursor.hotspot().y,
+ bitmap.buf, mask.buf);
needSetXCursor = false;
}
void SMsgWriter::writeSetXCursorRect(int width, int height,
int hotspotX, int hotspotY,
- const rdr::U8 pix0[],
- const rdr::U8 pix1[],
const void* data, const void* mask)
{
if (!cp->supportsLocalXCursor)
os->writeU16(height);
os->writeU32(pseudoEncodingXCursor);
if (width * height) {
- os->writeU8(pix0[0]);
- os->writeU8(pix0[1]);
- os->writeU8(pix0[2]);
- os->writeU8(pix1[0]);
- os->writeU8(pix1[1]);
- os->writeU8(pix1[2]);
+ os->writeU8(255);
+ os->writeU8(255);
+ os->writeU8(255);
+ os->writeU8(0);
+ os->writeU8(0);
+ os->writeU8(0);
os->writeBytes(data, (width+7)/8 * height);
os->writeBytes(mask, (width+7)/8 * height);
}
const void* data, const void* mask);
void writeSetXCursorRect(int width, int height,
int hotspotX, int hotspotY,
- const rdr::U8 pix0[], const rdr::U8 pix1[],
const void* data, const void* mask);
ConnParams* cp;
if (state() != RFBSTATE_NORMAL)
return;
- cp.setCursor(server->cursor);
+ cp.setCursor(*server->cursor);
if (!writer()->writeSetCursor()) {
if (!writer()->writeSetXCursor()) {
virtual void closeClients(const char* reason) = 0;
// setCursor() tells the server that the cursor has changed. The
- // cursorData argument contains width*height pixel values in the pixel
- // buffer's format. The mask argument is a bitmask with a 1-bit meaning
- // the corresponding pixel in cursorData is valid. The mask consists of
- // left-to-right, top-to-bottom scanlines, where each scanline is padded to
- // a whole number of bytes [(width+7)/8]. Within each byte the most
- // significant bit represents the leftmost pixel, and the bytes are simply
- // in left-to-right order. The server takes its own copy of the data in
- // cursorData and mask.
+ // cursorData argument contains width*height rgba quadruplets with
+ // non-premultiplied alpha.
virtual void setCursor(int width, int height, const Point& hotspot,
- const void* cursorData, const void* mask) = 0;
+ const rdr::U8* cursorData) = 0;
// setCursorPos() tells the server the current position of the cursor.
virtual void setCursorPos(const Point& p) = 0;
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2009-2014 Pierre Ossman for Cendio AB
+ * Copyright 2009-2017 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
: blHosts(&blacklist), desktop(desktop_), desktopStarted(false),
blockCounter(0), pb(0),
name(strDup(name_)), pointerClient(0), comparer(0),
+ cursor(new Cursor(0, 0, Point(), NULL)),
renderedCursorInvalid(false),
queryConnectionHandler(0), keyRemapper(&KeyRemapper::defInstance),
lastConnectionTime(0), disableclients(false),
if (comparer)
comparer->logStats();
delete comparer;
+
+ delete cursor;
}
}
comparer = new ComparingUpdateTracker(pb);
- cursor.setPF(pb->getPF());
renderedCursorInvalid = true;
// Make sure that we have at least one screen
}
void VNCServerST::setCursor(int width, int height, const Point& newHotspot,
- const void* data, const void* mask)
+ const rdr::U8* data)
{
- cursor.hotspot = newHotspot;
- cursor.setSize(width, height);
- cursor.imageRect(cursor.getRect(), data);
- memcpy(cursor.mask.buf, mask, cursor.maskLen());
-
- cursor.crop();
+ delete cursor;
+ cursor = new Cursor(width, height, newHotspot, data);
+ cursor->crop();
renderedCursorInvalid = true;
Region toCheck = ui.changed.union_(ui.copied);
if (renderCursor) {
- Rect clippedCursorRect
- = cursor.getRect(cursorPos.subtract(cursor.hotspot)).intersect(pb->getRect());
+ Rect clippedCursorRect = Rect(0, 0, cursor->width(), cursor->height())
+ .translate(cursorPos.subtract(cursor->hotspot()))
+ .intersect(pb->getRect());
if (!renderedCursorInvalid && (toCheck.intersect(clippedCursorRect)
.is_empty())) {
comparer->getUpdateInfo(&ui, pb->getRect());
if (renderCursor) {
- renderedCursor.update(pb, &cursor, cursorPos);
+ renderedCursor.update(pb, cursor, cursorPos);
renderedCursorInvalid = false;
}
virtual void add_changed(const Region ®ion);
virtual void add_copied(const Region &dest, const Point &delta);
virtual void setCursor(int width, int height, const Point& hotspot,
- const void* cursorData, const void* mask);
+ const rdr::U8* data);
virtual void setCursorPos(const Point& p);
virtual void bell();
ComparingUpdateTracker* comparer;
Point cursorPos;
- Cursor cursor;
+ Cursor* cursor;
RenderedCursor renderedCursor;
bool renderedCursorInvalid;
virtual void setDesktopSize(int w, int h);
virtual void setPixelFormat(const rfb::PixelFormat& pf);
- virtual void setCursor(int, int, const rfb::Point&, void*, void*);
+ virtual void setCursor(int, int, const rfb::Point&, const rdr::U8*);
virtual void framebufferUpdateStart();
virtual void framebufferUpdateEnd();
virtual void setColourMapEntries(int, int, rdr::U16*);
CConnection::setPixelFormat(filePF);
}
-void CConn::setCursor(int, int, const rfb::Point&, void*, void*)
+void CConn::setCursor(int, int, const rfb::Point&, const rdr::U8*)
{
}
unsigned long long& rawEquivalent);
virtual void setDesktopSize(int w, int h);
- virtual void setCursor(int, int, const rfb::Point&, void*, void*);
+ virtual void setCursor(int, int, const rfb::Point&, const rdr::U8*);
virtual void framebufferUpdateStart();
virtual void framebufferUpdateEnd();
virtual void dataRect(const rfb::Rect&, int);
setFramebuffer(pb);
}
-void CConn::setCursor(int, int, const rfb::Point&, void*, void*)
+void CConn::setCursor(int, int, const rfb::Point&, const rdr::U8*)
{
}
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2009-2015 Pierre Ossman for Cendio AB
+ * Copyright 2009-2017 Pierre Ossman for Cendio AB
* Copyright 2014 Brian P. Hinz
*
* This is free software; you can redistribute it and/or modify
const unsigned char *rgbaData)
{
rdr::U8* cursorData;
- rdr::U8* cursorMask;
- int rfbMaskBytesPerRow;
rdr::U8 *out;
const unsigned char *in;
- rdr::U8 rgb[3];
- cursorData = new rdr::U8[width * height * (getPF().bpp / 8)];
-
- rfbMaskBytesPerRow = (width + 7) / 8;
-
- cursorMask = new rdr::U8[rfbMaskBytesPerRow * height];
-
- memset(cursorMask, 0, rfbMaskBytesPerRow * height);
+ cursorData = new rdr::U8[width * height * 4];
+ // Un-premultiply alpha
in = rgbaData;
out = cursorData;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
- rgb[0] = *in++;
- rgb[1] = *in++;
- rgb[2] = *in++;
-
- getPF().bufferFromRGB(out, rgb, 1);
+ rdr::U8 alpha;
- if (*in++ > 127)
- cursorMask[y * rfbMaskBytesPerRow + x/8] |= 0x80>>(x%8);
+ alpha = in[3];
+ if (alpha == 0)
+ alpha = 1; // Avoid division by zero
- out += getPF().bpp/8;
+ *out++ = (unsigned)*in++ * 255/alpha;
+ *out++ = (unsigned)*in++ * 255/alpha;
+ *out++ = (unsigned)*in++ * 255/alpha;
+ *out++ = *in++;
}
}
try {
- server->setCursor(width, height, Point(hotX, hotY), cursorData, cursorMask);
+ server->setCursor(width, height, Point(hotX, hotY), cursorData);
} catch (rdr::Exception& e) {
vlog.error("XserverDesktop::setCursor: %s",e.str());
}
delete [] cursorData;
- delete [] cursorMask;
}
void XserverDesktop::add_changed(const rfb::Region ®ion)
}
void CConn::setCursor(int width, int height, const Point& hotspot,
- void* data, void* mask)
+ const rdr::U8* data)
{
- desktop->setCursor(width, height, hotspot, data, mask);
+ desktop->setCursor(width, height, hotspot, data);
}
void CConn::fence(rdr::U32 flags, unsigned len, const char data[])
void dataRect(const rfb::Rect& r, int encoding);
void setCursor(int width, int height, const rfb::Point& hotspot,
- void* data, void* mask);
+ const rdr::U8* data);
void fence(rdr::U32 flags, unsigned len, const char data[]);
void DesktopWindow::setCursor(int width, int height,
const rfb::Point& hotspot,
- void* data, void* mask)
+ const rdr::U8* data)
{
- viewport->setCursor(width, height, hotspot, data, mask);
+ viewport->setCursor(width, height, hotspot, data);
}
// New image for the locally rendered cursor
void setCursor(int width, int height, const rfb::Point& hotspot,
- void* data, void* mask);
+ const rdr::U8* data);
// Fl_Window callback methods
void draw();
" "};
void Viewport::setCursor(int width, int height, const Point& hotspot,
- void* data, void* mask)
+ const rdr::U8* data)
{
+ int i;
+
if (cursor) {
if (!cursor->alloc_array)
delete [] cursor->array;
delete cursor;
}
- int mask_len = ((width+7)/8) * height;
- int i;
-
- for (i = 0; i < mask_len; i++)
- if (((rdr::U8*)mask)[i]) break;
+ for (i = 0; i < width*height; i++)
+ if (data[i*4 + 3] != 0) break;
- if ((i == mask_len) && dotWhenNoCursor) {
+ if ((i == width*height) && dotWhenNoCursor) {
vlog.debug("cursor is empty - using dot");
Fl_Pixmap pxm(dotcursor_xpm);
cursor = new Fl_RGB_Image(buffer, 1, 1, 4);
cursorHotspot.x = cursorHotspot.y = 0;
} else {
- U8 *buffer = new U8[width*height*4];
- U8 *i, *o, *m;
- int m_width;
-
- const PixelFormat *pf;
-
- pf = &cc->cp.pf();
-
- i = (U8*)data;
- o = buffer;
- m = (U8*)mask;
- m_width = (width+7)/8;
- for (int y = 0;y < height;y++) {
- for (int x = 0;x < width;x++) {
- pf->rgbFromBuffer(o, i, 1);
-
- if (m[(m_width*y)+(x/8)] & 0x80>>(x%8))
- o[3] = 255;
- else
- o[3] = 0;
-
- o += 4;
- i += pf->bpp/8;
- }
- }
-
+ U8 *buffer = new U8[width * height * 4];
+ memcpy(buffer, data, width * height * 4);
cursor = new Fl_RGB_Image(buffer, width, height, 4);
-
cursorHotspot = hotspot;
}
}
// New image for the locally rendered cursor
void setCursor(int width, int height, const rfb::Point& hotspot,
- void* data, void* mask);
+ const rdr::U8* data);
void draw(Surface* dst);
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2014 Pierre Ossman for Cendio AB
+ * Copyright 2014-2017 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
// -=- DeviceFrameBuffer class
DeviceFrameBuffer::DeviceFrameBuffer(HDC deviceContext, const Rect& wRect)
- : DIBSectionBuffer(deviceContext), device(deviceContext), cursorBm(deviceContext),
+ : DIBSectionBuffer(deviceContext), device(deviceContext),
ignoreGrabErrors(false)
{
// Configure the underlying DIB to match the device
DIBSectionBuffer::setPF(DeviceContext::getPF(device));
DIBSectionBuffer::setSize(w, h);
-
- // Configure the cursor buffer
- cursorBm.setPF(format);
}
DeviceFrameBuffer::~DeviceFrameBuffer() {
// - If hCursor is null then there is no cursor - clear the old one
if (hCursor == 0) {
- server->setCursor(0, 0, Point(), 0, 0);
+ server->setCursor(0, 0, Point(), NULL);
return;
}
try {
- const rdr::U8* buffer;
- rdr::U8* rwbuffer;
- int stride;
+ int width, height;
+ rdr::U8Array buffer;
// - Get the size and other details about the cursor.
if (maskInfo.bmBitsPixel != 1)
throw rdr::Exception("unsupported cursor mask format");
- // - Create the cursor pixel buffer and mask storage
- // NB: The cursor pixel buffer is NOT used here. Instead, we
- // pass the cursorBm.data pointer directly, to save overhead.
+ width = maskInfo.bmWidth;
+ height = maskInfo.bmHeight;
+ if (!iconInfo.hbmColor)
+ height /= 2;
- cursor.setSize(maskInfo.bmWidth, maskInfo.bmHeight);
- cursor.setPF(format);
- cursor.hotspot = Point(iconInfo.xHotspot, iconInfo.yHotspot);
+ buffer.buf = new rdr::U8[width * height * 4];
- // - Get the AND and XOR masks. There is only an XOR mask if this is not a
- // colour cursor.
+ Point hotspot = Point(iconInfo.xHotspot, iconInfo.yHotspot);
- if (!iconInfo.hbmColor)
- cursor.setSize(cursor.width(), cursor.height() / 2);
- rdr::U8Array mask(maskInfo.bmWidthBytes * maskInfo.bmHeight);
- rdr::U8* xorMask = mask.buf + cursor.height() * maskInfo.bmWidthBytes;
-
- if (!GetBitmapBits(iconInfo.hbmMask,
- maskInfo.bmWidthBytes * maskInfo.bmHeight, mask.buf))
- throw rdr::SystemException("GetBitmapBits failed", GetLastError());
-
- // Configure the cursor bitmap
- cursorBm.setSize(cursor.width(), cursor.height());
-
- // Draw the cursor into the bitmap
- BitmapDC dc(device, cursorBm.bitmap);
- if (!DrawIconEx(dc, 0, 0, hCursor, 0, 0, 0, NULL, DI_NORMAL | DI_COMPAT))
- throw rdr::SystemException("unable to render cursor", GetLastError());
-
- // Replace any XORed pixels with xorColour, because RFB doesn't support
- // XORing of cursors. XORing is used for the I-beam cursor, which is most
- // often used over a white background, but also sometimes over a black
- // background. We set the XOR'd pixels to black, then draw a white outline
- // around the whole cursor.
-
- // *** should we replace any pixels not set in mask to zero, to ensure
- // that irrelevant data doesn't screw compression?
-
- bool doOutline = false;
- if (!iconInfo.hbmColor) {
- rwbuffer = cursorBm.getBufferRW(cursorBm.getRect(), &stride);
- Pixel xorColour = format.pixelFromRGB((rdr::U16)0, (rdr::U16)0, (rdr::U16)0);
- for (int y = 0; y < cursor.height(); y++) {
- for (int x = 0; x < cursor.width(); x++) {
+ if (iconInfo.hbmColor) {
+ // Colour cursor
+
+ BITMAPV5HEADER bi;
+ BitmapDC dc(device, iconInfo.hbmColor);
+
+ memset(&bi, 0, sizeof(BITMAPV5HEADER));
+
+ bi.bV5Size = sizeof(BITMAPV5HEADER);
+ bi.bV5Width = width;
+ bi.bV5Height = -height; // Negative for top-down
+ bi.bV5Planes = 1;
+ bi.bV5BitCount = 32;
+ bi.bV5Compression = BI_BITFIELDS;
+ bi.bV5RedMask = 0x000000FF;
+ bi.bV5GreenMask = 0x0000FF00;
+ bi.bV5BlueMask = 0x00FF0000;
+ bi.bV5AlphaMask = 0xFF000000;
+
+ if (!GetDIBits(dc, iconInfo.hbmColor, 0, height,
+ buffer.buf, (LPBITMAPINFO)&bi, DIB_RGB_COLORS))
+ throw rdr::SystemException("GetDIBits", GetLastError());
+
+ // We may not get the RGBA order we want, so shuffle things around
+ int ridx, gidx, bidx, aidx;
+
+ ridx = __builtin_ffs(bi.bV5RedMask) / 8;
+ gidx = __builtin_ffs(bi.bV5GreenMask) / 8;
+ bidx = __builtin_ffs(bi.bV5BlueMask) / 8;
+ // Usually not set properly
+ aidx = 6 - ridx - gidx - bidx;
+
+ if ((bi.bV5RedMask != ((unsigned)0xff << ridx*8)) ||
+ (bi.bV5GreenMask != ((unsigned)0xff << gidx*8)) ||
+ (bi.bV5BlueMask != ((unsigned)0xff << bidx*8)))
+ throw rdr::Exception("unsupported cursor colour format");
+
+ rdr::U8* rwbuffer = buffer.buf;
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ rdr::U8 r, g, b, a;
+
+ r = rwbuffer[ridx];
+ g = rwbuffer[gidx];
+ b = rwbuffer[bidx];
+ a = rwbuffer[aidx];
+
+ rwbuffer[0] = r;
+ rwbuffer[1] = g;
+ rwbuffer[2] = b;
+ rwbuffer[3] = a;
+
+ rwbuffer += 4;
+ }
+ }
+ } else {
+ // B/W cursor
+
+ rdr::U8Array mask(maskInfo.bmWidthBytes * maskInfo.bmHeight);
+ rdr::U8* andMask = mask.buf;
+ rdr::U8* xorMask = mask.buf + height * maskInfo.bmWidthBytes;
+
+ if (!GetBitmapBits(iconInfo.hbmMask,
+ maskInfo.bmWidthBytes * maskInfo.bmHeight, mask.buf))
+ throw rdr::SystemException("GetBitmapBits", GetLastError());
+
+ bool doOutline = false;
+ rdr::U8* rwbuffer = buffer.buf;
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
int byte = y * maskInfo.bmWidthBytes + x / 8;
int bit = 7 - x % 8;
- if ((mask.buf[byte] & (1 << bit)) && (xorMask[byte] & (1 << bit)))
- {
- mask.buf[byte] &= ~(1 << bit);
-
- switch (format.bpp) {
- case 8:
- rwbuffer[y * cursor.width() + x] = xorColour; break;
- case 16:
- rwbuffer[y * cursor.width() + x] = xorColour; break;
- case 32:
- rwbuffer[y * cursor.width() + x] = xorColour; break;
- }
+
+ if (!(andMask[byte] & (1 << bit))) {
+ // Valid pixel, so make it opaque
+ rwbuffer[3] = 0xff;
+
+ // Black or white?
+ if (xorMask[byte] & (1 << bit))
+ rwbuffer[0] = rwbuffer[1] = rwbuffer[2] = 0xff;
+ else
+ rwbuffer[0] = rwbuffer[1] = rwbuffer[2] = 0;
+ } else if (xorMask[byte] & (1 << bit)) {
+ // Replace any XORed pixels with black, because RFB doesn't support
+ // XORing of cursors. XORing is used for the I-beam cursor, which is most
+ // often used over a white background, but also sometimes over a black
+ // background. We set the XOR'd pixels to black, then draw a white outline
+ // around the whole cursor.
+
+ rwbuffer[0] = rwbuffer[1] = rwbuffer[2] = 0;
+ rwbuffer[3] = 0xff;
doOutline = true;
+ } else {
+ // Transparent pixel
+ rwbuffer[0] = rwbuffer[1] = rwbuffer[2] = rwbuffer[3] = 0;
}
+
+ rwbuffer += 4;
}
}
- cursorBm.commitBufferRW(cursorBm.getRect());
- }
-
- // Finally invert the AND mask so it's suitable for RFB and pack it into
- // the minimum number of bytes per row.
-
- int maskBytesPerRow = (cursor.width() + 7) / 8;
- for (int j = 0; j < cursor.height(); j++) {
- for (int i = 0; i < maskBytesPerRow; i++)
- cursor.mask.buf[j * maskBytesPerRow + i]
- = ~mask.buf[j * maskInfo.bmWidthBytes + i];
- }
-
- if (doOutline) {
- vlog.debug("drawing cursor outline!");
+ if (doOutline) {
+ vlog.debug("drawing cursor outline!");
+
+ // The buffer needs to be slightly larger to make sure there
+ // is room for the outline pixels
+ rdr::U8Array outline((width + 2)*(height + 2)*4);
+ memset(outline.buf, 0, (width + 2)*(height + 2)*4);
+
+ // Pass 1, outline everything
+ rdr::U8* in = buffer.buf;
+ rdr::U8* out = outline.buf + width*4 + 4;
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ // Visible pixel?
+ if (in[3] > 0) {
+ // Outline above...
+ memset(out - (width+2)*4 - 4, 0xff, 4 * 3);
+ // ...besides...
+ memset(out - 4, 0xff, 4 * 3);
+ // ...and above
+ memset(out + (width+2)*4 - 4, 0xff, 4 * 3);
+ }
+ in += 4;
+ out += 4;
+ }
+ // outline is slightly larger
+ out += 2*4;
+ }
- buffer = cursorBm.getBuffer(cursorBm.getRect(), &stride);
- cursor.imageRect(cursorBm.getRect(), buffer, stride);
+ // Pass 2, overwrite with actual cursor
+ in = buffer.buf;
+ out = outline.buf + width*4 + 4;
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ if (in[3] > 0)
+ memcpy(out, in, 4);
+ in += 4;
+ out += 4;
+ }
+ out += 2*4;
+ }
- cursor.drawOutline(format.pixelFromRGB((rdr::U16)0xffff, (rdr::U16)0xffff, (rdr::U16)0xffff));
+ width += 2;
+ height += 2;
+ hotspot.x += 1;
+ hotspot.y += 1;
- buffer = cursor.getBuffer(cursor.getRect(), &stride);
- cursorBm.imageRect(cursor.getRect(), buffer, stride);
+ delete [] buffer.buf;
+ buffer.buf = outline.takeBuf();
+ }
}
- buffer = cursorBm.getBuffer(cursorBm.getRect(), &stride);
- server->setCursor(cursor.width(), cursor.height(), cursor.hotspot,
- buffer, cursor.mask.buf);
+ server->setCursor(width, height, hotspot, buffer.buf);
} catch (rdr::Exception& e) {
vlog.error("%s", e.str());
Point desktopToDevice(const Point p) const {return p.translate(deviceCoords.tl);}
HDC device;
- DIBSectionBuffer cursorBm;
- Cursor cursor;
Rect deviceCoords;
bool ignoreGrabErrors;
};