Skip to content

Commit

Permalink
use libyuv downscaling with jpeg and webp
Browse files Browse the repository at this point in the history
  • Loading branch information
totaam committed Nov 20, 2021
1 parent 59eea5b commit b764753
Showing 1 changed file with 42 additions and 16 deletions.
58 changes: 42 additions & 16 deletions xpra/server/window/window_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -717,17 +717,6 @@ def do_set_client_properties(self, properties):
#(in case pillow was selected previously and the client side scaling changed)
for encoding, encoders in self._all_encoders.items():
self._encoders[encoding] = encoders[0]
#we may now want to downscale server-side,
#or convert to grayscale,
#and for that we need to use the pillow encoder:
grayscale = self.encoding=="grayscale"
if self.enc_pillow and ((DOWNSCALE and self.client_render_size) or grayscale):
crsw, crsh = self.client_render_size
ww, wh = self.window_dimensions
if grayscale or (ww-crsw>DOWNSCALE_THRESHOLD and wh-crsh>DOWNSCALE_THRESHOLD):
for x in self.enc_pillow.get_encodings():
if x in self.server_core_encodings:
self.add_encoder(x, self.pillow_encode)
self.update_encoding_selection(self.encoding)


Expand Down Expand Up @@ -2531,8 +2520,9 @@ def make_draw_packet(self, x, y, outw, outh, coding, data, outstride, client_opt

def webp_encode(self, coding, image, options):
assert coding=="webp"
q = options.get("quality", self._current_quality)
s = options.get("speed", self._current_speed)
r, image = self.may_scale(coding, image, options)
if r:
return r
pixel_format = image.get_pixel_format()
#the native webp encoder only takes BGRX / BGRA as input,
#but the client may be able to swap channels,
Expand All @@ -2542,6 +2532,8 @@ def webp_encode(self, coding, image, options):
if not rgb_reformat(image, client_rgb_formats, self.supports_transparency):
raise Exception("cannot find compatible rgb format to use for %s! (supported: %s)" % (
pixel_format, self.rgb_formats))
q = options.get("quality", self._current_quality)
s = options.get("speed", self._current_speed)
return webp_encode(image, self.supports_transparency, q, s, self.content_type)

def rgb_encode(self, coding, image, options):
Expand All @@ -2556,13 +2548,45 @@ def no_r210(self, image, rgb_formats):

def jpeg_encode(self, coding, image, options):
assert coding=="jpeg"
r, image = self.may_scale(coding, image, options)
if r:
return r
self.no_r210(image, ["RGB"])
q = options.get("quality", self._current_quality)
s = options.get("speed", self._current_speed)
return self.enc_jpeg.encode(image, q, s)

def may_scale(self, coding, image, options):
if self.encoding=="grayscale":
#only pillow can do grayscale at the moment:
return self.pillow_encode(coding, image, options), image
#now check for downscaling:
if not DOWNSCALE or not self.client_render_size:
return None, image
crsw, crsh = self.client_render_size
ww, wh = self.window_dimensions
if ww-crsw<DOWNSCALE_THRESHOLD and wh-crsh<DOWNSCALE_THRESHOLD:
return None, image
q = options.get("quality", self._current_quality)
try:
from xpra.codecs.csc_libyuv.colorspace_converter import argb_scale #pylint: disable=import-outside-toplevel
except ImportError as e:
log("cannot downscale: %s", e)
return self.pillow_encode(coding, image, options), image
#no point in using high quality or lossless when downscaling:
if q>80:
options["quality"] = 80
crsw, crsh = self.client_render_size
ww, wh = self.window_dimensions
width = image.get_width()*crsw//ww
height = image.get_height()*crsh//wh
return None, argb_scale(image, width, height)

def nvjpeg_encode(self, coding, image, options):
assert coding=="jpeg"
r, image = self.may_scale(coding, image, options)
if r:
return r
def fallback(reason):
log("nvjpeg_encode: %s", reason)
if self.enc_jpeg:
Expand All @@ -2577,8 +2601,10 @@ def fallback(reason):
h = image.get_height()
if w<16 or h<16:
return fallback("image size %ix%i is too small" % (w, h))
NVJPEG_INPUT_FORMATS = ("RGB", "BGR" )
self.no_r210(image, NVJPEG_INPUT_FORMATS)
pixel_format = image.get_pixel_format()
if pixel_format not in ("RGB", "BGR") and not argb_swap(image, ("RGB", "BGR" )):
if pixel_format not in NVJPEG_INPUT_FORMATS and not argb_swap(image, NVJPEG_INPUT_FORMATS):
return fallback("cannot handle %s" % pixel_format)
q = options.get("quality", self._current_quality)
s = options.get("speed", self._current_speed)
Expand All @@ -2602,8 +2628,6 @@ def pillow_encode(self, coding, image, options):
#for more information on pixel formats supported by PIL / Pillow, see:
#https://github.com/python-imaging/Pillow/blob/master/libImaging/Unpack.c
assert coding in self.server_core_encodings
q = options.get("quality", self._current_quality)
s = options.get("speed", self._current_speed)
transparency = self.supports_transparency and options.get("transparency", True)
grayscale = self.encoding=="grayscale"
resize = None
Expand All @@ -2616,6 +2640,8 @@ def pillow_encode(self, coding, image, options):
if ww-crsw>DOWNSCALE_THRESHOLD and wh-crsh>DOWNSCALE_THRESHOLD:
#keep the same proportions:
resize = w*crsw//ww, h*crsh//wh
q = options.get("quality", self._current_quality)
s = options.get("speed", self._current_speed)
return self.enc_pillow.encode(coding, image, q, s, transparency, grayscale, resize)

def mmap_encode(self, coding, image, _options):
Expand Down

0 comments on commit b764753

Please sign in to comment.