Skip to content

Commit

Permalink
#1445: add support for 8-bit depth via "png/P"
Browse files Browse the repository at this point in the history
git-svn-id: https://xpra.org/svn/Xpra/trunk@15123 3bb7dfac-3a0b-4e04-842a-767bc560f471
  • Loading branch information
totaam committed Feb 20, 2017
1 parent 2795f37 commit ee1a9f9
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 21 deletions.
4 changes: 4 additions & 0 deletions src/xpra/codecs/image_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ def __init__(self, x, y, width, height, pixels, pixel_format, depth, rowstride,
self.thread_safe = thread_safe
self.freed = False
self.timestamp = int(time.time()*1000)
self.palette = None

def _cn(self):
try:
Expand Down Expand Up @@ -79,6 +80,9 @@ def get_pixels(self):
def get_planes(self):
return self.planes

def get_palette(self):
return self.palette

def is_thread_safe(self):
""" if True, free() and clone_pixel_data() can be called from any thread,
if False, free() and clone_pixel_data() must be called from the same thread.
Expand Down
26 changes: 20 additions & 6 deletions src/xpra/codecs/pillow/encode.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from xpra.os_util import BytesIOClass, memoryview_to_bytes, _buffer
from xpra.net.compression import Compressed

from PIL import Image #@UnresolvedImport
from PIL import Image, ImagePalette #@UnresolvedImport
from xpra.codecs.pillow import PIL_VERSION
PIL_can_optimize = PIL_VERSION and PIL_VERSION>="2.2"

Expand Down Expand Up @@ -54,14 +54,16 @@ def get_info():

def encode(coding, image, quality, speed, supports_transparency):
pixel_format = image.get_pixel_format()
palette = None
w = image.get_width()
h = image.get_height()
rgb = {
"XRGB" : "RGB",
"BGRX" : "RGB",
"RGBA" : "RGBA",
"BGRA" : "RGBA",
}.get(pixel_format, pixel_format)
"RLE8" : "P",
"XRGB" : "RGB",
"BGRX" : "RGB",
"RGBA" : "RGBA",
"BGRA" : "RGBA",
}.get(pixel_format, pixel_format)
bpp = 32
#remove transparency if it cannot be handled:
try:
Expand Down Expand Up @@ -92,13 +94,25 @@ def encode(coding, image, quality, speed, supports_transparency):
pixel_format = "RGB"
rgb = "RGB"
bpp = 24
elif pixel_format=="RLE8":
pixel_format = "P"
palette = image.get_palette()
rp, gp, bp = [], [], []
for r, g, b in palette:
rp.append((r>>8) & 0xFF)
gp.append((g>>8) & 0xFF)
bp.append((b>>8) & 0xFF)
palette = rp+gp+bp
#PIL cannot use the memoryview directly:
if type(pixels)!=_buffer:
pixels = memoryview_to_bytes(pixels)
#it is safe to use frombuffer() here since the convert()
#calls below will not convert and modify the data in place
#and we save the compressed data then discard the image
im = Image.frombuffer(rgb, (w, h), pixels, "raw", pixel_format, image.get_rowstride(), 1)
if palette:
im.putpalette(palette)
im.palette = ImagePalette.ImagePalette("RGB", palette = palette, size = len(palette))
if coding.startswith("png") and not supports_transparency and rgb=="RGBA":
im = im.convert("RGB")
rgb = "RGB"
Expand Down
11 changes: 10 additions & 1 deletion src/xpra/server/window/window_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -753,7 +753,10 @@ def get_best_encoding_impl(self):
return self.encoding_is_mmap
elif self.encoding=="png/L":
#(png/L would look awful if we mixed it with something else)
return self.get_strict_encoding
return self.encoding_is_pngL
elif self.image_depth==8:
#no other option:
return self.encoding_is_pngP
elif self.strict and self.encoding!="auto":
#honour strict flag
if self.encoding=="rgb":
Expand Down Expand Up @@ -792,6 +795,12 @@ def get_best_encoding_impl_default(self):
def encoding_is_mmap(self, *args):
return "mmap"

def encoding_is_pngL(self, *args):
return "png/L"

def encoding_is_pngP(self, *args):
return "png/P"

def encoding_is_rgb32(self, *args):
return "rgb32"

Expand Down
13 changes: 10 additions & 3 deletions src/xpra/server/window/window_video_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,8 @@ def get_best_nonvideo_encoding(self, pixel_count, ww, wh, speed, quality, curren
#if we're here, then the window has no alpha (or the client cannot handle alpha)
#and we can ignore the current encoding
options = options or self.non_video_encodings
if self.image_depth==8:
return "png/P"
if pixel_count<self._rgb_auto_threshold:
#high speed and high quality, rgb is still good
if "rgb24" in options:
Expand Down Expand Up @@ -1537,9 +1539,12 @@ def get_video_fallback_encoding(self, order=PREFERED_ENCODING_ORDER):
return fallback_encodings[0]

def video_fallback(self, image, options, order=PREFERED_ENCODING_ORDER):
encoding = self.get_video_fallback_encoding(order)
if not encoding:
return None
if self.image_depth==8:
encoding = "png/P"
else:
encoding = self.get_video_fallback_encoding(order)
if not encoding:
return None
encode_fn = self._encoders[encoding]
return encode_fn(encoding, image, options)

Expand Down Expand Up @@ -1599,6 +1604,8 @@ def do_video_encode(self, encoding, image, options):
bpp = 4
if image.get_depth()==16:
bpp = 2
elif image.get_depth()==8:
bpp = 1
scroll_data.update(image.get_pixels(), x, y, w, h, image.get_rowstride(), bpp)
max_distance = min(1000, (100-SCROLL_MIN_PERCENT)*h//100)
scroll_data.calculate(max_distance)
Expand Down
96 changes: 85 additions & 11 deletions src/xpra/x11/bindings/ximage.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -64,18 +64,19 @@ cdef extern from "sys/shm.h":
cdef extern from "errno.h" nogil:
int errno

cdef extern from "X11/Xutil.h":
pass


include "constants.pxi"
ctypedef unsigned long CARD32
ctypedef unsigned short CARD16
ctypedef unsigned char CARD8
ctypedef CARD32 Colormap

cdef extern from "X11/X.h":
unsigned long NoSymbol

cdef extern from "X11/Xatom.h":
int XA_RGB_DEFAULT_MAP
int XA_RGB_BEST_MAP

cdef extern from "X11/Xlib.h":
ctypedef struct Display:
pass
Expand Down Expand Up @@ -106,6 +107,31 @@ cdef extern from "X11/Xlib.h":
int bits_per_rgb
int map_entries

ctypedef struct XVisualInfo:
Visual *visual
VisualID visualid
int screen
unsigned int depth
int c_class
unsigned long red_mask
unsigned long green_mask
unsigned long blue_mask
int colormap_size
int bits_per_rgb

ctypedef struct XColor:
unsigned long pixel # pixel value
unsigned short red, green, blue # rgb values
char flags # DoRed, DoGreen, DoBlue

int VisualIDMask
#query colors flags:
int DoRed
int DoGreen
int DoBlue
void XQueryColors(Display *display, Colormap colormap, XColor defs_in_out[], int ncolors)
VisualID XVisualIDFromVisual(Visual *visual)
XVisualInfo *XGetVisualInfo(Display *display, long vinfo_mask, XVisualInfo *vinfo_template, int *nitems_return)

int XFree(void * data)

Expand All @@ -115,9 +141,9 @@ cdef extern from "X11/Xlib.h":
ctypedef struct XWindowAttributes:
int x, y, width, height, depth, border_width
Visual *visual

Status XGetWindowAttributes(Display * display, Window w,
XWindowAttributes * attributes)
Colormap colormap
Bool map_installed
Status XGetWindowAttributes(Display * display, Window w, XWindowAttributes * attributes)

ctypedef char* XPointer

Expand Down Expand Up @@ -156,6 +182,8 @@ cdef extern from "X11/Xlib.h":
int *x_return, int *y_return, unsigned int *width_return, unsigned int *height_return,
unsigned int *border_width_return, unsigned int *depth_return)

cdef extern from "X11/Xutil.h":
pass

cdef extern from "X11/extensions/Xcomposite.h":
Bool XCompositeQueryExtension(Display *, int *, int *)
Expand Down Expand Up @@ -191,12 +219,12 @@ cdef extern from "X11/extensions/XShm.h":
int XShmGetEventBase(Display *display)



SBFirst = {
MSBFirst : "MSBFirst",
LSBFirst : "LSBFirst"
}

cdef const char *RLE8 = "RLE8"
cdef const char *RGB565 = "RGB565"
cdef const char *BGR565 = "BGR565"
cdef const char *XRGB = "XRGB"
Expand All @@ -209,7 +237,7 @@ cdef const char *RGBX = "RGBX"
cdef const char *R210 = "R210"
cdef const char *r210 = "r210"

RGB_FORMATS = [XRGB, BGRX, ARGB, BGRA, RGB, RGBA, RGBX, R210, r210, RGB565, BGR565]
RGB_FORMATS = [XRGB, BGRX, ARGB, BGRA, RGB, RGBA, RGBX, R210, r210, RGB565, BGR565, RLE8]


cdef int ximage_counter = 0
Expand All @@ -233,8 +261,9 @@ cdef class XImageWrapper(object):
cdef void *pixels
cdef object del_callback
cdef uint64_t timestamp
cdef object palette

def __cinit__(self, unsigned int x, unsigned int y, unsigned int width, unsigned int height, uintptr_t pixels=0, pixel_format="", unsigned int depth=24, unsigned int rowstride=0, int planes=0, thread_safe=False, sub=False):
def __cinit__(self, unsigned int x, unsigned int y, unsigned int width, unsigned int height, uintptr_t pixels=0, pixel_format="", unsigned int depth=24, unsigned int rowstride=0, int planes=0, thread_safe=False, sub=False, palette=None):
self.image = NULL
self.pixels = NULL
self.x = x
Expand All @@ -249,6 +278,7 @@ cdef class XImageWrapper(object):
self.sub = sub
self.pixels = <void *> pixels
self.timestamp = int(time.time()*1000)
self.palette = palette

cdef set_image(self, XImage* image):
assert not self.sub
Expand All @@ -269,6 +299,8 @@ cdef class XImageWrapper(object):
self.pixel_format = RGB565
else:
self.pixel_format = BGR565
elif self.depth==8:
self.pixel_format = RLE8
elif self.depth==32:
if image.byte_order==MSBFirst:
self.pixel_format = ARGB
Expand Down Expand Up @@ -303,6 +335,9 @@ cdef class XImageWrapper(object):
def get_rowstride(self):
return self.rowstride

def get_palette(self):
return self.palette

def get_planes(self):
return self.planes

Expand Down Expand Up @@ -332,7 +367,7 @@ cdef class XImageWrapper(object):
raise Exception("source image does not have pixels!")
cdef unsigned char Bpp = BYTESPERPIXEL(self.depth)
cdef uintptr_t sub_ptr = (<uintptr_t> src) + x*Bpp + y*self.rowstride
return XImageWrapper(self.x+x, self.y+y, w, h, sub_ptr, self.pixel_format, self.depth, self.rowstride, 0, True, True)
return XImageWrapper(self.x+x, self.y+y, w, h, sub_ptr, self.pixel_format, self.depth, self.rowstride, 0, True, True, self.palette)

cdef void *get_pixels_ptr(self):
if self.pixels!=NULL:
Expand All @@ -353,6 +388,9 @@ cdef class XImageWrapper(object):
return self.timestamp


def set_palette(self, palette):
self.palette = palette

def set_timestamp(self, timestamp):
self.timestamp = timestamp

Expand Down Expand Up @@ -572,9 +610,45 @@ cdef class XShmWrapper(object):
imageWrapper = XShmImageWrapper(x, y, w, h)
imageWrapper.set_image(self.image)
imageWrapper.set_free_callback(self.free_image_callback)
if self.depth==8:
imageWrapper.set_palette(self.read_palette())
xshmdebug("XShmWrapper.get_image(%#x, %i, %i, %i, %i)=%s (ref_count=%i)", drawable, x, y, w, h, imageWrapper, self.ref_count)
return imageWrapper

def read_palette(self):
#FIXME: we assume screen is zero
cdef Colormap colormap = 0
cdef XWindowAttributes attrs
cdef VisualID visualid
cdef XVisualInfo vinfo_template
cdef XVisualInfo *vinfo
cdef int count = 0
if not XGetWindowAttributes(self.display, self.window, &attrs):
return None
colormap = attrs.colormap
visualid = XVisualIDFromVisual(attrs.visual)
vinfo_template.visualid = visualid
vinfo = XGetVisualInfo(self.display, VisualIDMask, &vinfo_template, &count)
if count!=1 or vinfo==NULL:
log.error("Error: visual %i not found, count=%i, vinfo=%#x", visualid, count, <uintptr_t> vinfo)
if vinfo:
XFree(vinfo)
return None
log("visual: depth=%i, red mask=%#10x, green mask=%#10x, blue mask=%#10x, size=%i, bits per rgb=%i", vinfo.depth, vinfo.red_mask, vinfo.green_mask, vinfo.blue_mask, vinfo.colormap_size, vinfo.bits_per_rgb)
cdef unsigned int size = vinfo.colormap_size
XFree(vinfo)
if size>256:
log.error("invalid colormap size: %i", size)
return None
cdef XColor[256] colors
cdef int i
for i in range(size):
colors[i].flags = DoRed | DoGreen | DoBlue
colors[i].pixel = i
XQueryColors(self.display, colormap, colors, size)
palette = [(colors[i].red, colors[i].green, colors[i].blue) for i in range(256)]
return palette

def discard(self):
#force next get_image call to get a new image from the server
self.got_image = False
Expand Down

0 comments on commit ee1a9f9

Please sign in to comment.