From: Jinoh Kang Subject: [RFC PATCH] gdi32: allow passing BITMAPINFO with bmiHeader.biSizeImage == 0 for EMF DC. Message-Id: Date: Mon, 4 Oct 2021 04:04:02 +0900 User-facing APIs should accept biSizeImage == 0, and fix it up accordingly. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=51834 Signed-off-by: Jinoh Kang --- Some more testing is needed, particularly for when biSizeImage is set to a non-zero _but_ invalid value. dlls/gdi32/emfdc.c | 44 +++++++++++++++++++++++++++++++++------- dlls/gdi32/gdi_private.h | 7 +++++++ 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/dlls/gdi32/emfdc.c b/dlls/gdi32/emfdc.c index a1c7eb74fd5..0422d6ac9f5 100644 --- a/dlls/gdi32/emfdc.c +++ b/dlls/gdi32/emfdc.c @@ -185,6 +185,32 @@ err: return 0; } +static UINT get_user_dib_image_size( const BITMAPINFO *info ) +{ + UINT size; + + if (info->bmiHeader.biCompression != BI_RGB) + { + /* compressed image -- always trust biSizeImage */ + return info->bmiHeader.biSizeImage; + } + + size = get_dib_image_size( info ); + + /* biSizeImage can be zero if biCompression == BI_RGB */ + if (info->bmiHeader.biSizeImage) + { + if (info->bmiHeader.biSizeImage < size) + TRACE( "biSizeImage (%u) smaller than calculated size (%u)", + info->bmiHeader.biSizeImage, + size ); + else + size = info->bmiHeader.biSizeImage; /* FIXME */ + } + + return size; +} + static UINT emfdc_add_handle( struct emf *emf, HGDIOBJ obj ) { UINT index; @@ -1602,19 +1628,22 @@ BOOL EMFDC_StretchDIBits( DC_ATTR *dc_attr, INT x_dst, INT y_dst, INT width_dst, { EMRSTRETCHDIBITS *emr; BOOL ret; - UINT bmi_size, emr_size; + UINT bmi_size, emr_size, img_size; /* calculate the size of the colour table */ bmi_size = get_dib_info_size( info, usage ); - emr_size = sizeof (EMRSTRETCHDIBITS) + bmi_size + info->bmiHeader.biSizeImage; + /* obtain the size of the image */ + img_size = get_user_dib_image_size( info ); + + emr_size = sizeof (EMRSTRETCHDIBITS) + bmi_size + img_size; if (!(emr = HeapAlloc(GetProcessHeap(), 0, emr_size ))) return 0; /* write a bitmap info header (with colours) to the record */ memcpy( &emr[1], info, bmi_size); /* write bitmap bits to the record */ - memcpy ( (BYTE *)&emr[1] + bmi_size, bits, info->bmiHeader.biSizeImage ); + memcpy ( (BYTE *)&emr[1] + bmi_size, bits, img_size ); /* fill in the EMR header at the front of our piece of memory */ emr->emr.iType = EMR_STRETCHDIBITS; @@ -1632,7 +1661,7 @@ BOOL EMFDC_StretchDIBits( DC_ATTR *dc_attr, INT x_dst, INT y_dst, INT width_dst, emr->offBmiSrc = sizeof (EMRSTRETCHDIBITS); emr->cbBmiSrc = bmi_size; emr->offBitsSrc = emr->offBmiSrc + bmi_size; - emr->cbBitsSrc = info->bmiHeader.biSizeImage; + emr->cbBitsSrc = img_size; emr->cxSrc = width_src; emr->cySrc = height_src; @@ -1655,7 +1684,8 @@ BOOL EMFDC_SetDIBitsToDevice( DC_ATTR *dc_attr, INT x_dst, INT y_dst, DWORD widt { EMRSETDIBITSTODEVICE *emr; DWORD bmiSize = get_dib_info_size( info, usage ); - DWORD size = sizeof(EMRSETDIBITSTODEVICE) + bmiSize + info->bmiHeader.biSizeImage; + DWORD imgSize = get_user_dib_image_size( info ); + DWORD size = sizeof(EMRSETDIBITSTODEVICE) + bmiSize + imgSize; BOOL ret; if (!(emr = HeapAlloc( GetProcessHeap(), 0, size ))) return FALSE; @@ -1675,12 +1705,12 @@ BOOL EMFDC_SetDIBitsToDevice( DC_ATTR *dc_attr, INT x_dst, INT y_dst, DWORD widt emr->offBmiSrc = sizeof(EMRSETDIBITSTODEVICE); emr->cbBmiSrc = bmiSize; emr->offBitsSrc = sizeof(EMRSETDIBITSTODEVICE) + bmiSize; - emr->cbBitsSrc = info->bmiHeader.biSizeImage; + emr->cbBitsSrc = imgSize; emr->iUsageSrc = usage; emr->iStartScan = startscan; emr->cScans = lines; memcpy( (BYTE*)emr + emr->offBmiSrc, info, bmiSize ); - memcpy( (BYTE*)emr + emr->offBitsSrc, bits, info->bmiHeader.biSizeImage ); + memcpy( (BYTE*)emr + emr->offBitsSrc, bits, imgSize ); if ((ret = emfdc_record( dc_attr->emf, (EMR*)emr ))) emfdc_update_bounds( dc_attr->emf, &emr->rclBounds ); diff --git a/dlls/gdi32/gdi_private.h b/dlls/gdi32/gdi_private.h index 45bd5431a23..72e9ca165b5 100644 --- a/dlls/gdi32/gdi_private.h +++ b/dlls/gdi32/gdi_private.h @@ -24,6 +24,7 @@ #include #include +#include #include "windef.h" #include "winbase.h" @@ -291,6 +292,12 @@ static inline int get_dib_stride( int width, int bpp ) return ((width * bpp + 31) >> 3) & ~3; } +static inline int get_dib_image_size( const BITMAPINFO *info ) +{ + return get_dib_stride( info->bmiHeader.biWidth, info->bmiHeader.biBitCount ) + * abs( info->bmiHeader.biHeight ); +} + /* only for use on sanitized BITMAPINFO structures */ static inline int get_dib_info_size( const BITMAPINFO *info, UINT coloruse ) { -- 2.31.1