]> source.dussan.org Git - tigervnc.git/commitdiff
Add alpha blending support to surfaces
authorPierre Ossman <ossman@cendio.se>
Mon, 2 Jan 2017 19:07:10 +0000 (20:07 +0100)
committerPierre Ossman <ossman@cendio.se>
Fri, 10 Feb 2017 16:05:22 +0000 (17:05 +0100)
tests/CMakeLists.txt
tests/fbperf.cxx
vncviewer/CMakeLists.txt
vncviewer/Surface.h
vncviewer/Surface_OSX.cxx
vncviewer/Surface_Win32.cxx
vncviewer/Surface_X11.cxx

index 5fbaa11ab79593be666f37364ca6dd9e589fc3b1..8ea99255a9daccea0c7d224cfe4209894c4cbb4b 100644 (file)
@@ -33,6 +33,9 @@ else()
 endif()
 add_executable(fbperf ${FBPERF_SOURCES})
 target_link_libraries(fbperf test_util rfb ${FLTK_LIBRARIES} ${GETTEXT_LIBRARIES})
+if(WIN32)
+  target_link_libraries(fbperf msimg32)
+endif()
 if(APPLE)
   target_link_libraries(fbperf "-framework Cocoa" "-framework Carbon")
 endif()
index 7f1e2c1d1abc0381f8353c349fd1fe95bc353901..a19ee47901920103b00f05c77d3935201dd994ac 100644 (file)
@@ -214,8 +214,9 @@ void OverlayTestWindow::start(int width, int height)
   overlay = new Surface(400, 200);
   overlay->clear(0xff, 0x80, 0x00, 0xcc);
 
-  // X11 needs an off screen buffer for compositing to avoid flicker
-#if !defined(WIN32) && !defined(__APPLE__)
+  // X11 needs an off screen buffer for compositing to avoid flicker,
+  // and alpha blending doesn't work for windows on Win32
+#if !defined(__APPLE__)
   offscreen = new Surface(w(), h());
 #else
   offscreen = NULL;
@@ -270,9 +271,9 @@ void OverlayTestWindow::draw()
   fl_clip_box(ox, oy, ow, oh, X, Y, W, H);
   if ((W != 0) && (H != 0)) {
     if (offscreen)
-      overlay->draw(offscreen, X - ox, Y - oy, X, Y, W, H);
+      overlay->blend(offscreen, X - ox, Y - oy, X, Y, W, H);
     else
-      overlay->draw(X - ox, Y - oy, X, Y, W, H);
+      overlay->blend(X - ox, Y - oy, X, Y, W, H);
   }
 
   fl_pop_clip();
index d6a1b585e760426dc279e9abd035adfa75cd7694..1833e70450a8f7496bc5ded05c548d4009416713 100644 (file)
@@ -48,6 +48,10 @@ endif()
 
 target_link_libraries(vncviewer rfb network rdr os ${FLTK_LIBRARIES} ${GETTEXT_LIBRARIES})
 
+if(WIN32)
+  target_link_libraries(vncviewer msimg32)
+endif()
+
 if(APPLE)
   target_link_libraries(vncviewer "-framework Cocoa" "-framework Carbon")
 endif()
index 9b1788a5176e33d581ec52f738f83af3fd1a9f94..7d1646817b0ac88dc17e42437ed5d4b8c9073086 100644 (file)
@@ -44,6 +44,9 @@ public:
   void draw(int src_x, int src_y, int x, int y, int w, int h);
   void draw(Surface* dst, int src_x, int src_y, int x, int y, int w, int h);
 
+  void blend(int src_x, int src_y, int x, int y, int w, int h);
+  void blend(Surface* dst, int src_x, int src_y, int x, int y, int w, int h);
+
 protected:
   void alloc();
   void dealloc();
index dbf0c41819dd505f7be9a2e3971f9c53f29d6812..73c10f46ec689022ef309e89ce6312b3c6f0d819 100644 (file)
@@ -28,7 +28,7 @@
 
 #include "Surface.h"
 
-static void render(CGContextRef gc, CGImageRef image,
+static void render(CGContextRef gc, CGImageRef image, CGBlendMode mode,
                    int src_x, int src_y, int src_w, int src_h,
                    int x, int y, int w, int h)
 {
@@ -36,6 +36,8 @@ static void render(CGContextRef gc, CGImageRef image,
 
   CGContextSaveGState(gc);
 
+  CGContextSetBlendMode(gc, mode);
+
   // We have to use clipping to partially display an image
   rect.origin.x = x;
   rect.origin.y = y;
@@ -54,6 +56,27 @@ static void render(CGContextRef gc, CGImageRef image,
   CGContextRestoreGState(gc);
 }
 
+static CGContextRef make_bitmap(int width, int height, unsigned char* data)
+{
+  CGColorSpaceRef lut;
+  CGContextRef bitmap;
+
+  lut = CGDisplayCopyColorSpace(kCGDirectMainDisplay);
+  if (!lut) {
+    lut = CGColorSpaceCreateDeviceRGB();
+    if (!lut)
+      throw rdr::Exception("CGColorSpaceCreateDeviceRGB");
+  }
+
+  bitmap = CGBitmapContextCreate(data, width, height, 8, width*4, lut,
+                                 kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little);
+  CGColorSpaceRelease(lut);
+  if (!bitmap)
+    throw rdr::Exception("CGBitmapContextCreate");
+
+  return bitmap;
+}
+
 void Surface::clear(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
 {
   unsigned char* out;
@@ -86,35 +109,58 @@ void Surface::draw(int src_x, int src_y, int x, int y, int w, int h)
   src_y = height() - (src_y + h);
   y = Fl_Window::current()->h() - (y + h);
 
-  render(fl_gc, image, src_x, src_y, width(), height(), x, y, w, h);
+  render(fl_gc, image, kCGBlendModeCopy,
+         src_x, src_y, width(), height(), x, y, w, h);
 
   CGContextRestoreGState(fl_gc);
 }
 
 void Surface::draw(Surface* dst, int src_x, int src_y, int x, int y, int w, int h)
 {
-  CGColorSpaceRef lut;
   CGContextRef bitmap;
 
-  lut = CGDisplayCopyColorSpace(kCGDirectMainDisplay);
-  if (!lut) {
-    lut = CGColorSpaceCreateDeviceRGB();
-    if (!lut)
-      throw rdr::Exception("CGColorSpaceCreateDeviceRGB");
-  }
+  bitmap = make_bitmap(dst->width(), dst->height(), dst->data);
 
-  bitmap = CGBitmapContextCreate(dst->data, dst->width(),
-                                 dst->height(), 8, dst->width()*4, lut,
-                                 kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little);
-  CGColorSpaceRelease(lut);
-  if (!bitmap)
-    throw rdr::Exception("CGBitmapContextCreate");
+  // macOS Coordinates are from bottom left, not top left
+  src_y = height() - (src_y + h);
+  y = dst->height() - (y + h);
+
+  render(bitmap, image, kCGBlendModeCopy,
+         src_x, src_y, width(), height(), x, y, w, h);
+
+  CGContextRelease(bitmap);
+}
+
+void Surface::blend(int src_x, int src_y, int x, int y, int w, int h)
+{
+  CGContextSaveGState(fl_gc);
+
+  // Reset the transformation matrix back to the default identity
+  // matrix as otherwise we get a massive performance hit
+  CGContextConcatCTM(fl_gc, CGAffineTransformInvert(CGContextGetCTM(fl_gc)));
+
+  // macOS Coordinates are from bottom left, not top left
+  src_y = height() - (src_y + h);
+  y = Fl_Window::current()->h() - (y + h);
+
+  render(fl_gc, image, kCGBlendModeNormal,
+         src_x, src_y, width(), height(), x, y, w, h);
+
+  CGContextRestoreGState(fl_gc);
+}
+
+void Surface::blend(Surface* dst, int src_x, int src_y, int x, int y, int w, int h)
+{
+  CGContextRef bitmap;
+
+  bitmap = make_bitmap(dst->width(), dst->height(), dst->data);
 
   // macOS Coordinates are from bottom left, not top left
   src_y = height() - (src_y + h);
   y = dst->height() - (y + h);
 
-  render(bitmap, image, src_x, src_y, width(), height(), x, y, w, h);
+  render(bitmap, image, kCGBlendModeNormal,
+         src_x, src_y, width(), height(), x, y, w, h);
 
   CGContextRelease(bitmap);
 }
@@ -139,7 +185,7 @@ void Surface::alloc()
     throw rdr::Exception("CGDataProviderCreateWithData");
 
   image = CGImageCreate(width(), height(), 8, 32, width() * 4, lut,
-                        kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little,
+                        kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little,
                         provider, NULL, false, kCGRenderingIntentDefault);
   CGColorSpaceRelease(lut);
   CGDataProviderRelease(provider);
index 5eea2d1d8d3b9f727873914e9e71726771716e22..87b0cb2fdaf6cdf21d87b8ef1702165d8a578032 100644 (file)
@@ -89,6 +89,48 @@ void Surface::draw(Surface* dst, int src_x, int src_y, int x, int y, int w, int
   DeleteDC(dstdc);
 }
 
+void Surface::blend(int src_x, int src_y, int x, int y, int w, int h)
+{
+  // Compositing doesn't work properly for window DC:s
+  assert(false);
+}
+
+void Surface::blend(Surface* dst, int src_x, int src_y, int x, int y, int w, int h)
+{
+  HDC dstdc, srcdc;
+  BLENDFUNCTION blend;
+
+  dstdc = CreateCompatibleDC(NULL);
+  if (!dstdc)
+    throw rdr::SystemException("CreateCompatibleDC", GetLastError());
+  srcdc = CreateCompatibleDC(NULL);
+  if (!srcdc)
+    throw rdr::SystemException("CreateCompatibleDC", GetLastError());
+
+  if (!SelectObject(dstdc, dst->bitmap))
+    throw rdr::SystemException("SelectObject", GetLastError());
+  if (!SelectObject(srcdc, bitmap))
+    throw rdr::SystemException("SelectObject", GetLastError());
+
+  blend.BlendOp = AC_SRC_OVER;
+  blend.BlendFlags = 0;
+  blend.SourceConstantAlpha = 255;
+  blend.AlphaFormat = AC_SRC_ALPHA;
+
+  if (!AlphaBlend(dstdc, x, y, w, h, srcdc, src_x, src_y, w, h, blend)) {
+    // If the desktop we're rendering to is inactive (like when the screen
+    // is locked or the UAC is active), then GDI calls will randomly fail.
+    // This is completely undocumented so we have no idea how best to deal
+    // with it. For now, we've only seen this error and for this function
+    // so only ignore this combination.
+    if (GetLastError() != ERROR_INVALID_HANDLE)
+      throw rdr::SystemException("BitBlt", GetLastError());
+  }
+
+  DeleteDC(srcdc);
+  DeleteDC(dstdc);
+}
+
 void Surface::alloc()
 {
   BITMAPINFOHEADER bih;
index e761614351506a421c50c3a0be8a3505acdc24e8..f36976faaf6088bfc6a8624846266e688ed5d769 100644 (file)
@@ -54,6 +54,23 @@ void Surface::draw(Surface* dst, int src_x, int src_y, int x, int y, int w, int
                    src_x, src_y, 0, 0, x, y, w, h);
 }
 
+void Surface::blend(int src_x, int src_y, int x, int y, int w, int h)
+{
+  Picture winPict;
+
+  winPict = XRenderCreatePicture(fl_display, fl_window, visFormat, 0, NULL);
+  XRenderComposite(fl_display, PictOpOver, picture, None, winPict,
+                   src_x, src_y, 0, 0, x, y, w, h);
+  XRenderFreePicture(fl_display, winPict);
+}
+
+void Surface::blend(Surface* dst, int src_x, int src_y, int x, int y, int w, int h)
+{
+  XRenderComposite(fl_display, PictOpOver, picture, None, dst->picture,
+                   src_x, src_y, 0, 0, x, y, w, h);
+}
+
+
 void Surface::alloc()
 {
   XRenderPictFormat* format;