Skip to content

Commit

Permalink
fix cairo backing for gtk3 - LONG, see comments:
Browse files Browse the repository at this point in the history
* try cairo.ImageSurface.create_for_data (GTK2 only)
* try pixbuf (GTK2 only)
* fallback to PNG via PIL... (GTK3)

Also import cairo via compat wrapper so we can potentially interpose something else (even though we cannot use cairocffi with the do_paint callbacks..)

git-svn-id: https://xpra.org/svn/Xpra/trunk@6394 3bb7dfac-3a0b-4e04-842a-767bc560f471
  • Loading branch information
totaam committed May 7, 2014
1 parent 606f03c commit 1d104c1
Show file tree
Hide file tree
Showing 10 changed files with 85 additions and 34 deletions.
10 changes: 5 additions & 5 deletions src/xpra/client/gl/gl_window_backing.py
Original file line number Diff line number Diff line change
Expand Up @@ -434,13 +434,13 @@ def gl_expose_event(self, glarea, event):
finally:
drawable.gl_end()

def _do_paint_rgb32(self, img_data, x, y, width, height, rowstride, options, callbacks):
return self._do_paint_rgb(32, img_data, x, y, width, height, rowstride, options, callbacks)
def _do_paint_rgb32(self, img_data, x, y, width, height, rowstride, options):
return self._do_paint_rgb(32, img_data, x, y, width, height, rowstride, options)

def _do_paint_rgb24(self, img_data, x, y, width, height, rowstride, options, callbacks):
return self._do_paint_rgb(24, img_data, x, y, width, height, rowstride, options, callbacks)
def _do_paint_rgb24(self, img_data, x, y, width, height, rowstride, options):
return self._do_paint_rgb(24, img_data, x, y, width, height, rowstride, options)

def _do_paint_rgb(self, bpp, img_data, x, y, width, height, rowstride, options, callbacks):
def _do_paint_rgb(self, bpp, img_data, x, y, width, height, rowstride, options):
log("%s._do_paint_rgb(%s, %s bytes, x=%d, y=%d, width=%d, height=%d, rowstride=%d)", self, bpp, len(img_data), x, y, width, height, rowstride)
drawable = self.gl_init()
if not drawable:
Expand Down
6 changes: 3 additions & 3 deletions src/xpra/client/gtk2/client_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ def get_full_csc_modes(self):
#plain GTK2 window needs to use CSC modules to paint video
#so calculate the server CSC modes the server is allowed to use
#based on the client CSC modes we can convert to RGB(A):
target_rgb_modes = ["RGB", "RGBX"]
if HAS_ALPHA:
target_rgb_modes.append("RGBA")
target_rgb_modes = BACKING_CLASS.RGB_MODES
if not HAS_ALPHA:
target_rgb_modes = [x for x in target_rgb_modes if x.find("A")<0]
ClientWindow.full_csc_modes = getVideoHelper().get_server_full_csc_modes_for_rgb(*target_rgb_modes)
log("full csc modes (%s)=%s", target_rgb_modes, ClientWindow.full_csc_modes)
return ClientWindow.full_csc_modes
Expand Down
2 changes: 0 additions & 2 deletions src/xpra/client/gtk2/gtk2_window_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,6 @@ class GTK2WindowBase(GTKClientWindowBase):
WINDOW_STATE_MAXIMIZED = gdk.WINDOW_STATE_MAXIMIZED
WINDOW_STATE_ICONIFIED = gdk.WINDOW_STATE_ICONIFIED

#must be overriden by subclasses
BACKING_CLASS = None

def init_window(self, metadata):
if self._override_redirect:
Expand Down
2 changes: 2 additions & 0 deletions src/xpra/client/gtk2/pixmap_backing.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
"""
class PixmapBacking(GTK2WindowBacking):

RGB_MODES = ["RGB", "RGBX", "RGBA"]

def __repr__(self):
return "PixmapBacking(%s)" % self._backing

Expand Down
69 changes: 56 additions & 13 deletions src/xpra/client/gtk_base/cairo_backing.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.

import cairo

from xpra.gtk_common.gobject_compat import import_gdk, import_gobject, import_pixbufloader
from xpra.gtk_common.gobject_compat import import_gdk, import_gobject, import_pixbufloader, import_cairo, is_gtk3
gdk = import_gdk()
gobject = import_gobject()
cairo = import_cairo()
PixbufLoader = import_pixbufloader()

from xpra.os_util import BytesIOClass
from xpra.gtk_common.gtk_util import pixbuf_new_from_data, cairo_set_source_pixbuf, gdk_cairo_context, COLORSPACE_RGB
from xpra.client.gtk_base.gtk_window_backing_base import GTKWindowBacking
from xpra.client.window_backing_base import fire_paint_callbacks
from xpra.codecs.loader import get_codec
from xpra.os_util import builtins
_memoryview = builtins.__dict__.get("memoryview")

Expand All @@ -34,6 +34,8 @@
"""
class CairoBacking(GTKWindowBacking):

RGB_MODES = ["ARGB", "XRGB", "RGBA", "RGBX", "RGB"]

def __init__(self, wid, w, h, has_alpha):
GTKWindowBacking.__init__(self, wid)

Expand Down Expand Up @@ -131,25 +133,66 @@ def cairo_paint_surface(self, img_surface, x, y):
return True


def _do_paint_rgb24(self, img_data, x, y, width, height, rowstride, options, callbacks):
def _do_paint_rgb24(self, img_data, x, y, width, height, rowstride, options):
log("_do_paint_rgb24")
return self._do_paint_rgb(False, img_data, x, y, width, height, rowstride, options, callbacks)
return self._do_paint_rgb(cairo.FORMAT_RGB24, False, img_data, x, y, width, height, rowstride, options)

def _do_paint_rgb32(self, img_data, x, y, width, height, rowstride, options, callbacks):
def _do_paint_rgb32(self, img_data, x, y, width, height, rowstride, options):
log("_do_paint_rgb32")
return self._do_paint_rgb(True, img_data, x, y, width, height, rowstride, options, callbacks)
return self._do_paint_rgb(cairo.FORMAT_ARGB32, True, img_data, x, y, width, height, rowstride, options)

def _do_paint_rgb(self, has_alpha, img_data, x, y, width, height, rowstride, options, callbacks):
def _do_paint_rgb(self, cairo_format, has_alpha, img_data, x, y, width, height, rowstride, options):
""" must be called from UI thread """
log("cairo._do_paint_rgb(%s, %s bytes,%s,%s,%s,%s,%s,%s,%s)", has_alpha, len(img_data), x, y, width, height, rowstride, options, callbacks)
log("cairo._do_paint_rgb(%s, %s bytes,%s,%s,%s,%s,%s,%s)", has_alpha, len(img_data), x, y, width, height, rowstride, options)
rgb_format = options.strget("rgb_format", "RGB")
if rgb_format in ("RGBA", ):
img_data = self.unpremultiply(img_data)
if _memoryview and isinstance(img_data, _memoryview):
#Pixbuf cannot use the memoryview directly:
img_data = img_data.tobytes()
pixbuf = pixbuf_new_from_data(img_data, COLORSPACE_RGB, has_alpha, 8, width, height, rowstride)
return self.cairo_paint_pixbuf(pixbuf, x, y)
#"cairo.ImageSurface.create_for_data" is not implemented in GTK3! ARGH!
# http://cairographics.org/documentation/pycairo/3/reference/surfaces.html#cairo.ImageSurface.create_for_data
# "Not yet available in Python 3"
#
#It is available in the cffi cairo bindings, which can be used instead of pycairo
# but then we can't use it from the draw callbacks:
# https://mail.gnome.org/archives/python-hackers-list/2011-December/msg00004.html
# "PyGObject just lacks the glue code that allows it to pass the statically-wrapped
# cairo.Pattern to introspected methods"

if not is_gtk3() and rgb_format in ("ARGB", "XRGB"):
#the pixel format is also what cairo expects
#maybe we should also check that the stride is acceptable for cairo?
#cairo_stride = cairo.ImageSurface.format_stride_for_width(cairo_format, width)
#log("cairo_stride=%s, stride=%s", cairo_stride, rowstride)
img_surface = cairo.ImageSurface.create_for_data(img_data, cairo_format, width, height, rowstride)
return self.cairo_paint_surface(img_surface, x, y)

if not is_gtk3() and rgb_format in ("RGBA", "RGBX"):
#with GTK2, we can use a pixbuf from RGB(A) pixels
if rgb_format=="RGBA":
#we have to unpremultiply for pixbuf!
img_data = self.unpremultiply(img_data)
pixbuf = pixbuf_new_from_data(img_data, COLORSPACE_RGB, has_alpha, 8, width, height, rowstride)
return self.cairo_paint_pixbuf(pixbuf, x, y)

#PIL fallback
PIL = get_codec("PIL")
if has_alpha:
oformat = "RGBA"
else:
oformat = "RGB"
img = PIL.Image.frombuffer(oformat, (width,height), img_data, "raw", rgb_format, 0, 1)
#This is insane, the code below should work, but it doesn't:
# img_data = bytearray(img.tostring('raw', oformat, 0, 1))
# pixbuf = pixbuf_new_from_data(img_data, COLORSPACE_RGB, True, 8, width, height, width*4)
# success = self.cairo_paint_pixbuf(pixbuf, x, y)
#So we still rountrip via PNG:
png = BytesIOClass()
img.save(png, format="PNG")
reader = BytesIOClass(png.getvalue())
png.close()
img = cairo.ImageSurface.create_from_png(reader)
return self.cairo_paint_surface(img, x, y)


def cairo_draw(self, context):
log("cairo_draw(%s) backing=%s", context, self._backing)
Expand Down
4 changes: 2 additions & 2 deletions src/xpra/client/gtk_base/gtk_client_window_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@
keylog = Logger("window", "keyboard")

from xpra.util import AdHocStruct, nn
from xpra.gtk_common.gobject_compat import import_gtk, import_gdk
from xpra.gtk_common.gobject_compat import import_gtk, import_gdk, import_cairo
from xpra.client.client_window_base import ClientWindowBase
gtk = import_gtk()
gdk = import_gdk()
cairo = import_cairo()

import os
import cairo
import time
import math

Expand Down
4 changes: 2 additions & 2 deletions src/xpra/client/gtk_base/gtk_window_backing_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
# later version. See the file COPYING for details.

#pygtk3 vs pygtk2 (sigh)
from xpra.gtk_common.gobject_compat import import_gobject
from xpra.gtk_common.gobject_compat import import_gobject, import_cairo
gobject = import_gobject()
cairo = import_cairo()

from xpra.client.window_backing_base import WindowBackingBase
from xpra.log import Logger
Expand All @@ -29,7 +30,6 @@ def cairo_draw(self, context):
def cairo_draw_from_drawable(self, context, drawable):
if drawable is None:
return
import cairo
try:
context.set_source_pixmap(drawable, 0, 0)
context.set_operator(cairo.OPERATOR_SOURCE)
Expand Down
8 changes: 4 additions & 4 deletions src/xpra/client/window_backing_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,15 +207,15 @@ def do_paint_rgb24(self, img_data, x, y, width, height, rowstride, options, call
the actual paint code is in _do_paint_rgb24
"""
try:
success = (self._backing is not None) and self._do_paint_rgb24(img_data, x, y, width, height, rowstride, options, callbacks)
success = (self._backing is not None) and self._do_paint_rgb24(img_data, x, y, width, height, rowstride, options)
fire_paint_callbacks(callbacks, success)
except KeyboardInterrupt:
raise
except:
log.error("do_paint_rgb24 error", exc_info=True)
fire_paint_callbacks(callbacks, False)

def _do_paint_rgb24(self, img_data, x, y, width, height, rowstride, options, callbacks):
def _do_paint_rgb24(self, img_data, x, y, width, height, rowstride, options):
raise Exception("override me!")


Expand All @@ -233,15 +233,15 @@ def do_paint_rgb32(self, img_data, x, y, width, height, rowstride, options, call
the actual paint code is in _do_paint_rgb32
"""
try:
success = (self._backing is not None) and self._do_paint_rgb32(img_data, x, y, width, height, rowstride, options, callbacks)
success = (self._backing is not None) and self._do_paint_rgb32(img_data, x, y, width, height, rowstride, options)
fire_paint_callbacks(callbacks, success)
except KeyboardInterrupt:
raise
except:
log.error("do_paint_rgb32 error", exc_info=True)
fire_paint_callbacks(callbacks, False)

def _do_paint_rgb32(self, img_data, x, y, width, height, rowstride, options, callbacks):
def _do_paint_rgb32(self, img_data, x, y, width, height, rowstride, options):
raise Exception("override me!")


Expand Down
8 changes: 8 additions & 0 deletions src/xpra/gtk_common/gobject_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,11 @@ def import_pango3():
return Pango
def import_pango():
return _try_import(import_pango3, import_pango2)

def import_cairo():
#we cannot use cairocffi with the do_paint callbacks..
#import cairocffi #@UnresolvedImport
#cairocffi.install_as_pycairo()
#cairo = cairocffi
import cairo
return cairo
6 changes: 3 additions & 3 deletions src/xpra/gtk_common/gtk_spinner.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.

from xpra.gtk_common.gobject_compat import import_gtk, import_gobject
gtk = import_gtk()
from xpra.gtk_common.gobject_compat import import_gtk, import_gobject, import_cairo
gtk = import_gtk()
gobject = import_gobject()
import cairo
cairo = import_cairo()
import math


Expand Down

0 comments on commit 1d104c1

Please sign in to comment.