Exemple #1
0
 def get_image(self, x=0, y=0, width=0, height=0):
     start = time.time()
     metrics = get_virtualscreenmetrics()
     if self.metrics is None or self.metrics != metrics:
         #new metrics, start from scratch:
         self.metrics = metrics
         self.clean()
     log("get_image%s metrics=%s", (x, y, width, height), metrics)
     dx, dy, dw, dh = metrics
     if width == 0:
         width = dw
     if height == 0:
         height = dh
     #clamp rectangle requested to the virtual desktop size:
     if x < dx:
         width -= x - dx
         x = dx
     if y < dy:
         height -= y - dy
         y = dy
     if width > dw:
         width = dw
     if height > dh:
         height = dh
     if not self.dc:
         self.wnd = GetDesktopWindow()
         self.dc = GetWindowDC(self.wnd)
         assert self.dc, "failed to get a drawing context from the desktop window %s" % self.wnd
         self.bit_depth = GetDeviceCaps(self.dc, win32con.BITSPIXEL)
         self.memdc = CreateCompatibleDC(self.dc)
         assert self.memdc, "failed to get a compatible drawing context from %s" % self.dc
     bitmap = CreateCompatibleBitmap(self.dc, width, height)
     assert bitmap, "failed to get a compatible bitmap from %s" % self.dc
     r = SelectObject(self.memdc, bitmap)
     if r == 0:
         log.error("Error: cannot select bitmap object")
         return None
     select_time = time.time()
     log("get_image up to SelectObject (%s) took %ims",
         REGION_CONSTS.get(r, r), (select_time - start) * 1000)
     try:
         if BitBlt(self.memdc, 0, 0, width, height, self.dc, x, y,
                   win32con.SRCCOPY) == 0:
             e = ctypes.get_last_error()
             #rate limit the error message:
             now = time.time()
             if now - self.bitblt_err_time > 10:
                 log.error("Error: failed to blit the screen, error %i", e)
                 self.bitblt_err_time = now
             return None
     except Exception as e:
         log("BitBlt error", exc_info=True)
         log.error("Error: cannot capture screen")
         log.error(" %s", e)
         return None
     bitblt_time = time.time()
     log("get_image BitBlt took %ims", (bitblt_time - select_time) * 1000)
     rowstride = roundup(width * self.bit_depth // 8, 2)
     buf_size = rowstride * height
     pixels = ctypes.create_string_buffer(b"", buf_size)
     log("GetBitmapBits(%#x, %#x, %#x)", bitmap, buf_size,
         ctypes.addressof(pixels))
     r = GetBitmapBits(bitmap, buf_size, ctypes.byref(pixels))
     if r == 0:
         log.error("Error: failed to copy screen bitmap data")
         return None
     if r != buf_size:
         log.warn(
             "Warning: truncating pixel buffer, got %i bytes but expected %i",
             r, buf_size)
         pixels = pixels[:r]
     log("get_image GetBitmapBits took %ims",
         (time.time() - bitblt_time) * 1000)
     DeleteObject(bitmap)
     assert pixels, "no pixels returned from GetBitmapBits"
     if self.bit_depth == 32:
         rgb_format = "BGRX"
     elif self.bit_depth == 30:
         rgb_format = "r210"
     elif self.bit_depth == 24:
         rgb_format = "BGR"
     elif self.bit_depth == 16:
         rgb_format = "BGR565"
     elif self.bit_depth == 8:
         rgb_format = "RLE8"
     else:
         raise Exception("unsupported bit depth: %s" % self.bit_depth)
     bpp = self.bit_depth // 8
     v = ImageWrapper(0,
                      0,
                      width,
                      height,
                      pixels,
                      rgb_format,
                      self.bit_depth,
                      rowstride,
                      bpp,
                      planes=ImageWrapper.PACKED,
                      thread_safe=True)
     if self.bit_depth == 8:
         count = GetSystemPaletteEntries(self.dc, 0, 0, None)
         log("palette size: %s", count)
         palette = []
         if count > 0:
             buf = (PALETTEENTRY * count)()
             r = GetSystemPaletteEntries(self.dc, 0, count,
                                         ctypes.byref(buf))
             #we expect 16-bit values, so bit-shift them:
             for p in buf:
                 palette.append(
                     (p.peRed << 8, p.peGreen << 8, p.peBlue << 8))
         v.set_palette(palette)
     log("get_image%s=%s took %ims", (x, y, width, height), v,
         (time.time() - start) * 1000)
     return v
Exemple #2
0
def get_cursor_data(hCursor):
    #w, h = get_fixed_cursor_size()
    if not hCursor:
        return None
    x, y = 0, 0
    dc = None
    memdc = None
    bitmap = None
    old_handle = None
    pixels = None
    try:
        ii = ICONINFO()
        ii.cbSize = sizeof(ICONINFO)
        if not GetIconInfo(hCursor, byref(ii)):
            raise WindowsError()  #@UndefinedVariable
        x = ii.xHotspot
        y = ii.yHotspot
        cursorlog(
            "get_cursor_data(%#x) hotspot at %ix%i, hbmColor=%#x, hbmMask=%#x",
            hCursor, x, y, ii.hbmColor or 0, ii.hbmMask or 0)
        if not ii.hbmColor:
            #FIXME: we don't handle black and white cursors
            return None
        iie = ICONINFOEXW()
        iie.cbSize = sizeof(ICONINFOEXW)
        if not GetIconInfoExW(hCursor, byref(iie)):
            raise WindowsError()  #@UndefinedVariable
        name = iie.szResName[:MAX_PATH]
        cursorlog("wResID=%#x, sxModName=%s, szResName=%s", iie.wResID,
                  iie.sxModName[:MAX_PATH], name)
        bm = Bitmap()
        if not GetObjectA(ii.hbmColor, sizeof(Bitmap), byref(bm)):
            raise WindowsError()  #@UndefinedVariable
        cursorlog(
            "cursor bitmap: type=%i, width=%i, height=%i, width bytes=%i, planes=%i, bits pixel=%i, bits=%#x",
            bm.bmType, bm.bmWidth, bm.bmHeight, bm.bmWidthBytes, bm.bmPlanes,
            bm.bmBitsPixel, bm.bmBits or 0)
        w = bm.bmWidth
        h = bm.bmHeight
        dc = GetDC(None)
        assert dc, "failed to get a drawing context"
        memdc = CreateCompatibleDC(dc)
        assert memdc, "failed to get a compatible drawing context from %s" % dc
        bitmap = CreateCompatibleBitmap(dc, w, h)
        assert bitmap, "failed to get a compatible bitmap from %s" % dc
        old_handle = SelectObject(memdc, bitmap)

        #check if icon is animated:
        UINT_MAX = 2**32 - 1
        if not DrawIconEx(memdc, 0, 0, hCursor, w, h, UINT_MAX, 0, 0):
            cursorlog("cursor is animated!")

        #if not DrawIcon(memdc, 0, 0, hCursor):
        if not DrawIconEx(memdc, 0, 0, hCursor, w, h, 0, 0,
                          win32con.DI_NORMAL):
            raise WindowsError()  #@UndefinedVariable

        buf_size = bm.bmWidthBytes * h
        buf = create_string_buffer(b"", buf_size)
        r = GetBitmapBits(bitmap, buf_size, byref(buf))
        cursorlog("get_cursor_data(%#x) GetBitmapBits(%#x, %#x, %#x)=%i",
                  hCursor, bitmap, buf_size, addressof(buf), r)
        if not r:
            cursorlog.error("Error: failed to copy screen bitmap data")
            return None
        elif r != buf_size:
            cursorlog.warn(
                "Warning: invalid cursor buffer size, got %i bytes but expected %i",
                r, buf_size)
            return None
        else:
            #32-bit data:
            pixels = bytearray(strtobytes(buf.raw))
            has_alpha = False
            has_pixels = False
            for i in range(len(pixels) // 4):
                has_pixels = has_pixels or pixels[i * 4] != 0 or pixels[
                    i * 4 + 1] != 0 or pixels[i * 4 + 2] != 0
                has_alpha = has_alpha or pixels[i * 4 + 3] != 0
                if has_pixels and has_alpha:
                    break
            if has_pixels and not has_alpha:
                #generate missing alpha - don't ask me why
                for i in range(len(pixels) // 4):
                    if pixels[i * 4] != 0 or pixels[i * 4 +
                                                    1] != 0 or pixels[i * 4 +
                                                                      2] != 0:
                        pixels[i * 4 + 3] = 0xff
        return [0, 0, w, h, x, y, hCursor, bytes(pixels), strtobytes(name)]
    except Exception as e:
        cursorlog("get_cursor_data(%#x)", hCursor, exc_info=True)
        cursorlog.error("Error: failed to grab cursor:")
        cursorlog.error(" %s", str(e) or type(e))
        return None
    finally:
        if old_handle:
            SelectObject(memdc, old_handle)
        if bitmap:
            DeleteObject(bitmap)
        if memdc:
            DeleteDC(memdc)
        if dc:
            ReleaseDC(None, dc)
Exemple #3
0
 def get_image(self, x=0, y=0, width=0, height=0):
     start = time.time()
     x, y, width, height = self.get_capture_coords(x, y, width, height)
     if not self.dc:
         self.wnd = GetDesktopWindow()
         if not self.wnd:
             log.error("Error: cannot access the desktop window")
             log.error(" capturing the screen is not possible")
             return None
         self.dc = GetWindowDC(self.wnd)
         if not self.dc:
             log.error("Error: cannot get a drawing context")
             log.error(" capturing the screen is not possible")
             log.error(" desktop window=%#x", self.wnd)
             return None
         self.bit_depth = GetDeviceCaps(self.dc, win32con.BITSPIXEL)
         self.memdc = CreateCompatibleDC(self.dc)
         assert self.memdc, "failed to get a compatible drawing context from %s" % self.dc
     bitmap = CreateCompatibleBitmap(self.dc, width, height)
     if not bitmap:
         log.error("Error: failed to get a compatible bitmap")
         log.error(" from drawing context %#x with size %ix%i", self.dc,
                   width, height)
         self.clean_dc()
         return None
     r = SelectObject(self.memdc, bitmap)
     if not r:
         log.error("Error: cannot select bitmap object")
         return None
     select_time = time.time()
     log("get_image up to SelectObject (%s) took %ims",
         REGION_CONSTS.get(r, r), (select_time - start) * 1000)
     try:
         if BitBlt(self.memdc, 0, 0, width, height, self.dc, x, y,
                   win32con.SRCCOPY) == 0:
             e = ctypes.get_last_error()
             #rate limit the error message:
             now = time.time()
             if now - self.bitblt_err_time > 10:
                 log.error("Error: failed to blit the screen, error %i", e)
                 self.bitblt_err_time = now
             return None
     except Exception as e:
         log("BitBlt error", exc_info=True)
         log.error("Error: cannot capture screen with BitBlt")
         log.error(" %s", e)
         self.clean_dc()
         return None
     bitblt_time = time.time()
     log("get_image BitBlt took %ims", (bitblt_time - select_time) * 1000)
     rowstride = roundup(width * self.bit_depth // 8, 2)
     buf_size = rowstride * height
     pixels = ctypes.create_string_buffer(b"", buf_size)
     log("GetBitmapBits(%#x, %#x, %#x)", bitmap, buf_size,
         ctypes.addressof(pixels))
     r = GetBitmapBits(bitmap, buf_size, ctypes.byref(pixels))
     if not r:
         log.error("Error: failed to copy screen bitmap data")
         self.clean_dc()
         return None
     if r != buf_size:
         log.warn(
             "Warning: truncating pixel buffer, got %i bytes but expected %i",
             r, buf_size)
         pixels = pixels[:r]
     log("get_image GetBitmapBits took %ims",
         (time.time() - bitblt_time) * 1000)
     DeleteObject(bitmap)
     assert pixels, "no pixels returned from GetBitmapBits"
     rgb_format = RGB_FORMATS.get(self.bit_depth)
     if not rgb_format:
         raise Exception("unsupported bit depth: %s" % self.bit_depth)
     bpp = self.bit_depth // 8
     v = ImageWrapper(0,
                      0,
                      width,
                      height,
                      pixels,
                      rgb_format,
                      self.bit_depth,
                      rowstride,
                      bpp,
                      planes=ImageWrapper.PACKED,
                      thread_safe=True)
     if self.bit_depth == 8:
         palette = get_palette(self.dc)
         v.set_palette(palette)
     log("get_image%s=%s took %ims", (x, y, width, height), v,
         (time.time() - start) * 1000)
     return v