def paint_webp(self, img_data, x, y, width, height, options, callbacks): if not self.webp_decoder or WEBP_PILLOW: #if webp is enabled, then Pillow should be able to take care of it: return self.paint_image("webp", img_data, x, y, width, height, options, callbacks) rgb_format = options.strget("rgb_format") has_alpha = options.boolget("has_alpha", False) buffer_wrapper, width, height, stride, has_alpha, rgb_format = self.webp_decoder.decompress(img_data, has_alpha, rgb_format, self.RGB_MODES) def free_buffer(*_args): buffer_wrapper.free() callbacks.append(free_buffer) data = buffer_wrapper.get_pixels() #if the backing can't handle this format, #ie: tray only supports RGBA if rgb_format not in self.RGB_MODES: from xpra.codecs.rgb_transform import rgb_reformat from xpra.codecs.image_wrapper import ImageWrapper img = ImageWrapper(x, y, width, height, data, rgb_format, len(rgb_format)*8, stride, len(rgb_format), ImageWrapper.PACKED, True, None) #log("rgb_reformat(%s, %s, %s) %i bytes", img, self.RGB_MODES, has_alpha and self._alpha_enabled, len(img_data)) rgb_reformat(img, self.RGB_MODES, has_alpha and self._alpha_enabled) rgb_format = img.get_pixel_format() data = img.get_pixels() stride = img.get_rowstride() #replace with the actual rgb format we get from the decoder: options[b"rgb_format"] = rgb_format return self.paint_rgb(rgb_format, data, x, y, width, height, stride, options, callbacks)
def tight_encode(window, x, y, w, h, quality=0): img = window.get_image(x, y, w, h) window.acknowledge_changes() if not img: return [] if quality==10: #Fill Compression header = make_header(RFBEncoding.TIGHT, x, y, w, h) header += struct.pack(b"!B", 0x80) pixel_format = bytestostr(img.get_pixel_format()) log.warn("fill compression of %s", pixel_format) if not rgb_reformat(img, ("RGB",), False): log.error("Error: cannot convert %s to RGB", pixel_format) return [header, raw_pixels(img)] #try jpeg: data = pillow_encode("jpeg", img) header = tight_header(RFBEncoding.TIGHT, x, y, w, h, 0x90, len(data)) return [header, data]
def mmap_send(mmap, mmap_size, image, rgb_formats, supports_transparency): if mmap_write is None: if first_time("mmap_write missing"): log.warn("Warning: cannot use mmap, no write method support") return None if image.get_pixel_format() not in rgb_formats: if not rgb_reformat(image, rgb_formats, supports_transparency): warning_key = "mmap_send(%s)" % image.get_pixel_format() if first_time(warning_key): log.warn("Waening: cannot use mmap to send %s" % image.get_pixel_format()) return None start = monotonic_time() data = image.get_pixels() assert data, "failed to get pixels from %s" % image mmap_data, mmap_free_size = mmap_write(mmap, mmap_size, data) elapsed = monotonic_time()-start+0.000000001 #make sure never zero! log("%s MBytes/s - %s bytes written to mmap in %.1f ms", int(len(data)/elapsed/1024/1024), len(data), 1000*elapsed) if mmap_data is None: return None #replace pixels with mmap info: return mmap_data, mmap_free_size, len(data)
def rgb_encode(coding, image, rgb_formats, supports_transparency, speed, rgb_zlib=True, rgb_lz4=True, rgb_lzo=False): pixel_format = bytestostr(image.get_pixel_format()) #log("rgb_encode%s pixel_format=%s, rgb_formats=%s", # (coding, image, rgb_formats, supports_transparency, speed, rgb_zlib, rgb_lz4), pixel_format, rgb_formats) if pixel_format not in rgb_formats: log( "rgb_encode reformatting because %s not in %s, supports_transparency=%s", pixel_format, rgb_formats, supports_transparency) if not rgb_reformat(image, rgb_formats, supports_transparency): raise Exception( "cannot find compatible rgb format to use for %s! (supported: %s)" % (pixel_format, rgb_formats)) #get the new format: pixel_format = bytestostr(image.get_pixel_format()) #switch encoding if necessary: if len(pixel_format) == 4: coding = "rgb32" elif len(pixel_format) == 3: coding = "rgb24" else: raise Exception("invalid pixel format %s" % pixel_format) #we may still want to re-stride: image.may_restride() #always tell client which pixel format we are sending: options = {"rgb_format": pixel_format} #compress here and return a wrapper so network code knows it is already zlib compressed: pixels = image.get_pixels() assert pixels, "failed to get pixels from %s" % image width = image.get_width() height = image.get_height() stride = image.get_rowstride() #compression stage: level = 0 algo = "not" l = len(pixels) if l >= 512 and speed < 100: if l >= 4096: #speed=99 -> level=1, speed=0 -> level=9 level = 1 + max(0, min(8, int(100 - speed) // 12)) else: #fewer pixels, make it more likely we won't bother compressing #and use a lower level (max=5) level = max(0, min(5, int(115 - speed) // 20)) if level > 0: cwrapper = compression.compressed_wrapper(coding, pixels, level=level, zlib=rgb_zlib, lz4=rgb_lz4, lzo=rgb_lzo, brotli=False, none=True) algo = cwrapper.algorithm if algo == "none" or len(cwrapper) >= (len(pixels) - 32): #no compression is enabled, or compressed is actually bigger! #(fall through to uncompressed) level = 0 else: #add compressed marker: options[algo] = level #remove network layer compression marker #so that this data will be decompressed by the decode thread client side: cwrapper.level = 0 if level == 0: #can't pass a raw buffer to bencode / rencode, #and even if we could, the image containing those pixels may be freed by the time we get to the encoder algo = "not" cwrapper = compression.Compressed(coding, pixels_to_bytes(pixels), True) if pixel_format.find("A") >= 0 or pixel_format.find("X") >= 0: bpp = 32 else: bpp = 24 log( "rgb_encode using level=%s for %5i bytes at %3i speed, %s compressed %4sx%-4s in %s/%s: %5s bytes down to %5s", level, l, speed, algo, image.get_width(), image.get_height(), coding, pixel_format, len(pixels), len(cwrapper.data)) #wrap it using "Compressed" so the network layer receiving it #won't decompress it (leave it to the client's draw thread) return coding, cwrapper, options, width, height, stride, bpp
def encode(coding: str, image, options: dict): pixel_format = image.get_pixel_format() #log("rgb_encode%s pixel_format=%s, rgb_formats=%s", # (coding, image, rgb_formats, supports_transparency, speed, rgb_zlib, rgb_lz4), pixel_format, rgb_formats) if pixel_format in ("BGRX", "BGRA", "RGBA"): rgb_formats = options.get("rgb_formats", ("BGRX", "BGRA", "RGBA")) elif pixel_format in ("RGB", "BGR"): rgb_formats = options.get("rgb_formats", ("RGB", "BGR")) else: raise Exception("unsupported pixel format %s" % pixel_format) supports_transparency = options.get("alpha", True) if pixel_format not in rgb_formats: log( "rgb_encode reformatting because %s not in %s, supports_transparency=%s", pixel_format, rgb_formats, supports_transparency) if not rgb_reformat(image, rgb_formats, supports_transparency): raise Exception( "cannot find compatible rgb format to use for %s! (supported: %s)" % (pixel_format, rgb_formats)) #get the new format: pixel_format = image.get_pixel_format() #switch encoding if necessary: if len(pixel_format) == 4: coding = "rgb32" elif len(pixel_format) == 3: coding = "rgb24" else: raise Exception("invalid pixel format %s" % pixel_format) #we may still want to re-stride: image.may_restride() #always tell client which pixel format we are sending: client_options = {"rgb_format": pixel_format} #compress here and return a wrapper so network code knows it is already zlib compressed: pixels = image.get_pixels() assert pixels, "failed to get pixels from %s" % image width = image.get_width() height = image.get_height() stride = image.get_rowstride() speed = options.get("speed", 50) #compression stage: level = 0 algo = "not" l = len(pixels) lz4 = options.get("lz4", False) if l >= 512 and (lz4 or speed < 100): if l >= 4096: level = 1 + max(0, min(7, int(100 - speed) // 14)) else: #fewer pixels, make it more likely we won't bother compressing #and use a lower level (max=3) level = max(0, min(3, int(125 - speed) // 35)) if level > 0: zlib = options.get("zlib", False) can_inline = l <= 32768 cwrapper = compressed_wrapper(coding, pixels, level=level, zlib=zlib, lz4=lz4, brotli=False, none=False, can_inline=can_inline) if isinstance(cwrapper, LevelCompressed): #add compressed marker: client_options[cwrapper.algorithm] = cwrapper.level & 0xF #remove network layer compression marker #so that this data will be decompressed by the decode thread client side: cwrapper.level = 0 elif can_inline and isinstance(pixels, memoryview): assert isinstance(cwrapper, Compressed) assert cwrapper.data == pixels #compression either did not work or was not enabled #and memoryview pixel data cannot be handled by the packet encoders #so we convert it to bytes so it can still be inlined with the packet data: cwrapper.data = rgb_transform.pixels_to_bytes(pixels) else: #can't pass a raw buffer to bencode / rencode, #and even if we could, the image containing those pixels may be freed by the time we get to the encoder algo = "not" cwrapper = Compressed(coding, rgb_transform.pixels_to_bytes(pixels), True) if pixel_format.find("A") >= 0 or pixel_format.find("X") >= 0: bpp = 32 else: bpp = 24 log( "rgb_encode using level=%s for %5i bytes at %3i speed, %s compressed %4sx%-4s in %s/%s: %5s bytes down to %5s", level, l, speed, algo, width, height, coding, pixel_format, len(pixels), len(cwrapper.data)) #wrap it using "Compressed" so the network layer receiving it #won't decompress it (leave it to the client's draw thread) return coding, cwrapper, client_options, width, height, stride, bpp