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;
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();
#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)
{
CGContextSaveGState(gc);
+ CGContextSetBlendMode(gc, mode);
+
// We have to use clipping to partially display an image
rect.origin.x = x;
rect.origin.y = y;
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;
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);
}
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);
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;
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;