Browse Source

Abstract platform rendering to "surfaces"

This will allow us to render more things than just the framebuffer.
tags/v1.7.90
Pierre Ossman 7 years ago
parent
commit
403ac27d23

+ 5
- 5
tests/CMakeLists.txt View File

@@ -22,14 +22,14 @@ target_link_libraries(hostport rfb)

set(FBPERF_SOURCES
fbperf.cxx
../vncviewer/FLTKPixelBuffer.cxx
../vncviewer/PlatformPixelBuffer.cxx)
../vncviewer/PlatformPixelBuffer.cxx
../vncviewer/Surface.cxx)
if(WIN32)
set(FBPERF_SOURCES ${FBPERF_SOURCES} ../vncviewer/Win32PixelBuffer.cxx)
set(FBPERF_SOURCES ${FBPERF_SOURCES} ../vncviewer/Surface_Win32.cxx)
elseif(APPLE)
set(FBPERF_SOURCES ${FBPERF_SOURCES} ../vncviewer/OSXPixelBuffer.cxx)
set(FBPERF_SOURCES ${FBPERF_SOURCES} ../vncviewer/Surface_OSX.cxx)
else()
set(FBPERF_SOURCES ${FBPERF_SOURCES} ../vncviewer/X11PixelBuffer.cxx)
set(FBPERF_SOURCES ${FBPERF_SOURCES} ../vncviewer/Surface_X11.cxx)
endif()
add_executable(fbperf ${FBPERF_SOURCES})
target_link_libraries(fbperf test_util rfb ${FLTK_LIBRARIES} ${GETTEXT_LIBRARIES})

+ 74
- 20
tests/fbperf.cxx View File

@@ -27,15 +27,6 @@
#include <rfb/util.h>

#include "../vncviewer/PlatformPixelBuffer.h"
#include "../vncviewer/FLTKPixelBuffer.h"

#if defined(WIN32)
#include "../vncviewer/Win32PixelBuffer.h"
#elif defined(__APPLE__)
#include "../vncviewer/OSXPixelBuffer.h"
#else
#include "../vncviewer/X11PixelBuffer.h"
#endif

#include "util.h"

@@ -70,6 +61,19 @@ protected:
virtual void changefb();
};

class OverlayTestWindow: public PartialTestWindow {
public:
OverlayTestWindow();

virtual void start(int width, int height);
virtual void stop();

virtual void draw();

protected:
Surface* overlay;
};

TestWindow::TestWindow() :
Fl_Window(0, 0, "Framebuffer Performance Test"),
fb(NULL)
@@ -93,17 +97,7 @@ void TestWindow::start(int width, int height)
frames = 0;
time = 0;

try {
#if defined(WIN32)
fb = new Win32PixelBuffer(w(), h());
#elif defined(__APPLE__)
fb = new OSXPixelBuffer(w(), h());
#else
fb = new X11PixelBuffer(w(), h());
#endif
} catch (rdr::Exception& e) {
fb = new FLTKPixelBuffer(w(), h());
}
fb = new PlatformPixelBuffer(w(), h());

pixel = 0;
fb->fillRect(fb->getRect(), &pixel);
@@ -207,6 +201,60 @@ void PartialTestWindow::changefb()
fb->fillRect(r, &pixel);
}

OverlayTestWindow::OverlayTestWindow() :
overlay(NULL)
{
}

void OverlayTestWindow::start(int width, int height)
{
PartialTestWindow::start(width, height);

overlay = new Surface(400, 200);
overlay->clear(0xff, 0x80, 0x00, 0xcc);
}

void OverlayTestWindow::stop()
{
PartialTestWindow::stop();

delete overlay;
overlay = NULL;
}

void OverlayTestWindow::draw()
{
int ox, oy, ow, oh;
int X, Y, W, H;

// Check what actually needs updating
fl_clip_box(0, 0, w(), h(), X, Y, W, H);
if ((W == 0) || (H == 0))
return;

PartialTestWindow::draw();

// We might get a redraw before we are fully ready
if (!overlay)
return;

// Simplify the clip region to a simple rectangle in order to
// properly draw all the layers even if they only partially overlap
fl_push_no_clip();
fl_push_clip(X, Y, W, H);

ox = (w() - overlay->width()) / 2;
oy = h() / 4 - overlay->height() / 2;
ow = overlay->width();
oh = overlay->height();
fl_clip_box(ox, oy, ow, oh, X, Y, W, H);
if ((W != 0) && (H != 0))
overlay->draw(X - ox, Y - oy, X, Y, W, H);

fl_pop_clip();
fl_pop_clip();
}

static void dosubtest(TestWindow* win, int width, int height,
unsigned long long* pixels,
unsigned long long* frames,
@@ -311,5 +359,11 @@ int main(int argc, char** argv)
delete win;
fprintf(stderr, "\n");

fprintf(stderr, "Partial window update with overlay:\n\n");
win = new OverlayTestWindow();
dotest(win);
delete win;
fprintf(stderr, "\n");

return 0;
}

+ 4
- 4
vncviewer/CMakeLists.txt View File

@@ -6,9 +6,9 @@ set(VNCVIEWER_SOURCES
menukey.cxx
CConn.cxx
DesktopWindow.cxx
FLTKPixelBuffer.cxx
UserDialog.cxx
ServerDialog.cxx
Surface.cxx
OptionsDialog.cxx
PlatformPixelBuffer.cxx
Viewport.cxx
@@ -33,11 +33,11 @@ elseif(APPLE)
endif()

if(WIN32)
set(VNCVIEWER_SOURCES ${VNCVIEWER_SOURCES} Win32PixelBuffer.cxx)
set(VNCVIEWER_SOURCES ${VNCVIEWER_SOURCES} Surface_Win32.cxx)
elseif(APPLE)
set(VNCVIEWER_SOURCES ${VNCVIEWER_SOURCES} OSXPixelBuffer.cxx)
set(VNCVIEWER_SOURCES ${VNCVIEWER_SOURCES} Surface_OSX.cxx)
else()
set(VNCVIEWER_SOURCES ${VNCVIEWER_SOURCES} X11PixelBuffer.cxx)
set(VNCVIEWER_SOURCES ${VNCVIEWER_SOURCES} Surface_X11.cxx)
endif()

if(WIN32 AND NOT CMAKE_BUILD_TYPE STREQUAL "Debug")

+ 0
- 114
vncviewer/OSXPixelBuffer.cxx View File

@@ -1,114 +0,0 @@
/* Copyright 2011-2014 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <ApplicationServices/ApplicationServices.h>

#include <FL/Fl_Window.H>
#include <FL/x.H>

#include <rfb/LogWriter.h>
#include <rfb/Exception.h>

#include "i18n.h"
#include "OSXPixelBuffer.h"

using namespace rfb;

static rfb::LogWriter vlog("OSXPixelBuffer");

OSXPixelBuffer::OSXPixelBuffer(int width, int height) :
PlatformPixelBuffer(rfb::PixelFormat(32, 24, false, true,
255, 255, 255, 16, 8, 0),
width, height, NULL, width),
image(NULL)
{
CGColorSpaceRef lut;
CGDataProviderRef provider;

data = new rdr::U8[width * height * format.bpp/8];
if (data == NULL)
throw rfb::Exception(_("Not enough memory for framebuffer"));

lut = CGDisplayCopyColorSpace(kCGDirectMainDisplay);
if (!lut) {
vlog.error(_("Could not get screen color space. Using slower fallback."));
lut = CGColorSpaceCreateDeviceRGB();
if (!lut)
throw rfb::Exception("CGColorSpaceCreateDeviceRGB");
}

provider = CGDataProviderCreateWithData(NULL, data,
width * height * 4, NULL);
if (!provider)
throw rfb::Exception("CGDataProviderCreateWithData");

image = CGImageCreate(width, height, 8, 32, width * 4, lut,
kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little,
provider, NULL, false, kCGRenderingIntentDefault);
CGColorSpaceRelease(lut);
CGDataProviderRelease(provider);
if (!image)
throw rfb::Exception("CGImageCreate");
}


OSXPixelBuffer::~OSXPixelBuffer()
{
CGImageRelease(image);
delete [] data;
}


void OSXPixelBuffer::draw(int src_x, int src_y, int x, int y, int w, int h)
{
CGContextRef gc;
CGRect rect;

gc = fl_gc;

CGContextSaveGState(gc);

// macOS Coordinates are from bottom left, not top left
src_y = height() - (src_y + h);
y = Fl_Window::current()->h() - (y + h);

// Reset the transformation matrix back to the default identity
// matrix as otherwise we get a massive performance hit
CGContextConcatCTM(gc, CGAffineTransformInvert(CGContextGetCTM(gc)));

// We have to use clipping to partially display an image
rect.origin.x = x;
rect.origin.y = y;
rect.size.width = w;
rect.size.height = h;

CGContextClipToRect(gc, rect);

rect.origin.x = x - src_x;
rect.origin.y = y - src_y;
rect.size.width = width();
rect.size.height = height();

CGContextDrawImage(gc, rect, image);

CGContextRestoreGState(gc);
}

+ 161
- 5
vncviewer/PlatformPixelBuffer.cxx View File

@@ -1,4 +1,4 @@
/* Copyright 2011-2014 Pierre Ossman for Cendio AB
/* Copyright 2011-2016 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
@@ -16,13 +16,70 @@
* USA.
*/

#include <assert.h>

#if !defined(WIN32) && !defined(__APPLE__)
#include <sys/ipc.h>
#include <sys/shm.h>
#endif

#include <FL/Fl.H>
#include <FL/x.H>

#include <rfb/LogWriter.h>
#include <rdr/Exception.h>

#include "PlatformPixelBuffer.h"

PlatformPixelBuffer::PlatformPixelBuffer(const rfb::PixelFormat& pf,
int width, int height,
rdr::U8* data, int stride) :
FullFramePixelBuffer(pf, width, height, data, stride)
static rfb::LogWriter vlog("PlatformPixelBuffer");

PlatformPixelBuffer::PlatformPixelBuffer(int width, int height) :
FullFramePixelBuffer(rfb::PixelFormat(32, 24, false, true,
255, 255, 255, 16, 8, 0),
width, height, 0, stride),
Surface(width, height)
#if !defined(WIN32) && !defined(__APPLE__)
, shminfo(NULL), xim(NULL)
#endif
{
#if !defined(WIN32) && !defined(__APPLE__)
if (!setupShm()) {
xim = XCreateImage(fl_display, CopyFromParent, 32,
ZPixmap, 0, 0, width, height, 32, 0);
if (!xim)
throw rdr::Exception("XCreateImage");

xim->data = (char*)malloc(xim->bytes_per_line * xim->height);
if (!xim->data)
throw rdr::Exception("malloc");

vlog.debug("Using standard XImage");
}

data = (rdr::U8*)xim->data;
stride = xim->bytes_per_line / (getPF().bpp/8);
#else
FullFramePixelBuffer::data = (rdr::U8*)Surface::data;
stride = width;
#endif
}

PlatformPixelBuffer::~PlatformPixelBuffer()
{
#if !defined(WIN32) && !defined(__APPLE__)
if (shminfo) {
vlog.debug("Freeing shared memory XImage");
shmdt(shminfo->shmaddr);
shmctl(shminfo->shmid, IPC_RMID, 0);
delete shminfo;
shminfo = NULL;
}

// XDestroyImage() will free(xim->data) if appropriate
if (xim)
XDestroyImage(xim);
xim = NULL;
#endif
}

void PlatformPixelBuffer::commitBufferRW(const rfb::Rect& r)
@@ -42,5 +99,104 @@ rfb::Rect PlatformPixelBuffer::getDamage(void)
damage.clear();
mutex.unlock();

#if !defined(WIN32) && !defined(__APPLE__)
GC gc;

gc = XCreateGC(fl_display, pixmap, 0, NULL);
if (shminfo) {
XShmPutImage(fl_display, pixmap, gc, xim,
r.tl.x, r.tl.y, r.tl.x, r.tl.y,
r.width(), r.height(), False);
// Need to make sure the X server has finished reading the
// shared memory before we return
XSync(fl_display, False);
} else {
XPutImage(fl_display, pixmap, gc, xim,
r.tl.x, r.tl.y, r.tl.x, r.tl.y, r.width(), r.height());
}
XFreeGC(fl_display, gc);
#endif

return r;
}

#if !defined(WIN32) && !defined(__APPLE__)

static bool caughtError;

static int XShmAttachErrorHandler(Display *dpy, XErrorEvent *error)
{
caughtError = true;
return 0;
}

bool PlatformPixelBuffer::setupShm()
{
int major, minor;
Bool pixmaps;
XErrorHandler old_handler;
const char *display_name = XDisplayName (NULL);

/* Don't use MIT-SHM on remote displays */
if (*display_name && *display_name != ':')
return false;

if (!XShmQueryVersion(fl_display, &major, &minor, &pixmaps))
return false;

shminfo = new XShmSegmentInfo;

xim = XShmCreateImage(fl_display, CopyFromParent, 32,
ZPixmap, 0, shminfo, width(), height());
if (!xim)
goto free_shminfo;

shminfo->shmid = shmget(IPC_PRIVATE,
xim->bytes_per_line * xim->height,
IPC_CREAT|0777);
if (shminfo->shmid == -1)
goto free_xim;

shminfo->shmaddr = xim->data = (char*)shmat(shminfo->shmid, 0, 0);
shmctl(shminfo->shmid, IPC_RMID, 0); // to avoid memory leakage
if (shminfo->shmaddr == (char *)-1)
goto free_xim;

shminfo->readOnly = True;

// This is the only way we can detect that shared memory won't work
// (e.g. because we're accessing a remote X11 server)
caughtError = false;
old_handler = XSetErrorHandler(XShmAttachErrorHandler);

if (!XShmAttach(fl_display, shminfo)) {
XSetErrorHandler(old_handler);
goto free_shmaddr;
}

XSync(fl_display, False);

XSetErrorHandler(old_handler);

if (caughtError)
goto free_shmaddr;

vlog.debug("Using shared memory XImage");

return true;

free_shmaddr:
shmdt(shminfo->shmaddr);

free_xim:
XDestroyImage(xim);
xim = NULL;

free_shminfo:
delete shminfo;
shminfo = NULL;

return 0;
}

#endif

+ 24
- 5
vncviewer/PlatformPixelBuffer.h View File

@@ -1,4 +1,4 @@
/* Copyright 2011-2014 Pierre Ossman for Cendio AB
/* Copyright 2011-2016 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
@@ -19,24 +19,43 @@
#ifndef __PLATFORMPIXELBUFFER_H__
#define __PLATFORMPIXELBUFFER_H__

#if !defined(WIN32) && !defined(__APPLE__)
#include <X11/extensions/XShm.h>
#endif

#include <list>

#include <os/Mutex.h>

#include <rfb/PixelBuffer.h>
#include <rfb/Region.h>

class PlatformPixelBuffer: public rfb::FullFramePixelBuffer {
#include "Surface.h"

class PlatformPixelBuffer: public rfb::FullFramePixelBuffer, public Surface {
public:
PlatformPixelBuffer(const rfb::PixelFormat& pf, int width, int height,
rdr::U8* data, int stride);
PlatformPixelBuffer(int width, int height);
~PlatformPixelBuffer();

virtual void commitBufferRW(const rfb::Rect& r);

virtual void draw(int src_x, int src_y, int x, int y, int w, int h) = 0;
rfb::Rect getDamage(void);

using rfb::FullFramePixelBuffer::width;
using rfb::FullFramePixelBuffer::height;

protected:
os::Mutex mutex;
rfb::Region damage;

#if !defined(WIN32) && !defined(__APPLE__)
protected:
bool setupShm();

protected:
XShmSegmentInfo *shminfo;
XImage *xim;
#endif
};

#endif

vncviewer/Win32PixelBuffer.h → vncviewer/Surface.cxx View File

@@ -1,4 +1,4 @@
/* Copyright 2011-2014 Pierre Ossman for Cendio AB
/* Copyright 2016 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
@@ -16,23 +16,24 @@
* USA.
*/

#ifndef __WIN32PIXELBUFFER_H__
#define __WIN32PIXELBUFFER_H__
#include <FL/Fl_RGB_Image.H>

#include <windows.h>
#include "Surface.h"

#include "PlatformPixelBuffer.h"
Surface::Surface(int width, int height) :
w(width), h(height)
{
alloc();
}

class Win32PixelBuffer: public PlatformPixelBuffer {
public:
Win32PixelBuffer(int width, int height);
~Win32PixelBuffer();
Surface::Surface(const Fl_RGB_Image* image) :
w(image->w()), h(image->h())
{
alloc();
update(image);
}

virtual void draw(int src_x, int src_y, int x, int y, int w, int h);

protected:
HBITMAP bitmap;
};


#endif
Surface::~Surface()
{
dealloc();
}

vncviewer/OSXPixelBuffer.h → vncviewer/Surface.h View File

@@ -1,4 +1,4 @@
/* Copyright 2011-2014 Pierre Ossman for Cendio AB
/* Copyright 2016 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
@@ -16,24 +16,53 @@
* USA.
*/

#ifndef __OSXPIXELBUFFER_H__
#define __OSXPIXELBUFFER_H__

#include "PlatformPixelBuffer.h"
#ifndef __SURFACE_H__
#define __SURFACE_H__

#if defined(WIN32)
#include <windows.h>
#elif defined(__APPLE__)
// Apple headers conflict with FLTK, so redefine types here
typedef struct CGImage* CGImageRef;
#else
#include <X11/extensions/Xrender.h>
#endif

class Fl_RGB_Image;

class OSXPixelBuffer: public PlatformPixelBuffer {
class Surface {
public:
OSXPixelBuffer(int width, int height);
~OSXPixelBuffer();
Surface(int width, int height);
Surface(const Fl_RGB_Image* image);
~Surface();

virtual void draw(int src_x, int src_y, int x, int y, int w, int h);
int width() { return w; }
int height() { return h; }

void clear(unsigned char r, unsigned char g, unsigned char b, unsigned char a=255);

void draw(int src_x, int src_y, int x, int y, int w, int h);

protected:
void alloc();
void dealloc();
void update(const Fl_RGB_Image* image);

protected:
int w, h;

#if defined(WIN32)
RGBQUAD* data;
HBITMAP bitmap;
#elif defined(__APPLE__)
unsigned char* data;
CGImageRef image;
#else
Pixmap pixmap;
Picture picture;
XRenderPictFormat* visFormat;
#endif
};


#endif


+ 162
- 0
vncviewer/Surface_OSX.cxx View File

@@ -0,0 +1,162 @@
/* Copyright 2016 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/

#include <assert.h>

#include <ApplicationServices/ApplicationServices.h>

#include <FL/Fl_RGB_Image.H>
#include <FL/Fl_Window.H>
#include <FL/x.H>

#include <rdr/Exception.h>

#include "Surface.h"

void Surface::clear(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{
unsigned char* out;
int x, y;

r = (unsigned)r * a / 255;
g = (unsigned)g * a / 255;
b = (unsigned)b * a / 255;

out = data;
for (y = 0;y < width();y++) {
for (x = 0;x < height();x++) {
*out++ = b;
*out++ = g;
*out++ = r;
*out++ = a;
}
}
}

void Surface::draw(int src_x, int src_y, int x, int y, int w, int h)
{
CGRect rect;

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);

// We have to use clipping to partially display an image
rect.origin.x = x;
rect.origin.y = y;
rect.size.width = w;
rect.size.height = h;

CGContextClipToRect(fl_gc, rect);

rect.origin.x = x - src_x;
rect.origin.y = y - src_y;
rect.size.width = width();
rect.size.height = height();

CGContextDrawImage(fl_gc, rect, image);

CGContextRestoreGState(fl_gc);
}

void Surface::alloc()
{
CGColorSpaceRef lut;
CGDataProviderRef provider;

data = new unsigned char[width() * height() * 4];

lut = CGDisplayCopyColorSpace(kCGDirectMainDisplay);
if (!lut) {
lut = CGColorSpaceCreateDeviceRGB();
if (!lut)
throw rdr::Exception("CGColorSpaceCreateDeviceRGB");
}

provider = CGDataProviderCreateWithData(NULL, data,
width() * height() * 4, NULL);
if (!provider)
throw rdr::Exception("CGDataProviderCreateWithData");

image = CGImageCreate(width(), height(), 8, 32, width() * 4, lut,
kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little,
provider, NULL, false, kCGRenderingIntentDefault);
CGColorSpaceRelease(lut);
CGDataProviderRelease(provider);
if (!image)
throw rdr::Exception("CGImageCreate");
}

void Surface::dealloc()
{
CGImageRelease(image);
delete [] data;
}

void Surface::update(const Fl_RGB_Image* image)
{
int x, y;
const unsigned char* in;
unsigned char* out;

assert(image->w() == width());
assert(image->h() == height());

// Convert data and pre-multiply alpha
in = (const unsigned char*)image->data()[0];
out = data;
for (y = 0;y < image->h();y++) {
for (x = 0;x < image->w();x++) {
switch (image->d()) {
case 1:
*out++ = in[0];
*out++ = in[0];
*out++ = in[0];
*out++ = 0xff;
break;
case 2:
*out++ = (unsigned)in[0] * in[1] / 255;
*out++ = (unsigned)in[0] * in[1] / 255;
*out++ = (unsigned)in[0] * in[1] / 255;
*out++ = in[1];
break;
case 3:
*out++ = in[2];
*out++ = in[1];
*out++ = in[0];
*out++ = 0xff;
break;
case 4:
*out++ = (unsigned)in[2] * in[3] / 255;
*out++ = (unsigned)in[1] * in[3] / 255;
*out++ = (unsigned)in[0] * in[3] / 255;
*out++ = in[3];
break;
}
in += image->d();
}
if (image->ld() != 0)
in += image->ld() - image->w() * image->d();
}
}

+ 146
- 0
vncviewer/Surface_Win32.cxx View File

@@ -0,0 +1,146 @@
/* Copyright 2016 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/

#include <assert.h>

#include <FL/Fl_RGB_Image.H>
#include <FL/x.H>

#include <rdr/Exception.h>

#include "Surface.h"

void Surface::clear(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{
RGBQUAD* out;
int x, y;

r = (unsigned)r * a / 255;
g = (unsigned)g * a / 255;
b = (unsigned)b * a / 255;

out = data;
for (y = 0;y < width();y++) {
for (x = 0;x < height();x++) {
out->rgbRed = r;
out->rgbGreen = g;
out->rgbBlue = b;
out->rgbReserved = a;
out++;
}
}
}

void Surface::draw(int src_x, int src_y, int x, int y, int w, int h)
{
HDC dc;

dc = CreateCompatibleDC(fl_gc);
if (!dc)
throw rdr::SystemException("CreateCompatibleDC", GetLastError());

if (!SelectObject(dc, bitmap))
throw rdr::SystemException("SelectObject", GetLastError());

if (!BitBlt(fl_gc, x, y, w, h, dc, src_x, src_y, SRCCOPY)) {
// 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(dc);
}

void Surface::alloc()
{
BITMAPINFOHEADER bih;

data = new RGBQUAD[width() * height()];

memset(&bih, 0, sizeof(bih));

bih.biSize = sizeof(BITMAPINFOHEADER);
bih.biBitCount = 32;
bih.biPlanes = 1;
bih.biWidth = width();
bih.biHeight = -height(); // Negative to get top-down
bih.biCompression = BI_RGB;

bitmap = CreateDIBSection(NULL, (BITMAPINFO*)&bih,
DIB_RGB_COLORS, (void**)&data, NULL, 0);
if (!bitmap)
throw rdr::SystemException("CreateDIBSection", GetLastError());
}

void Surface::dealloc()
{
DeleteObject(bitmap);
}

void Surface::update(const Fl_RGB_Image* image)
{
const unsigned char* in;
RGBQUAD* out;
int x, y;

assert(image->w() == width());
assert(image->h() == height());

// Convert data and pre-multiply alpha
in = (const unsigned char*)image->data()[0];
out = data;
for (y = 0;y < image->w();y++) {
for (x = 0;x < image->h();x++) {
switch (image->d()) {
case 1:
out->rgbBlue = in[0];
out->rgbGreen = in[0];
out->rgbRed = in[0];
out->rgbReserved = 0xff;
break;
case 2:
out->rgbBlue = (unsigned)in[0] * in[1] / 255;
out->rgbGreen = (unsigned)in[0] * in[1] / 255;
out->rgbRed = (unsigned)in[0] * in[1] / 255;
out->rgbReserved = in[1];
break;
case 3:
out->rgbBlue = in[2];
out->rgbGreen = in[1];
out->rgbRed = in[0];
out->rgbReserved = 0xff;
break;
case 4:
out->rgbBlue = (unsigned)in[2] * in[3] / 255;
out->rgbGreen = (unsigned)in[1] * in[3] / 255;
out->rgbRed = (unsigned)in[0] * in[3] / 255;
out->rgbReserved = in[3];
break;
}
in += image->d();
out++;
}
if (image->ld() != 0)
in += image->ld() - image->w() * image->d();
}
}


+ 139
- 0
vncviewer/Surface_X11.cxx View File

@@ -0,0 +1,139 @@
/* Copyright 2016 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/

#include <assert.h>

#include <FL/Fl_RGB_Image.H>
#include <FL/x.H>

#include <rdr/Exception.h>

#include "Surface.h"

void Surface::clear(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{
XRenderColor color;

color.red = (unsigned)r * 65535 / 255 * a / 255;
color.green = (unsigned)g * 65535 / 255 * a / 255;
color.blue = (unsigned)b * 65535 / 255 * a / 255;
color.alpha = (unsigned)a * 65535 / 255;

XRenderFillRectangle(fl_display, PictOpSrc, picture, &color,
0, 0, width(), height());
}

void Surface::draw(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, PictOpSrc, picture, None, winPict,
src_x, src_y, 0, 0, x, y, w, h);
XRenderFreePicture(fl_display, winPict);
}

void Surface::alloc()
{
XRenderPictFormat* format;

// Might not be open at this point
fl_open_display();

pixmap = XCreatePixmap(fl_display, XDefaultRootWindow(fl_display),
width(), height(), 32);

format = XRenderFindStandardFormat(fl_display, PictStandardARGB32);
picture = XRenderCreatePicture(fl_display, pixmap, format, 0, NULL);

visFormat = XRenderFindVisualFormat(fl_display, fl_visual->visual);
}

void Surface::dealloc()
{
XRenderFreePicture(fl_display, picture);
XFreePixmap(fl_display, pixmap);
}

void Surface::update(const Fl_RGB_Image* image)
{
XImage* img;
GC gc;

int x, y;
const unsigned char* in;
unsigned char* out;

assert(image->w() == width());
assert(image->h() == height());

img = XCreateImage(fl_display, CopyFromParent, 32,
ZPixmap, 0, NULL, width(), height(),
32, 0);
if (!img)
throw rdr::Exception("XCreateImage");

img->data = (char*)malloc(img->bytes_per_line * img->height);
if (!img->data)
throw rdr::Exception("malloc");

// Convert data and pre-multiply alpha
in = (const unsigned char*)image->data()[0];
out = (unsigned char*)img->data;
for (y = 0;y < img->height;y++) {
for (x = 0;x < img->width;x++) {
switch (image->d()) {
case 1:
*out++ = in[0];
*out++ = in[0];
*out++ = in[0];
*out++ = 0xff;
break;
case 2:
*out++ = (unsigned)in[0] * in[1] / 255;
*out++ = (unsigned)in[0] * in[1] / 255;
*out++ = (unsigned)in[0] * in[1] / 255;
*out++ = in[1];
break;
case 3:
*out++ = in[2];
*out++ = in[1];
*out++ = in[0];
*out++ = 0xff;
break;
case 4:
*out++ = (unsigned)in[2] * in[3] / 255;
*out++ = (unsigned)in[1] * in[3] / 255;
*out++ = (unsigned)in[0] * in[3] / 255;
*out++ = in[3];
break;
}
in += image->d();
}
if (image->ld() != 0)
in += image->ld() - image->w() * image->d();
}

gc = XCreateGC(fl_display, pixmap, 0, NULL);
XPutImage(fl_display, pixmap, gc, img,
0, 0, 0, 0, img->width, img->height);
XFreeGC(fl_display, gc);

XDestroyImage(img);
}


+ 2
- 34
vncviewer/Viewport.cxx View File

@@ -62,15 +62,6 @@
#include "vncviewer.h"

#include "PlatformPixelBuffer.h"
#include "FLTKPixelBuffer.h"

#if defined(WIN32)
#include "Win32PixelBuffer.h"
#elif defined(__APPLE__)
#include "OSXPixelBuffer.h"
#else
#include "X11PixelBuffer.h"
#endif

#include <FL/fl_draw.H>
#include <FL/fl_ask.H>
@@ -111,7 +102,7 @@ Viewport::Viewport(int w, int h, const rfb::PixelFormat& serverPF, CConn* cc_)
// We need to intercept keyboard events early
Fl::add_system_handler(handleSystemEvent, this);

frameBuffer = createFramebuffer(w, h);
frameBuffer = new PlatformPixelBuffer(w, h);
assert(frameBuffer);
cc->setFramebuffer(frameBuffer);

@@ -272,7 +263,7 @@ void Viewport::resize(int x, int y, int w, int h)
vlog.debug("Resizing framebuffer from %dx%d to %dx%d",
frameBuffer->width(), frameBuffer->height(), w, h);

frameBuffer = createFramebuffer(w, h);
frameBuffer = new PlatformPixelBuffer(w, h);
assert(frameBuffer);
cc->setFramebuffer(frameBuffer);
}
@@ -374,29 +365,6 @@ int Viewport::handle(int event)
return Fl_Widget::handle(event);
}


PlatformPixelBuffer* Viewport::createFramebuffer(int w, int h)
{
PlatformPixelBuffer *fb;

try {
#if defined(WIN32)
fb = new Win32PixelBuffer(w, h);
#elif defined(__APPLE__)
fb = new OSXPixelBuffer(w, h);
#else
fb = new X11PixelBuffer(w, h);
#endif
} catch (rdr::Exception& e) {
vlog.error(_("Unable to create platform specific framebuffer: %s"), e.str());
vlog.error(_("Using platform independent framebuffer"));
fb = new FLTKPixelBuffer(w, h);
}

return fb;
}


void Viewport::handleClipboardChange(int source, void *data)
{
Viewport *self = (Viewport *)data;

+ 0
- 2
vncviewer/Viewport.h View File

@@ -56,8 +56,6 @@ public:

private:

PlatformPixelBuffer* createFramebuffer(int w, int h);

static void handleClipboardChange(int source, void *data);

void handlePointerEvent(const rfb::Point& pos, int buttonMask);

+ 0
- 93
vncviewer/Win32PixelBuffer.cxx View File

@@ -1,93 +0,0 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2011-2014 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdlib.h>

#include <windows.h>

#include <FL/x.H>

#include <rfb/LogWriter.h>
#include <rfb/Exception.h>

#include "i18n.h"
#include "Win32PixelBuffer.h"

using namespace rfb;

static rfb::LogWriter vlog("Win32PixelBuffer");

Win32PixelBuffer::Win32PixelBuffer(int width, int height) :
PlatformPixelBuffer(rfb::PixelFormat(32, 24, false, true,
255, 255, 255, 16, 8, 0),
width, height, NULL, width),
bitmap(NULL)
{
BITMAPINFOHEADER bih;

memset(&bih, 0, sizeof(bih));

bih.biSize = sizeof(BITMAPINFOHEADER);
bih.biBitCount = getPF().bpp;
bih.biSizeImage = (getPF().bpp / 8) * width * height;
bih.biPlanes = 1;
bih.biWidth = width;
bih.biHeight = -height; // Negative to get top-down
bih.biCompression = BI_RGB;

bitmap = CreateDIBSection(NULL, (BITMAPINFO*)&bih,
DIB_RGB_COLORS, (void**)&data, NULL, 0);
if (!bitmap)
throw rdr::SystemException("CreateDIBSection", GetLastError());
}


Win32PixelBuffer::~Win32PixelBuffer()
{
DeleteObject(bitmap);
}


void Win32PixelBuffer::draw(int src_x, int src_y, int x, int y, int w, int h)
{
HDC dc;

dc = CreateCompatibleDC(fl_gc);
if (!dc)
throw rdr::SystemException("CreateCompatibleDC", GetLastError());

if (!SelectObject(dc, bitmap))
throw rdr::SystemException("SelectObject", GetLastError());

if (!BitBlt(fl_gc, x, y, w, h, dc, src_x, src_y, SRCCOPY)) {
// 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(dc);
}

+ 0
- 229
vncviewer/X11PixelBuffer.cxx View File

@@ -1,229 +0,0 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2011-2014 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <assert.h>
#include <stdlib.h>

#include <FL/Fl.H>
#include <FL/x.H>

#include <rfb/LogWriter.h>
#include <rfb/Exception.h>

#include "i18n.h"
#include "X11PixelBuffer.h"

using namespace rfb;

static rfb::LogWriter vlog("X11PixelBuffer");

static PixelFormat display_pf()
{
int i;

int bpp;
int trueColour, bigEndian;
int redShift, greenShift, blueShift;
int redMax, greenMax, blueMax;

int nformats;
XPixmapFormatValues* format;

// Might not be open at this point
fl_open_display();

format = XListPixmapFormats(fl_display, &nformats);

for (i = 0; i < nformats; i++)
if (format[i].depth == fl_visual->depth) break;

if (i == nformats)
// TRANSLATORS: "pixmap" is an X11 concept and may not be suitable
// to translate.
throw rfb::Exception(_("Display lacks pixmap format for default depth"));

switch (format[i].bits_per_pixel) {
case 8:
case 16:
case 32:
bpp = format[i].bits_per_pixel;
break;
default:
// TRANSLATORS: "pixmap" is an X11 concept and may not be suitable
// to translate.
throw rfb::Exception(_("Couldn't find suitable pixmap format"));
}

XFree(format);

bigEndian = (ImageByteOrder(fl_display) == MSBFirst);
trueColour = (fl_visual->c_class == TrueColor);

if (!trueColour)
throw rfb::Exception(_("Only true colour displays supported"));

vlog.info(_("Using default colormap and visual, TrueColor, depth %d."),
fl_visual->depth);

redShift = ffs(fl_visual->red_mask) - 1;
greenShift = ffs(fl_visual->green_mask) - 1;
blueShift = ffs(fl_visual->blue_mask) - 1;
redMax = fl_visual->red_mask >> redShift;
greenMax = fl_visual->green_mask >> greenShift;
blueMax = fl_visual->blue_mask >> blueShift;

return PixelFormat(bpp, fl_visual->depth, bigEndian, trueColour,
redMax, greenMax, blueMax,
redShift, greenShift, blueShift);
}

X11PixelBuffer::X11PixelBuffer(int width, int height) :
PlatformPixelBuffer(display_pf(), width, height, NULL, 0),
shminfo(NULL), xim(NULL)
{
// Might not be open at this point
fl_open_display();

if (!setupShm()) {
xim = XCreateImage(fl_display, fl_visual->visual, fl_visual->depth,
ZPixmap, 0, 0, width, height, BitmapPad(fl_display), 0);
if (!xim)
throw rfb::Exception("XCreateImage");

xim->data = (char*)malloc(xim->bytes_per_line * xim->height);
if (!xim->data)
throw rfb::Exception(_("Not enough memory for framebuffer"));

vlog.debug("Using standard XImage");
}

data = (rdr::U8*)xim->data;
stride = xim->bytes_per_line / (getPF().bpp/8);
}


X11PixelBuffer::~X11PixelBuffer()
{
if (shminfo) {
vlog.debug("Freeing shared memory XImage");
shmdt(shminfo->shmaddr);
shmctl(shminfo->shmid, IPC_RMID, 0);
delete shminfo;
shminfo = NULL;
}

// XDestroyImage() will free(xim->data) if appropriate
if (xim)
XDestroyImage(xim);
xim = NULL;
}


void X11PixelBuffer::draw(int src_x, int src_y, int x, int y, int w, int h)
{
if (shminfo) {
XShmPutImage(fl_display, fl_window, fl_gc, xim, src_x, src_y, x, y, w, h, False);
// Need to make sure the X server has finished reading the
// shared memory before we return
XSync(fl_display, False);
} else {
XPutImage(fl_display, fl_window, fl_gc, xim, src_x, src_y, x, y, w, h);
}
}

static bool caughtError;

static int XShmAttachErrorHandler(Display *dpy, XErrorEvent *error)
{
caughtError = true;
return 0;
}

int X11PixelBuffer::setupShm()
{
int major, minor;
Bool pixmaps;
XErrorHandler old_handler;
const char *display_name = XDisplayName (NULL);

/* Don't use MIT-SHM on remote displays */
if (*display_name && *display_name != ':')
return 0;

if (!XShmQueryVersion(fl_display, &major, &minor, &pixmaps))
return 0;

shminfo = new XShmSegmentInfo;

xim = XShmCreateImage(fl_display, fl_visual->visual, fl_visual->depth,
ZPixmap, 0, shminfo, width(), height());
if (!xim)
goto free_shminfo;

shminfo->shmid = shmget(IPC_PRIVATE,
xim->bytes_per_line * xim->height,
IPC_CREAT|0777);
if (shminfo->shmid == -1)
goto free_xim;

shminfo->shmaddr = xim->data = (char*)shmat(shminfo->shmid, 0, 0);
shmctl(shminfo->shmid, IPC_RMID, 0); // to avoid memory leakage
if (shminfo->shmaddr == (char *)-1)
goto free_xim;

shminfo->readOnly = True;

// This is the only way we can detect that shared memory won't work
// (e.g. because we're accessing a remote X11 server)
caughtError = false;
old_handler = XSetErrorHandler(XShmAttachErrorHandler);

if (!XShmAttach(fl_display, shminfo)) {
XSetErrorHandler(old_handler);
goto free_shmaddr;
}

XSync(fl_display, False);

XSetErrorHandler(old_handler);

if (caughtError)
goto free_shmaddr;

vlog.debug("Using shared memory XImage");

return 1;

free_shmaddr:
shmdt(shminfo->shmaddr);

free_xim:
XDestroyImage(xim);
xim = NULL;

free_shminfo:
delete shminfo;
shminfo = NULL;

return 0;
}

+ 0
- 47
vncviewer/X11PixelBuffer.h View File

@@ -1,47 +0,0 @@
/* Copyright 2011-2014 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/

#ifndef __X11PIXELBUFFER_H__
#define __X11PIXELBUFFER_H__

#include <X11/Xlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <X11/extensions/XShm.h>

#include <list>

#include "PlatformPixelBuffer.h"

class X11PixelBuffer: public PlatformPixelBuffer {
public:
X11PixelBuffer(int width, int height);
~X11PixelBuffer();

virtual void draw(int src_x, int src_y, int x, int y, int w, int h);

protected:
int setupShm();

protected:
XShmSegmentInfo *shminfo;
XImage *xim;
};


#endif

Loading…
Cancel
Save