static cairo_status_t
_cairo_win32_display_surface_finish (void *abstract_surface)
{
    cairo_win32_display_surface_t *surface = abstract_surface;

    if (surface->image && to_image_surface(surface->image)->parent) {
	assert (to_image_surface(surface->image)->parent == &surface->win32.base);
	/* Unhook ourselves first to avoid the double-unref from the image */
	to_image_surface(surface->image)->parent = NULL;
	cairo_surface_finish (surface->image);
	cairo_surface_destroy (surface->image);
    }

    /* If we created the Bitmap and DC, destroy them */
    if (surface->bitmap) {
	SelectObject (surface->win32.dc, surface->saved_dc_bitmap);
	DeleteObject (surface->bitmap);
	DeleteDC (surface->win32.dc);
    }

    _cairo_win32_display_surface_discard_fallback (surface);

    if (surface->initial_clip_rgn)
	DeleteObject (surface->initial_clip_rgn);

    return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
alpha_blend_boxes (cairo_win32_display_surface_t *dst,
		   const cairo_pattern_t *source,
		   cairo_boxes_t *boxes,
		   uint8_t alpha)
{
    const cairo_surface_pattern_t *pattern;
    struct copy_box cb;
    cairo_surface_t *surface;
    cairo_win32_display_surface_t *src;
    cairo_status_t status;

    TRACE ((stderr, "%s\n", __FUNCTION__));
    if (source->type != CAIRO_PATTERN_TYPE_SURFACE)
	return CAIRO_INT_STATUS_UNSUPPORTED;

    pattern = (const cairo_surface_pattern_t *) source;
    surface = _cairo_surface_get_source (pattern->surface, &cb.limit);
    if (surface->type == CAIRO_SURFACE_TYPE_IMAGE) {
	surface = to_image_surface(surface)->parent;
	if (surface == NULL)
	    return CAIRO_INT_STATUS_UNSUPPORTED;
    }
    if (surface->type != CAIRO_SURFACE_TYPE_WIN32)
	return CAIRO_INT_STATUS_UNSUPPORTED;

    if (! _cairo_matrix_is_integer_translation (&source->matrix,
						&cb.tx, &cb.ty))
	return CAIRO_INT_STATUS_UNSUPPORTED;

    src = to_win32_display_surface (surface);
    cb.dst = dst->win32.dc;
    cb.src = src->win32.dc;

    /* First check that the data is entirely within the image */
    if (! _cairo_boxes_for_each_box (boxes, source_contains_box, &cb))
	return CAIRO_INT_STATUS_UNSUPPORTED;

    status = __cairo_surface_flush (&src->win32.base, 0);
    if (status)
	return status;

    cb.bf.BlendOp = AC_SRC_OVER;
    cb.bf.BlendFlags = 0;
    cb.bf.SourceConstantAlpha = alpha;
    cb.bf.AlphaFormat = (src->win32.format == CAIRO_FORMAT_ARGB32) ? AC_SRC_ALPHA : 0;
    cb.alpha_blend = to_win32_device(dst->win32.base.device)->alpha_blend;

    cb.tx += cb.limit.x;
    cb.ty += cb.limit.y;
    status = CAIRO_STATUS_SUCCESS;
    if (! _cairo_boxes_for_each_box (boxes, alpha_box, &cb))
	status = CAIRO_INT_STATUS_UNSUPPORTED;

    _cairo_win32_display_surface_discard_fallback (dst);
    return status;
}
static cairo_status_t
copy_boxes (cairo_win32_display_surface_t *dst,
	    const cairo_pattern_t *source,
	    cairo_boxes_t *boxes)
{
    const cairo_surface_pattern_t *pattern;
    struct copy_box cb;
    cairo_surface_t *surface;
    cairo_status_t status;
    cairo_win32_surface_t *src;

    TRACE ((stderr, "%s\n", __FUNCTION__));

    pattern = (const cairo_surface_pattern_t *) source;
    surface = _cairo_surface_get_source (pattern->surface, &cb.limit);
    if (surface->type == CAIRO_SURFACE_TYPE_IMAGE) {
	surface = to_image_surface(surface)->parent;
	if (surface == NULL)
	    return CAIRO_INT_STATUS_UNSUPPORTED;
    }
    if (surface->type != CAIRO_SURFACE_TYPE_WIN32)
	return CAIRO_INT_STATUS_UNSUPPORTED;

    if (! _cairo_matrix_is_integer_translation (&source->matrix,
						&cb.tx, &cb.ty))
	return CAIRO_INT_STATUS_UNSUPPORTED;

    src = to_win32_surface(surface);

    if (src->format != dst->win32.format &&
	!(src->format == CAIRO_FORMAT_ARGB32 && dst->win32.format == CAIRO_FORMAT_RGB24))
    {
	/* forbid copy different surfaces unless it is from argb32 to
	 * rgb (dropping alpha) */
        return CAIRO_INT_STATUS_UNSUPPORTED;
    }
    cb.dst = dst->win32.dc;
    cb.src = src->dc;

    /* First check that the data is entirely within the image */
    if (! _cairo_boxes_for_each_box (boxes, source_contains_box, &cb))
	return CAIRO_INT_STATUS_UNSUPPORTED;

    status = __cairo_surface_flush (surface, 0);
    if (status)
	return status;

    cb.tx += cb.limit.x;
    cb.ty += cb.limit.y;
    status = CAIRO_STATUS_SUCCESS;
    if (! _cairo_boxes_for_each_box (boxes, copy_box, &cb))
	status = CAIRO_INT_STATUS_UNSUPPORTED;

    _cairo_win32_display_surface_discard_fallback (dst);
    return status;
}
static cairo_surface_t *
_cairo_win32_display_surface_create_for_dc (HDC             original_dc,
					    cairo_format_t  format,
					    int	            width,
					    int	            height)
{
    cairo_status_t status;
    cairo_device_t *device;
    cairo_win32_display_surface_t *surface;
    unsigned char *bits;
    int rowstride;

    surface = malloc (sizeof (*surface));
    if (surface == NULL)
	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));

    surface->fallback = NULL;

    status = _create_dc_and_bitmap (surface, original_dc, format,
				    width, height,
				    &bits, &rowstride);
    if (status)
	goto FAIL;

    surface->image = cairo_image_surface_create_for_data (bits, format,
							  width, height, rowstride);
    status = surface->image->status;
    if (status)
	goto FAIL;

    _cairo_image_surface_set_parent (to_image_surface(surface->image),
				     &surface->win32.base);

    surface->win32.format = format;

    surface->win32.extents.x = 0;
    surface->win32.extents.y = 0;
    surface->win32.extents.width = width;
    surface->win32.extents.height = height;

    surface->initial_clip_rgn = NULL;
    surface->had_simple_clip = FALSE;

    device = _cairo_win32_device_get ();

    _cairo_surface_init (&surface->win32.base,
			 &cairo_win32_display_surface_backend,
			 device,
			 _cairo_content_from_format (format));

    cairo_device_destroy (device);

    return &surface->win32.base;

 FAIL:
    if (surface->bitmap) {
	SelectObject (surface->win32.dc, surface->saved_dc_bitmap);
	DeleteObject (surface->bitmap);
	DeleteDC (surface->win32.dc);
    }
    free (surface);

    return _cairo_surface_create_in_error (status);
}
static cairo_status_t
upload_boxes (cairo_win32_display_surface_t *dst,
	      const cairo_pattern_t *source,
	      cairo_boxes_t *boxes)
{
    const cairo_surface_pattern_t *pattern;
    struct upload_box cb;
    cairo_surface_t *surface;
    cairo_image_surface_t *image;
    void *image_extra;
    cairo_status_t status;

    TRACE ((stderr, "%s\n", __FUNCTION__));

    if ((dst->win32.flags & CAIRO_WIN32_SURFACE_CAN_STRETCHDIB) == 0)
	return CAIRO_INT_STATUS_UNSUPPORTED;

    if (! _cairo_matrix_is_integer_translation (&source->matrix,
						&cb.tx, &cb.ty))
	return CAIRO_INT_STATUS_UNSUPPORTED;

    pattern = (const cairo_surface_pattern_t *) source;
    surface = _cairo_surface_get_source (pattern->surface, &cb.limit);

    /* First check that the data is entirely within the image */
    if (! _cairo_boxes_for_each_box (boxes, source_contains_box, &cb))
	return CAIRO_INT_STATUS_UNSUPPORTED;

    if (surface->type != CAIRO_SURFACE_TYPE_IMAGE) {
	status = _cairo_surface_acquire_source_image (surface,
						      &image, &image_extra);
	if (status)
	    return status;
    } else
	image = to_image_surface(surface);

    status = CAIRO_INT_STATUS_UNSUPPORTED;
    if (!(image->format == CAIRO_FORMAT_ARGB32 ||
	  image->format == CAIRO_FORMAT_RGB24))
	goto err;
    if (image->stride != 4*image->width)
	goto err;

    cb.dst = dst->win32.dc;
    cb.data = image->data;

    cb.bi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
    cb.bi.bmiHeader.biWidth = image->width;
    cb.bi.bmiHeader.biHeight = -image->height;
    cb.bi.bmiHeader.biSizeImage = 0;
    cb.bi.bmiHeader.biXPelsPerMeter = PELS_72DPI;
    cb.bi.bmiHeader.biYPelsPerMeter = PELS_72DPI;
    cb.bi.bmiHeader.biPlanes = 1;
    cb.bi.bmiHeader.biBitCount = 32;
    cb.bi.bmiHeader.biCompression = BI_RGB;
    cb.bi.bmiHeader.biClrUsed = 0;
    cb.bi.bmiHeader.biClrImportant = 0;

    cb.tx += cb.limit.x;
    cb.ty += cb.limit.y;
    status = CAIRO_STATUS_SUCCESS;
    if (! _cairo_boxes_for_each_box (boxes, upload_box, &cb))
	status = CAIRO_INT_STATUS_UNSUPPORTED;

    _cairo_win32_display_surface_discard_fallback (dst);
err:
    if (&image->base != surface)
	_cairo_surface_release_source_image (surface, image, image_extra);

    return status;
}