Ejemplo n.º 1
0
def load_icon_from_file(filename):
    if filename.endswith("xpm"):
        try:
            from xpra.gtk_common.gobject_compat import import_pixbufloader
            from xpra.gtk_common.gtk_util import pixbuf_save_to_memory
            data = load_binary_file(filename)
            loader = import_pixbufloader()()
            loader.write(data)
            loader.close()
            pixbuf = loader.get_pixbuf()
            pngicondata = pixbuf_save_to_memory(pixbuf, "png")
            return pngicondata, "png"
        except Exception as e:
            log("pixbuf error loading %s", filename, exc_info=True)
            log.error("Error loading '%s':", filename)
            log.error(" %s", e)
        #try PIL:
        from PIL import Image
        try:
            img = Image.open(filename)
        except Exception as e:
            log("Image.open(%s)", filename, exc_info=True)
            log.error("Error loading '%s':", filename)
            log.error(" %s", e)
            return None
        buf = BytesIOClass()
        img.save(buf, "PNG")
        pngicondata = buf.getvalue()
        buf.close()
        return pngicondata, "png"
    icondata = load_binary_file(filename)
    if not icondata:
        return None
    log("got icon data from '%s': %i bytes", filename, len(icondata))
    return icondata, os.path.splitext(filename)[1].rstrip(".")
Ejemplo n.º 2
0
 def do_send_cursor(self, delay):
     self.cursor_timer = None
     cd = self.get_cursor_data_cb()
     if cd and cd[0]:
         cursor_data = list(cd[0])
         cursor_sizes = cd[1]
         #skip first two fields (if present) as those are coordinates:
         if self.last_cursor_sent and self.last_cursor_sent[
                 2:9] == cursor_data[2:9]:
             cursorlog(
                 "do_send_cursor(..) cursor identical to the last one we sent, nothing to do"
             )
             return
         self.last_cursor_sent = cursor_data[:9]
         w, h, _xhot, _yhot, serial, pixels, name = cursor_data[2:9]
         #compress pixels if needed:
         encoding = None
         if pixels is not None:
             #convert bytearray to string:
             cpixels = strtobytes(pixels)
             if "png" in self.cursor_encodings:
                 from xpra.codecs.loader import get_codec
                 PIL = get_codec("PIL")
                 assert PIL
                 cursorlog(
                     "do_send_cursor() loading %i bytes of cursor pixel data for %ix%i cursor named '%s'",
                     len(cpixels), w, h, name)
                 img = PIL.Image.frombytes("RGBA", (w, h), cpixels, "raw",
                                           "BGRA", w * 4, 1)
                 buf = BytesIOClass()
                 img.save(buf, "PNG")
                 pngdata = buf.getvalue()
                 buf.close()
                 cpixels = Compressed("png cursor",
                                      pngdata,
                                      can_inline=True)
                 encoding = "png"
                 if SAVE_CURSORS:
                     with open("raw-cursor-%#x.png" % serial, "wb") as f:
                         f.write(pngdata)
             elif len(cpixels) >= 256 and ("raw" in self.cursor_encodings
                                           or not self.cursor_encodings):
                 cpixels = self.compressed_wrapper("cursor", pixels)
                 cursorlog("do_send_cursor(..) pixels=%s ", cpixels)
                 encoding = "raw"
             cursor_data[7] = cpixels
         cursorlog(
             "do_send_cursor(..) %sx%s %s cursor name='%s', serial=%#x with delay=%s (cursor_encodings=%s)",
             w, h, (encoding or "empty"), name, serial, delay,
             self.cursor_encodings)
         args = list(cursor_data[:9]) + [cursor_sizes[0]] + list(
             cursor_sizes[1])
         if self.cursor_encodings and encoding:
             args = [encoding] + args
     else:
         cursorlog("do_send_cursor(..) sending empty cursor with delay=%s",
                   delay)
         args = [""]
         self.last_cursor_sent = None
     self.send_more("cursor", *args)
Ejemplo n.º 3
0
 def nasty_rgb_via_png_paint(self, cairo_format, has_alpha, img_data, x, y, width, height, rowstride, rgb_format):
     log("nasty_rgb_via_png_paint%s", (cairo_format, has_alpha, len(img_data), x, y, width, height, rowstride, rgb_format))
     #PIL fallback
     PIL = get_codec("PIL")
     if has_alpha:
         oformat = "RGBA"
     else:
         oformat = "RGB"
     #use frombytes rather than frombuffer to be compatible with python3 new-style buffers
     #this is slower, but since this codepath is already dreadfully slow, we don't care
     bdata = strtobytes(memoryview_to_bytes(img_data))
     try:
         img = PIL.Image.frombytes(oformat, (width,height), bdata, "raw", rgb_format.replace("X", "A"), rowstride, 1)
     except ValueError as e:
         raise Exception("failed to parse raw %s data to %s: %s" % (rgb_format, oformat, e))
     #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, rowstride)
     # 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)
     self.cairo_paint_surface(img, x, y)
     return True
Ejemplo n.º 4
0
def image_data(img):
    buf = BytesIOClass()
    img.save(buf, "png")
    data = buf.getvalue()
    buf.close()
    w,h = img.size
    return ("png", w, h, data)
Ejemplo n.º 5
0
 def paint_pil_image(self, pil_image, width, height, rowstride, options,
                     callbacks):
     buf = BytesIOClass()
     pil_image.save(buf, format="PNG")
     png_data = buf.getvalue()
     buf.close()
     self.idle_add(self.paint_png, png_data, 0, 0, width, height, rowstride,
                   options, callbacks)
Ejemplo n.º 6
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.encode 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)
         end = monotonic_time()
         log("webcam frame capture took %ims", (end-start)*1000)
         start = monotonic_time()
         from PIL import Image
         image = Image.fromarray(rgb)
         buf = BytesIOClass()
         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()
Ejemplo n.º 7
0
    def test_webcam(self):
        if not POSIX or OSX:
            get_util_logger().info("webcam test skipped: %s not supported yet",
                                   sys.platform)
            return
        from xpra.platform.xposix.webcam import get_virtual_video_devices, check_virtual_dir
        if not check_virtual_dir():
            get_util_logger().info(
                "webcam test skipped: no virtual video device directory")
            return
        devices = get_virtual_video_devices()
        if not devices:
            get_util_logger().info(
                "webcam test skipped: no virtual video devices found")
            return
        from xpra.server.source.webcam_mixin import WebcamMixin
        server = AdHocStruct()
        wm = WebcamMixin()
        server.webcam_enabled = True
        server.webcam_device = None
        server.webcam_encodings = ["png", "jpeg"]
        wm.init_from(None, server)
        wm.init_state()
        wm.hello_sent = True
        packets = []

        def send(*args):
            packets.append(args)

        #wm.send = send
        wm.send_async = send
        try:
            assert wm.get_info()
            device_id = 0
            w, h = 640, 480
            assert wm.start_virtual_webcam(device_id, w, h)
            assert wm.get_info().get("webcam", {}).get("active-devices",
                                                       0) == 1
            assert len(packets) == 1  #ack sent
            frame_no = 0
            encoding = "png"
            buf = BytesIOClass()
            from PIL import Image
            image = Image.new('RGB', size=(w, h), color=(155, 0, 0))
            image.save(buf, 'jpeg')
            data = buf.getvalue()
            buf.close()
            wm.process_webcam_frame(device_id, frame_no, encoding, w, h, data)
            assert len(packets) == 2  #ack sent
            wm.stop_virtual_webcam(device_id)
        finally:
            wm.cleanup()
Ejemplo n.º 8
0
 def take_screenshot(self):
     x, y, w, h = get_virtualscreenmetrics()
     image = self.get_image(x, y, w, h)
     if not image:
         return None
     assert image.get_width()==w and image.get_height()==h
     assert image.get_pixel_format()=="BGRX"
     img = Image.frombuffer("RGB", (w, h), image.get_pixels(), "raw", "BGRX", 0, 1)
     out = BytesIOClass()
     img.save(out, format="PNG")
     screenshot = (img.width, img.height, "png", img.width*3, out.getvalue())
     out.close()
     return screenshot
Ejemplo n.º 9
0
    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, %s bytes,%s,%s,%s,%s,%s,%s)", cairo_format, has_alpha, len(img_data), x, y, width, height, rowstride, options)
        rgb_format = options.strget("rgb_format", "RGB")
        if _memoryview and isinstance(img_data, _memoryview):
            #Pixbuf cannot use the memoryview directly:
            img_data = img_data.tobytes()
        #"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, rowstride, 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, rowstride)
        # 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)
Ejemplo n.º 10
0
def selftest(full=False):
    global ENCODINGS
    import binascii
    from xpra.os_util import BytesIOClass
    #test data generated using the encoder:
    for encoding, hexdata in (
                       ('png',      "89504e470d0a1a0a0000000d4948445200000020000000200806000000737a7af40000002849444154785eedd08100000000c3a0f9531fe4855061c0800103060c183060c0800103060cbc0f0c102000013337932a0000000049454e44ae426082"),
                       ('png',      "89504e470d0a1a0a0000000d4948445200000020000000200802000000fc18eda30000002549444154785eedd03101000000c2a0f54fed610d884061c0800103060c183060c080810f0c0c20000174754ae90000000049454e44ae426082"),
                       ('png/L',    "89504e470d0a1a0a0000000d4948445200000020000000200800000000561125280000000274524e5300ff5b9122b50000002049444154785e63fccf801f3011906718550009a1d170180d07e4bc323cd20300a33d013f95f841e70000000049454e44ae426082"),
                       ('png/L',    "89504e470d0a1a0a0000000d4948445200000020000000200800000000561125280000001549444154785e63601805a321301a02a321803d0400042000017854be5c0000000049454e44ae426082"),
                       ('png/P',    "89504e470d0a1a0a0000000d494844520000002000000020080300000044a48ac600000300504c5445000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b330f4880000010074524e53ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0053f707250000001c49444154785e63f84f00308c2a0087c068384012c268388ca87000003f68fc2e077ed1070000000049454e44ae426082"),
                       ('png/P',    "89504e470d0a1a0a0000000d494844520000002000000020080300000044a48ac600000300504c5445000000000000000000000000000000000000000000000000000000000000000000330000660000990000cc0000ff0000003300333300663300993300cc3300ff3300006600336600666600996600cc6600ff6600009900339900669900999900cc9900ff990000cc0033cc0066cc0099cc00cccc00ffcc0000ff0033ff0066ff0099ff00ccff00ffff00000033330033660033990033cc0033ff0033003333333333663333993333cc3333ff3333006633336633666633996633cc6633ff6633009933339933669933999933cc9933ff993300cc3333cc3366cc3399cc33cccc33ffcc3300ff3333ff3366ff3399ff33ccff33ffff33000066330066660066990066cc0066ff0066003366333366663366993366cc3366ff3366006666336666666666996666cc6666ff6666009966339966669966999966cc9966ff996600cc6633cc6666cc6699cc66cccc66ffcc6600ff6633ff6666ff6699ff66ccff66ffff66000099330099660099990099cc0099ff0099003399333399663399993399cc3399ff3399006699336699666699996699cc6699ff6699009999339999669999999999cc9999ff999900cc9933cc9966cc9999cc99cccc99ffcc9900ff9933ff9966ff9999ff99ccff99ffff990000cc3300cc6600cc9900cccc00ccff00cc0033cc3333cc6633cc9933cccc33ccff33cc0066cc3366cc6666cc9966cccc66ccff66cc0099cc3399cc6699cc9999cccc99ccff99cc00cccc33cccc66cccc99ccccccccccffcccc00ffcc33ffcc66ffcc99ffccccffccffffcc0000ff3300ff6600ff9900ffcc00ffff00ff0033ff3333ff6633ff9933ffcc33ffff33ff0066ff3366ff6666ff9966ffcc66ffff66ff0099ff3399ff6699ff9999ffcc99ffff99ff00ccff33ccff66ccff99ccffccccffffccff00ffff33ffff66ffff99ffffccffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023faca40000001549444154785e63601805a321301a02a321803d0400042000017854be5c0000000049454e44ae426082"),
                       ('jpeg',     "ffd8ffe000104a46494600010100000100010000ffdb004300100b0c0e0c0a100e0d0e1211101318281a181616183123251d283a333d3c3933383740485c4e404457453738506d51575f626768673e4d71797064785c656763ffdb0043011112121815182f1a1a2f634238426363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363ffc00011080020002003012200021101031101ffc4001500010100000000000000000000000000000007ffc40014100100000000000000000000000000000000ffc40014010100000000000000000000000000000000ffc40014110100000000000000000000000000000000ffda000c03010002110311003f009f800000000000ffd9"),
                       ('jpeg',     "ffd8ffe000104a46494600010100000100010000ffdb004300100b0c0e0c0a100e0d0e1211101318281a181616183123251d283a333d3c3933383740485c4e404457453738506d51575f626768673e4d71797064785c656763ffdb0043011112121815182f1a1a2f634238426363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363ffc00011080020002003012200021101031101ffc4001500010100000000000000000000000000000007ffc40014100100000000000000000000000000000000ffc40014010100000000000000000000000000000000ffc40014110100000000000000000000000000000000ffda000c03010002110311003f009f800000000000ffd9"),
                       ('webp',     "524946465c00000057454250565038580a000000100000001f00001f0000414c50480f00000001071011110012c2ffef7a44ff530f005650382026000000d002009d012a200020003ed162aa4fa825a3a2280801001a096900003da3a000fef39d800000"),
                       ('webp',     "524946465c00000057454250565038580a000000100000001f00001f0000414c50480f00000001071011110012c2ffef7a44ff530f005650382026000000d002009d012a200020003ed162aa4fa825a3a2280801001a096900003da3a000fef39d800000"),
                       ):
        if encoding not in ENCODINGS:
            #removed already
            continue
        try:
            cdata = binascii.unhexlify(hexdata)
            buf = BytesIOClass(cdata)
            img = PIL.Image.open(buf)
            assert img, "failed to open image data"
            data_fn = getattr(img, "tobytes", getattr(img, "tostring", None))
            raw_data = data_fn("raw", img.mode)
            assert raw_data
            #now try with junk:
            cdata = binascii.unhexlify("ABCD"+hexdata)
            buf = BytesIOClass(cdata)
            try:
                img = PIL.Image.open(buf)
                log.warn("Pillow failed to generate an error parsing invalid input")
            except Exception as e:
                log("correctly raised exception for invalid input: %s", e)
        except Exception as e:
            try:
                #py2k:
                datainfo = cdata.encode("string_escape")
            except:
                try:
                    datainfo = cdata.encode("unicode_escape").decode()
                except:
                    datainfo = str(hexdata)
            log.warn("Pillow error decoding %s with data=%s..", encoding, datainfo[:16])
            log.warn(" %s", e, exc_info=True)
            ENCODINGS.remove(encoding)
Ejemplo n.º 11
0
 def parse_hints(self, h):
     hints = {}
     for x in ("action-icons", "category", "desktop-entry", "resident",
               "transient", "x", "y", "urgency"):
         v = h.get(x)
         if v is not None:
             hints[x] = native_to_dbus(v)
     image_data = h.get("image-data")
     if image_data and bytestostr(image_data[0]) == "png":
         try:
             img_data = image_data[3]
             from PIL import Image
             buf = BytesIOClass(img_data)
             img = Image.open(buf)
             w, h = img.size
             channels = len(img.mode)
             rowstride = w * channels
             has_alpha = img.mode == "RGBA"
             pixel_data = bytearray(img.tobytes("raw", img.mode))
             log.info("pixel_data=%s", type(pixel_data))
             args = w, h, rowstride, has_alpha, 8, channels, pixel_data
             hints["image-data"] = tuple(native_to_dbus(x) for x in args)
             #hints["image-data"] = args
         except Exception as e:
             log("parse_hints(%s) error on image-data=%s",
                 h,
                 image_data,
                 exc_info=True)
             log.error("Error parsing notification image:")
             log.error(" %s", e)
     log("parse_hints(%s)=%s", h, hints)
     #return dbus.types.Dictionary(hints, signature="sv")
     return hints
Ejemplo n.º 12
0
 def paint_image(self, coding, img_data, x, y, width, height, options,
                 callbacks):
     """ can be called from any thread """
     #log("paint_image(%s, %s bytes, %s, %s, %s, %s, %s, %s)", coding, len(img_data), x, y, width, height, options, callbacks)
     assert PIL
     buf = BytesIOClass(img_data)
     img = PIL.Image.open(buf)
     assert img.mode in ("L", "P", "RGB",
                         "RGBA"), "invalid image mode: %s" % img.mode
     if img.mode in ("P", "L"):
         #TODO: use RGB for images without transparency
         img = img.convert("RGB")
     raw_data = img.tostring("raw", img.mode)
     if img.mode == "RGB":
         #PIL flattens the data to a continuous straightforward RGB format:
         rowstride = width * 3
         img_data = self.process_delta(raw_data, width, height, rowstride,
                                       options)
         self.idle_add(self.do_paint_rgb24, img_data, x, y, width, height,
                       rowstride, options, callbacks)
     elif img.mode == "RGBA":
         rowstride = width * 4
         img_data = self.process_delta(raw_data, width, height, rowstride,
                                       options)
         self.idle_add(self.do_paint_rgb32, img_data, x, y, width, height,
                       rowstride, options, callbacks)
     return False
Ejemplo n.º 13
0
    def paint_image(self, coding, img_data, x, y, width, height, options,
                    callbacks):
        """ can be called from any thread """
        #log("paint_image(%s, %s bytes, %s, %s, %s, %s, %s, %s)", coding, len(img_data), x, y, width, height, options, callbacks)
        PIL = get_codec("PIL")
        assert PIL, "PIL not found"
        buf = BytesIOClass(img_data)
        img = PIL.Image.open(buf)
        assert img.mode in ("L", "P", "RGB",
                            "RGBA"), "invalid image mode: %s" % img.mode
        transparency = options.get("transparency", -1)
        if img.mode == "P":
            if transparency >= 0:
                #this deals with alpha without any extra work
                img = img.convert("RGBA")
            else:
                img = img.convert("RGB")
        elif img.mode == "L":
            if transparency >= 0:
                #why do we have to deal with alpha ourselves??
                def mask_value(a):
                    if a != transparency:
                        return 255
                    return 0

                mask = PIL.Image.eval(img, mask_value)
                mask = mask.convert("L")

                def nomask_value(a):
                    if a != transparency:
                        return a
                    return 0

                img = PIL.Image.eval(img, nomask_value)
                img = img.convert("RGBA")
                img.putalpha(mask)
            else:
                img = img.convert("RGB")

        #use tobytes() if present, fallback to tostring():
        data_fn = getattr(img, "tobytes", getattr(img, "tostring", None))
        raw_data = data_fn("raw", img.mode)
        paint_options = typedict(options)
        if img.mode == "RGB":
            #PIL flattens the data to a continuous straightforward RGB format:
            rowstride = width * 3
            paint_options["rgb_format"] = "RGB"
            img_data = self.process_delta(raw_data, width, height, rowstride,
                                          options)
            self.idle_add(self.do_paint_rgb24, img_data, x, y, width, height,
                          rowstride, paint_options, callbacks)
        elif img.mode == "RGBA":
            rowstride = width * 4
            paint_options["rgb_format"] = "RGBA"
            img_data = self.process_delta(raw_data, width, height, rowstride,
                                          options)
            self.idle_add(self.do_paint_rgb32, img_data, x, y, width, height,
                          rowstride, paint_options, callbacks)
        return False
Ejemplo n.º 14
0
 def do_paint_rgb24(self, img_data, x, y, width, height, rowstride, options, callbacks):
     """ must be called from UI thread """
     if DRAW_DEBUG:
         log.info("cairo_paint_rgb24(..,%s,%s,%s,%s,%s,%s,%s)", x, y, width, height, rowstride, options, callbacks)
     if self._backing is None:
         fire_paint_callbacks(callbacks, False)
         return  False
     assert PIL, "cannot paint without PIL!"
     if rowstride==0:
         rowstride = width * 3
     im = PIL.Image.frombuffer("RGB", (width, height), img_data, "raw", "RGB", rowstride)
     buf = BytesIOClass()
     im.save(buf, "PNG")
     data = buf.getvalue()
     buf.close()
     img_data = BytesIOClass(data)
     self.do_paint_png(img_data, x, y, width, height, rowstride, options, callbacks)
     return  False
Ejemplo n.º 15
0
 def process_webcam_frame(self, device_id, frame_no, encoding, w, h, data):
     webcam = self.webcam_forwarding_devices.get(device_id)
     log("process_webcam_frame: device %s, frame no %i: %s %ix%i, %i bytes, webcam=%s", device_id, frame_no, encoding, w, h, len(data), webcam)
     assert encoding and w and h and data
     if not webcam:
         log.error("Error: webcam forwarding is not active, dropping frame")
         self.send_webcam_stop(device_id, "not started")
         return
     try:
         from xpra.codecs.pillow.decode import get_encodings
         assert encoding in get_encodings(), "invalid encoding specified: %s (must be one of %s)" % (encoding, get_encodings())
         rgb_pixel_format = "BGRX"       #BGRX
         from PIL import Image
         buf = BytesIOClass(data)
         img = Image.open(buf)
         pixels = img.tobytes('raw', rgb_pixel_format)
         from xpra.codecs.image_wrapper import ImageWrapper
         bgrx_image = ImageWrapper(0, 0, w, h, pixels, rgb_pixel_format, 32, w*4, planes=ImageWrapper.PACKED)
         src_format = webcam.get_src_format()
         if not src_format:
             #closed / closing
             return
         #one of those two should be present
         try:
             csc_mod = "csc_libyuv"
             from xpra.codecs.csc_libyuv.colorspace_converter import get_input_colorspaces, get_output_colorspaces, ColorspaceConverter        #@UnresolvedImport
         except ImportError:
             self.send_webcam_stop(device_id, "no csc module")
             return
         try:
             assert rgb_pixel_format in get_input_colorspaces(), "unsupported RGB pixel format %s" % rgb_pixel_format
             assert src_format in get_output_colorspaces(rgb_pixel_format), "unsupported output colourspace format %s" % src_format
         except Exception as e:
             log.error("Error: cannot convert %s to %s using %s:", rgb_pixel_format, src_format, csc_mod)
             log.error(" input-colorspaces: %s", csv(get_input_colorspaces()))
             log.error(" output-colorspaces: %s", csv(get_output_colorspaces(rgb_pixel_format)))
             self.send_webcam_stop(device_id, "csc format error")
             return
         tw = webcam.get_width()
         th = webcam.get_height()
         csc = ColorspaceConverter()
         csc.init_context(w, h, rgb_pixel_format, tw, th, src_format)
         image = csc.convert_image(bgrx_image)
         webcam.push_image(image)
         #tell the client all is good:
         self.send_webcam_ack(device_id, frame_no)
     except Exception as e:
         log("error on %ix%i frame %i using encoding %s", w, h, frame_no, encoding, exc_info=True)
         log.error("Error processing webcam frame:")
         msg = str(e)
         if not msg:
             msg = "unknown error"
             log.error(" %s error" % webcam, exc_info=True)
         log.error(" %s", msg)
         self.send_webcam_stop(device_id, msg)
         self.stop_virtual_webcam(device_id)
Ejemplo n.º 16
0
 def get_notification_icon(self, icon_string):
     #the string may be:
     # * a path which we will load using pillow
     # * a name we lookup in the current them
     if not icon_string:
         return ()
     img = None
     from PIL import Image
     if os.path.isabs(icon_string):
         if os.path.exists(icon_string) and os.path.isfile(icon_string):
             img = Image.open(icon_string)
             w, h = img.size
     else:
         #try to find it in the theme:
         theme = gtk.icon_theme_get_default()
         if theme:
             try:
                 icon = theme.load_icon(icon_string, gtk.ICON_SIZE_BUTTON,
                                        0)
             except Exception as e:
                 notifylog(
                     "failed to load icon '%s' from default theme: %s",
                     icon_string, e)
             else:
                 data = icon.get_pixels()
                 w = icon.get_width()
                 h = icon.get_height()
                 rowstride = icon.get_rowstride()
                 mode = "RGB"
                 if icon.get_has_alpha():
                     mode = "RGBA"
                 img = Image.frombytes(mode, (w, h), data, "raw", mode,
                                       rowstride)
     if img:
         if w > 256 or h > 256:
             img = img.resize((256, 256), Image.ANTIALIAS)
             w = h = 256
         buf = BytesIOClass()
         img.save(buf, "PNG")
         cpixels = buf.getvalue()
         buf.close()
         return "png", w, h, cpixels
     return ()
Ejemplo n.º 17
0
def NetWMIcons(disp, data):
    icons = []
    stream = BytesIOClass(data)
    while True:
        icon = _read_image(disp, stream)
        if icon is None:
            break
        icons.append(icon)
    if not icons:
        return None
    return icons
Ejemplo n.º 18
0
    def test_webcam(self):
        if not POSIX or OSX:
            return
        from xpra.platform.xposix.webcam import get_virtual_video_devices, check_virtual_dir
        if not check_virtual_dir():
            return
        devices = get_virtual_video_devices()
        if not devices:
            return
        from xpra.server.source.webcam_mixin import WebcamMixin
        wm = WebcamMixin(True, None, ["png", "jpeg"])
        wm.init_state()
        wm.hello_sent = True
        packets = []

        def send(*args):
            packets.append(args)

        #wm.send = send
        wm.send_async = send
        try:
            assert wm.get_info()
            device_id = 0
            w, h = 640, 480
            assert wm.start_virtual_webcam(device_id, w, h)
            assert wm.get_info().get("webcam", {}).get("active-devices",
                                                       0) == 1
            assert len(packets) == 1  #ack sent
            frame_no = 0
            encoding = "png"
            buf = BytesIOClass()
            from PIL import Image
            image = Image.new('RGB', size=(w, h), color=(155, 0, 0))
            image.save(buf, 'jpeg')
            data = buf.getvalue()
            buf.close()
            wm.process_webcam_frame(device_id, frame_no, encoding, w, h, data)
            assert len(packets) == 2  #ack sent
            wm.stop_virtual_webcam(device_id)
        finally:
            wm.cleanup()
Ejemplo n.º 19
0
def NetWMIcons(disp, data):
    icons = []
    stream = BytesIOClass(data)
    while True:
        size_image = _read_image(disp, stream)
        if size_image is None:
            break
        icons.append(size_image)
    if not icons:
        return None
    icons.sort()
    return icons[-1][1]
Ejemplo n.º 20
0
 def nasty_rgb_via_png_paint(self, cairo_format, has_alpha, img_data, x, y,
                             width, height, rowstride, rgb_format):
     log("nasty_rgb_via_png_paint%s",
         (cairo_format, has_alpha, len(img_data), x, y, width, height,
          rowstride, rgb_format))
     #PIL fallback
     PIL = get_codec("PIL")
     if has_alpha:
         oformat = "RGBA"
     else:
         oformat = "RGB"
     #use frombytes rather than frombuffer to be compatible with python3 new-style buffers
     #this is slower, but since this codepath is already dreadfully slow, we don't care
     bdata = strtobytes(memoryview_to_bytes(img_data))
     img = PIL.Image.frombytes(oformat, (width, height), bdata, "raw",
                               rgb_format, rowstride, 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, rowstride)
     # 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)
     self.cairo_paint_surface(img, x, y)
     return True
Ejemplo n.º 21
0
 def nasty_rgb_via_png_paint(self, cairo_format, has_alpha, img_data, x, y, width, height, rowstride, rgb_format):
     log.warn("nasty_rgb_via_png_paint%s",
              (cairo_format, has_alpha, len(img_data), x, y, width, height, rowstride, rgb_format))
     #PIL fallback
     from PIL import Image
     if has_alpha:
         oformat = "RGBA"
     else:
         oformat = "RGB"
     #use frombytes rather than frombuffer to be compatible with python3 new-style buffers
     #this is slower, but since this codepath is already dreadfully slow, we don't care
     bdata = strtobytes(memoryview_to_bytes(img_data))
     src_format = rgb_format.replace("X", "A")
     try:
         img = Image.frombytes(oformat, (width,height), bdata, "raw", src_format, rowstride, 1)
     except ValueError as e:
         log("PIL Image frombytes:", exc_info=True)
         raise Exception("failed to parse raw %s data as %s to %s: %s" % (rgb_format, src_format, oformat, e))
     #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, rowstride)
     # 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)
     self.cairo_paint_surface(img, x, y)
     return True
Ejemplo n.º 22
0
 def command_callback(hwnd, cid):
     if cid == 1024:
         from xpra.platform.win32.win32_balloon import notify
         from xpra.os_util import BytesIOClass
         try:
             from PIL import Image  #@UnresolvedImport
             img = Image.open("icons\\printer.png")
             buf = BytesIOClass()
             img.save(buf, "PNG")
             data = buf.getvalue()
             buf.close()
             icon = (b"png", img.size[0], img.size[1], data)
         except Exception as e:
             print("could not find icon: %s" % (e, ))
             icon = None
         else:
             pass
         notify(hwnd, 0, "hello", "world", timeout=1000, icon=icon)
     elif cid == 1025:
         print("Goodbye")
         DestroyWindow(hwnd)
     else:
         print("OnCommand for ID=%s" % cid)
Ejemplo n.º 23
0
    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, %s bytes,%s,%s,%s,%s,%s,%s)",
            cairo_format, has_alpha, len(img_data), x, y, width, height,
            rowstride, options)
        rgb_format = options.strget("rgb_format", "RGB")
        if _memoryview and isinstance(img_data, _memoryview):
            #Pixbuf cannot use the memoryview directly:
            img_data = img_data.tobytes()
        #"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, rowstride, 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, rowstride)
        # 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)
Ejemplo n.º 24
0
 def ui_paint_image():
     if not self._backing:
         fire_paint_callbacks(callbacks, False)
         return
     try:
         if coding.startswith("png"):
             reader = BytesIOClass(img_data)
             img = cairo.ImageSurface.create_from_png(reader)
             success = self.cairo_paint_surface(img, x, y)
         else:
             assert coding == "jpeg"
             pbl = PixbufLoader()
             pbl.write(img_data)
             pbl.close()
             pixbuf = pbl.get_pixbuf()
             del pbl
             success = self.cairo_paint_pixbuf(pixbuf, x, y)
     except:
         log.error("cairo error during paint", exc_info=True)
         success = False
     fire_paint_callbacks(callbacks, success)
Ejemplo n.º 25
0
def notify(hwnd, app_id, title, message, timeout=5000, icon=None):
    nid = PyNOTIFYICONDATA()
    nid.hWnd = hwnd
    nid.uID = app_id
    nid.uFlags = NIF_INFO
    nid.guidItem = XPRA_GUID_BYTES
    nid.szInfo = chop_string(message, 255, False)        #prevent overflow
    nid.szInfoTitle = chop_string(title, 63)
    if timeout<=0:
        timeout = 5000
    nid.uTimeoutOrVersion = timeout
    #if no icon is supplied, we can use:
    # NIIF_INFO, NIIF_WARNING or NIIF_ERROR
    nid.dwInfoFlags = NIIF_INFO
    if icon:
        try:
            w, h, data = icon[1:4]
            buf = BytesIOClass(data)
            from PIL import Image       #@UnresolvedImport
            from xpra.platform.win32.win32_NotifyIcon import image_to_ICONINFO
            img = Image.open(buf)
            iw = GetSystemMetrics(SM_CXSMICON)
            ih = GetSystemMetrics(SM_CYSMICON)
            if w!=iw or h!=ih:
                img = img.resize((iw, ih), Image.ANTIALIAS)
                log("notification icon resized to %s", img.size)
            hicon = image_to_ICONINFO(img)
            log("notify: image_to_ICONINFO(%s)=%#x", img, hicon)
            nid.hIcon = hicon
            nid.hBalloonIcon = hicon
        except Exception as e:
            log("notify%s", (hwnd, app_id, title, message, timeout, icon), exc_info=True)
            log.error("Error: failed to set notification icon:")
            log.error(" %s", e)
        else:
            nid.dwInfoFlags = NIIF_USER
    Shell_NotifyIcon = windll.shell32.Shell_NotifyIcon
    Shell_NotifyIcon(NIM_MODIFY, nid.pack())
    log("notify using %s", Shell_NotifyIcon)
Ejemplo n.º 26
0
 def do_paint_rgb24(self, img_data, x, y, width, height, rowstride, options, callbacks):
     """ must be called from UI thread """
     if DRAW_DEBUG:
         log.info("cairo_paint_rgb24(..,%s,%s,%s,%s,%s,%s,%s)", x, y, width, height, rowstride, options, callbacks)
     if self._backing is None:
         fire_paint_callbacks(callbacks, False)
         return  False
     PIL = get_codec("PIL")
     assert PIL, "cannot paint without PIL!"
     if rowstride==0:
         rowstride = width * 3
     im = PIL.Image.frombuffer("RGB", (width, height), img_data, "raw", "RGB", rowstride)
     buf = BytesIOClass()
     im.save(buf, "PNG")
     data = buf.getvalue()
     buf.close()
     img_data = BytesIOClass(data)
     self.do_paint_png(img_data, x, y, width, height, rowstride, options, callbacks)
     return  False
Ejemplo n.º 27
0
    def paint_image(self, coding, img_data, x, y, width, height, options, callbacks):
        """ can be called from any thread """
        buf = BytesIOClass(img_data)
        img = Image.open(buf)
        assert img.mode in ("L", "P", "RGB", "RGBA", "RGBX"), "invalid image mode: %s" % img.mode
        transparency = options.intget("transparency", -1)
        if img.mode=="P":
            if transparency>=0:
                #this deals with alpha without any extra work
                img = img.convert("RGBA")
            else:
                img = img.convert("RGB")
        elif img.mode=="L":
            if transparency>=0:
                #why do we have to deal with alpha ourselves??
                def mask_value(a):
                    if a!=transparency:
                        return 255
                    return 0
                mask = Image.eval(img, mask_value)
                mask = mask.convert("L")
                def nomask_value(a):
                    if a!=transparency:
                        return a
                    return 0
                img = Image.eval(img, nomask_value)
                img = img.convert("RGBA")
                img.putalpha(mask)
            else:
                img = img.convert("RGB")

        raw_data = img.tobytes("raw", img.mode)
        paint_options = typedict(options)
        if img.mode=="RGB":
            #PIL flattens the data to a continuous straightforward RGB format:
            rowstride = width*3
            rgb_format = options.strget("rgb_format", "")
            rgb_format = rgb_format.replace("A", "").replace("X", "")
            #the webp encoder only takes BGRX input,
            #so we have to swap things around if it was fed "RGB":
            if rgb_format=="RGB":
                rgb_format = "BGR"
            else:
                rgb_format = "RGB"
            img_data = self.process_delta(raw_data, width, height, rowstride, options)
        elif img.mode in ("RGBA", "RGBX"):
            rowstride = width*4
            rgb_format = options.strget("rgb_format", img.mode)
            if coding=="webp":
                #the webp encoder only takes BGRX input,
                #so we have to swap things around if it was fed "RGBA":
                if rgb_format=="RGBA":
                    rgb_format = "BGRA"
                elif rgb_format=="RGBX":
                    rgb_format = "BGRX"
                elif rgb_format=="BGRA":
                    rgb_format = "RGBA"
                elif rgb_format=="BGRX":
                    rgb_format = "RGBX"
                else:
                    log.warn("Warning: unexpected RGB format '%s'", rgb_format)
            img_data = self.process_delta(raw_data, width, height, rowstride, options)
        else:
            raise Exception("invalid image mode: %s" % img.mode)
        self.idle_add(self.do_paint_rgb, rgb_format, img_data, x, y, width, height, rowstride, paint_options, callbacks)
        return False
Ejemplo n.º 28
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
Ejemplo n.º 29
0
 def paint_pil_image(self, pil_image, width, height, rowstride, options, callbacks):
     buf = BytesIOClass()
     pil_image.save(buf, format="PNG")
     png_data = buf.getvalue()
     buf.close()
     self.idle_add(self.paint_png, png_data, 0, 0, width, height, rowstride, options, callbacks)
Ejemplo n.º 30
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
        w, h, pixel_format, pixel_data = idata
        log("compress_and_send_window_icon() %ix%i in %s format, %i bytes", w, h, pixel_format, len(pixel_data))
        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
        use_png = self.has_png and (must_scale or not self.has_premult or w*h>=MAX_ARGB_PIXELS)
        log("compress_and_send_window_icon: %sx%s (max-size=%s, standard-size=%s), sending as png=%s, pixel_format=%s",
            w, h, self.window_icon_max_size, self.window_icon_size, use_png, pixel_format)
        must_convert = (use_png and pixel_format!="png") or (pixel_format=="png" and not use_png) or (pixel_format=="BGRA" and not self.has_premult)
        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(BytesIOClass(pixel_data))
            else:
                #note: little endian makes this confusing.. RGBA for pillow is BGRA in memory
                if pixel_format=="RGBA":
                    src_format = "BGRA"
                else:
                    src_format = "RGBA"
                image = Image.frombuffer("RGBA", (w,h), memoryview_to_bytes(pixel_data), "raw", src_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 use_png:
            if image:
                #image got converted or scaled, get the new pixel data:
                output = BytesIOClass()
                image.save(output, "png")
                pixel_data = output.getvalue()
                output.close()
                w, h = image.size
            wrapper = compression.Compressed("png", pixel_data)
        else:
            if image:
                pixel_data = image.tobytes("raw", "RGBA")
            wrapper = self.compressed_wrapper("premult_argb32", memoryview_to_bytes(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)
Ejemplo n.º 31
0
def encode(coding, image, quality, speed, supports_transparency):
    log("pillow.encode%s", (coding, image, quality, speed, supports_transparency))
    pixel_format = bytestostr(image.get_pixel_format())
    palette = None
    w = image.get_width()
    h = image.get_height()
    rgb = {
        "RLE8"  : "P",
        "XRGB"  : "RGB",
        "BGRX"  : "RGB",
        "RGBX"  : "RGB",
        "RGBA"  : "RGBA",
        "BGRA"  : "RGBA",
        "BGR"   : "RGB",
        }.get(pixel_format, pixel_format)
    bpp = 32
    #remove transparency if it cannot be handled,
    #and deal with non 24-bit formats:
    try:
        pixels = image.get_pixels()
        assert pixels, "failed to get pixels from %s" % image
        if pixel_format=="r210":
            from xpra.codecs.argb.argb import r210_to_rgba, r210_to_rgb #@UnresolvedImport
            if supports_transparency:
                pixels = r210_to_rgba(pixels)
                pixel_format = "RGBA"
                rgb = "RGBA"
            else:
                image.set_rowstride(image.get_rowstride()*3//4)
                pixels = r210_to_rgb(pixels)
                pixel_format = "RGB"
                rgb = "RGB"
                bpp = 24
        elif pixel_format=="BGR565":
            from xpra.codecs.argb.argb import bgr565_to_rgbx, bgr565_to_rgb    #@UnresolvedImport
            if supports_transparency:
                image.set_rowstride(image.get_rowstride()*2)
                pixels = bgr565_to_rgbx(pixels)
                pixel_format = "RGBA"
                rgb = "RGBA"
            else:
                image.set_rowstride(image.get_rowstride()*3//2)
                pixels = bgr565_to_rgb(pixels)
                pixel_format = "RGB"
                rgb = "RGB"
                bpp = 24
        elif pixel_format=="RLE8":
            pixel_format = "P"
            palette = []
            #pillow requires 8 bit palette values,
            #but we get 16-bit values from the image wrapper (X11 palettes are 16-bit):
            for r, g, b in image.get_palette():
                palette.append((r>>8) & 0xFF)
                palette.append((g>>8) & 0xFF)
                palette.append((b>>8) & 0xFF)
            bpp = 8
        #PIL cannot use the memoryview directly:
        if isinstance(pixels, memoryview):
            pixels = pixels.tobytes()
        #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"
            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
    client_options = {}
    if coding in ("jpeg", "webp", "jpeg2000"):
        #newer versions of pillow require explicit conversion to non-alpha:
        if pixel_format.find("A")>=0:
            im = im.convert("RGB")
        q = int(min(100, max(1, quality)))
        kwargs = im.info
        kwargs["quality"] = q
        client_options["quality"] = q
        if coding=="jpeg2000":
            kwargs["quality_mode"] = "rates"    #other option: "dB"
            kwargs["quality_layers"] = max(1, (100-q)*5)
        elif coding=="jpeg" and speed<50:
            #(optimizing jpeg is pretty cheap and worth doing)
            kwargs["optimize"] = True
            client_options["optimize"] = True
        elif coding=="webp" and q>=100:
            kwargs["lossless"] = 1
        pil_fmt = coding.upper()
    else:
        assert coding in ("png", "png/P", "png/L"), "unsupported 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 = Image.eval(alpha, mask_value)
        else:
            #no transparency
            mask = None
        if coding=="png/L":
            im = im.convert("L", palette=Image.ADAPTIVE, colors=255)
            bpp = 8
        elif coding=="png/P":
            #convert to 255 indexed colour if:
            # * we're not in palette mode yet (source is >8bpp)
            # * we need space for the mask (256 -> 255)
            if palette is None or mask:
                #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=Image.WEB, colors=255)
            bpp = 8
        kwargs = im.info
        if mask:
            # paste the alpha mask to the color of index 255
            im.paste(255, mask)
            client_options["transparency"] = 255
            kwargs["transparency"] = 255
        if 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
        #no need to expose to the client:
        #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"] = Image.DEFAULT_STRATEGY
        pil_fmt = "PNG"
    buf = BytesIOClass()
    im.save(buf, pil_fmt, **kwargs)
    if SAVE_TO_FILE:
        filename = "./%s.%s" % (time.time(), pil_fmt)
        im.save(filename, pil_fmt)
        log.info("saved %s to %s", coding, filename)
    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, Compressed(coding, data), client_options, image.get_width(), image.get_height(), 0, bpp
Ejemplo n.º 32
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)
Ejemplo n.º 33
0
def selftest(_full=False):
    global ENCODINGS
    import binascii
    from xpra.os_util import BytesIOClass
    #test data generated using the encoder:
    for encoding, hexdata in (
        ('png',
         "89504e470d0a1a0a0000000d4948445200000020000000200806000000737a7af40000002849444154785eedd08100000000c3a0f9531fe4855061c0800103060c183060c0800103060cbc0f0c102000013337932a0000000049454e44ae426082"
         ),
        ('png',
         "89504e470d0a1a0a0000000d4948445200000020000000200802000000fc18eda30000002549444154785eedd03101000000c2a0f54fed610d884061c0800103060c183060c080810f0c0c20000174754ae90000000049454e44ae426082"
         ),
        ('png/L',
         "89504e470d0a1a0a0000000d4948445200000020000000200800000000561125280000000274524e5300ff5b9122b50000002049444154785e63fccf801f3011906718550009a1d170180d07e4bc323cd20300a33d013f95f841e70000000049454e44ae426082"
         ),
        ('png/L',
         "89504e470d0a1a0a0000000d4948445200000020000000200800000000561125280000001549444154785e63601805a321301a02a321803d0400042000017854be5c0000000049454e44ae426082"
         ),
        ('png/P',
         "89504e470d0a1a0a0000000d494844520000002000000020080300000044a48ac600000300504c5445000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b330f4880000010074524e53ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0053f707250000001c49444154785e63f84f00308c2a0087c068384012c268388ca87000003f68fc2e077ed1070000000049454e44ae426082"
         ),
        ('png/P',
         "89504e470d0a1a0a0000000d494844520000002000000020080300000044a48ac600000300504c5445000000000000000000000000000000000000000000000000000000000000000000330000660000990000cc0000ff0000003300333300663300993300cc3300ff3300006600336600666600996600cc6600ff6600009900339900669900999900cc9900ff990000cc0033cc0066cc0099cc00cccc00ffcc0000ff0033ff0066ff0099ff00ccff00ffff00000033330033660033990033cc0033ff0033003333333333663333993333cc3333ff3333006633336633666633996633cc6633ff6633009933339933669933999933cc9933ff993300cc3333cc3366cc3399cc33cccc33ffcc3300ff3333ff3366ff3399ff33ccff33ffff33000066330066660066990066cc0066ff0066003366333366663366993366cc3366ff3366006666336666666666996666cc6666ff6666009966339966669966999966cc9966ff996600cc6633cc6666cc6699cc66cccc66ffcc6600ff6633ff6666ff6699ff66ccff66ffff66000099330099660099990099cc0099ff0099003399333399663399993399cc3399ff3399006699336699666699996699cc6699ff6699009999339999669999999999cc9999ff999900cc9933cc9966cc9999cc99cccc99ffcc9900ff9933ff9966ff9999ff99ccff99ffff990000cc3300cc6600cc9900cccc00ccff00cc0033cc3333cc6633cc9933cccc33ccff33cc0066cc3366cc6666cc9966cccc66ccff66cc0099cc3399cc6699cc9999cccc99ccff99cc00cccc33cccc66cccc99ccccccccccffcccc00ffcc33ffcc66ffcc99ffccccffccffffcc0000ff3300ff6600ff9900ffcc00ffff00ff0033ff3333ff6633ff9933ffcc33ffff33ff0066ff3366ff6666ff9966ffcc66ffff66ff0099ff3399ff6699ff9999ffcc99ffff99ff00ccff33ccff66ccff99ccffccccffffccff00ffff33ffff66ffff99ffffccffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023faca40000001549444154785e63601805a321301a02a321803d0400042000017854be5c0000000049454e44ae426082"
         ),
        ('jpeg',
         "ffd8ffe000104a46494600010100000100010000ffdb004300100b0c0e0c0a100e0d0e1211101318281a181616183123251d283a333d3c3933383740485c4e404457453738506d51575f626768673e4d71797064785c656763ffdb0043011112121815182f1a1a2f634238426363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363ffc00011080020002003012200021101031101ffc4001500010100000000000000000000000000000007ffc40014100100000000000000000000000000000000ffc40014010100000000000000000000000000000000ffc40014110100000000000000000000000000000000ffda000c03010002110311003f009f800000000000ffd9"
         ),
        ('jpeg',
         "ffd8ffe000104a46494600010100000100010000ffdb004300100b0c0e0c0a100e0d0e1211101318281a181616183123251d283a333d3c3933383740485c4e404457453738506d51575f626768673e4d71797064785c656763ffdb0043011112121815182f1a1a2f634238426363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363ffc00011080020002003012200021101031101ffc4001500010100000000000000000000000000000007ffc40014100100000000000000000000000000000000ffc40014010100000000000000000000000000000000ffc40014110100000000000000000000000000000000ffda000c03010002110311003f009f800000000000ffd9"
         ),
        ('webp',
         "524946465c00000057454250565038580a000000100000001f00001f0000414c50480f00000001071011110012c2ffef7a44ff530f005650382026000000d002009d012a200020003ed162aa4fa825a3a2280801001a096900003da3a000fef39d800000"
         ),
        ('webp',
         "524946465c00000057454250565038580a000000100000001f00001f0000414c50480f00000001071011110012c2ffef7a44ff530f005650382026000000d002009d012a200020003ed162aa4fa825a3a2280801001a096900003da3a000fef39d800000"
         ),
        ('jpeg2000',
         "0000000c6a5020200d0a870a00000014667479706a703220000000006a7032200000002d6a703268000000166968647200000020000000200003070700000000000f636f6c720100000000001000000d8b6a703263ff4fff51002f000000000020000000200000000000000000000000200000002000000000000000000003070101070101070101ff52000c00000001000504040001ff5c00134040484850484850484850484850484850ff640025000143726561746564206279204f70656e4a5045472076657273696f6e20322e332e30ff90000a000000000d040001ff93c7d4040977c1f20106c7d404049fc07c20c0f900c07c8040080107c03a0c010c0084070707c0f90140f900c03e1040057f0305c1f38483e70907d40a0d5baab30c1897bf0b2ccdf607c03a141f38487da0a003cf0e13572c01e2b616dfc1f38483e70907d40a0d6a9ce90c459c710ff00205bfc3ea1187d41d0fb44411e2c3e4379101f73b3459b6a61066ff7f07649d884d89d90068baf1b3327f143c7a1cb72bb9df9494fa4cd8b4aec09fc7da211f68943ed1300a1613b5a6ecf3266bc9eaa4aab16e2717f77070d476d1ec90c7455f4761dcf465f302db51faec9e1b5c1cc854f250334e36e95a9fc3ea108fb44a1f689012abc11315c3e36f7995c0c44a9f3a5917df22504861216db8faca9431194af1b32b16847435f00b2817291621b99fcd79e5cce3c7da7b1f69fc7e08a026d03038ccfa865709f8fa0ebea3622eaf4f68a3672f799b0c95f3f93d793feb3ec88a21c6faa1ad7d154b21e40d1788a2442808f0594ef8d945d2f5fd17f95dc4129d3aad41246afaff2b5b61dbcd75a8f85056654a1f8eb6276d776c82321ab42845d07fa64283529ca80c46189524f52f8dd6479ceabeb07916d90a1740e9c1455434770c78797532824a27bb3df6265724c4e34a5d1c7ef87b2810eebe19104476c5b1abc42d1f9c52e4ac8902cba5a676f57293bc7119de8d7f6f821b1edfc7da873f0448fc1240173329ccc8cc1e00bf57e27208b6c7d1143b43bd351840f54f08f51bdace2d423e66b6f24cf61fae9a247158f196c44b41eb2a8f9f1f960cd4f4bd7cbf4f2be8992c7f48a83e2c6f182eff5a602c57ce86daceec1bd379018825ec2264078e7dc7f7f6a496115619cde81b18a829e8df8a5b5e188a0e7f23c4fc728edcc78cf77ba2b5c0df967f302adc347b999d512564cebd7d89c66fc579a8dd05547a181e4be9079acb1d9aa0b160981da8d3c38f451fbdfc217689831681d206d039c4a36f7909fb1d11d7e978215cb03a9c0e3bc7da8b3f0449f9a580198f765dce94872bb3e08a4fc1c743931627796b4efc0012ae795767e9e08e6f4a4f16bca96d0581cb5e09f10e068ed53c26297d927cc57d03c38beb213b0ea513e82b4caf48ac318401d37f34e79411c323bbb89509dfe4b4e0aa036d87a46dff1e529788bc2048fc28907f04a1d85024d15a22e1fa3ecb1d4d5264f7d72f4fa7d51851074efb387f0760929f7134e14472776446154ac013368e3711f63d6392f856197dcc74af2cf37241e89a893c8a0e6704903c92f86f4f9a83fde7d961d1505efb7f589c06c46d06d1fb4b6cded97310dfcfc2fe9f8682cfceff0010e715ca18e9343f215b1b4f38cd8c23c020f122af4cc1b9d265644f425d9279a479e9cdea4eb33864945cca8bc7148026429e977a508a39bf44f0ea5e1ba474ad9131083f77af6394e471ff83e77c3465f8a37c98680fd9443662d3ded723aa65c3bf8f6f4fff045b1ef10e68b5f53b78ef6d863aded77d5ce9fc5db54b37b2a4e22bb8aefc776149ddd58b0292e9dccca564ac2cbe802b1ff5c6530c243fa50fa4043431f982604881dc430471d64a4a247d4cf58d10a1b8422509205df5a087468f25c6798329961a9ad22f1ed39782673c4c6ddd2f12276ee794063a73d8208c92708b37a5c00e64be8ee21c639511854f24636ea5d7896e6de25b81ab6e2ba7e3d494b2f1f6c812143a51c3bcd60e3665cab9316af52befe64f6040d0d6a3254aa6232d3e085260a1ff650110ca5e13ea208e46e1c18a0f1e8ecd702bb7400687b0b9a64b52b63ee8653fc2681eba192ec26449c155f4c0bc45340341f0552ffe49e9ff2743ee6b3c81c5e79d91e71af4b6e1be023879e6d294fe45e20a7b06a1c2c4d79b3974a9a02347d55a2bd6006c6eb5627af3105cbf106f1aaf75efb9d75f7102339b1f7e67929cfe7c94fcbff4b603b347cd61babe2af40ee461416152f1704152af09f0a2a32e6c21e43ad0eb8e3917d776687a80ac0ad691c6f45459d800ca65dc0cbe187fc0ad61d33a0d90404a6491ecb77a903d90f35135211934a060c11096eea69b989383ed43f71834dab91fda63a8dccf9b39a85d5df94a59b6a88bde98f95ca7a037a46023efb85e2230cd5d4ee126038cd43f26f4199532e48eaf6fb88a2ea710e9ac84c8631aa5ae3132c23efb223b77552b7fbac317b472744c1bea68f8e8963bf20a4341383a2eceff6186eac2bd07a6cd7ecbed28557288fdad0d2f72172ce71237166b9d1b6fc692bafce66ed62754850df9210de6b558eb5ed329ae1ae8c1d83c2a7abeeb4861593cb9aa60c3e685c41716c63ee9dcc197078446346440f1bd385df84935829bf5554afda187344c3c1f4d214479384651ea15c1eb08dfaf52941d61a39fc8755b35b8eea0f220467ab36b9c1fcfc2fa9f8687c7e1a1203625a30755cc1c5536012034f6376b5a5315f867c613c12ada7fb95bdb6b93f41a727b238d473c64060f59e99f81927422f7627504e02f34b17b2ca62fa85e5c17a87726d0a91996e5aeebc4224f26bdbdeaf35da463ebc009dc29ee8fb9d003b6282f515241df887c2a3df30ee0332ec78f672ca29a5f6893bb051b86763ae80214f2e1f3a1765380691d9836a8f8ab1e7c735fcc55a15d405f6eee202ec968f3c9bcc98cee2df70d509ab1e54578a79ef982fbfaeb5d2a6c1e060a1f5221c5ce7bf26bc00b5da7a1bcd34638dad21b0df045591f5090bd74fb2dff3b4e95b91091b573ddb5baa67ce3bd1dd0cd3330cea9ca65a0180633cdf31d2b721c22789aa7005af0db25e627bbb9b6662f2f54b3715cd1059f210d33a9711ce012596c3d7b47322a14577ffcdbd0ddf9a67729ba4665fd9e788b224dfabd912fde48c074d2326bdbc749018afbf777b9898a4e7c60901f2d288aa6e6a8b6073afd740cc98e50d9e8070f1afb0ca7657e17b653f8f3725e1f0b2a48f4572d6f589a11ea8aba88bbf7ec76ce923040aab560a78b61237ee74b17691e5690b45478229f9bfbca60f4928c6e255933e8c934fc68e4c8d087812b9fa4a63d0b44a124babc5d3646e6dc5eafb08452e359d369936a0c8eb75d26ff768a79cdfc4c6eac67544f9dc80fd2902ba04db49e5463b4c9b73efefaa3d7ce4c8963e514894d5d4b00760baba04bed35fe1c7362523104ccff67bef0b755a482f8d2c2812f1b1ee3cb10511f4f83e645dd5963c78f460064e60a326dc2f850cc1d326d9694d4ce99b0f0929dc9b11c8be766168b4b3aad54ee9fbbc90bcddfe54fcac6b6cc97eeaf43b49ddd17957ba002f2e6e55a1060cc89948c4b23d0ceaa7052210f61bca27186efd57283f47ac149e05b75329e304423f44836c12fa401bbd5ea84e7b1f5cba33f29763a3b1a91991d8ca42b8f3ef1379decaa917dc854cd58884d22f92fdaa37873420b6b8d1a02c8810dc17fe26081b3896bed67fc7fa5729d3d1c0342eb36e7087a211636aca5faf7cd6d0550bc2102bf6a712edc9bbe278e87323f54132383b6bd07a1da98ec9dcef396dc31e941425acbcfc341a7e1a313f3bfc0168671ec6f790cb71a9be74053f6b01518c63cb8379070ac84a10e0f0116a5d4e665526a1f36ab615359b64e5c91bb4d5027cd64d949f8b93f837b5edd8a4cb8257e3f3313fb0c114ac0db7e3a0d2d320352ae2d041843311e3684e5a6c4e4dd21dbb71547d082fd92be477ddb4c0183b93105a0da75a5ba61e2fa9ba4c75338ff1d27dd045b55d399fc86c2237a3028a33d1efba6b3f29566282cc75c5ac3dfa61e2d0b802e538906da154fa172130ccbe2ea11ea903c6e0b332866728b6a67f9f671693720fd88bf80f9f6f31644beb4c7409461053743a6ccd27cf8ab8e5052d9e017fe6fe793b67a96480cb70cfbb6a8f1c73ae0fb48040117dd2bf14e8faaed6e58fdbf00fe36df559074d73cb52c62ae894025a7247426317ea1e1522f9729838b8997ee26210e047d7658c35e58f863381f229265c8b2f8db03b11333b67c3b0b3e1665080d2c0943bae79d107c63f8516933ff6c5d45b3881fff16afa9585f5d0b1d834766a7a2cb3e474cab7782d2b687941a8ae33f3133af0b741f30598dca4e9384bad8c0848c5031d4cc219db1051a10f0273cadd88f37bb772babf5f44556e3c07e8282e65a48ce52f19986df89424ce1cb6759af2c53ed9bf05f1f8b5aca8f12d2f10334a063ef5fc88a45ef1f0a75ae85d6a17bacf5a2f221b4cf51eb5f92af0ca2610c687f127cae994072b63bb9a638b7816c9c7c1055e7f5ccbbb1311681dd7c79191325cf566f164cbb5d20a03b4e7a43f82db50faa8cfa0c3c56b98f4c83e4b4f35d172124e7fd1501eaf127b1f7b1ba9a71c03c56efc3e764868ccac0c45ad8782faac9fcd4bedba8350526e4609f7de69b06722f3ec35a29b23041714c0879a3591afe52c548d8201cfd92a555944f26e33c9938eb808241cb0091af358b41f7fd1347e4d618e9f759b6f51f646c6b5dbb1a98e3a0e0df7b11861fa438ed6c2a52d527b0a651491bd2cb9e170f8d5d1086ac7dc893bf6548ef4fc739457aa5de09a77e9261e8862c816c2f69f6ccb578532c314f97eb66e1779cda7f22c1caa0b2a54ec8fd6588cd9edd06ef06ce0b0806e35a88e92bdb9abc65f61024a48e06c8266fd42837fbdf00ebf3b5ede43b6391bfffd9"
         ),
        ('jpeg2000',
         "0000000c6a5020200d0a870a00000014667479706a703220000000006a7032200000002d6a703268000000166968647200000020000000200003070700000000000f636f6c720100000000001000000d8b6a703263ff4fff51002f000000000020000000200000000000000000000000200000002000000000000000000003070101070101070101ff52000c00000001000504040001ff5c00134040484850484850484850484850484850ff640025000143726561746564206279204f70656e4a5045472076657273696f6e20322e332e30ff90000a000000000d040001ff93c7d4040977c1f20106c7d404049fc07c20c0f900c07c8040080107c03a0c010c0084070707c0f90140f900c03e1040057f0305c1f38483e70907d40a0d5baab30c1897bf0b2ccdf607c03a141f38487da0a003cf0e13572c01e2b616dfc1f38483e70907d40a0d6a9ce90c459c710ff00205bfc3ea1187d41d0fb44411e2c3e4379101f73b3459b6a61066ff7f07649d884d89d90068baf1b3327f143c7a1cb72bb9df9494fa4cd8b4aec09fc7da211f68943ed1300a1613b5a6ecf3266bc9eaa4aab16e2717f77070d476d1ec90c7455f4761dcf465f302db51faec9e1b5c1cc854f250334e36e95a9fc3ea108fb44a1f689012abc11315c3e36f7995c0c44a9f3a5917df22504861216db8faca9431194af1b32b16847435f00b2817291621b99fcd79e5cce3c7da7b1f69fc7e08a026d03038ccfa865709f8fa0ebea3622eaf4f68a3672f799b0c95f3f93d793feb3ec88a21c6faa1ad7d154b21e40d1788a2442808f0594ef8d945d2f5fd17f95dc4129d3aad41246afaff2b5b61dbcd75a8f85056654a1f8eb6276d776c82321ab42845d07fa64283529ca80c46189524f52f8dd6479ceabeb07916d90a1740e9c1455434770c78797532824a27bb3df6265724c4e34a5d1c7ef87b2810eebe19104476c5b1abc42d1f9c52e4ac8902cba5a676f57293bc7119de8d7f6f821b1edfc7da873f0448fc1240173329ccc8cc1e00bf57e27208b6c7d1143b43bd351840f54f08f51bdace2d423e66b6f24cf61fae9a247158f196c44b41eb2a8f9f1f960cd4f4bd7cbf4f2be8992c7f48a83e2c6f182eff5a602c57ce86daceec1bd379018825ec2264078e7dc7f7f6a496115619cde81b18a829e8df8a5b5e188a0e7f23c4fc728edcc78cf77ba2b5c0df967f302adc347b999d512564cebd7d89c66fc579a8dd05547a181e4be9079acb1d9aa0b160981da8d3c38f451fbdfc217689831681d206d039c4a36f7909fb1d11d7e978215cb03a9c0e3bc7da8b3f0449f9a580198f765dce94872bb3e08a4fc1c743931627796b4efc0012ae795767e9e08e6f4a4f16bca96d0581cb5e09f10e068ed53c26297d927cc57d03c38beb213b0ea513e82b4caf48ac318401d37f34e79411c323bbb89509dfe4b4e0aa036d87a46dff1e529788bc2048fc28907f04a1d85024d15a22e1fa3ecb1d4d5264f7d72f4fa7d51851074efb387f0760929f7134e14472776446154ac013368e3711f63d6392f856197dcc74af2cf37241e89a893c8a0e6704903c92f86f4f9a83fde7d961d1505efb7f589c06c46d06d1fb4b6cded97310dfcfc2fe9f8682cfceff0010e715ca18e9343f215b1b4f38cd8c23c020f122af4cc1b9d265644f425d9279a479e9cdea4eb33864945cca8bc7148026429e977a508a39bf44f0ea5e1ba474ad9131083f77af6394e471ff83e77c3465f8a37c98680fd9443662d3ded723aa65c3bf8f6f4fff045b1ef10e68b5f53b78ef6d863aded77d5ce9fc5db54b37b2a4e22bb8aefc776149ddd58b0292e9dccca564ac2cbe802b1ff5c6530c243fa50fa4043431f982604881dc430471d64a4a247d4cf58d10a1b8422509205df5a087468f25c6798329961a9ad22f1ed39782673c4c6ddd2f12276ee794063a73d8208c92708b37a5c00e64be8ee21c639511854f24636ea5d7896e6de25b81ab6e2ba7e3d494b2f1f6c812143a51c3bcd60e3665cab9316af52befe64f6040d0d6a3254aa6232d3e085260a1ff650110ca5e13ea208e46e1c18a0f1e8ecd702bb7400687b0b9a64b52b63ee8653fc2681eba192ec26449c155f4c0bc45340341f0552ffe49e9ff2743ee6b3c81c5e79d91e71af4b6e1be023879e6d294fe45e20a7b06a1c2c4d79b3974a9a02347d55a2bd6006c6eb5627af3105cbf106f1aaf75efb9d75f7102339b1f7e67929cfe7c94fcbff4b603b347cd61babe2af40ee461416152f1704152af09f0a2a32e6c21e43ad0eb8e3917d776687a80ac0ad691c6f45459d800ca65dc0cbe187fc0ad61d33a0d90404a6491ecb77a903d90f35135211934a060c11096eea69b989383ed43f71834dab91fda63a8dccf9b39a85d5df94a59b6a88bde98f95ca7a037a46023efb85e2230cd5d4ee126038cd43f26f4199532e48eaf6fb88a2ea710e9ac84c8631aa5ae3132c23efb223b77552b7fbac317b472744c1bea68f8e8963bf20a4341383a2eceff6186eac2bd07a6cd7ecbed28557288fdad0d2f72172ce71237166b9d1b6fc692bafce66ed62754850df9210de6b558eb5ed329ae1ae8c1d83c2a7abeeb4861593cb9aa60c3e685c41716c63ee9dcc197078446346440f1bd385df84935829bf5554afda187344c3c1f4d214479384651ea15c1eb08dfaf52941d61a39fc8755b35b8eea0f220467ab36b9c1fcfc2fa9f8687c7e1a1203625a30755cc1c5536012034f6376b5a5315f867c613c12ada7fb95bdb6b93f41a727b238d473c64060f59e99f81927422f7627504e02f34b17b2ca62fa85e5c17a87726d0a91996e5aeebc4224f26bdbdeaf35da463ebc009dc29ee8fb9d003b6282f515241df887c2a3df30ee0332ec78f672ca29a5f6893bb051b86763ae80214f2e1f3a1765380691d9836a8f8ab1e7c735fcc55a15d405f6eee202ec968f3c9bcc98cee2df70d509ab1e54578a79ef982fbfaeb5d2a6c1e060a1f5221c5ce7bf26bc00b5da7a1bcd34638dad21b0df045591f5090bd74fb2dff3b4e95b91091b573ddb5baa67ce3bd1dd0cd3330cea9ca65a0180633cdf31d2b721c22789aa7005af0db25e627bbb9b6662f2f54b3715cd1059f210d33a9711ce012596c3d7b47322a14577ffcdbd0ddf9a67729ba4665fd9e788b224dfabd912fde48c074d2326bdbc749018afbf777b9898a4e7c60901f2d288aa6e6a8b6073afd740cc98e50d9e8070f1afb0ca7657e17b653f8f3725e1f0b2a48f4572d6f589a11ea8aba88bbf7ec76ce923040aab560a78b61237ee74b17691e5690b45478229f9bfbca60f4928c6e255933e8c934fc68e4c8d087812b9fa4a63d0b44a124babc5d3646e6dc5eafb08452e359d369936a0c8eb75d26ff768a79cdfc4c6eac67544f9dc80fd2902ba04db49e5463b4c9b73efefaa3d7ce4c8963e514894d5d4b00760baba04bed35fe1c7362523104ccff67bef0b755a482f8d2c2812f1b1ee3cb10511f4f83e645dd5963c78f460064e60a326dc2f850cc1d326d9694d4ce99b0f0929dc9b11c8be766168b4b3aad54ee9fbbc90bcddfe54fcac6b6cc97eeaf43b49ddd17957ba002f2e6e55a1060cc89948c4b23d0ceaa7052210f61bca27186efd57283f47ac149e05b75329e304423f44836c12fa401bbd5ea84e7b1f5cba33f29763a3b1a91991d8ca42b8f3ef1379decaa917dc854cd58884d22f92fdaa37873420b6b8d1a02c8810dc17fe26081b3896bed67fc7fa5729d3d1c0342eb36e7087a211636aca5faf7cd6d0550bc2102bf6a712edc9bbe278e87323f54132383b6bd07a1da98ec9dcef396dc31e941425acbcfc341a7e1a313f3bfc0168671ec6f790cb71a9be74053f6b01518c63cb8379070ac84a10e0f0116a5d4e665526a1f36ab615359b64e5c91bb4d5027cd64d949f8b93f837b5edd8a4cb8257e3f3313fb0c114ac0db7e3a0d2d320352ae2d041843311e3684e5a6c4e4dd21dbb71547d082fd92be477ddb4c0183b93105a0da75a5ba61e2fa9ba4c75338ff1d27dd045b55d399fc86c2237a3028a33d1efba6b3f29566282cc75c5ac3dfa61e2d0b802e538906da154fa172130ccbe2ea11ea903c6e0b332866728b6a67f9f671693720fd88bf80f9f6f31644beb4c7409461053743a6ccd27cf8ab8e5052d9e017fe6fe793b67a96480cb70cfbb6a8f1c73ae0fb48040117dd2bf14e8faaed6e58fdbf00fe36df559074d73cb52c62ae894025a7247426317ea1e1522f9729838b8997ee26210e047d7658c35e58f863381f229265c8b2f8db03b11333b67c3b0b3e1665080d2c0943bae79d107c63f8516933ff6c5d45b3881fff16afa9585f5d0b1d834766a7a2cb3e474cab7782d2b687941a8ae33f3133af0b741f30598dca4e9384bad8c0848c5031d4cc219db1051a10f0273cadd88f37bb772babf5f44556e3c07e8282e65a48ce52f19986df89424ce1cb6759af2c53ed9bf05f1f8b5aca8f12d2f10334a063ef5fc88a45ef1f0a75ae85d6a17bacf5a2f221b4cf51eb5f92af0ca2610c687f127cae994072b63bb9a638b7816c9c7c1055e7f5ccbbb1311681dd7c79191325cf566f164cbb5d20a03b4e7a43f82db50faa8cfa0c3c56b98f4c83e4b4f35d172124e7fd1501eaf127b1f7b1ba9a71c03c56efc3e764868ccac0c45ad8782faac9fcd4bedba8350526e4609f7de69b06722f3ec35a29b23041714c0879a3591afe52c548d8201cfd92a555944f26e33c9938eb808241cb0091af358b41f7fd1347e4d618e9f759b6f51f646c6b5dbb1a98e3a0e0df7b11861fa438ed6c2a52d527b0a651491bd2cb9e170f8d5d1086ac7dc893bf6548ef4fc739457aa5de09a77e9261e8862c816c2f69f6ccb578532c314f97eb66e1779cda7f22c1caa0b2a54ec8fd6588cd9edd06ef06ce0b0806e35a88e92bdb9abc65f61024a48e06c8266fd42837fbdf00ebf3b5ede43b6391bfffd9"
         ),
    ):
        if encoding not in ENCODINGS:
            #removed already
            continue
        try:
            cdata = binascii.unhexlify(hexdata)
            buf = BytesIOClass(cdata)
            img = PIL.Image.open(buf)
            assert img, "failed to open image data"
            raw_data = img.tobytes("raw", img.mode)
            assert raw_data
            #now try with junk:
            cdata = binascii.unhexlify("ABCD" + hexdata)
            buf = BytesIOClass(cdata)
            try:
                img = PIL.Image.open(buf)
                log.warn(
                    "Pillow failed to generate an error parsing invalid input")
            except Exception as e:
                log("correctly raised exception for invalid input: %s", e)
        except Exception as e:
            try:
                #py2k:
                datainfo = cdata.encode("string_escape")
            except:
                try:
                    datainfo = cdata.encode("unicode_escape").decode()
                except:
                    datainfo = str(hexdata)
            if encoding == u"jpeg2000":
                l = log.debug
            else:
                l = log.warn
            l("Pillow error decoding %s with data=%s..", encoding,
              datainfo[:16])
            l(" %s", e, exc_info=True)
            ENCODINGS.remove(encoding)
Ejemplo n.º 34
0
 def make_cursor(self, cursor_data):
     #if present, try cursor ny name:
     display = display_get_default()
     cursorlog(
         "make_cursor: has-name=%s, has-cursor-types=%s, xscale=%s, yscale=%s, USE_LOCAL_CURSORS=%s",
         len(cursor_data) >= 10, bool(cursor_types), self.xscale,
         self.yscale, USE_LOCAL_CURSORS)
     #named cursors cannot be scaled (round to 10 to compare so 0.95 and 1.05 are considered the same as 1.0, no scaling):
     if len(cursor_data) >= 10 and cursor_types and iround(
             self.xscale * 10) == 10 and iround(self.yscale * 10) == 10:
         cursor_name = bytestostr(cursor_data[9])
         if cursor_name and USE_LOCAL_CURSORS:
             gdk_cursor = cursor_types.get(cursor_name.upper())
             if gdk_cursor is not None:
                 cursorlog("setting new cursor by name: %s=%s", cursor_name,
                           gdk_cursor)
                 return new_Cursor_for_display(display, gdk_cursor)
             else:
                 global missing_cursor_names
                 if cursor_name not in missing_cursor_names:
                     cursorlog("cursor name '%s' not found", cursor_name)
                     missing_cursor_names.add(cursor_name)
     #create cursor from the pixel data:
     encoding, _, _, w, h, xhot, yhot, serial, pixels = cursor_data[0:9]
     if encoding == b"png":
         from xpra.os_util import BytesIOClass
         from PIL import Image
         buf = BytesIOClass(pixels)
         img = Image.open(buf)
         pixels = img.tobytes("raw", "BGRA")
         cursorlog("used PIL to convert png cursor to raw")
     elif encoding != b"raw":
         cursorlog.warn("Warning: invalid cursor encoding: %s", encoding)
         return None
     if len(pixels) < w * h * 4:
         import binascii
         cursorlog.warn(
             "not enough pixels provided in cursor data: %s needed and only %s bytes found (%s)",
             w * h * 4, len(pixels),
             binascii.hexlify(pixels)[:100])
         return None
     pixbuf = get_pixbuf_from_data(pixels, True, w, h, w * 4)
     x = max(0, min(xhot, w - 1))
     y = max(0, min(yhot, h - 1))
     csize = display.get_default_cursor_size()
     cmaxw, cmaxh = display.get_maximal_cursor_size()
     if len(cursor_data) >= 12:
         ssize = cursor_data[10]
         smax = cursor_data[11]
         cursorlog("server cursor sizes: default=%s, max=%s", ssize, smax)
     cursorlog(
         "new %s cursor at %s,%s with serial=%s, dimensions: %sx%s, len(pixels)=%s, default cursor size is %s, maximum=%s",
         encoding, xhot, yhot, serial, w, h, len(pixels), csize,
         (cmaxw, cmaxh))
     fw, fh = get_fixed_cursor_size()
     if fw > 0 and fh > 0 and (w != fw or h != fh):
         #OS wants a fixed cursor size! (win32 does, and GTK doesn't do this for us)
         if w <= fw and h <= fh:
             cursorlog(
                 "pasting cursor of size %ix%i onto clear pixbuf of size %ix%i",
                 w, h, fw, fh)
             cursor_pixbuf = get_pixbuf_from_data("\0" * fw * fh * 4, True,
                                                  fw, fh, fw * 4)
             pixbuf.copy_area(0, 0, w, h, cursor_pixbuf, 0, 0)
         else:
             cursorlog("scaling cursor from %ix%i to fixed OS size %ix%i",
                       w, h, fw, fh)
             cursor_pixbuf = pixbuf.scale_simple(fw, fh, INTERP_BILINEAR)
             xratio, yratio = float(w) / fw, float(h) / fh
             x, y = iround(x / xratio), iround(y / yratio)
     else:
         sx, sy, sw, sh = x, y, w, h
         #scale the cursors:
         if self.xscale != 1 or self.yscale != 1:
             sx, sy, sw, sh = self.srect(x, y, w, h)
         sw = max(1, sw)
         sh = max(1, sh)
         #ensure we honour the max size if there is one:
         if (cmaxw > 0 and sw > cmaxw) or (cmaxh > 0 and sh > cmaxh):
             ratio = 1.0
             if cmaxw > 0:
                 ratio = max(ratio, float(w) / cmaxw)
             if cmaxh > 0:
                 ratio = max(ratio, float(h) / cmaxh)
             cursorlog("clamping cursor size to %ix%i using ratio=%s",
                       cmaxw, cmaxh, ratio)
             sx, sy, sw, sh = iround(x / ratio), iround(y / ratio), min(
                 cmaxw, iround(w / ratio)), min(cmaxh, iround(h / ratio))
         if sw != w or sh != h:
             cursorlog(
                 "scaling cursor from %ix%i hotspot at %ix%i to %ix%i hotspot at %ix%i",
                 w, h, x, y, sw, sh, sx, sy)
             cursor_pixbuf = pixbuf.scale_simple(sw, sh, INTERP_BILINEAR)
             x, y = sx, sy
         else:
             cursor_pixbuf = pixbuf
     #clamp to pixbuf size:
     w = cursor_pixbuf.get_width()
     h = cursor_pixbuf.get_height()
     x = max(0, min(x, w - 1))
     y = max(0, min(y, h - 1))
     try:
         c = new_Cursor_from_pixbuf(display, cursor_pixbuf, x, y)
     except RuntimeError as e:
         log.error("Error: failed to create cursor:")
         log.error(" %s", e)
         log.error(" using %s of size %ix%i with hotspot at %ix%i",
                   cursor_pixbuf, w, h, x, y)
         c = None
     return c