Beispiel #1
0
def webp_encode(image, supports_transparency, quality, speed, content_type):
    stride = image.get_rowstride()
    pixel_format = image.get_pixel_format()
    enc_webp = get_codec("enc_webp")
    #log("WEBP_PILLOW=%s, enc_webp=%s, stride=%s, pixel_format=%s", WEBP_PILLOW, enc_webp, stride, pixel_format)
    if not WEBP_PILLOW and enc_webp and stride > 0 and stride % 4 == 0 and pixel_format in (
            "BGRA", "BGRX", "RGBA", "RGBX"):
        #prefer Cython module:
        cdata, client_options = enc_webp.encode(image, quality, speed,
                                                supports_transparency,
                                                content_type)
        return "webp", compression.Compressed(
            "webp", cdata), client_options, image.get_width(
            ), image.get_height(), 0, 24
    #fallback using Pillow:
    enc_pillow = get_codec("enc_pillow")
    if enc_pillow:
        if not WEBP_PILLOW:
            log.warn("Warning: using PIL fallback for webp")
            log.warn(" enc_webp=%s, stride=%s, pixel format=%s", enc_webp,
                     stride, image.get_pixel_format())
        for x in ("webp", "png"):
            if x in enc_pillow.get_encodings():
                return enc_pillow.encode(x, image, quality, speed,
                                         supports_transparency)
    raise Exception(
        "BUG: cannot use 'webp' encoding and none of the PIL fallbacks are available!"
    )
Beispiel #2
0
def webp_encode(coding, image, rgb_formats, supports_transparency, quality,
                speed, options):
    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 not in rgb_formats:
        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()
    stride = image.get_rowstride()
    enc_webp = get_codec("enc_webp")
    #log("webp_encode%s stride=%s, enc_webp=%s", (coding, image, rgb_formats, supports_transparency, quality, speed, options), stride, enc_webp)
    if enc_webp and stride > 0 and stride % 4 == 0 and image.get_pixel_format(
    ) in ("BGRA", "BGRX", "RGBA", "RGBX"):
        #prefer Cython module:
        alpha = supports_transparency and image.get_pixel_format().find(
            "A") >= 0
        w = image.get_width()
        h = image.get_height()
        if quality == 100:
            #webp lossless is unbearibly slow for only marginal compression improvements,
            #so force max speed:
            speed = 100
        else:
            #normalize speed for webp: avoid low speeds!
            speed = int(sqrt(speed) * 10)
        speed = max(0, min(100, speed))
        pixels = image.get_pixels()
        assert pixels, "failed to get pixels from %s" % image
        cdata = enc_webp.compress(pixels,
                                  w,
                                  h,
                                  stride=stride / 4,
                                  quality=quality,
                                  speed=speed,
                                  has_alpha=alpha)
        client_options = {"speed": speed, "rgb_format": pixel_format}
        if quality >= 0 and quality <= 100:
            client_options["quality"] = quality
        if alpha:
            client_options["has_alpha"] = True
        return "webp", compression.Compressed(
            "webp", cdata), client_options, image.get_width(
            ), image.get_height(), 0, 24
    #fallback to PIL
    enc_pillow = get_codec("enc_pillow")
    if enc_pillow:
        log(
            "using PIL fallback for webp: enc_webp=%s, stride=%s, pixel format=%s",
            enc_webp, stride, image.get_pixel_format())
        for x in ("webp", "png"):
            if x in enc_pillow.get_encodings():
                return enc_pillow.encode(x, image, quality, speed,
                                         supports_transparency)
    raise Exception(
        "BUG: cannot use 'webp' encoding and none of the PIL fallbacks are available!"
    )
Beispiel #3
0
 def send_webcam_frame(self):
     if not self.webcam_lock.acquire(False):
         return False
     log("send_webcam_frame() webcam_device=%s", self.webcam_device)
     try:
         assert self.webcam_device_no>=0, "device number is not set"
         assert self.webcam_device, "no webcam device to capture from"
         from xpra.codecs.pillow.encoder import get_encodings
         client_webcam_encodings = get_encodings()
         common_encodings = list(set(self.server_webcam_encodings).intersection(client_webcam_encodings))
         log("common encodings (server=%s, client=%s): %s",
             csv(self.server_encodings), csv(client_webcam_encodings), csv(common_encodings))
         if not common_encodings:
             log.error("Error: cannot send webcam image, no common formats")
             log.error(" the server supports: %s", csv(self.server_webcam_encodings))
             log.error(" the client supports: %s", csv(client_webcam_encodings))
             self.stop_sending_webcam()
             return False
         preferred_order = ["jpeg", "png", "png/L", "png/P", "webp"]
         formats = [x for x in preferred_order if x in common_encodings] + common_encodings
         encoding = formats[0]
         start = monotonic_time()
         import cv2
         ret, frame = self.webcam_device.read()
         assert ret, "capture failed"
         assert frame.ndim==3, "invalid frame data"
         h, w, Bpp = frame.shape
         assert Bpp==3 and frame.size==w*h*Bpp
         rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)  # @UndefinedVariable
         end = monotonic_time()
         log("webcam frame capture took %ims", (end-start)*1000)
         start = monotonic_time()
         from PIL import Image
         from io import BytesIO
         image = Image.fromarray(rgb)
         buf = BytesIO()
         image.save(buf, format=encoding)
         data = buf.getvalue()
         buf.close()
         end = monotonic_time()
         log("webcam frame compression to %s took %ims", encoding, (end-start)*1000)
         frame_no = self.webcam_frame_no
         self.webcam_frame_no += 1
         self.send("webcam-frame", self.webcam_device_no, frame_no, encoding,
                   w, h, compression.Compressed(encoding, data))
         self.cancel_webcam_check_ack_timer()
         self.webcam_ack_check_timer = self.timeout_add(10*1000, self.webcam_check_acks)
         return True
     except Exception as e:
         log.error("webcam frame %i failed", self.webcam_frame_no, exc_info=True)
         log.error("Error sending webcam frame: %s", e)
         self.stop_sending_webcam()
         summary = "Webcam forwarding has failed"
         body = "The system encountered the following error:\n" + \
             ("%s\n" % e)
         self.may_notify(XPRA_WEBCAM_NOTIFICATION_ID, summary, body, expire_timeout=10*1000, icon_name="webcam")
         return False
     finally:
         self.webcam_lock.release()
Beispiel #4
0
 def compressed_wrapper(self, datatype, data, level=5):
     #FIXME: ugly assumptions here, should pass by name!
     zlib = "zlib" in self.server_compressors and compression.use_zlib
     lz4 = "lz4" in self.server_compressors and compression.use_lz4
     lzo = "lzo" in self.server_compressors and compression.use_lzo
     if level>0 and len(data)>=256 and (zlib or lz4 or lzo):
         cw = compression.compressed_wrapper(datatype, data, level=level, zlib=zlib, lz4=lz4, lzo=lzo, can_inline=False)
         if len(cw)<len(data):
             #the compressed version is smaller, use it:
             return cw
     #we can't compress, so at least avoid warnings in the protocol layer:
     return compression.Compressed("raw %s" % datatype, data, can_inline=True)
Beispiel #5
0
 def compressed_wrapper(self, datatype, data, level=5, **kwargs):
     if level>0 and len(data)>=256:
         kw = {}
         #brotli is not enabled by default as a generic compressor
         #but callers may choose to enable it via kwargs:
         for algo, defval in {
             "zlib" : True,
             "lz4" : True,
             "brotli" : False,
             }.items():
             kw[algo] = algo in self.server_compressors and compression.use(algo) and kwargs.get(algo, defval)
         cw = compression.compressed_wrapper(datatype, data, level=level, can_inline=False, **kw)
         if len(cw)<len(data):
             #the compressed version is smaller, use it:
             return cw
     #we can't compress, so at least avoid warnings in the protocol layer:
     return compression.Compressed("raw %s" % datatype, data, can_inline=True)
Beispiel #6
0
 def compressed_wrapper(self, datatype, data):
     #FIXME: ugly assumptions here, should pass by name!
     from xpra.net import compression
     zlib = "zlib" in self.server_compressors and compression.use_zlib
     lz4 = "lz4" in self.server_compressors and compression.use_lz4
     lzo = "lzo" in self.server_compressors and compression.use_lzo
     if zlib or lz4 or lzo:
         return compression.compressed_wrapper(datatype,
                                               data,
                                               zlib=zlib,
                                               lz4=lz4,
                                               lzo=lzo,
                                               can_inline=False)
     #we can't compress, so at least avoid warnings in the protocol layer:
     return compression.Compressed("raw %s" % datatype,
                                   data,
                                   can_inline=True)
Beispiel #7
0
def webp_encode(image, rgb_formats, supports_transparency, quality, speed):
    pixel_format = image.get_pixel_format()
    #the native webp encoder only takes this pixel format as input:
    if pixel_format not in ("BGRA", "BGRX", "RGBX", "RGBA"):
        watnted_formats = [
            x for x in rgb_formats if x in ("BGRA", "BGRX", "RGBA", "RGBX")
        ]
        if not rgb_reformat(image, watnted_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()
    stride = image.get_rowstride()
    enc_webp = get_codec("enc_webp")
    #log("WEBP_PILLOW=%s, enc_webp=%s, stride=%s, pixel_format=%s", WEBP_PILLOW, enc_webp, stride, pixel_format)
    if not WEBP_PILLOW and enc_webp and stride > 0 and stride % 4 == 0 and pixel_format in (
            "BGRA", "BGRX", "RGBA", "RGBX"):
        #prefer Cython module:
        cdata, client_options = enc_webp.compress(image, quality, speed,
                                                  supports_transparency)
        return "webp", compression.Compressed(
            "webp", cdata), client_options, image.get_width(
            ), image.get_height(), 0, 24
    #fallback using Pillow:
    enc_pillow = get_codec("enc_pillow")
    if enc_pillow:
        if not WEBP_PILLOW:
            log.warn("Warning: using PIL fallback for webp")
            log.warn(" enc_webp=%s, stride=%s, pixel format=%s", enc_webp,
                     stride, image.get_pixel_format())
        for x in ("webp", "png"):
            if x in enc_pillow.get_encodings():
                return enc_pillow.encode(x, image, quality, speed,
                                         supports_transparency)
    raise Exception(
        "BUG: cannot use 'webp' encoding and none of the PIL fallbacks are available!"
    )
Beispiel #8
0
 def compressed_wrapper(self, datatype, data, level=5):
     if level > 0 and len(data) >= 256:
         #ugly assumptions here, should pass by name
         zlib = "zlib" in self.server_compressors
         lz4 = "lz4" in self.server_compressors
         lzo = "lzo" in self.server_compressors
         #never use brotli as a generic compressor
         #brotli = "brotli" in self.server_compressors and compression.use_brotli
         cw = compression.compressed_wrapper(datatype,
                                             data,
                                             level=level,
                                             zlib=zlib,
                                             lz4=lz4,
                                             lzo=lzo,
                                             brotli=False,
                                             none=True,
                                             can_inline=False)
         if len(cw) < len(data):
             #the compressed version is smaller, use it:
             return cw
     #we can't compress, so at least avoid warnings in the protocol layer:
     return compression.Compressed("raw %s" % datatype,
                                   data,
                                   can_inline=True)
Beispiel #9
0
def encode(coding, image, quality, speed, supports_transparency):
    pixel_format = image.get_pixel_format()
    w = image.get_width()
    h = image.get_height()
    rgb = {
           "XRGB"   : "RGB",
           "BGRX"   : "RGB",
           "RGBA"   : "RGBA",
           "BGRA"   : "RGBA",
           }.get(pixel_format, pixel_format)
    bpp = 32
    #remove transparency if it cannot be handled:
    try:
        pixels = image.get_pixels()
        assert pixels, "failed to get pixels from %s" % image
        #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 = PIL.Image.frombuffer(rgb, (w, h), pixels, "raw", pixel_format, image.get_rowstride())
        if coding.startswith("png") and not supports_transparency and rgb=="RGBA":
            im = im.convert("RGB")
            rgb = "RGB"
            bpp = 24
    except Exception:
        log.error("PIL_encode%s converting %s pixels from %s to %s failed", (w, h, coding, "%s bytes" % image.get_size(), pixel_format, image.get_rowstride()), type(pixels), pixel_format, rgb, exc_info=True)
        raise
    buf = BytesIOClass()
    client_options = {}
    #only optimize with Pillow>=2.2 and when speed is zero
    if coding in ("jpeg", "webp"):
        q = int(min(99, max(1, quality)))
        kwargs = im.info
        kwargs["quality"] = q
        client_options["quality"] = q
        if coding=="jpeg" and PIL_can_optimize and speed<70:
            #(optimizing jpeg is pretty cheap and worth doing)
            kwargs["optimize"] = True
            client_options["optimize"] = True
        im.save(buf, coding.upper(), **kwargs)
    else:
        assert coding in ("png", "png/P", "png/L"), "unsupported png encoding: %s" % coding
        if coding in ("png/L", "png/P") and supports_transparency and rgb=="RGBA":
            #grab alpha channel (the last one):
            #we use the last channel because we know it is RGBA,
            #otherwise we should do: alpha_index= image.getbands().index('A')
            alpha = im.split()[-1]
            #convert to simple on or off mask:
            #set all pixel values below 128 to 255, and the rest to 0
            def mask_value(a):
                if a<=128:
                    return 255
                return 0
            mask = PIL.Image.eval(alpha, mask_value)
        else:
            #no transparency
            mask = None
        if coding=="png/L":
            im = im.convert("L", palette=PIL.Image.ADAPTIVE, colors=255)
            bpp = 8
        elif coding=="png/P":
            #I wanted to use the "better" adaptive method,
            #but this does NOT work (produces a black image instead):
            #im.convert("P", palette=Image.ADAPTIVE)
            im = im.convert("P", palette=PIL.Image.WEB, colors=255)
            bpp = 8
        if mask:
            # paste the alpha mask to the color of index 255
            im.paste(255, mask)
        kwargs = im.info
        if mask is not None:
            client_options["transparency"] = 255
            kwargs["transparency"] = 255
        if PIL_can_optimize and speed==0:
            #optimizing png is very rarely worth doing
            kwargs["optimize"] = True
            client_options["optimize"] = True
        #level can range from 0 to 9, but anything above 5 is way too slow for small gains:
        #76-100   -> 1
        #51-76    -> 2
        #etc
        level = max(1, min(5, (125-speed)//25))
        kwargs["compress_level"] = level
        client_options["compress_level"] = level
        #default is good enough, no need to override, other options:
        #DEFAULT_STRATEGY, FILTERED, HUFFMAN_ONLY, RLE, FIXED
        #kwargs["compress_type"] = PIL.Image.DEFAULT_STRATEGY
        im.save(buf, "PNG", **kwargs)
    log("sending %sx%s %s as %s, mode=%s, options=%s", w, h, pixel_format, coding, im.mode, kwargs)
    data = buf.getvalue()
    buf.close()
    return coding, compression.Compressed(coding, data), client_options, image.get_width(), image.get_height(), 0, bpp
Beispiel #10
0
    def compress_and_send_window_icon(self):
        #this runs in the work queue
        self.send_window_icon_timer = 0
        idata = self.window_icon_data
        if not idata or not self.has_png:
            return
        w, h, pixel_format, pixel_data = idata
        log("compress_and_send_window_icon() %ix%i in %s format, %i bytes for wid=%i",
            w, h, pixel_format, len(pixel_data), self.wid)
        assert pixel_format in ("BGRA", "RGBA", "png"), "invalid window icon format %s" % pixel_format
        if pixel_format=="BGRA":
            #BGRA data is always unpremultiplied
            #(that's what we get from NetWMIcons)
            from xpra.codecs.argb.argb import premultiply_argb  #@UnresolvedImport
            pixel_data = premultiply_argb(pixel_data)

        max_w, max_h = self.window_icon_max_size
        #use png if supported and if "premult_argb32" is not supported by the client (ie: html5)
        #or if we must downscale it (bigger than what the client is willing to deal with),
        #or if we want to save window icons
        must_scale = w>max_w or h>max_h
        log("compress_and_send_window_icon: %sx%s (max-size=%s, standard-size=%s), pixel_format=%s",
            w, h, self.window_icon_max_size, self.window_icon_size, pixel_format)
        must_convert = pixel_format!="png"
        log(" must convert=%s, must scale=%s", must_convert, must_scale)

        image = None
        if must_scale or must_convert or SAVE_WINDOW_ICONS:
            #we're going to need a PIL Image:
            if pixel_format=="png":
                image = Image.open(BytesIO(pixel_data))
            else:
                image = Image.frombuffer("RGBA", (w,h), memoryview_to_bytes(pixel_data), "raw", pixel_format, 0, 1)
            if must_scale:
                #scale the icon down to the size the client wants
                #(we should scale + paste to preserve the aspect ratio, meh)
                icon_w, icon_h = self.window_icon_size
                if float(w)/icon_w>=float(h)/icon_h:
                    rh = min(max_h, h*icon_w//w)
                    rw = icon_w
                else:
                    rw = min(max_w, w*icon_h//h)
                    rh = icon_h
                log("scaling window icon down to %sx%s", rw, rh)
                image = image.resize((rw, rh), Image.ANTIALIAS)
            if SAVE_WINDOW_ICONS:
                filename = "server-window-%i-icon-%i.png" % (self.wid, int(monotonic_time()))
                image.save(filename, 'PNG')
                log("server window icon saved to %s", filename)

        if image:
            #image got converted or scaled, get the new pixel data:
            output = BytesIO()
            image.save(output, "png")
            pixel_data = output.getvalue()
            output.close()
            w, h = image.size
        wrapper = compression.Compressed("png", pixel_data)
        packet = ("window-icon", self.wid, w, h, wrapper.datatype, wrapper)
        log("queuing window icon update: %s", packet)
        self.queue_packet(packet, wait_for_more=True)
Beispiel #11
0
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
Beispiel #12
0
 def compress_and_send_window_icon(self):
     #this runs in the work queue
     self.send_window_icon_timer = 0
     idata = self.window_icon_data
     if not idata:
         return
     pixel_data, pixel_format, stride, w, h = idata
     PIL = get_codec("PIL")
     max_w, max_h = self.window_icon_max_size
     if stride != w * 4:
         #re-stride it (I don't think this ever fires?)
         pixel_data = b"".join(pixel_data[stride * y:stride * y + w * 4]
                               for y in range(h))
         stride = w * 4
     #use png if supported and if "premult_argb32" is not supported by the client (ie: html5)
     #or if we must downscale it (bigger than what the client is willing to deal with),
     #or if we want to save window icons
     has_png = PIL and PNG_ICONS and ("png" in self.window_icon_encodings)
     has_premult = ARGB_ICONS and "premult_argb32" in self.window_icon_encodings
     use_png = has_png and (SAVE_WINDOW_ICONS or w > max_w or h > max_h
                            or w * h >= 1024 or (not has_premult) or
                            (pixel_format != "BGRA"))
     log(
         "compress_and_send_window_icon: %sx%s (max-size=%s, standard-size=%s), sending as png=%s, has_png=%s, has_premult=%s, pixel_format=%s",
         w, h, self.window_icon_max_size, self.window_icon_size, use_png,
         has_png, has_premult, pixel_format)
     if use_png:
         img = PIL.Image.frombuffer("RGBA", (w, h), pixel_data, "raw",
                                    pixel_format, 0, 1)
         if w > max_w or h > max_h:
             #scale the icon down to the size the client wants
             icon_w, icon_h = self.window_icon_size
             if float(w) / icon_w >= float(h) / icon_h:
                 h = min(max_h, h * icon_w // w)
                 w = icon_w
             else:
                 w = min(max_w, w * icon_h // h)
                 h = icon_h
             log("scaling window icon down to %sx%s", w, h)
             img = img.resize((w, h), PIL.Image.ANTIALIAS)
         output = BytesIOClass()
         img.save(output, 'PNG')
         compressed_data = output.getvalue()
         output.close()
         wrapper = compression.Compressed("png", compressed_data)
         if SAVE_WINDOW_ICONS:
             filename = "server-window-%i-icon-%i.png" % (
                 self.wid, int(monotonic_time()))
             img.save(filename, 'PNG')
             log("server window icon saved to %s", filename)
     elif ("premult_argb32"
           in self.window_icon_encodings) and pixel_format == "BGRA":
         wrapper = self.compressed_wrapper("premult_argb32",
                                           str(pixel_data))
     else:
         log("cannot send window icon, supported encodings: %s",
             self.window_icon_encodings)
         return
     assert wrapper.datatype in (
         "premult_argb32",
         "png"), "invalid wrapper datatype %s" % wrapper.datatype
     packet = ("window-icon", self.wid, w, h, wrapper.datatype, wrapper)
     log("queuing window icon update: %s", packet)
     self.queue_packet(packet, wait_for_more=True)
Beispiel #13
0
def rgb_encode(coding,
               image,
               rgb_formats,
               supports_transparency,
               speed,
               rgb_zlib=True,
               rgb_lz4=True,
               rgb_lzo=False):
    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 not in rgb_formats:
        log("rgb_encode reformatting because %s not in %s", pixel_format,
            rgb_formats)
        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 and coding == "rgb24":
            coding = "rgb32"
        elif len(pixel_format) == 3 and coding == "rgb32":
            coding = "rgb24"
    #always tell client which pixel format we are sending:
    options = {"rgb_format": pixel_format}

    #we may want to re-stride:
    image.may_restride()

    #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"
    if len(pixels) >= 256:
        level = max(0, min(5, int(115 - speed) / 20))
        if len(pixels) < 1024:
            #fewer pixels, make it more likely we won't bother compressing:
            level = level // 2
    if level > 0:
        if rgb_lz4 and compression.use_lz4:
            cwrapper = compression.compressed_wrapper(coding, pixels, lz4=True)
            algo = "lz4"
            level = 1
        elif rgb_lzo and compression.use_lzo:
            cwrapper = compression.compressed_wrapper(coding, pixels, lzo=True)
            algo = "lzo"
            level = 1
        elif rgb_zlib and compression.use_zlib:
            cwrapper = compression.compressed_wrapper(coding,
                                                      pixels,
                                                      zlib=True,
                                                      level=level)
            algo = "zlib"
        else:
            cwrapper = None
        if cwrapper is 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.upper().find("A") >= 0 or pixel_format.upper().find(
            "X") >= 0:
        bpp = 32
    else:
        bpp = 24
    log(
        "rgb_encode using level=%s, %s compressed %sx%s in %s/%s: %s bytes down to %s",
        level, 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
Beispiel #14
0
def rgb_encode(coding,
               image,
               rgb_formats,
               supports_transparency,
               speed,
               rgb_zlib=True,
               rgb_lz4=True,
               rgb_lzo=False,
               encoding_client_options=True,
               supports_rgb24zlib=True):
    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, encoding_client_options, supports_rgb24zlib), pixel_format, rgb_formats)
    if pixel_format not in rgb_formats:
        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 and coding == "rgb24":
            coding = "rgb32"
        elif len(pixel_format) == 3 and coding == "rgb32":
            coding = "rgb24"
    #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()

    #special case for when rowstride is so much bigger than the width
    #that we would end up sending large chunks of padding with each row of pixels
    #this happens with XShm pixel data (the default)
    stride = image.get_rowstride()
    width = image.get_width()
    rstride = roundup(width * len(pixel_format),
                      4)  #a reasonable stride: rounded up to 4
    height = image.get_height()
    if stride > 8 and rstride < stride:
        al = len(pixels)  #current buffer size
        el = rstride * height  #desirable size we could have
        if al - el > 1024 and el * 110 / 100 < al:  #is it worth re-striding to save space?
            #we'll save at least 1KB and 10%, do it
            #Note: we could also change the pixel format whilst we're at it
            # and convert BGRX to RGB for example (assuming RGB is also supported by the client)
            rows = []
            for y in range(height):
                rows.append(
                    memoryview_to_bytes(pixels[stride * y:stride * y +
                                               rstride]))
            pixels = "".join(rows)
            log(
                "rgb_encode: %s pixels re-stride saving %i%% from %s (%s bytes) to %s (%s bytes)",
                pixel_format, 100 - 100 * el / al, stride, al, rstride, el)
            stride = rstride

    #compression
    #by default, wire=raw:
    raw_data = str(pixels)
    wire_data = raw_data
    level = 0
    algo = "not"
    if len(pixels) >= 256 and (rgb_zlib and compression.use_zlib) or (
            rgb_lz4 and compression.use_lz4) or (rgb_lzo
                                                 and compression.use_lzo):
        level = max(0, min(5, int(115 - speed) / 20))
        if len(pixels) < 1024:
            #fewer pixels, make it more likely we won't bother compressing:
            level = level / 2
    if level > 0:
        if rgb_lz4 and compression.use_lz4:
            wire_data = compression.compressed_wrapper(coding,
                                                       pixels,
                                                       lz4=True)
            algo = "lz4"
            level = 1
        elif rgb_lzo and compression.use_lzo:
            wire_data = compression.compressed_wrapper(coding,
                                                       pixels,
                                                       lzo=True)
            algo = "lzo"
            level = 1
        else:
            assert rgb_zlib and compression.use_zlib
            wire_data = compression.compressed_wrapper(coding,
                                                       pixels,
                                                       zlib=True,
                                                       level=level)
            algo = "zlib"
        raw_data = wire_data.data
        #log("%s/%s data compressed from %s bytes down to %s (%s%%) with lz4=%s",
        #         coding, pixel_format, len(pixels), len(raw_data), int(100.0*len(raw_data)/len(pixels)), self.rgb_lz4)
        if len(raw_data) >= (len(pixels) - 32):
            #compressed is actually bigger! (use uncompressed)
            level = 0
            wire_data = str(pixels)
            raw_data = wire_data
        else:
            #add compressed marker:
            options[algo] = level
    if pixel_format.upper().find("A") >= 0 or pixel_format.upper().find(
            "X") >= 0:
        bpp = 32
    else:
        bpp = 24
    log(
        "rgb_encode using level=%s, %s compressed %sx%s in %s/%s: %s bytes down to %s",
        level, algo, image.get_width(), image.get_height(), coding,
        pixel_format, len(pixels), len(raw_data))
    if not encoding_client_options or not supports_rgb24zlib:
        return coding, wire_data, {}, width, height, stride, bpp
    #wrap it using "Compressed" so the network layer receiving it
    #won't decompress it (leave it to the client's draw thread)
    return coding, compression.Compressed(
        coding, raw_data, True), options, width, height, stride, bpp
Beispiel #15
0
        #76-100   -> 1
        #51-76    -> 2
        #etc
        level = max(1, min(5, (125 - speed) // 25))
        kwargs["compress_level"] = level
        client_options["compress_level"] = level
        #default is good enough, no need to override, other options:
        #DEFAULT_STRATEGY, FILTERED, HUFFMAN_ONLY, RLE, FIXED
        #kwargs["compress_type"] = PIL.Image.DEFAULT_STRATEGY
        im.save(buf, "PNG", **kwargs)
    log("sending %sx%s %s as %s, mode=%s, options=%s", w, h, pixel_format,
        coding, im.mode, kwargs)
    data = buf.getvalue()
    buf.close()
    return coding, compression.Compressed(
        coding,
        data), client_options, image.get_width(), image.get_height(), 0, bpp


def argb_swap(image, rgb_formats, supports_transparency):
    """ use the argb codec to do the RGB byte swapping """
    pixel_format = image.get_pixel_format()
    if None in (bgra_to_rgb, bgra_to_rgba, argb_to_rgb, argb_to_rgba):
        warn_encoding_once(
            "argb-module-missing",
            "no argb module, cannot convert %s to one of: %s" %
            (pixel_format, rgb_formats))
        return False

    #try to fallback to argb module
    #if we have one of the target pixel formats: