]> source.dussan.org Git - tigervnc.git/commitdiff
Change cursor API to use RGBA data
authorPierre Ossman <ossman@cendio.se>
Sun, 19 Feb 2017 14:48:17 +0000 (15:48 +0100)
committerPierre Ossman <ossman@cendio.se>
Wed, 22 Feb 2017 15:58:10 +0000 (16:58 +0100)
This will allow us to use better formats that preserve the entire
alpha channel.

25 files changed:
common/rfb/CMsgHandler.h
common/rfb/CMsgReader.cxx
common/rfb/ConnParams.cxx
common/rfb/ConnParams.h
common/rfb/Cursor.cxx
common/rfb/Cursor.h
common/rfb/PixelBuffer.cxx
common/rfb/PixelBuffer.h
common/rfb/SMsgWriter.cxx
common/rfb/SMsgWriter.h
common/rfb/VNCSConnectionST.cxx
common/rfb/VNCServer.h
common/rfb/VNCServerST.cxx
common/rfb/VNCServerST.h
tests/decperf.cxx
tests/encperf.cxx
unix/xserver/hw/vnc/XserverDesktop.cc
vncviewer/CConn.cxx
vncviewer/CConn.h
vncviewer/DesktopWindow.cxx
vncviewer/DesktopWindow.h
vncviewer/Viewport.cxx
vncviewer/Viewport.h
win/rfb_win32/DeviceFrameBuffer.cxx
win/rfb_win32/DeviceFrameBuffer.h

index 7d2cdc207af6ec694559dda9dd4cbc2fe7c56898..2686712e368d709db59710502b2db01d917dcaf9 100644 (file)
@@ -50,7 +50,7 @@ namespace rfb {
                                         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[]);
index 96ddf4436ef4a01c4302729ddfbada2f12f559b9..bbb9909619b6d9a6724b82db3b5b78e5a6b8f914 100644 (file)
@@ -1,5 +1,5 @@
 /* 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
@@ -198,10 +198,35 @@ void CMsgReader::readSetCursor(int width, int height, const Point& hotspot)
   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)
index ab3b884a9c0f7593ed417cd688a2bcd72b945719..dc9ebf58a4a2395efcbeec3e1c5385b897b02359 100644 (file)
@@ -39,11 +39,13 @@ ConnParams::ConnParams()
     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)
@@ -86,17 +88,8 @@ void ConnParams::setName(const char* name)
 
 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
index 9e647baabb7904e2f91ac7a66ab8b35c05c4a5b2..517e649045a3ceadb03c0f1fa8a3fe571f024e5a 100644 (file)
@@ -77,7 +77,7 @@ namespace rfb {
     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;
@@ -106,7 +106,7 @@ namespace rfb {
 
     PixelFormat pf_;
     char* name_;
-    Cursor cursor_;
+    Cursor* cursor_;
     std::set<rdr::S32> encodings_;
     char verStr[13];
     int verStrPos;
index e226118cac7a3424ab059a5af42aa32dfd9608bd..a79f0460695674cfdf44c8dd65905550d991a179 100644 (file)
@@ -1,5 +1,5 @@
 /* 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
@@ -26,71 +26,63 @@ using namespace rfb;
 
 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;
@@ -98,24 +90,13 @@ rdr::U8* Cursor::getBitmap(Pixel* pix0, Pixel* pix1) const
     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
@@ -126,58 +107,40 @@ rdr::U8* Cursor::getBitmap(Pixel* pix0, Pixel* pix1) const
 
 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()
@@ -207,26 +170,48 @@ void RenderedCursor::update(PixelBuffer* framebuffer,
   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);
+    }
+  }
 }
index 560e4d818525605dce043b84a4976c6f8409b514..6c6db7eea4407ed44721579097d3ddfa3a4c1c48 100644 (file)
@@ -1,5 +1,5 @@
 /* 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 {
index e788fcada1d2ec700a148f668986f27739662807..1edd9c3d60fe51bed3a8b4e51cc8ea1b3b4ec2b9 100644 (file)
@@ -200,110 +200,6 @@ void ModifiablePixelBuffer::imageRect(const Rect& r,
   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)
 {
index b38999aed7aa9d8d48c513275391ccd11d2dd183..75caa63f518479ac517141021862431f17a82385 100644 (file)
@@ -127,20 +127,6 @@ namespace rfb {
     // 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.
index 5040b6580da9b66a52c3807f7dd69d4f7c88c143..51e1105a5b9113a1237191733c309af3d51ffc9e 100644 (file)
@@ -1,6 +1,6 @@
 /* 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
@@ -301,38 +301,36 @@ void SMsgWriter::endMsg()
 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;
   }
 
@@ -452,8 +450,6 @@ void SMsgWriter::writeSetCursorRect(int width, int height,
 
 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)
@@ -467,12 +463,12 @@ void SMsgWriter::writeSetXCursorRect(int width, int height,
   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);
   }
index 917b933cc05fb2a83d3283359ce359cfdb54ca5a..a516e103cab764ddc5c33974e4a50dcc83a8aa9a 100644 (file)
@@ -126,7 +126,6 @@ namespace rfb {
                             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;
index 74c40d155f3f3d373774c4b2aaff7632e792de13..e7a5dcf11d072f2f90926e41c571ff4cc6406ede 100644 (file)
@@ -1119,7 +1119,7 @@ void VNCSConnectionST::setCursor()
   if (state() != RFBSTATE_NORMAL)
     return;
 
-  cp.setCursor(server->cursor);
+  cp.setCursor(*server->cursor);
 
   if (!writer()->writeSetCursor()) {
     if (!writer()->writeSetXCursor()) {
index c76e5c9c9d7a1e8067059fd1c523ad95261a8f20..982a4ff54e9b3f203910ad117bf61ded37bac9d5 100644 (file)
@@ -64,16 +64,10 @@ namespace rfb {
     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;
index e15cd701263cbce37cf4b0bdcfb4819421b10c16..81eed37a200e3a05529722091100c83c48a1ba74 100644 (file)
@@ -1,5 +1,5 @@
 /* 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
@@ -82,6 +82,7 @@ VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
   : 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),
@@ -113,6 +114,8 @@ VNCServerST::~VNCServerST()
   if (comparer)
     comparer->logStats();
   delete comparer;
+
+  delete cursor;
 }
 
 
@@ -314,7 +317,6 @@ void VNCServerST::setPixelBuffer(PixelBuffer* pb_, const ScreenSet& layout)
   }
 
   comparer = new ComparingUpdateTracker(pb);
-  cursor.setPF(pb->getPF());
   renderedCursorInvalid = true;
 
   // Make sure that we have at least one screen
@@ -430,14 +432,11 @@ void VNCServerST::add_copied(const Region& dest, const Point& delta)
 }
 
 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;
 
@@ -618,8 +617,9 @@ bool VNCServerST::checkUpdate()
   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())) {
@@ -640,7 +640,7 @@ bool VNCServerST::checkUpdate()
     comparer->getUpdateInfo(&ui, pb->getRect());
 
   if (renderCursor) {
-    renderedCursor.update(pb, &cursor, cursorPos);
+    renderedCursor.update(pb, cursor, cursorPos);
     renderedCursorInvalid = false;
   }
 
index 0ced12a4e6055749b0026a17ccadb0902599611e..497571643332c641e0dec2e188ebee062f9f2337 100644 (file)
@@ -98,7 +98,7 @@ namespace rfb {
     virtual void add_changed(const Region &region);
     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();
@@ -218,7 +218,7 @@ namespace rfb {
     ComparingUpdateTracker* comparer;
 
     Point cursorPos;
-    Cursor cursor;
+    Cursor* cursor;
     RenderedCursor renderedCursor;
     bool renderedCursorInvalid;
 
index 1fd763e53a50bc22bb30763759c40e4ace9c2651..3b929a4b29b62088d65cb4c0b0b4fb3ff43524af 100644 (file)
@@ -49,7 +49,7 @@ public:
 
   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*);
@@ -94,7 +94,7 @@ void CConn::setPixelFormat(const rfb::PixelFormat& pf)
   CConnection::setPixelFormat(filePF);
 }
 
-void CConn::setCursor(int, int, const rfb::Point&, void*, void*)
+void CConn::setCursor(int, int, const rfb::Point&, const rdr::U8*)
 {
 }
 
index d58d82eac24b8ccdd7608bcda0490731ae8ff688..7b9ff8162aa37fbb9d6b0864181d83dec4d91024 100644 (file)
@@ -90,7 +90,7 @@ public:
                 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);
@@ -207,7 +207,7 @@ void CConn::setDesktopSize(int w, int h)
   setFramebuffer(pb);
 }
 
-void CConn::setCursor(int, int, const rfb::Point&, void*, void*)
+void CConn::setCursor(int, int, const rfb::Point&, const rdr::U8*)
 {
 }
 
index 1428791eb735f54315e043837c4c1d84f5a163dc..4eb0dff14e8f9cba8e6a63698dbc30574d787db9 100644 (file)
@@ -1,5 +1,5 @@
 /* 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
@@ -344,46 +344,37 @@ void XserverDesktop::setCursor(int width, int height, int hotX, int hotY,
                                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 &region)
index cc3562634dca0c2c61a94e2434317591383c0b94..a692732c2fc6a40a03fd002702296ae859de72ff 100644 (file)
@@ -444,9 +444,9 @@ void CConn::dataRect(const Rect& r, int encoding)
 }
 
 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[])
index c934f3da55a7bceb31cfc4c033cfbada13e266a4..d6dd4a755086ec4c658309d3cb274d4b334e7404 100644 (file)
@@ -66,7 +66,7 @@ public:
   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[]);
 
index 190b39ccbde9828c3d5aa5819a56705827eecb2c..acb0a729cb26f50da64908d691e1398cd08f78fd 100644 (file)
@@ -266,9 +266,9 @@ void DesktopWindow::resizeFramebuffer(int new_w, int new_h)
 
 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);
 }
 
 
index 3dfaaea8eea6ac312785b6ea1a2707240c52c346..11f3dc2059cfe1a9f4d9aa6d872d2208f881e018 100644 (file)
@@ -62,7 +62,7 @@ public:
 
   // 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();
index b5c516fa30a86388bce31c8eafc8fc861ef9177c..6a23526a919264b0bc5e3869a6360791b593aad7 100644 (file)
@@ -180,21 +180,20 @@ static const char * dotcursor_xpm[] = {
   "     "};
 
 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);
@@ -207,34 +206,9 @@ void Viewport::setCursor(int width, int height, const Point& hotspot,
       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;
     }
   }
index 0967fcbbd3289173c840feffb3ae4bc41c065c6a..6f0710d354b8473973c34eac88eb0a7c7bcfc5fd 100644 (file)
@@ -45,7 +45,7 @@ public:
 
   // 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);
 
index 22841f7e1039948becc900185bd768820a54c5fe..3138cce2d6bb733f77d27a4cc9a0f01855e6191c 100644 (file)
@@ -1,5 +1,5 @@
 /* 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
@@ -42,7 +42,7 @@ BoolParameter DeviceFrameBuffer::useCaptureBlt("UseCaptureBlt",
 // -=- DeviceFrameBuffer class
 
 DeviceFrameBuffer::DeviceFrameBuffer(HDC deviceContext, const Rect& wRect)
-  : DIBSectionBuffer(deviceContext), device(deviceContext), cursorBm(deviceContext),
+  : DIBSectionBuffer(deviceContext), device(deviceContext),
     ignoreGrabErrors(false)
 {
 
@@ -76,9 +76,6 @@ DeviceFrameBuffer::DeviceFrameBuffer(HDC deviceContext, const Rect& wRect)
   // 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() {
@@ -134,15 +131,14 @@ void DeviceFrameBuffer::setCursor(HCURSOR hCursor, VNCServer* server)
   // - 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.
 
@@ -156,97 +152,170 @@ void DeviceFrameBuffer::setCursor(HCURSOR hCursor, VNCServer* server)
     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());
index 8e280f8af2bb8a752f1486c8f634a59f2a1a64d5..6fccf9fa706cb115f7b50cfbb367c87cd442e902 100644 (file)
@@ -92,8 +92,6 @@ namespace rfb {
       Point desktopToDevice(const Point p) const {return p.translate(deviceCoords.tl);}
 
       HDC device;
-      DIBSectionBuffer cursorBm;
-      Cursor cursor;
       Rect deviceCoords;
       bool ignoreGrabErrors;
     };