From 3cb26488bdee73c39c04f8644d83bddca1bea141 Mon Sep 17 00:00:00 2001 From: Huw Davies Date: Mon, 25 Jul 2011 10:56:30 +0100 Subject: [PATCH] gdi32: Rewrite SetDIBits to use the PutImage gdi driver function. --- dlls/gdi32/bitblt.c | 2 +- dlls/gdi32/dib.c | 252 ++++++++++++++++++++++++++++++++++++++- dlls/gdi32/gdi_private.h | 2 + 3 files changed, 253 insertions(+), 3 deletions(-) diff --git a/dlls/gdi32/bitblt.c b/dlls/gdi32/bitblt.c index b10415337c2..c32302b05a2 100644 --- a/dlls/gdi32/bitblt.c +++ b/dlls/gdi32/bitblt.c @@ -152,7 +152,7 @@ static BOOL get_vis_rectangles( DC *dc_dst, struct bitblt_coords *dst, return TRUE; } -static void free_heap_bits( struct gdi_image_bits *bits ) +void free_heap_bits( struct gdi_image_bits *bits ) { HeapFree( GetProcessHeap(), 0, bits->ptr ); } diff --git a/dlls/gdi32/dib.c b/dlls/gdi32/dib.c index e9f0772d731..f7b90377506 100644 --- a/dlls/gdi32/dib.c +++ b/dlls/gdi32/dib.c @@ -233,6 +233,157 @@ static int fill_color_table_from_palette( BITMAPINFO *info, DC *dc ) return colors; } +static void *get_pixel_ptr( const BITMAPINFO *info, void *bits, int x, int y ) +{ + const int width = info->bmiHeader.biWidth, height = info->bmiHeader.biHeight; + const int bpp = info->bmiHeader.biBitCount; + + if (height > 0) + return (char *)bits + (height - y - 1) * get_dib_stride( width, bpp ) + x * bpp / 8; + else + return (char *)bits + y * get_dib_stride( width, bpp ) + x * bpp / 8; +} + +static BOOL build_rle_bitmap( const BITMAPINFO *info, struct gdi_image_bits *bits, HRGN *clip ) +{ + int i = 0; + int left, right; + int x, y, width = info->bmiHeader.biWidth, height = info->bmiHeader.biHeight; + HRGN run = NULL; + BYTE skip, num, data; + BYTE *out_bits, *in_bits = bits->ptr; + + *clip = NULL; + + assert( info->bmiHeader.biBitCount == 4 || info->bmiHeader.biBitCount == 8 ); + + out_bits = HeapAlloc( GetProcessHeap(), 0, get_dib_image_size( info ) ); + *clip = CreateRectRgn( 0, 0, 0, 0 ); + run = CreateRectRgn( 0, 0, 0, 0 ); + if (!out_bits || !*clip || !run) goto fail; + + x = left = right = 0; + y = height - 1; + + while (i < info->bmiHeader.biSizeImage - 1) + { + num = in_bits[i]; + data = in_bits[i + 1]; + i += 2; + + if (num) + { + if (x + num > width) num = width - x; + if (num) + { + BYTE s = data, *out_ptr = get_pixel_ptr( info, out_bits, x, y ); + if (info->bmiHeader.biBitCount == 8) + memset( out_ptr, s, num ); + else + { + if(x & 1) + { + s = ((s >> 4) & 0x0f) | ((s << 4) & 0xf0); + *out_ptr = (*out_ptr & 0xf0) | (s & 0x0f); + out_ptr++; + x++; + num--; + } + /* this will write one too many if num is odd, but that doesn't matter */ + if (num) memset( out_ptr, s, (num + 1) / 2 ); + } + } + x += num; + right = x; + } + else + { + if (data < 3) + { + if(left != right) + { + SetRectRgn( run, left, y, right, y + 1 ); + CombineRgn( *clip, run, *clip, RGN_OR ); + } + switch (data) + { + case 0: /* eol */ + left = right = x = 0; + y--; + if(y < 0) goto done; + break; + + case 1: /* eod */ + goto done; + + case 2: /* delta */ + if (i >= info->bmiHeader.biSizeImage - 1) goto done; + x += in_bits[i]; + if (x > width) x = width; + left = right = x; + y -= in_bits[i + 1]; + if(y < 0) goto done; + i += 2; + } + } + else /* data bytes of data */ + { + num = data; + skip = (num * info->bmiHeader.biBitCount + 7) / 8; + if (skip > info->bmiHeader.biSizeImage - i) goto done; + skip = (skip + 1) & ~1; + if (x + num > width) num = width - x; + if (num) + { + BYTE *out_ptr = get_pixel_ptr( info, out_bits, x, y ); + if (info->bmiHeader.biBitCount == 8) + memcpy( out_ptr, in_bits + i, num ); + else + { + if(x & 1) + { + const BYTE *in_ptr = in_bits + i; + for ( ; num; num--, x++) + { + if (x & 1) + { + *out_ptr = (*out_ptr & 0xf0) | ((*in_ptr >> 4) & 0x0f); + out_ptr++; + } + else + *out_ptr = (*in_ptr++ << 4) & 0xf0; + } + } + else + memcpy( out_ptr, in_bits + i, (num + 1) / 2); + } + } + x += num; + right = x; + i += skip; + } + } + } + +done: + DeleteObject( run ); + if (bits->free) bits->free( bits ); + + bits->ptr = out_bits; + bits->is_copy = TRUE; + bits->free = free_heap_bits; + + return TRUE; + +fail: + if (run) DeleteObject( run ); + if (*clip) DeleteObject( *clip ); + HeapFree( GetProcessHeap(), 0, out_bits ); + return FALSE; +} + + + /* 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, @@ -355,7 +506,19 @@ INT WINAPI SetDIBits( HDC hdc, HBITMAP hbitmap, UINT startscan, BITMAPOBJ *bitmap; char src_bmibuf[FIELD_OFFSET( BITMAPINFO, bmiColors[256] )]; BITMAPINFO *src_info = (BITMAPINFO *)src_bmibuf; + char dst_bmibuf[FIELD_OFFSET( BITMAPINFO, bmiColors[256] )]; + BITMAPINFO *dst_info = (BITMAPINFO *)dst_bmibuf; INT result = 0; + DWORD err; + struct gdi_image_bits src_bits; + struct bitblt_coords src, dst; + INT src_to_dst_offset; + HRGN clip = 0; + + src_bits.ptr = (void *)bits; + src_bits.is_copy = FALSE; + src_bits.free = NULL; + src_bits.param = NULL; if (coloruse == DIB_RGB_COLORS && !dc) { @@ -377,12 +540,97 @@ INT WINAPI SetDIBits( HDC hdc, HBITMAP hbitmap, UINT startscan, if (!bitmapinfo_from_user_bitmapinfo( src_info, info, coloruse )) goto done; - physdev = GET_DC_PHYSDEV( dc, pSetDIBits ); + if (coloruse == DIB_PAL_COLORS) + if (!fill_color_table_from_palette( src_info, dc )) goto done; + + if (src_info->bmiHeader.biCompression == BI_RLE4 || src_info->bmiHeader.biCompression == BI_RLE8) + { + if ( src_info->bmiHeader.biHeight < 0 || + (src_info->bmiHeader.biCompression == BI_RLE4 && src_info->bmiHeader.biBitCount != 4) || + (src_info->bmiHeader.biCompression == BI_RLE8 && src_info->bmiHeader.biBitCount != 8) ) + { + SetLastError( ERROR_INVALID_PARAMETER ); + goto done; + } + + if (lines == 0) goto done; + else lines = src_info->bmiHeader.biHeight; + startscan = 0; + + if (!build_rle_bitmap( src_info, &src_bits, &clip )) goto done; + } + + dst.visrect.left = 0; + dst.visrect.top = 0; + dst.visrect.right = bitmap->bitmap.bmWidth; + dst.visrect.bottom = bitmap->bitmap.bmHeight; + + src.visrect.left = 0; + src.visrect.top = 0; + src.visrect.right = src_info->bmiHeader.biWidth; + src.visrect.bottom = abs( src_info->bmiHeader.biHeight ); + + if (src_info->bmiHeader.biHeight > 0) + { + src_to_dst_offset = -startscan; + lines = min( lines, src.visrect.bottom - startscan ); + if (lines < src.visrect.bottom) src.visrect.top = src.visrect.bottom - lines; + } + else + { + src_to_dst_offset = src.visrect.bottom - lines - startscan; + /* Unlike the bottom-up case, Windows doesn't limit lines. */ + if (lines < src.visrect.bottom) src.visrect.bottom = lines; + } + + physdev = GET_DC_PHYSDEV( dc, pPutImage ); if (!BITMAP_SetOwnerDC( hbitmap, physdev )) goto done; - result = physdev->funcs->pSetDIBits( physdev, hbitmap, startscan, lines, bits, src_info, coloruse ); + result = lines; + + offset_rect( &src.visrect, 0, src_to_dst_offset ); + if (!intersect_rect( &dst.visrect, &src.visrect, &dst.visrect )) goto done; + src.visrect = dst.visrect; + offset_rect( &src.visrect, 0, -src_to_dst_offset ); + + src.x = src.visrect.left; + src.y = src.visrect.top; + src.width = src.visrect.right - src.visrect.left; + src.height = src.visrect.bottom - src.visrect.top; + + dst.x = dst.visrect.left; + dst.y = dst.visrect.top; + dst.width = dst.visrect.right - dst.visrect.left; + dst.height = dst.visrect.bottom - dst.visrect.top; + + memcpy( dst_info, src_info, FIELD_OFFSET( BITMAPINFO, bmiColors[256] )); + + err = physdev->funcs->pPutImage( physdev, hbitmap, clip, dst_info, &src_bits, &src, &dst, 0 ); + if (err == ERROR_BAD_FORMAT) + { + void *ptr; + + dst_info->bmiHeader.biWidth = dst.width; + ptr = HeapAlloc( GetProcessHeap(), 0, get_dib_image_size( dst_info )); + if (ptr) + { + err = convert_bitmapinfo( src_info, src_bits.ptr, &src.visrect, dst_info, ptr ); + { + if (src_bits.free) src_bits.free( &src_bits ); + src_bits.ptr = ptr; + src_bits.is_copy = TRUE; + src_bits.free = free_heap_bits; + if (!err) + err = physdev->funcs->pPutImage( physdev, hbitmap, clip, dst_info, &src_bits, &src, &dst, 0 ); + } + } + else err = ERROR_OUTOFMEMORY; + } + if(err) result = 0; done: + if (src_bits.free) src_bits.free( &src_bits ); + if (clip) DeleteObject( clip ); GDI_ReleaseObj( hbitmap ); release_dc_ptr( dc ); if (delete_hdc) DeleteDC(hdc); diff --git a/dlls/gdi32/gdi_private.h b/dlls/gdi32/gdi_private.h index 4c8ed582c7b..ea01d1bc545 100644 --- a/dlls/gdi32/gdi_private.h +++ b/dlls/gdi32/gdi_private.h @@ -560,4 +560,6 @@ static inline int get_dib_image_size( const BITMAPINFO *info ) * abs( info->bmiHeader.biHeight ); } +extern void free_heap_bits( struct gdi_image_bits *bits ) DECLSPEC_HIDDEN; + #endif /* __WINE_GDI_PRIVATE_H */