From: Charles Davis Subject: [PATCH 3/4] winemac.drv: Really fix our handling of DIBs on the clipboard. Message-Id: <1462302206-47381-3-git-send-email-cdavis5x@gmail.com> Date: Tue, 3 May 2016 12:03:25 -0700 In-Reply-To: <1462302206-47381-2-git-send-email-cdavis5x@gmail.com> References: <1462302206-47381-1-git-send-email-cdavis5x@gmail.com> <1462302206-47381-2-git-send-email-cdavis5x@gmail.com> Additional testing has shown that I was (once again) wrong. Signed-off-by: Charles Davis --- dlls/winemac.drv/clipboard.c | 413 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 386 insertions(+), 27 deletions(-) diff --git a/dlls/winemac.drv/clipboard.c b/dlls/winemac.drv/clipboard.c index 2082cad..af67c63 100644 --- a/dlls/winemac.drv/clipboard.c +++ b/dlls/winemac.drv/clipboard.c @@ -73,7 +73,10 @@ typedef struct _WINE_CLIPFORMAT static HANDLE import_clipboard_data(CFDataRef data); static HANDLE import_bmp_to_bitmap(CFDataRef data); static HANDLE import_bmp_to_dib(CFDataRef data); +static HANDLE import_bmp_to_dibv5(CFDataRef data); static HANDLE import_dib_to_bitmap(CFDataRef data); +static HANDLE import_dib_to_dibv5(CFDataRef data); +static HANDLE import_dibv5_to_dib(CFDataRef data); static HANDLE import_enhmetafile(CFDataRef data); static HANDLE import_enhmetafile_to_metafilepict(CFDataRef data); static HANDLE import_metafilepict(CFDataRef data); @@ -95,7 +98,10 @@ static HANDLE import_utf16_to_unicodetext(CFDataRef data); static CFDataRef export_clipboard_data(HANDLE data); static CFDataRef export_bitmap_to_bmp(HANDLE data); static CFDataRef export_bitmap_to_dib(HANDLE data); +static CFDataRef export_bitmap_to_dibv5(HANDLE data); static CFDataRef export_dib_to_bmp(HANDLE data); +static CFDataRef export_dib_to_dibv5(HANDLE data); +static CFDataRef export_dibv5_to_dib(HANDLE data); static CFDataRef export_enhmetafile(HANDLE data); static CFDataRef export_hdrop_to_filenames(HANDLE data); static CFDataRef export_metafilepict(HANDLE data); @@ -196,19 +202,19 @@ static const struct { CF_BITMAP, CFSTR("org.winehq.builtin.bitmap"), import_bmp_to_bitmap, export_bitmap_to_bmp, FALSE }, { CF_DIB, CFSTR("org.winehq.builtin.bitmap"), import_bmp_to_dib, export_dib_to_bmp, TRUE }, - { CF_DIBV5, CFSTR("org.winehq.builtin.bitmap"), import_bmp_to_dib, export_dib_to_bmp, TRUE }, + { CF_DIBV5, CFSTR("org.winehq.builtin.bitmap"), import_bmp_to_dibv5, export_dib_to_bmp, TRUE }, { CF_DIB, CFSTR("org.winehq.builtin.dib"), import_clipboard_data, export_clipboard_data, FALSE }, { CF_BITMAP, CFSTR("org.winehq.builtin.dib"), import_dib_to_bitmap, export_bitmap_to_dib, TRUE }, - { CF_DIBV5, CFSTR("org.winehq.builtin.dib"), import_clipboard_data, export_clipboard_data, TRUE }, + { CF_DIBV5, CFSTR("org.winehq.builtin.dib"), import_dib_to_dibv5, export_dibv5_to_dib, TRUE }, { CF_DIBV5, CFSTR("org.winehq.builtin.dibv5"), import_clipboard_data, export_clipboard_data, FALSE }, - { CF_BITMAP, CFSTR("org.winehq.builtin.dibv5"), import_dib_to_bitmap, export_bitmap_to_dib, TRUE }, - { CF_DIB, CFSTR("org.winehq.builtin.dibv5"), import_clipboard_data, export_clipboard_data, TRUE }, + { CF_BITMAP, CFSTR("org.winehq.builtin.dibv5"), import_dib_to_bitmap, export_bitmap_to_dibv5, TRUE }, + { CF_DIB, CFSTR("org.winehq.builtin.dibv5"), import_dibv5_to_dib, export_dib_to_dibv5, TRUE }, { CF_BITMAP, CFSTR("com.microsoft.bmp"), import_bmp_to_bitmap, export_bitmap_to_bmp, TRUE }, { CF_DIB, CFSTR("com.microsoft.bmp"), import_bmp_to_dib, export_dib_to_bmp, TRUE }, - { CF_DIBV5, CFSTR("com.microsoft.bmp"), import_bmp_to_dib, export_dib_to_bmp, TRUE }, + { CF_DIBV5, CFSTR("com.microsoft.bmp"), import_bmp_to_dibv5, export_dib_to_bmp, TRUE }, { CF_HDROP, CFSTR("org.winehq.builtin.hdrop"), import_clipboard_data, export_clipboard_data, FALSE }, { CF_HDROP, CFSTR("NSFilenamesPboardType"), import_nsfilenames_to_hdrop, export_hdrop_to_filenames, TRUE }, @@ -615,6 +621,74 @@ static HGLOBAL create_dib_from_bitmap(HBITMAP hBmp) } +/*********************************************************************** + * create_dib_from_dibv5 + * + * Given a packed version 5 DIB, allocates a packed version 3/4 DIB and + * copies the DIB data into it. If the source DIB is already version 3 + * or 4, the source handle will be directly returned. + */ +static HGLOBAL create_dib_from_dibv5(HGLOBAL src) +{ + HGLOBAL dest; + BYTE *psrc, *pdest; + BITMAPINFOHEADER *bmih; + const BITMAPV5HEADER *bv5; + unsigned int data_size, packed_size, offset_src, offset_dest; + + psrc = GlobalLock(src); + bv5 = (BITMAPV5HEADER *)psrc; + + /* Already not a V5 DIB? */ + if (bv5->bV5Size < sizeof(BITMAPV5HEADER)) + { + /* Nothing to do. */ + GlobalUnlock(src); + return src; + } + + /* Calculate the size of the new packed DIB */ + data_size = bv5->bV5SizeImage; + packed_size = sizeof(BITMAPINFOHEADER) + + ((bv5->bV5BitCount <= 8) ? (sizeof(RGBQUAD) * bv5->bV5ClrUsed) : 0) + + ((bv5->bV5Compression == BI_BITFIELDS) ? (sizeof(DWORD) * 3) : 0) + + data_size; + /* Get the offset to the bits */ + offset_dest = packed_size - data_size; + offset_src = bv5->bV5Size + + ((bv5->bV5BitCount <= 8) ? (sizeof(RGBQUAD) * bv5->bV5ClrUsed) : 0); + + /* Allocate the packed DIB */ + TRACE("\tAllocating packed DIB of size %d\n", packed_size); + dest = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, packed_size); + if (!dest) + { + WARN("Could not allocate packed DIB!\n"); + GlobalUnlock(src); + return 0; + } + + /* A packed DIB starts with a BITMAPINFOHEADER */ + pdest = GlobalLock(dest); + bmih = (BITMAPINFOHEADER *)pdest; + + /* Init the BITMAPINFOHEADER */ + memcpy(bmih, bv5, sizeof(BITMAPINFOHEADER)); + bmih->biSize = sizeof(BITMAPINFOHEADER); + if (bv5->bV5Compression == BI_BITFIELDS) + memcpy(bmih+1, &bv5->bV5RedMask, 3*sizeof(DWORD)); + else if (bv5->bV5BitCount <= 8) + memcpy(bmih+1, bv5+1, bv5->bV5ClrUsed*sizeof(RGBQUAD)); + + /* Copy the bits. */ + memcpy(pdest+offset_dest, psrc+offset_src, data_size); + GlobalUnlock(dest); + GlobalUnlock(src); + + return dest; +} + + /************************************************************************** * create_bitmap_from_dib * @@ -645,6 +719,157 @@ static HANDLE create_bitmap_from_dib(HANDLE dib) } +/*********************************************************************** + * create_dibv5_from_dib + * + * Given a packed version 3 or 4 DIB, allocates a packed version 5 DIB + * and copies the DIB data into it. If the source DIB is already + * version 5, the source handle will be directly returned. + */ +static HGLOBAL create_dibv5_from_dib(HGLOBAL src) +{ + HDC hdc; + HGLOBAL dest; + LOGCOLORSPACEW lcs; + BYTE *psrc, *pdest; + const BITMAPINFOHEADER *bmih; + BITMAPV5HEADER *bv5; + unsigned int data_size, packed_size, offset_src, offset_dest; + + psrc = GlobalLock(src); + bmih = (BITMAPINFOHEADER *)psrc; + + /* Already a V5 DIB? */ + if (bmih->biSize >= sizeof(BITMAPV5HEADER)) + { + /* Nothing to do. */ + GlobalUnlock(src); + return src; + } + + /* Calculate the size of the new packed DIB */ + if (bmih->biSize < sizeof(BITMAPINFOHEADER)) + { + /* Assume OS/2 bitmap */ + const BITMAPCOREHEADER *bch = (BITMAPCOREHEADER *)bmih; + data_size = bch->bcHeight * (((bch->bcWidth * bch->bcBitCount + 31) / 8) & ~3); + packed_size = sizeof(BITMAPV5HEADER) + + ((bch->bcBitCount <= 8) ? (sizeof(RGBQUAD) * (1 << bch->bcBitCount)) : 0) + + data_size; + offset_src = bch->bcSize + + ((bch->bcBitCount <= 8) ? (sizeof(RGBTRIPLE) * (1 << bch->bcBitCount)) : + 0); + } + else + { + data_size = bmih->biSizeImage; + packed_size = sizeof(BITMAPV5HEADER) + + ((bmih->biBitCount <= 8) ? (sizeof(RGBQUAD) * bmih->biClrUsed) : 0) + + data_size; + offset_src = bmih->biSize + + ((bmih->biBitCount <= 8) ? (sizeof(RGBQUAD) * bmih->biClrUsed) : 0); + } + /* Get the offset to the bits */ + offset_dest = packed_size - data_size; + + /* Allocate the packed DIB */ + TRACE("\tAllocating packed DIB of size %d\n", packed_size); + dest = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, packed_size); + if (!dest) + { + WARN("Could not allocate packed DIB!\n"); + GlobalUnlock(src); + return 0; + } + + pdest = GlobalLock(dest); + bv5 = (BITMAPV5HEADER *)pdest; + + /* Init the BITMAPV5HEADER */ + if (bmih->biSize < sizeof(BITMAPINFOHEADER)) + { + const BITMAPCOREHEADER *bch = (BITMAPCOREHEADER *)bmih; + + bv5->bV5Width = bch->bcWidth; + bv5->bV5Height = bch->bcHeight; + bv5->bV5Planes = bch->bcPlanes; + bv5->bV5BitCount = bch->bcBitCount; + bv5->bV5Compression = BI_RGB; + bv5->bV5SizeImage = data_size; + bv5->bV5XPelsPerMeter = 0; + bv5->bV5YPelsPerMeter = 0; + bv5->bV5ClrUsed = bch->bcBitCount <= 8 ? (1 << bch->bcBitCount) : 0; + bv5->bV5ClrImportant = 0; + if (bv5->bV5BitCount <= 8) + { + /* Unfortunately, this must be expanded manually. */ + RGBQUAD *rgbq = (RGBQUAD *)(bv5+1); + const RGBTRIPLE *rgbt = (RGBTRIPLE *)(bch+1); + int i; + + for (i = 0; i < bv5->bV5ClrUsed; ++i) + { + rgbq[i].rgbBlue = rgbt[i].rgbtBlue; + rgbq[i].rgbGreen = rgbt[i].rgbtGreen; + rgbq[i].rgbRed = rgbt[i].rgbtRed; + rgbq[i].rgbReserved = 0; + } + } + } + else + { + memcpy(bv5, bmih, bmih->biSize); + if (bv5->bV5BitCount <= 8) + memcpy(bv5+1, bmih+1, bv5->bV5ClrUsed*sizeof(RGBQUAD)); + } + bv5->bV5Size = sizeof(BITMAPV5HEADER); + if (bv5->bV5Compression == BI_BITFIELDS && bmih->biSize < sizeof(BITMAPV4HEADER)) + { + memcpy(&bv5->bV5RedMask, bmih+1, 3*sizeof(DWORD)); + bv5->bV5AlphaMask = 0; + } + + hdc = GetDC(0); + if (GetLogColorSpaceW(GetColorSpace(hdc), &lcs, sizeof(lcs))) + { + if (bmih->biSize < sizeof(BITMAPV4HEADER)) + { + bv5->bV5CSType = lcs.lcsCSType; + bv5->bV5Endpoints = lcs.lcsEndpoints; + bv5->bV5GammaRed = lcs.lcsGammaRed; + bv5->bV5GammaGreen = lcs.lcsGammaGreen; + bv5->bV5GammaBlue = lcs.lcsGammaBlue; + } + bv5->bV5Intent = lcs.lcsIntent; + } + else + { + if (bmih->biSize < sizeof(BITMAPV4HEADER)) + { + bv5->bV5CSType = LCS_DEVICE_RGB; + memset(&bv5->bV5Endpoints, 0, sizeof(bv5->bV5Endpoints)); + bv5->bV5Endpoints.ciexyzRed.ciexyzX = 0xffffffff; + bv5->bV5Endpoints.ciexyzGreen.ciexyzY = 0xffffffff; + bv5->bV5Endpoints.ciexyzBlue.ciexyzZ = 0xffffffff; + bv5->bV5GammaRed = 0; + bv5->bV5GammaGreen = 0; + bv5->bV5GammaBlue = 0; + } + bv5->bV5Intent = LCS_GM_GRAPHICS; + } + bv5->bV5ProfileData = 0; + bv5->bV5ProfileSize = 0; + bv5->bV5Reserved = 0; + + /* Copy the bits. */ + memcpy(pdest+offset_dest, psrc+offset_src, data_size); + GlobalUnlock(dest); + GlobalUnlock(src); + + return dest; +} + + /************************************************************************** * import_clipboard_data * @@ -681,29 +906,12 @@ static HANDLE import_clipboard_data(CFDataRef data) /************************************************************************** - * import_bmp_to_bitmap + * import_bmp_to_dib_any * - * Import BMP data, converting to CF_BITMAP format. + * Import BMP data, converting to device-independent bitmap format. This + * just entails stripping the BMP file format header. */ -static HANDLE import_bmp_to_bitmap(CFDataRef data) -{ - HANDLE ret; - HANDLE dib = import_bmp_to_dib(data); - - ret = create_bitmap_from_dib(dib); - - GlobalFree(dib); - return ret; -} - - -/************************************************************************** - * import_bmp_to_dib - * - * Import BMP data, converting to CF_DIB or CF_DIBV5 format. This just - * entails stripping the BMP file format header. - */ -static HANDLE import_bmp_to_dib(CFDataRef data) +static HANDLE import_bmp_to_dib_any(CFDataRef data) { HANDLE ret = 0; BITMAPFILEHEADER *bfh = (BITMAPFILEHEADER*)CFDataGetBytePtr(data); @@ -724,6 +932,7 @@ static HANDLE import_bmp_to_dib(CFDataRef data) } memcpy(p, bmi, len); + GlobalUnlock(ret); } @@ -732,6 +941,57 @@ static HANDLE import_bmp_to_dib(CFDataRef data) /************************************************************************** + * import_bmp_to_bitmap + * + * Import BMP data, converting to CF_BITMAP format. + */ +static HANDLE import_bmp_to_bitmap(CFDataRef data) +{ + HANDLE ret; + HANDLE dib = import_bmp_to_dib_any(data); + + ret = create_bitmap_from_dib(dib); + + GlobalFree(dib); + return ret; +} + + +/************************************************************************** + * import_bmp_to_dib + * + * Import BMP data, converting to CF_DIB format. + */ +static HANDLE import_bmp_to_dib(CFDataRef data) +{ + HANDLE ret; + HANDLE dib = import_bmp_to_dib_any(data); + + ret = create_dib_from_dibv5(dib); + + if (dib != ret) GlobalFree(dib); + return ret; +} + + +/************************************************************************** + * import_bmp_to_dibv5 + * + * Import BMP data, converting to CF_DIBV5 format. + */ +static HANDLE import_bmp_to_dibv5(CFDataRef data) +{ + HANDLE ret; + HANDLE dib = import_bmp_to_dib_any(data); + + ret = create_dibv5_from_dib(dib); + + if (dib != ret) GlobalFree(dib); + return ret; +} + + +/************************************************************************** * import_dib_to_bitmap * * Import device-independent bitmap data, converting to CF_BITMAP format. @@ -750,6 +1010,44 @@ static HANDLE import_dib_to_bitmap(CFDataRef data) /************************************************************************** + * import_dib_to_dibv5 + * + * Import version 3 device-independent bitmap data, converting to CF_DIBV5 + * format. + */ +static HANDLE import_dib_to_dibv5(CFDataRef data) +{ + HANDLE ret; + HANDLE dib = import_clipboard_data(data); + + ret = create_dibv5_from_dib(dib); + + if (dib != ret) GlobalFree(dib); + + return ret; +} + + +/************************************************************************** + * import_dibv5_to_dib + * + * Import version 5 device-independent bitmap data, converting to CF_DIB + * format. + */ +static HANDLE import_dibv5_to_dib(CFDataRef data) +{ + HANDLE ret; + HANDLE dib = import_clipboard_data(data); + + ret = create_dib_from_dibv5(dib); + + if (dib != ret) GlobalFree(dib); + + return ret; +} + + +/************************************************************************** * import_enhmetafile * * Import enhanced metafile data, converting it to CF_ENHMETAFILE. @@ -1254,8 +1552,35 @@ static CFDataRef export_bitmap_to_dib(HANDLE data) dib = create_dib_from_bitmap(data); if (dib) { - ret = export_clipboard_data(dib); + HGLOBAL dib3 = create_dib_from_dibv5(dib); + + ret = export_clipboard_data(dib3); + GlobalFree(dib); + if (dib3 != dib) GlobalFree(dib3); + } + + return ret; +} + + +/************************************************************************** + * export_bitmap_to_dibv5 + * + * Export CF_BITMAP to a raw version 5 packed device-independent bitmap. + */ +static CFDataRef export_bitmap_to_dibv5(HANDLE data) +{ + CFDataRef ret = NULL; + HGLOBAL dib; + + dib = create_dib_from_bitmap(data); + if (dib) + { + HGLOBAL dib5 = create_dibv5_from_dib(dib); + + ret = export_clipboard_data(dib5); GlobalFree(dib); + if (dib5 != dib) GlobalFree(dib5); } return ret; @@ -1349,6 +1674,40 @@ static CFDataRef export_dib_to_bmp(HANDLE data) /************************************************************************** + * export_dib_to_dibv5 + * + * Export CF_DIB to CF_DIBV5 format. + */ +static CFDataRef export_dib_to_dibv5(HANDLE data) +{ + CFDataRef ret = NULL; + HANDLE dib5 = create_dibv5_from_dib(data); + + ret = export_clipboard_data(dib5); + if (dib5 != data) GlobalFree(dib5); + + return ret; +} + + +/************************************************************************** + * export_dibv5_to_dib + * + * Export CF_DIBV5 to CF_DIB format. + */ +static CFDataRef export_dibv5_to_dib(HANDLE data) +{ + CFDataRef ret = NULL; + HANDLE dib3 = create_dib_from_dibv5(data); + + ret = export_clipboard_data(dib3); + if (dib3 != data) GlobalFree(dib3); + + return ret; +} + + +/************************************************************************** * export_enhmetafile * * Export an enhanced metafile to data. -- 2.8.1