gdi32: Implement nulldrv_StretchDIBits using the PutImage gdi driver function.

This commit is contained in:
Huw Davies 2011-10-17 15:46:07 +01:00 committed by Alexandre Julliard
parent a01844b013
commit c9a7bb715d
2 changed files with 116 additions and 48 deletions

View File

@ -388,68 +388,136 @@ fail:
/* nulldrv fallback implementation using SetDIBits/StretchBlt */
INT nulldrv_StretchDIBits( PHYSDEV dev, INT xDst, INT yDst, INT widthDst, INT heightDst,
INT xSrc, INT ySrc, INT widthSrc, INT heightSrc, const void *bits,
BITMAPINFO *info, UINT coloruse, DWORD rop )
BITMAPINFO *src_info, UINT coloruse, DWORD rop )
{
DC *dc = get_nulldrv_dc( dev );
INT ret;
LONG height;
HBITMAP hBitmap;
HDC hdcMem;
char dst_buffer[FIELD_OFFSET( BITMAPINFO, bmiColors[256] )];
BITMAPINFO *dst_info = (BITMAPINFO *)dst_buffer;
struct bitblt_coords src, dst;
struct gdi_image_bits src_bits;
DWORD err;
HRGN clip = NULL;
INT ret = 0;
INT height = abs( src_info->bmiHeader.biHeight );
BOOL top_down = src_info->bmiHeader.biHeight < 0, non_stretch_from_origin = FALSE;
RECT rect, clip_rect;
/* make sure we have a real implementation for StretchBlt and PutImage */
if (GET_DC_PHYSDEV( dc, pStretchBlt ) == dev || GET_DC_PHYSDEV( dc, pPutImage ) == dev)
return 0;
TRACE("%d %d %d %d <- %d %d %d %d rop %08x\n", xDst, yDst, widthDst, heightDst,
xSrc, ySrc, widthSrc, heightSrc, rop);
height = info->bmiHeader.biHeight;
src_bits.ptr = (void*)bits;
src_bits.is_copy = FALSE;
src_bits.free = NULL;
if (xSrc == 0 && ySrc == 0 && widthDst == widthSrc && heightDst == heightSrc &&
info->bmiHeader.biCompression == BI_RGB)
if (coloruse == DIB_PAL_COLORS && !fill_color_table_from_palette( src_info, dev->hdc )) return 0;
rect.left = xDst;
rect.top = yDst;
rect.right = xDst + widthDst;
rect.bottom = yDst + heightDst;
LPtoDP( dc->hSelf, (POINT *)&rect, 2 );
dst.x = rect.left;
dst.y = rect.top;
dst.width = rect.right - rect.left;
dst.height = rect.bottom - rect.top;
if (dc->layout & LAYOUT_RTL && rop & NOMIRRORBITMAP)
{
/* Windows appears to have a fast case optimization
* that uses the wrong origin for top-down DIBs */
if (height < 0 && heightSrc < abs(height)) ySrc = abs(height) - heightSrc;
dst.x += dst.width;
dst.width = -dst.width;
}
rop &= ~NOMIRRORBITMAP;
if (xDst == 0 && yDst == 0 && info->bmiHeader.biCompression == BI_RGB && rop == SRCCOPY)
src.x = xSrc;
src.width = widthSrc;
src.y = ySrc;
src.height = heightSrc;
if (src.x == 0 && src.y == 0 && src.width == dst.width && src.height == dst.height)
non_stretch_from_origin = TRUE;
if (src_info->bmiHeader.biCompression == BI_RLE4 || src_info->bmiHeader.biCompression == BI_RLE8)
{
BOOL want_clip = non_stretch_from_origin && (rop == SRCCOPY);
if (!build_rle_bitmap( src_info, &src_bits, want_clip ? &clip : NULL )) return 0;
}
if (rop != SRCCOPY || non_stretch_from_origin)
{
if (dst.width == 1 && src.width > 1) src.width--;
if (dst.height == 1 && src.height > 1) src.height--;
}
if (rop != SRCCOPY)
{
if (dst.width < 0 && dst.width == src.width)
{
BITMAP bm;
hBitmap = GetCurrentObject( dev->hdc, OBJ_BITMAP );
if (GetObjectW( hBitmap, sizeof(bm), &bm ) &&
bm.bmWidth == widthSrc && bm.bmHeight == heightSrc &&
bm.bmBitsPixel == info->bmiHeader.biBitCount && bm.bmPlanes == 1)
{
/* fast path */
return SetDIBits( dev->hdc, hBitmap, 0, abs( height ), bits, info, coloruse );
}
/* This is off-by-one, but that's what Windows does */
dst.x += dst.width;
src.x += src.width;
dst.width = -dst.width;
src.width = -src.width;
}
if (dst.height < 0 && dst.height == src.height)
{
dst.y += dst.height;
src.y += src.height;
dst.height = -dst.height;
src.height = -src.height;
}
}
hdcMem = CreateCompatibleDC( dev->hdc );
hBitmap = CreateCompatibleBitmap( dev->hdc, info->bmiHeader.biWidth, height );
SelectObject( hdcMem, hBitmap );
if (coloruse == DIB_PAL_COLORS)
SelectPalette( hdcMem, GetCurrentObject( dev->hdc, OBJ_PAL ), FALSE );
if (!top_down || (rop == SRCCOPY && !non_stretch_from_origin)) src.y = height - src.y - src.height;
if (info->bmiHeader.biCompression == BI_RLE4 || info->bmiHeader.biCompression == BI_RLE8)
if (src.y >= height && src.y + src.height + 1 < height)
src.y = height - 1;
else if (src.y > 0 && src.y + src.height + 1 < 0)
src.y = -src.height - 1;
get_bounding_rect( &rect, src.x, src.y, src.width, src.height );
src.visrect.left = 0;
src.visrect.right = src_info->bmiHeader.biWidth;
src.visrect.top = 0;
src.visrect.bottom = height;
if (!intersect_rect( &src.visrect, &src.visrect, &rect )) goto done;
get_bounding_rect( &rect, dst.x, dst.y, dst.width, dst.height );
if (get_clip_box( dc, &clip_rect ))
intersect_rect( &dst.visrect, &rect, &clip_rect );
else
dst.visrect = rect;
if (is_rect_empty( &dst.visrect )) goto done;
if (!intersect_vis_rectangles( &dst, &src )) goto done;
if (clip) OffsetRgn( clip, dst.x - src.x, dst.y - src.y );
dev = GET_DC_PHYSDEV( dc, pPutImage );
memcpy( dst_info, src_info, FIELD_OFFSET( BITMAPINFO, bmiColors[256] ));
err = dev->funcs->pPutImage( dev, 0, clip, dst_info, &src_bits, &src, &dst, rop );
if (err == ERROR_BAD_FORMAT)
{
/* when RLE compression is used, there may be some gaps (ie the DIB doesn't
* contain all the rectangle described in bmiHeader, but only part of it.
* This mean that those undescribed pixels must be left untouched.
* So, we first copy on a memory bitmap the current content of the
* destination rectangle, blit the DIB bits on top of it - hence leaving
* the gaps untouched -, and blitting the rectangle back.
* This insure that gaps are untouched on the destination rectangle
*/
StretchBlt( hdcMem, xSrc, abs(height) - heightSrc - ySrc, widthSrc, heightSrc,
dev->hdc, xDst, yDst, widthDst, heightDst, rop );
err = convert_bits( src_info, &src, dst_info, &src_bits, FALSE );
if (!err) err = dev->funcs->pPutImage( dev, 0, clip, dst_info, &src_bits, &src, &dst, rop );
}
ret = SetDIBits( hdcMem, hBitmap, 0, abs( height ), bits, info, coloruse );
if (ret) StretchBlt( dev->hdc, xDst, yDst, widthDst, heightDst,
hdcMem, xSrc, abs(height) - heightSrc - ySrc, widthSrc, heightSrc, rop );
DeleteDC( hdcMem );
DeleteObject( hBitmap );
if (err == ERROR_TRANSFORM_NOT_SUPPORTED)
{
memcpy( src_info, dst_info, FIELD_OFFSET( BITMAPINFO, bmiColors[256] ));
err = stretch_bits( src_info, &src, dst_info, &dst, &src_bits, GetStretchBltMode( dev->hdc ) );
if (!err) err = dev->funcs->pPutImage( dev, 0, NULL, dst_info, &src_bits, &src, &dst, rop );
}
if (err) ret = 0;
else if (rop == SRCCOPY) ret = height;
else ret = src_info->bmiHeader.biHeight;
done:
if (src_bits.free) src_bits.free( &src_bits );
if (clip) DeleteObject( clip );
return ret;
}

View File

@ -984,7 +984,7 @@ static void test_dib_formats(void)
ret = SetDIBitsToDevice( memdc, 0, 0, 1, 1, 0, 0, 0, 1, data, bi, DIB_RGB_COLORS );
ok( ret, "SetDIBitsToDevice failed with null bitfields\n" );
ret = StretchDIBits( memdc, 0, 0, 1, 1, 0, 0, 1, 1, data, bi, DIB_RGB_COLORS, SRCCOPY );
todo_wine ok( ret, "StretchDIBits failed with null bitfields\n" );
ok( ret, "StretchDIBits failed with null bitfields\n" );
ret = GetDIBits(hdc, hbmp, 0, 2, data, bi, DIB_RGB_COLORS);
ok( ret, "GetDIBits failed with null bitfields\n" );
bi->bmiHeader.biPlanes = 1;
@ -3152,7 +3152,7 @@ static void test_StretchDIBits(void)
expected[2] = 0x00000000, expected[3] = 0x00000000;
legacy_expected[0] = 0x00543210, legacy_expected[1] = 0x00000000;
legacy_expected[2] = 0x00000000, legacy_expected[3] = 0x00000000;
todo_wine check_StretchDIBits_stretch(hdcDst, dstBuffer, srcBuffer,
check_StretchDIBits_stretch(hdcDst, dstBuffer, srcBuffer,
0, 0, 1, 1, 0, 0, 2, 2, expected, legacy_expected, __LINE__);
expected[0] = 0x00000000, expected[1] = 0x00000000;