static cairo_int_status_t
fill_boxes (cairo_win32_display_surface_t	*dst,
	    const cairo_pattern_t		*src,
	    cairo_boxes_t			*boxes)
{
    const cairo_color_t *color = &((cairo_solid_pattern_t *) src)->color;
    cairo_status_t status = CAIRO_STATUS_SUCCESS;
    struct fill_box fb;

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

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

    fb.dc = dst->win32.dc;
    fb.brush = CreateSolidBrush (color_to_rgb(color));
    if (!fb.brush)
	return _cairo_win32_print_gdi_error (__FUNCTION__);

    if (! _cairo_boxes_for_each_box (boxes, fill_box, &fb))
	status = CAIRO_INT_STATUS_UNSUPPORTED;

    DeleteObject (fb.brush);

    return status;
}
static cairo_int_status_t
_cairo_win32_save_initial_clip (HDC hdc, cairo_win32_display_surface_t *surface)
{
    RECT rect;
    int clipBoxType;
    int gm;
    XFORM saved_xform;

    /* GetClipBox/GetClipRgn and friends interact badly with a world transform
     * set.  GetClipBox returns values in logical (transformed) coordinates;
     * it's unclear what GetClipRgn returns, because the region is empty in the
     * case of a SIMPLEREGION clip, but I assume device (untransformed) coordinates.
     * Similarly, IntersectClipRect works in logical units, whereas SelectClipRgn
     * works in device units.
     *
     * So, avoid the whole mess and get rid of the world transform
     * while we store our initial data and when we restore initial coordinates.
     *
     * XXX we may need to modify x/y by the ViewportOrg or WindowOrg
     * here in GM_COMPATIBLE; unclear.
     */
    gm = GetGraphicsMode (hdc);
    if (gm == GM_ADVANCED) {
	GetWorldTransform (hdc, &saved_xform);
	ModifyWorldTransform (hdc, NULL, MWT_IDENTITY);
    }

    clipBoxType = GetClipBox (hdc, &rect);
    if (clipBoxType == ERROR) {
	_cairo_win32_print_gdi_error (__FUNCTION__);
	SetGraphicsMode (hdc, gm);
	/* XXX: Can we make a more reasonable guess at the error cause here? */
	return _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
    }

    surface->win32.extents.x = rect.left;
    surface->win32.extents.y = rect.top;
    surface->win32.extents.width = rect.right - rect.left;
    surface->win32.extents.height = rect.bottom - rect.top;

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

    if (clipBoxType == COMPLEXREGION) {
	surface->initial_clip_rgn = CreateRectRgn (0, 0, 0, 0);
	if (GetClipRgn (hdc, surface->initial_clip_rgn) <= 0) {
	    DeleteObject(surface->initial_clip_rgn);
	    surface->initial_clip_rgn = NULL;
	}
    } else if (clipBoxType == SIMPLEREGION) {
	surface->had_simple_clip = TRUE;
    }

    if (gm == GM_ADVANCED)
	SetWorldTransform (hdc, &saved_xform);

    return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_cairo_win32_surface_get_subimage (cairo_win32_surface_t  *surface,
				   int                     x,
				   int                     y,
				   int                     width,
				   int                     height,
				   cairo_win32_surface_t **local_out)
{
    cairo_win32_surface_t *local;
    cairo_status_t status;
    cairo_content_t content = _cairo_content_from_format (surface->format);

    local = 
	(cairo_win32_surface_t *) _cairo_win32_surface_create_similar (surface,
								       content,
								       width,
								       height);
    if (local->base.status)
	return CAIRO_STATUS_NO_MEMORY;
    
    if (!BitBlt (local->dc, 
		 0, 0,
		 width, height,
		 surface->dc,
		 x, y,
		 SRCCOPY)) {
	/* If we fail to BitBlt here, most likely the source is a printer.
	 * You can't reliably get bits from a printer DC, so just fill in
	 * the surface as white (common case for printing).
	 */

	RECT r;
	r.left = r.top = 0;
	r.right = width;
	r.bottom = height;
	FillRect(local->dc, &r, (HBRUSH)GetStockObject(WHITE_BRUSH));
    }

    *local_out = local;
    
    return CAIRO_STATUS_SUCCESS;

 FAIL:
    status = _cairo_win32_print_gdi_error ("_cairo_win32_surface_get_subimage");

    if (local)
	cairo_surface_destroy (&local->base);

    return status;
}
static void
_cairo_win32_surface_release_dest_image (void                   *abstract_surface,
					 cairo_rectangle_t      *interest_rect,
					 cairo_image_surface_t  *image,
					 cairo_rectangle_t      *image_rect,
					 void                   *image_extra)
{
    cairo_win32_surface_t *surface = abstract_surface;
    cairo_win32_surface_t *local = image_extra;
    
    if (!local)
	return;

    if (!BitBlt (surface->dc,
		 image_rect->x, image_rect->y,
		 image_rect->width, image_rect->height,
		 local->dc,
		 0, 0,
		 SRCCOPY))
	_cairo_win32_print_gdi_error ("_cairo_win32_surface_release_dest_image");

    cairo_surface_destroy ((cairo_surface_t *)local);
}
static void
create_printer_dc (win32_target_closure_t *ptc)
{
    char *printer_name;
    DWORD size;
    int x_dpi, y_dpi, left_margin, top_margin, page_height, printable_height;
    XFORM xform;

    ptc->dc = NULL;
    GetDefaultPrinter (NULL, &size);
    printer_name = malloc (size);

    if (printer_name == NULL)
	return;

    if (GetDefaultPrinter (printer_name, &size) == 0) {
	free (printer_name);
	return;
    }

    /* printf("\nPrinting to : %s\n", printer_name); */
    ptc->dc = CreateDC (NULL, printer_name, NULL, NULL);
    free (printer_name);

    if (!printer_is_postscript_level_3 (ptc->dc)) {
	printf("The default printer driver must be a color PostScript level 3 printer\n");
	ptc->dc = NULL;
	return;
    }

    /* The printer device units on win32 are 1 unit == 1 dot and the
     * origin is the start of the printable area. We transform the
     * cordinate space to 1 unit is 1 point as expected by the
     * tests. As the page size is larger than the test surface, the
     * origin is translated down so that the each test is drawn at the
     * bottom left corner of the page. This is because the bottom left
     * corner of the PNG image that ghostscript creates is positioned
     * at origin of the PS coordinates (ie the bottom left of the
     * page).  The left and bottom margins are stored in
     * win32_target_closure as size of the PNG image needs to be
     * increased because the test output is offset from the bottom
     * left by the non printable margins. After the PNG is created the
     * margins will be chopped off so the image matches the reference
     * image.
     */
    printable_height = GetDeviceCaps (ptc->dc, VERTRES);
    x_dpi = GetDeviceCaps (ptc->dc, LOGPIXELSX);
    y_dpi = GetDeviceCaps (ptc->dc, LOGPIXELSY);
    left_margin = GetDeviceCaps (ptc->dc, PHYSICALOFFSETX);
    top_margin = GetDeviceCaps (ptc->dc, PHYSICALOFFSETY);
    page_height = GetDeviceCaps (ptc->dc, PHYSICALHEIGHT);

    SetGraphicsMode (ptc->dc, GM_ADVANCED);
    xform.eM11 = x_dpi/72.0;
    xform.eM12 = 0;
    xform.eM21 = 0;
    xform.eM22 = y_dpi/72.0;
    xform.eDx = 0;
    xform.eDy = printable_height - ptc->height*y_dpi/72.0;
    if (!SetWorldTransform (ptc->dc, &xform)) {
	_cairo_win32_print_gdi_error ("cairo-boilerplate-win32-printing:SetWorldTransform");
	return;
    }

    ptc->left_margin = 72.0*left_margin/x_dpi;
    ptc->bottom_margin = 72.0*(page_height - printable_height - top_margin)/y_dpi;
}
cairo_status_t
_cairo_win32_display_surface_set_clip (cairo_win32_display_surface_t *surface,
				       cairo_clip_t *clip)
{
    char stack[512];
    cairo_rectangle_int_t extents;
    int num_rects;
    RGNDATA *data;
    size_t data_size;
    RECT *rects;
    int i;
    HRGN gdi_region;
    cairo_status_t status;
    cairo_region_t *region;

    /* The semantics we want is that any clip set by cairo combines
     * is intersected with the clip on device context that the
     * surface was created for. To implement this, we need to
     * save the original clip when first setting a clip on surface.
     */

    assert (_cairo_clip_is_region (clip));
    region = _cairo_clip_get_region (clip);
    if (region == NULL)
	return CAIRO_STATUS_SUCCESS;

    cairo_region_get_extents (region, &extents);
    num_rects = cairo_region_num_rectangles (region);

    /* XXX see notes in _cairo_win32_save_initial_clip --
     * this code will interact badly with a HDC which had an initial
     * world transform -- we should probably manually transform the
     * region rects, because SelectClipRgn takes device units, not
     * logical units (unlike IntersectClipRect).
     */

    data_size = sizeof (RGNDATAHEADER) + num_rects * sizeof (RECT);
    if (data_size > sizeof (stack)) {
	data = malloc (data_size);
	if (!data)
	    return _cairo_error(CAIRO_STATUS_NO_MEMORY);
    } else
	data = (RGNDATA *)stack;

    data->rdh.dwSize = sizeof (RGNDATAHEADER);
    data->rdh.iType = RDH_RECTANGLES;
    data->rdh.nCount = num_rects;
    data->rdh.nRgnSize = num_rects * sizeof (RECT);
    data->rdh.rcBound.left = extents.x;
    data->rdh.rcBound.top = extents.y;
    data->rdh.rcBound.right = extents.x + extents.width;
    data->rdh.rcBound.bottom = extents.y + extents.height;

    rects = (RECT *)data->Buffer;
    for (i = 0; i < num_rects; i++) {
	cairo_rectangle_int_t rect;

	cairo_region_get_rectangle (region, i, &rect);

	rects[i].left   = rect.x;
	rects[i].top    = rect.y;
	rects[i].right  = rect.x + rect.width;
	rects[i].bottom = rect.y + rect.height;
    }

    gdi_region = ExtCreateRegion (NULL, data_size, data);
    if ((char *)data != stack)
	free (data);

    if (!gdi_region)
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);

    /* AND the new region into our DC */
    status = CAIRO_STATUS_SUCCESS;
    if (ExtSelectClipRgn (surface->win32.dc, gdi_region, RGN_AND) == ERROR)
	status = _cairo_win32_print_gdi_error (__FUNCTION__);

    DeleteObject (gdi_region);

    return status;
}
static cairo_status_t
_cairo_win32_display_surface_flush (void *abstract_surface, unsigned flags)
{
    cairo_win32_display_surface_t *surface = abstract_surface;
    cairo_status_t status = CAIRO_STATUS_SUCCESS;

    if (flags)
	return CAIRO_STATUS_SUCCESS;

    TRACE ((stderr, "%s (surface=%d)\n",
	    __FUNCTION__, surface->win32.base.unique_id));
    if (surface->fallback == NULL)
	return CAIRO_STATUS_SUCCESS;

    if (surface->fallback->damage) {
	cairo_win32_display_surface_t *fallback;
	cairo_damage_t *damage;

	damage = _cairo_damage_reduce (surface->fallback->damage);
	surface->fallback->damage = NULL;

	fallback = to_win32_display_surface (surface->fallback);
	assert (fallback->image);

	TRACE ((stderr, "%s: flushing damage x %d\n", __FUNCTION__,
		damage->region ? cairo_region_num_rectangles (damage->region) : 0));

	if (damage->status) {
	    if (!BitBlt (surface->win32.dc,
			 0, 0,
			 surface->win32.extents.width,
			 surface->win32.extents.height,
			 fallback->win32.dc,
			 0, 0,
			 SRCCOPY))
		status = _cairo_win32_print_gdi_error (__FUNCTION__);
	} else if (damage->region) {
	    int n = cairo_region_num_rectangles (damage->region), i;
	    for (i = 0; i < n; i++) {
		cairo_rectangle_int_t rect;

		cairo_region_get_rectangle (damage->region, i, &rect);
		TRACE ((stderr, "%s: damage (%d,%d)x(%d,%d)\n", __FUNCTION__,
			rect.x, rect.y,
			rect.width, rect.height));
		if (!BitBlt (surface->win32.dc,
			     rect.x, rect.y,
			     rect.width, rect.height,
			     fallback->win32.dc,
			     rect.x, rect.y,
			     SRCCOPY)) {
		    status = _cairo_win32_print_gdi_error (__FUNCTION__);
		    break;
		}
	    }
	}
	_cairo_damage_destroy (damage);
    } else {
	cairo_surface_destroy (surface->fallback);
	surface->fallback = NULL;
    }

    return status;
}
static cairo_status_t
_create_dc_and_bitmap (cairo_win32_display_surface_t *surface,
		       HDC                    original_dc,
		       cairo_format_t         format,
		       int                    width,
		       int                    height,
		       unsigned char        **bits_out,
		       int                   *rowstride_out)
{
    cairo_status_t status;

    BITMAPINFO *bitmap_info = NULL;
    struct {
	BITMAPINFOHEADER bmiHeader;
	RGBQUAD bmiColors[2];
    } bmi_stack;
    void *bits;

    int num_palette = 0;	/* Quiet GCC */
    int i;

    surface->win32.dc = NULL;
    surface->bitmap = NULL;
    surface->is_dib = FALSE;

    switch (format) {
    default:
    case CAIRO_FORMAT_INVALID:
    case CAIRO_FORMAT_RGB16_565:
    case CAIRO_FORMAT_RGB30:
	return _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
    case CAIRO_FORMAT_ARGB32:
    case CAIRO_FORMAT_RGB24:
	num_palette = 0;
	break;

    case CAIRO_FORMAT_A8:
	num_palette = 256;
	break;

    case CAIRO_FORMAT_A1:
	num_palette = 2;
	break;
    }

    if (num_palette > 2) {
	bitmap_info = _cairo_malloc_ab_plus_c (num_palette, sizeof(RGBQUAD), sizeof(BITMAPINFOHEADER));
	if (!bitmap_info)
	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    } else {
	bitmap_info = (BITMAPINFO *)&bmi_stack;
    }

    bitmap_info->bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
    bitmap_info->bmiHeader.biWidth = width == 0 ? 1 : width;
    bitmap_info->bmiHeader.biHeight = height == 0 ? -1 : - height; /* top-down */
    bitmap_info->bmiHeader.biSizeImage = 0;
    bitmap_info->bmiHeader.biXPelsPerMeter = PELS_72DPI; /* unused here */
    bitmap_info->bmiHeader.biYPelsPerMeter = PELS_72DPI; /* unused here */
    bitmap_info->bmiHeader.biPlanes = 1;

    switch (format) {
    case CAIRO_FORMAT_INVALID:
    case CAIRO_FORMAT_RGB16_565:
    case CAIRO_FORMAT_RGB30:
	ASSERT_NOT_REACHED;
    /* We can't create real RGB24 bitmaps because something seems to
     * break if we do, especially if we don't set up an image
     * fallback.  It could be a bug with using a 24bpp pixman image
     * (and creating one with masks).  So treat them like 32bpp.
     * Note: This causes problems when using BitBlt/AlphaBlend/etc!
     * see end of file.
     */
    case CAIRO_FORMAT_RGB24:
    case CAIRO_FORMAT_ARGB32:
	bitmap_info->bmiHeader.biBitCount = 32;
	bitmap_info->bmiHeader.biCompression = BI_RGB;
	bitmap_info->bmiHeader.biClrUsed = 0;	/* unused */
	bitmap_info->bmiHeader.biClrImportant = 0;
	break;

    case CAIRO_FORMAT_A8:
	bitmap_info->bmiHeader.biBitCount = 8;
	bitmap_info->bmiHeader.biCompression = BI_RGB;
	bitmap_info->bmiHeader.biClrUsed = 256;
	bitmap_info->bmiHeader.biClrImportant = 0;

	for (i = 0; i < 256; i++) {
	    bitmap_info->bmiColors[i].rgbBlue = i;
	    bitmap_info->bmiColors[i].rgbGreen = i;
	    bitmap_info->bmiColors[i].rgbRed = i;
	    bitmap_info->bmiColors[i].rgbReserved = 0;
	}
	break;

    case CAIRO_FORMAT_A1:
	bitmap_info->bmiHeader.biBitCount = 1;
	bitmap_info->bmiHeader.biCompression = BI_RGB;
	bitmap_info->bmiHeader.biClrUsed = 2;
	bitmap_info->bmiHeader.biClrImportant = 0;

	for (i = 0; i < 2; i++) {
	    bitmap_info->bmiColors[i].rgbBlue = i * 255;
	    bitmap_info->bmiColors[i].rgbGreen = i * 255;
	    bitmap_info->bmiColors[i].rgbRed = i * 255;
	    bitmap_info->bmiColors[i].rgbReserved = 0;
	}
	break;
    }

    surface->win32.dc = CreateCompatibleDC (original_dc);
    if (!surface->win32.dc)
	goto FAIL;

    surface->bitmap = CreateDIBSection (surface->win32.dc,
			                bitmap_info,
			                DIB_RGB_COLORS,
			                &bits,
			                NULL, 0);
    if (!surface->bitmap)
	goto FAIL;

    surface->is_dib = TRUE;

    GdiFlush();

    surface->saved_dc_bitmap = SelectObject (surface->win32.dc,
					     surface->bitmap);
    if (!surface->saved_dc_bitmap)
	goto FAIL;

    if (bitmap_info && num_palette > 2)
	free (bitmap_info);

    if (bits_out)
	*bits_out = bits;

    if (rowstride_out) {
	/* Windows bitmaps are padded to 32-bit (dword) boundaries */
	switch (format) {
	case CAIRO_FORMAT_INVALID:
	case CAIRO_FORMAT_RGB16_565:
	case CAIRO_FORMAT_RGB30:
	    ASSERT_NOT_REACHED;
	case CAIRO_FORMAT_ARGB32:
	case CAIRO_FORMAT_RGB24:
	    *rowstride_out = 4 * width;
	    break;

	case CAIRO_FORMAT_A8:
	    *rowstride_out = (width + 3) & ~3;
	    break;

	case CAIRO_FORMAT_A1:
	    *rowstride_out = ((width + 31) & ~31) / 8;
	    break;
	}
    }

    surface->win32.flags = _cairo_win32_flags_for_dc (surface->win32.dc);

    return CAIRO_STATUS_SUCCESS;

 FAIL:
    status = _cairo_win32_print_gdi_error (__FUNCTION__);

    if (bitmap_info && num_palette > 2)
	free (bitmap_info);

    if (surface->saved_dc_bitmap) {
	SelectObject (surface->win32.dc, surface->saved_dc_bitmap);
	surface->saved_dc_bitmap = NULL;
    }

    if (surface->bitmap) {
	DeleteObject (surface->bitmap);
	surface->bitmap = NULL;
    }

    if (surface->win32.dc) {
	DeleteDC (surface->win32.dc);
	surface->win32.dc = NULL;
    }

    return status;
}
cairo_int_status_t
_cairo_win32_surface_emit_glyphs (cairo_win32_surface_t *dst,
                                  const cairo_pattern_t *source,
                                  cairo_glyph_t	 *glyphs,
                                  int			  num_glyphs,
                                  cairo_scaled_font_t	 *scaled_font,
                                  cairo_bool_t		  glyph_indexing)
{
#if CAIRO_HAS_WIN32_FONT
    WORD glyph_buf_stack[STACK_GLYPH_SIZE];
    WORD *glyph_buf = glyph_buf_stack;
    int dxy_buf_stack[2 * STACK_GLYPH_SIZE];
    int *dxy_buf = dxy_buf_stack;

    BOOL win_result = 0;
    int i, j;

    cairo_solid_pattern_t *solid_pattern;
    COLORREF color;

    cairo_matrix_t device_to_logical;

    int start_x, start_y;
    double user_x, user_y;
    int logical_x, logical_y;
    unsigned int glyph_index_option;

    /* We can only handle win32 fonts */
    assert (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_WIN32);

    /* We can only handle opaque solid color sources and destinations */
    assert (_cairo_pattern_is_opaque_solid(source));
    assert (dst->format == CAIRO_FORMAT_RGB24);

    solid_pattern = (cairo_solid_pattern_t *)source;
    color = RGB(((int)solid_pattern->color.red_short) >> 8,
                ((int)solid_pattern->color.green_short) >> 8,
                ((int)solid_pattern->color.blue_short) >> 8);

    cairo_win32_scaled_font_get_device_to_logical(scaled_font, &device_to_logical);

    SaveDC(dst->dc);

    cairo_win32_scaled_font_select_font(scaled_font, dst->dc);
    SetTextColor(dst->dc, color);
    SetTextAlign(dst->dc, TA_BASELINE | TA_LEFT);
    SetBkMode(dst->dc, TRANSPARENT);

    if (num_glyphs > STACK_GLYPH_SIZE) {
        glyph_buf = (WORD *) _cairo_malloc_ab (num_glyphs, sizeof(WORD));
        dxy_buf = (int *) _cairo_malloc_abc (num_glyphs, sizeof(int), 2);
    }

    /* It is vital that dx values for dxy_buf are calculated from the delta of
     * _logical_ x coordinates (not user x coordinates) or else the sum of all
     * previous dx values may start to diverge from the current glyph's x
     * coordinate due to accumulated rounding error. As a result strings could
     * be painted shorter or longer than expected. */

    user_x = glyphs[0].x;
    user_y = glyphs[0].y;

    cairo_matrix_transform_point(&device_to_logical,
                                 &user_x, &user_y);

    logical_x = _cairo_lround (user_x);
    logical_y = _cairo_lround (user_y);

    start_x = logical_x;
    start_y = logical_y;

    for (i = 0, j = 0; i < num_glyphs; ++i, j = 2 * i) {
        glyph_buf[i] = (WORD) glyphs[i].index;
        if (i == num_glyphs - 1) {
            dxy_buf[j] = 0;
            dxy_buf[j+1] = 0;
        } else {
            double next_user_x = glyphs[i+1].x;
            double next_user_y = glyphs[i+1].y;
            int next_logical_x, next_logical_y;

            cairo_matrix_transform_point(&device_to_logical,
                                         &next_user_x, &next_user_y);

            next_logical_x = _cairo_lround (next_user_x);
            next_logical_y = _cairo_lround (next_user_y);

            dxy_buf[j] = _cairo_lround (next_logical_x - logical_x);
            dxy_buf[j+1] = _cairo_lround (next_logical_y - logical_y);

            logical_x = next_logical_x;
            logical_y = next_logical_y;
        }
    }

    if (glyph_indexing)
        glyph_index_option = ETO_GLYPH_INDEX;
    else
        glyph_index_option = 0;

    win_result = ExtTextOutW(dst->dc,
                             start_x,
                             start_y,
                             glyph_index_option | ETO_PDY,
                             NULL,
                             glyph_buf,
                             num_glyphs,
                             dxy_buf);
    if (!win_result) {
        _cairo_win32_print_gdi_error("_cairo_win32_surface_show_glyphs(ExtTextOutW failed)");
    }

    RestoreDC(dst->dc, -1);

    if (glyph_buf != glyph_buf_stack) {
        free(glyph_buf);
        free(dxy_buf);
    }
    return (win_result) ? CAIRO_STATUS_SUCCESS : CAIRO_INT_STATUS_UNSUPPORTED;
#else
    return CAIRO_INT_STATUS_UNSUPPORTED;
#endif
}
static cairo_status_t
_create_dc_and_bitmap (cairo_win32_surface_t *surface,
		       HDC                    original_dc,
		       cairo_format_t         format,
		       int                    width,
		       int                    height,
		       char                 **bits_out,
		       int                   *rowstride_out)
{
    cairo_status_t status;

    BITMAPINFO *bitmap_info = NULL;
    struct {
	BITMAPINFOHEADER bmiHeader;
	RGBQUAD bmiColors[2];
    } bmi_stack;
    void *bits;

    int num_palette = 0;	/* Quiet GCC */
    int i;

    surface->dc = NULL;
    surface->bitmap = NULL;

    switch (format) {
    case CAIRO_FORMAT_ARGB32:
    case CAIRO_FORMAT_RGB24:
	num_palette = 0;
	break;
	
    case CAIRO_FORMAT_A8:
	num_palette = 256;
	break;
	
    case CAIRO_FORMAT_A1:
	num_palette = 2;
	break;
    }

    if (num_palette > 2) {
	bitmap_info = malloc (sizeof (BITMAPINFOHEADER) + num_palette * sizeof (RGBQUAD));
	if (!bitmap_info)
	    return CAIRO_STATUS_NO_MEMORY;
    } else {
	bitmap_info = (BITMAPINFO *)&bmi_stack;
    }

    bitmap_info->bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
    bitmap_info->bmiHeader.biWidth = width == 0 ? 1 : width;
    bitmap_info->bmiHeader.biHeight = height == 0 ? -1 : - height; /* top-down */
    bitmap_info->bmiHeader.biSizeImage = 0;
    bitmap_info->bmiHeader.biXPelsPerMeter = 72. / 0.0254; /* unused here */
    bitmap_info->bmiHeader.biYPelsPerMeter = 72. / 0.0254; /* unused here */
    bitmap_info->bmiHeader.biPlanes = 1;
    
    switch (format) {
    case CAIRO_FORMAT_ARGB32:
    case CAIRO_FORMAT_RGB24:
	bitmap_info->bmiHeader.biBitCount = 32;
	bitmap_info->bmiHeader.biCompression = BI_RGB;
	bitmap_info->bmiHeader.biClrUsed = 0;	/* unused */
	bitmap_info->bmiHeader.biClrImportant = 0;
	break;
	
    case CAIRO_FORMAT_A8:
	bitmap_info->bmiHeader.biBitCount = 8;
	bitmap_info->bmiHeader.biCompression = BI_RGB;
	bitmap_info->bmiHeader.biClrUsed = 256;
	bitmap_info->bmiHeader.biClrImportant = 0;

	for (i = 0; i < 256; i++) {
	    bitmap_info->bmiColors[i].rgbBlue = i;
	    bitmap_info->bmiColors[i].rgbGreen = i;
	    bitmap_info->bmiColors[i].rgbRed = i;
	    bitmap_info->bmiColors[i].rgbReserved = 0;
	}
	
	break;
	
    case CAIRO_FORMAT_A1:
	bitmap_info->bmiHeader.biBitCount = 1;
	bitmap_info->bmiHeader.biCompression = BI_RGB;
	bitmap_info->bmiHeader.biClrUsed = 2;
	bitmap_info->bmiHeader.biClrImportant = 0;

	for (i = 0; i < 2; i++) {
	    bitmap_info->bmiColors[i].rgbBlue = i * 255;
	    bitmap_info->bmiColors[i].rgbGreen = i * 255;
	    bitmap_info->bmiColors[i].rgbRed = i * 255;
	    bitmap_info->bmiColors[i].rgbReserved = 0;
	    break;
	}
    }

    surface->dc = CreateCompatibleDC (original_dc);
    if (!surface->dc)
	goto FAIL;

    surface->bitmap = CreateDIBSection (surface->dc,
			                bitmap_info,
			                DIB_RGB_COLORS,
			                &bits,
			                NULL, 0);
    if (!surface->bitmap)
	goto FAIL;

    surface->saved_dc_bitmap = SelectObject (surface->dc,
					     surface->bitmap);
    if (!surface->saved_dc_bitmap)
	goto FAIL;
    
    if (bitmap_info && num_palette > 2)
	free (bitmap_info);

    if (bits_out)
	*bits_out = bits;

    if (rowstride_out) {
	/* Windows bitmaps are padded to 32-bit (dword) boundaries */
	switch (format) {
	case CAIRO_FORMAT_ARGB32:
	case CAIRO_FORMAT_RGB24:
	    *rowstride_out = 4 * width;
	    break;
	    
	case CAIRO_FORMAT_A8:
	    *rowstride_out = (width + 3) & ~3;
	    break;
	
	case CAIRO_FORMAT_A1:
	    *rowstride_out = ((width + 31) & ~31) / 8;
	    break;
	}
    }

    return CAIRO_STATUS_SUCCESS;

 FAIL:
    status = _cairo_win32_print_gdi_error ("_create_dc_and_bitmap");
    
    if (bitmap_info && num_palette > 2)
	free (bitmap_info);

    if (surface->saved_dc_bitmap) {
	SelectObject (surface->dc, surface->saved_dc_bitmap);
	surface->saved_dc_bitmap = NULL;
    }
    
    if (surface->bitmap) {
	DeleteObject (surface->bitmap);
	surface->bitmap = NULL;
    }
    
    if (surface->dc) {
 	DeleteDC (surface->dc);
	surface->dc = NULL;
    }
 
    return status;
}
static cairo_int_status_t
_composite_alpha_blend (cairo_win32_surface_t *dst,
			cairo_win32_surface_t *src,
			int                    alpha,
			int                    src_x,
			int                    src_y,
			int                    dst_x,
			int                    dst_y,
			int                    width,
			int                    height)
{
    static unsigned alpha_blend_checked = FALSE;
    static cairo_alpha_blend_func_t alpha_blend = NULL;

    BLENDFUNCTION blend_function;

    /* Check for AlphaBlend dynamically to allow compiling on
     * MSVC 6 and use on older windows versions
     */
    if (!alpha_blend_checked) {
	OSVERSIONINFO os;
    
	os.dwOSVersionInfoSize = sizeof (os);
	GetVersionEx (&os);
	
	/* If running on Win98, disable using AlphaBlend()
	 * to avoid Win98 AlphaBlend() bug */
	if (VER_PLATFORM_WIN32_WINDOWS != os.dwPlatformId ||
	    os.dwMajorVersion != 4 || os.dwMinorVersion != 10)
	{
	    HMODULE msimg32_dll = LoadLibrary ("msimg32");
	    
	    if (msimg32_dll != NULL)
		alpha_blend = (cairo_alpha_blend_func_t)GetProcAddress (msimg32_dll,
									"AlphaBlend");
	}
	    
	alpha_blend_checked = TRUE;
    }

    if (alpha_blend == NULL)
	return CAIRO_INT_STATUS_UNSUPPORTED;
    if (GetDeviceCaps(dst->dc, SHADEBLENDCAPS) == SB_NONE)
	return CAIRO_INT_STATUS_UNSUPPORTED;
    
    blend_function.BlendOp = AC_SRC_OVER;
    blend_function.BlendFlags = 0;
    blend_function.SourceConstantAlpha = alpha;
    blend_function.AlphaFormat = src->format == CAIRO_FORMAT_ARGB32 ? AC_SRC_ALPHA : 0;

    if (!alpha_blend (dst->dc,
		      dst_x, dst_y,
		      width, height,
		      src->dc,
		      src_x, src_y,
		      width, height,
		      blend_function))
	return _cairo_win32_print_gdi_error ("_cairo_win32_surface_composite");
    
    return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_cairo_win32_surface_acquire_dest_image (void                    *abstract_surface,
					 cairo_rectangle_t       *interest_rect,
					 cairo_image_surface_t  **image_out,
					 cairo_rectangle_t       *image_rect,
					 void                   **image_extra)
{
    cairo_win32_surface_t *surface = abstract_surface;
    cairo_win32_surface_t *local = NULL;
    cairo_status_t status;
    RECT clip_box;
    int x1, y1, x2, y2;

    if (surface->image) {
	image_rect->x = 0;
	image_rect->y = 0;
	image_rect->width = surface->clip_rect.width;
	image_rect->height = surface->clip_rect.height;

	*image_out = (cairo_image_surface_t *)surface->image;
	*image_extra = NULL;

	return CAIRO_STATUS_SUCCESS;
    }

    if (GetClipBox (surface->dc, &clip_box) == ERROR)
	return _cairo_win32_print_gdi_error ("_cairo_win3_surface_acquire_dest_image");

    x1 = clip_box.left;
    x2 = clip_box.right;
    y1 = clip_box.top;
    y2 = clip_box.bottom;

    if (interest_rect->x > x1)
	x1 = interest_rect->x;
    if (interest_rect->y > y1)
	y1 = interest_rect->y;
    if (interest_rect->x + interest_rect->width < x2)
	x2 = interest_rect->x + interest_rect->width;
    if (interest_rect->y + interest_rect->height < y2)
	y2 = interest_rect->y + interest_rect->height;
    
    if (x1 >= x2 || y1 >= y2) {
	*image_out = NULL;
	*image_extra = NULL;
	
	return CAIRO_STATUS_SUCCESS;
    }
	
    status = _cairo_win32_surface_get_subimage (abstract_surface, 
						x1, y1, x2 - x1, y2 - y1,
						&local);
    if (status)
	return status;

    *image_out = (cairo_image_surface_t *)local->image;
    *image_extra = local;
    
    image_rect->x = x1;
    image_rect->y = y1;
    image_rect->width = x2 - x1;
    image_rect->height = y2 - y1;

    return CAIRO_STATUS_SUCCESS;
}