diff --git a/winpr/libwinpr/clipboard/synthetic.c b/winpr/libwinpr/clipboard/synthetic.c index 545f8dbc2..7c693c76b 100644 --- a/winpr/libwinpr/clipboard/synthetic.c +++ b/winpr/libwinpr/clipboard/synthetic.c @@ -22,9 +22,22 @@ #include #include #include +#include #include "clipboard.h" +static const char* mime_bitmap[] = { "image/bmp", "image/x-bmp", "image/x-MS-bmp", + "image/x-win-bitmap" }; + +#if defined(WINPR_UTILS_IMAGE_WEBP) +static const char mime_webp[] = "image/webp"; +#endif +#if defined(WINPR_UTILS_IMAGE_PNG) +static const char mime_png[] = "image/png"; +#endif +#if defined(WINPR_UTILS_IMAGE_JPEG) +static const char mime_jpeg[] = "image/jpeg"; +#endif /** * Standard Clipboard Formats: * http://msdn.microsoft.com/en-us/library/windows/desktop/ff729168/ @@ -189,6 +202,19 @@ static void* clipboard_synthesize_utf8_string(wClipboard* clipboard, UINT32 form return NULL; } +static BOOL is_format_bitmap(wClipboard* clipboard, UINT32 formatId) +{ + for (size_t x = 0; x < ARRAYSIZE(mime_bitmap); x++) + { + const char* mime = mime_bitmap[x]; + const UINT32 altFormatId = ClipboardGetFormatId(clipboard, mime); + if (altFormatId == formatId) + return TRUE; + } + + return FALSE; +} + /** * "CF_DIB": * @@ -206,9 +232,9 @@ static void* clipboard_synthesize_cf_dib(wClipboard* clipboard, UINT32 formatId, if (formatId == CF_DIBV5) { } - else if (formatId == ClipboardGetFormatId(clipboard, "image/bmp")) + else if (is_format_bitmap(clipboard, formatId)) { - const BITMAPFILEHEADER* pFileHeader = NULL; + const BITMAPFILEHEADER* pFileHeader; if (SrcSize < (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER))) return NULL; @@ -245,13 +271,41 @@ static void* clipboard_synthesize_cf_dibv5(wClipboard* clipboard, UINT32 formatI if (formatId == CF_DIB) { } - else if (formatId == ClipboardGetFormatId(clipboard, "image/bmp")) + else if (is_format_bitmap(clipboard, formatId)) { } return NULL; } +static void* clipboard_prepend_bmp_header(const BITMAPINFOHEADER* pInfoHeader, const void* data, + size_t size, UINT32* pSize) +{ + WINPR_ASSERT(pInfoHeader); + WINPR_ASSERT(pSize); + + *pSize = 0; + if ((pInfoHeader->biBitCount < 1) || (pInfoHeader->biBitCount > 32)) + return NULL; + + const size_t DstSize = sizeof(BITMAPFILEHEADER) + size; + BYTE* pDstData = (BYTE*)malloc(DstSize); + + if (!pDstData) + return NULL; + + BITMAPFILEHEADER* pFileHeader = (BITMAPFILEHEADER*)pDstData; + pFileHeader->bfType = 0x4D42; + pFileHeader->bfSize = DstSize; + pFileHeader->bfReserved1 = 0; + pFileHeader->bfReserved2 = 0; + pFileHeader->bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); + unsigned char* pDst = &pDstData[sizeof(BITMAPFILEHEADER)]; + CopyMemory(pDst, data, size); + *pSize = DstSize; + return pDstData; +} + /** * "image/bmp": * @@ -261,41 +315,15 @@ static void* clipboard_synthesize_cf_dibv5(wClipboard* clipboard, UINT32 formatI static void* clipboard_synthesize_image_bmp(wClipboard* clipboard, UINT32 formatId, const void* data, UINT32* pSize) { - UINT32 SrcSize = 0; - UINT32 DstSize = 0; - BYTE* pDstData = NULL; - SrcSize = *pSize; + UINT32 SrcSize = *pSize; if (formatId == CF_DIB) { - BYTE* pDst = NULL; - const BITMAPINFOHEADER* pInfoHeader = NULL; - BITMAPFILEHEADER* pFileHeader = NULL; - if (SrcSize < sizeof(BITMAPINFOHEADER)) return NULL; - pInfoHeader = (const BITMAPINFOHEADER*)data; - - if ((pInfoHeader->biBitCount < 1) || (pInfoHeader->biBitCount > 32)) - return NULL; - - DstSize = sizeof(BITMAPFILEHEADER) + SrcSize; - pDstData = (BYTE*)malloc(DstSize); - - if (!pDstData) - return NULL; - - pFileHeader = (BITMAPFILEHEADER*)pDstData; - pFileHeader->bfType = 0x4D42; - pFileHeader->bfSize = DstSize; - pFileHeader->bfReserved1 = 0; - pFileHeader->bfReserved2 = 0; - pFileHeader->bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); - pDst = &pDstData[sizeof(BITMAPFILEHEADER)]; - CopyMemory(pDst, data, SrcSize); - *pSize = DstSize; - return pDstData; + const BITMAPINFOHEADER* pInfoHeader = (const BITMAPINFOHEADER*)data; + return clipboard_prepend_bmp_header(pInfoHeader, data, SrcSize, pSize); } else if (formatId == CF_DIBV5) { @@ -304,6 +332,119 @@ static void* clipboard_synthesize_image_bmp(wClipboard* clipboard, UINT32 format return NULL; } +static void* clipboard_synthesize_image_bmp_to_format(wClipboard* clipboard, UINT32 formatId, + UINT32 bmpFormat, const void* data, + UINT32* pSize) +{ + WINPR_ASSERT(clipboard); + WINPR_ASSERT(data); + WINPR_ASSERT(pSize); + + size_t dsize = 0; + void* result = NULL; + + wImage* img = winpr_image_new(); + void* bmp = clipboard_synthesize_image_bmp(clipboard, formatId, data, pSize); + const UINT32 SrcSize = *pSize; + *pSize = 0; + + if (!bmp || !img) + goto fail; + + if (winpr_image_read_buffer(img, bmp, SrcSize) <= 0) + goto fail; + + result = winpr_image_write_buffer(img, bmpFormat, &dsize); + if (result) + *pSize = dsize; + +fail: + free(bmp); + winpr_image_free(img, TRUE); + return result; +} + +#if defined(WINPR_UTILS_IMAGE_PNG) +static void* clipboard_synthesize_image_bmp_to_png(wClipboard* clipboard, UINT32 formatId, + const void* data, UINT32* pSize) +{ + return clipboard_synthesize_image_bmp_to_format(clipboard, formatId, WINPR_IMAGE_PNG, data, + pSize); +} + +static void* clipboard_synthesize_image_format_to_bmp(wClipboard* clipboard, UINT32 srcFormatId, + const void* data, UINT32* pSize) +{ + WINPR_ASSERT(clipboard); + WINPR_ASSERT(data); + WINPR_ASSERT(pSize); + + void* dst = NULL; + const UINT32 SrcSize = *pSize; + size_t size = 0; + wImage* image = winpr_image_new(); + if (!image) + goto fail; + + const int res = winpr_image_read_buffer(image, data, SrcSize); + if (res <= 0) + goto fail; + + dst = winpr_image_write_buffer(image, WINPR_IMAGE_BITMAP, &size); + if ((size < sizeof(WINPR_BITMAP_FILE_HEADER)) || (size > UINT32_MAX)) + { + free(dst); + dst = NULL; + goto fail; + } + *pSize = (UINT32)size; + +fail: + winpr_image_free(image, TRUE); + + if (dst) + memmove(dst, &dst[sizeof(WINPR_BITMAP_FILE_HEADER)], + size - sizeof(WINPR_BITMAP_FILE_HEADER)); + return dst; +} + +static void* clipboard_synthesize_image_png_to_bmp(wClipboard* clipboard, UINT32 formatId, + const void* data, UINT32* pSize) +{ + return clipboard_synthesize_image_format_to_bmp(clipboard, formatId, data, pSize); +} +#endif + +#if defined(WINPR_UTILS_IMAGE_WEBP) +static void* clipboard_synthesize_image_bmp_to_webp(wClipboard* clipboard, UINT32 formatId, + const void* data, UINT32* pSize) +{ + return clipboard_synthesize_image_bmp_to_format(clipboard, formatId, WINPR_IMAGE_WEBP, data, + pSize); +} + +static void* clipboard_synthesize_image_webp_to_bmp(wClipboard* clipboard, UINT32 formatId, + const void* data, UINT32* pSize) +{ + return clipboard_synthesize_image_format_to_bmp(clipboard, formatId, data, pSize); +} +#endif + +#if defined(WINPR_UTILS_IMAGE_JPEG) +static void* clipboard_synthesize_image_bmp_to_jpeg(wClipboard* clipboard, UINT32 formatId, + const void* data, UINT32* pSize) +{ + return clipboard_synthesize_image_bmp_to_format(clipboard, formatId, WINPR_IMAGE_JPEG, data, + pSize); +} + +static void* clipboard_synthesize_image_jpeg_to_bmp(wClipboard* clipboard, UINT32 formatId, + const void* data, UINT32* pSize) +{ + return clipboard_synthesize_image_format_to_bmp(clipboard, formatId, data, pSize); +} +#endif + /** * "HTML Format": * @@ -476,127 +617,209 @@ static void* clipboard_synthesize_text_html(wClipboard* clipboard, UINT32 format BOOL ClipboardInitSynthesizers(wClipboard* clipboard) { - UINT32 formatId = 0; - UINT32 altFormatId = 0; /** * CF_TEXT */ - ClipboardRegisterSynthesizer(clipboard, CF_TEXT, CF_OEMTEXT, clipboard_synthesize_cf_oemtext); - ClipboardRegisterSynthesizer(clipboard, CF_TEXT, CF_UNICODETEXT, - clipboard_synthesize_cf_unicodetext); - ClipboardRegisterSynthesizer(clipboard, CF_TEXT, CF_LOCALE, clipboard_synthesize_cf_locale); - altFormatId = ClipboardRegisterFormat(clipboard, mime_text_plain); - ClipboardRegisterSynthesizer(clipboard, CF_TEXT, altFormatId, clipboard_synthesize_utf8_string); + { + ClipboardRegisterSynthesizer(clipboard, CF_TEXT, CF_OEMTEXT, + clipboard_synthesize_cf_oemtext); + ClipboardRegisterSynthesizer(clipboard, CF_TEXT, CF_UNICODETEXT, + clipboard_synthesize_cf_unicodetext); + ClipboardRegisterSynthesizer(clipboard, CF_TEXT, CF_LOCALE, clipboard_synthesize_cf_locale); + + UINT32 altFormatId = ClipboardRegisterFormat(clipboard, mime_text_plain); + ClipboardRegisterSynthesizer(clipboard, CF_TEXT, altFormatId, + clipboard_synthesize_utf8_string); + } /** * CF_OEMTEXT */ - ClipboardRegisterSynthesizer(clipboard, CF_OEMTEXT, CF_TEXT, clipboard_synthesize_cf_text); - ClipboardRegisterSynthesizer(clipboard, CF_OEMTEXT, CF_UNICODETEXT, - clipboard_synthesize_cf_unicodetext); - ClipboardRegisterSynthesizer(clipboard, CF_OEMTEXT, CF_LOCALE, clipboard_synthesize_cf_locale); - altFormatId = ClipboardRegisterFormat(clipboard, mime_text_plain); - ClipboardRegisterSynthesizer(clipboard, CF_OEMTEXT, altFormatId, - clipboard_synthesize_utf8_string); + { + ClipboardRegisterSynthesizer(clipboard, CF_OEMTEXT, CF_TEXT, clipboard_synthesize_cf_text); + ClipboardRegisterSynthesizer(clipboard, CF_OEMTEXT, CF_UNICODETEXT, + clipboard_synthesize_cf_unicodetext); + ClipboardRegisterSynthesizer(clipboard, CF_OEMTEXT, CF_LOCALE, + clipboard_synthesize_cf_locale); + UINT32 altFormatId = ClipboardRegisterFormat(clipboard, mime_text_plain); + ClipboardRegisterSynthesizer(clipboard, CF_OEMTEXT, altFormatId, + clipboard_synthesize_utf8_string); + } /** * CF_UNICODETEXT */ - ClipboardRegisterSynthesizer(clipboard, CF_UNICODETEXT, CF_TEXT, clipboard_synthesize_cf_text); - ClipboardRegisterSynthesizer(clipboard, CF_UNICODETEXT, CF_OEMTEXT, - clipboard_synthesize_cf_oemtext); - ClipboardRegisterSynthesizer(clipboard, CF_UNICODETEXT, CF_LOCALE, - clipboard_synthesize_cf_locale); - altFormatId = ClipboardRegisterFormat(clipboard, mime_text_plain); - ClipboardRegisterSynthesizer(clipboard, CF_UNICODETEXT, altFormatId, - clipboard_synthesize_utf8_string); + { + ClipboardRegisterSynthesizer(clipboard, CF_UNICODETEXT, CF_TEXT, + clipboard_synthesize_cf_text); + ClipboardRegisterSynthesizer(clipboard, CF_UNICODETEXT, CF_OEMTEXT, + clipboard_synthesize_cf_oemtext); + ClipboardRegisterSynthesizer(clipboard, CF_UNICODETEXT, CF_LOCALE, + clipboard_synthesize_cf_locale); + UINT32 altFormatId = ClipboardRegisterFormat(clipboard, mime_text_plain); + ClipboardRegisterSynthesizer(clipboard, CF_UNICODETEXT, altFormatId, + clipboard_synthesize_utf8_string); + } /** * UTF8_STRING */ - formatId = ClipboardRegisterFormat(clipboard, mime_text_plain); - - if (formatId) { - ClipboardRegisterSynthesizer(clipboard, formatId, CF_TEXT, clipboard_synthesize_cf_text); - ClipboardRegisterSynthesizer(clipboard, formatId, CF_OEMTEXT, - clipboard_synthesize_cf_oemtext); - ClipboardRegisterSynthesizer(clipboard, formatId, CF_UNICODETEXT, - clipboard_synthesize_cf_unicodetext); - ClipboardRegisterSynthesizer(clipboard, formatId, CF_LOCALE, - clipboard_synthesize_cf_locale); - } + UINT32 formatId = ClipboardRegisterFormat(clipboard, mime_text_plain); + if (formatId) + { + ClipboardRegisterSynthesizer(clipboard, formatId, CF_TEXT, + clipboard_synthesize_cf_text); + ClipboardRegisterSynthesizer(clipboard, formatId, CF_OEMTEXT, + clipboard_synthesize_cf_oemtext); + ClipboardRegisterSynthesizer(clipboard, formatId, CF_UNICODETEXT, + clipboard_synthesize_cf_unicodetext); + ClipboardRegisterSynthesizer(clipboard, formatId, CF_LOCALE, + clipboard_synthesize_cf_locale); + } + } /** * text/plain */ - formatId = ClipboardRegisterFormat(clipboard, mime_text_plain); - - if (formatId) { - ClipboardRegisterSynthesizer(clipboard, formatId, CF_TEXT, clipboard_synthesize_cf_text); - ClipboardRegisterSynthesizer(clipboard, formatId, CF_OEMTEXT, - clipboard_synthesize_cf_oemtext); - ClipboardRegisterSynthesizer(clipboard, formatId, CF_UNICODETEXT, - clipboard_synthesize_cf_unicodetext); - ClipboardRegisterSynthesizer(clipboard, formatId, CF_LOCALE, - clipboard_synthesize_cf_locale); - } + UINT32 formatId = ClipboardRegisterFormat(clipboard, mime_text_plain); + if (formatId) + { + ClipboardRegisterSynthesizer(clipboard, formatId, CF_TEXT, + clipboard_synthesize_cf_text); + ClipboardRegisterSynthesizer(clipboard, formatId, CF_OEMTEXT, + clipboard_synthesize_cf_oemtext); + ClipboardRegisterSynthesizer(clipboard, formatId, CF_UNICODETEXT, + clipboard_synthesize_cf_unicodetext); + ClipboardRegisterSynthesizer(clipboard, formatId, CF_LOCALE, + clipboard_synthesize_cf_locale); + } + } /** * CF_DIB */ - - if (formatId) { ClipboardRegisterSynthesizer(clipboard, CF_DIB, CF_DIBV5, clipboard_synthesize_cf_dibv5); - altFormatId = ClipboardRegisterFormat(clipboard, "image/bmp"); - ClipboardRegisterSynthesizer(clipboard, CF_DIB, altFormatId, - clipboard_synthesize_image_bmp); + for (size_t x = 0; x < ARRAYSIZE(mime_bitmap); x++) + { + const char* mime = mime_bitmap[x]; + const UINT32 altFormatId = ClipboardRegisterFormat(clipboard, mime); + if (altFormatId == 0) + continue; + ClipboardRegisterSynthesizer(clipboard, CF_DIB, altFormatId, + clipboard_synthesize_image_bmp); + } } /** * CF_DIBV5 */ - if (formatId && 0) + if (0) { ClipboardRegisterSynthesizer(clipboard, CF_DIBV5, CF_DIB, clipboard_synthesize_cf_dib); - altFormatId = ClipboardRegisterFormat(clipboard, "image/bmp"); - ClipboardRegisterSynthesizer(clipboard, CF_DIBV5, altFormatId, - clipboard_synthesize_image_bmp); + + for (size_t x = 0; x < ARRAYSIZE(mime_bitmap); x++) + { + const char* mime = mime_bitmap[x]; + const UINT32 altFormatId = ClipboardRegisterFormat(clipboard, mime); + if (altFormatId == 0) + continue; + ClipboardRegisterSynthesizer(clipboard, CF_DIBV5, altFormatId, + clipboard_synthesize_image_bmp); + } } /** * image/bmp */ - formatId = ClipboardRegisterFormat(clipboard, "image/bmp"); - - if (formatId) + for (size_t x = 0; x < ARRAYSIZE(mime_bitmap); x++) { - ClipboardRegisterSynthesizer(clipboard, formatId, CF_DIB, clipboard_synthesize_cf_dib); - ClipboardRegisterSynthesizer(clipboard, formatId, CF_DIBV5, clipboard_synthesize_cf_dibv5); + const char* mime = mime_bitmap[x]; + const UINT32 altFormatId = ClipboardRegisterFormat(clipboard, mime); + if (altFormatId == 0) + continue; + ClipboardRegisterSynthesizer(clipboard, altFormatId, CF_DIB, clipboard_synthesize_cf_dib); + ClipboardRegisterSynthesizer(clipboard, altFormatId, CF_DIBV5, + clipboard_synthesize_cf_dibv5); } + /** + * image/png + */ +#if defined(WINPR_UTILS_IMAGE_PNG) + { + const UINT32 altFormatId = ClipboardRegisterFormat(clipboard, mime_png); + ClipboardRegisterSynthesizer(clipboard, CF_DIB, altFormatId, + clipboard_synthesize_image_bmp_to_png); + ClipboardRegisterSynthesizer(clipboard, CF_DIBV5, altFormatId, + clipboard_synthesize_image_bmp_to_png); + ClipboardRegisterSynthesizer(clipboard, altFormatId, CF_DIB, + clipboard_synthesize_image_png_to_bmp); + ClipboardRegisterSynthesizer(clipboard, altFormatId, CF_DIBV5, + clipboard_synthesize_image_png_to_bmp); + } +#endif + + /** + * image/webp + */ +#if defined(WINPR_UTILS_IMAGE_WEBP) + { + const UINT32 altFormatId = ClipboardRegisterFormat(clipboard, mime_webp); + ClipboardRegisterSynthesizer(clipboard, CF_DIB, altFormatId, + clipboard_synthesize_image_bmp_to_webp); + ClipboardRegisterSynthesizer(clipboard, CF_DIBV5, altFormatId, + clipboard_synthesize_image_webp_to_bmp); + ClipboardRegisterSynthesizer(clipboard, altFormatId, CF_DIB, + clipboard_synthesize_image_bmp_to_webp); + ClipboardRegisterSynthesizer(clipboard, altFormatId, CF_DIBV5, + clipboard_synthesize_image_webp_to_bmp); + } +#endif + + /** + * image/jpeg + */ +#if defined(WINPR_UTILS_IMAGE_JPEG) + { + const UINT32 altFormatId = ClipboardRegisterFormat(clipboard, mime_jpeg); + ClipboardRegisterSynthesizer(clipboard, CF_DIB, altFormatId, + clipboard_synthesize_image_bmp_to_jpeg); + ClipboardRegisterSynthesizer(clipboard, CF_DIBV5, altFormatId, + clipboard_synthesize_image_jpeg_to_bmp); + ClipboardRegisterSynthesizer(clipboard, altFormatId, CF_DIB, + clipboard_synthesize_image_bmp_to_jpeg); + ClipboardRegisterSynthesizer(clipboard, altFormatId, CF_DIBV5, + clipboard_synthesize_image_jpeg_to_bmp); + } +#endif + /** * HTML Format */ - formatId = ClipboardRegisterFormat(clipboard, "HTML Format"); - - if (formatId) { - altFormatId = ClipboardRegisterFormat(clipboard, "text/html"); - ClipboardRegisterSynthesizer(clipboard, formatId, altFormatId, - clipboard_synthesize_text_html); + UINT32 formatId = ClipboardRegisterFormat(clipboard, "HTML Format"); + + if (formatId) + { + const UINT32 altFormatId = ClipboardRegisterFormat(clipboard, "text/html"); + ClipboardRegisterSynthesizer(clipboard, formatId, altFormatId, + clipboard_synthesize_text_html); + } } /** * text/html */ - formatId = ClipboardRegisterFormat(clipboard, "text/html"); - - if (formatId) { - altFormatId = ClipboardRegisterFormat(clipboard, "HTML Format"); - ClipboardRegisterSynthesizer(clipboard, formatId, altFormatId, - clipboard_synthesize_html_format); + UINT32 formatId = ClipboardRegisterFormat(clipboard, "text/html"); + + if (formatId) + { + const UINT32 altFormatId = ClipboardRegisterFormat(clipboard, "HTML Format"); + ClipboardRegisterSynthesizer(clipboard, formatId, altFormatId, + clipboard_synthesize_html_format); + } } return TRUE; diff --git a/winpr/libwinpr/utils/image.c b/winpr/libwinpr/utils/image.c index 0769960b6..95af5ea5f 100644 --- a/winpr/libwinpr/utils/image.c +++ b/winpr/libwinpr/utils/image.c @@ -29,8 +29,6 @@ #include -#include "image.h" - #if defined(WINPR_UTILS_IMAGE_PNG) #include #endif @@ -54,6 +52,13 @@ #include "../log.h" #define TAG WINPR_TAG("utils.image") +static SSIZE_T winpr_convert_from_jpeg(const char* comp_data, size_t comp_data_bytes, UINT32* width, + UINT32* height, UINT32* bpp, char** ppdecomp_data); +static SSIZE_T winpr_convert_from_png(const char* comp_data, size_t comp_data_bytes, UINT32* width, + UINT32* height, UINT32* bpp, char** ppdecomp_data); +static SSIZE_T winpr_convert_from_webp(const char* comp_data, size_t comp_data_bytes, UINT32* width, + UINT32* height, UINT32* bpp, char** ppdecomp_data); + static BOOL writeBitmapFileHeader(wStream* s, const WINPR_BITMAP_FILE_HEADER* bf) { if (!Stream_EnsureRemainingCapacity(s, sizeof(WINPR_BITMAP_FILE_HEADER))) @@ -79,7 +84,15 @@ static BOOL readBitmapFileHeader(wStream* s, WINPR_BITMAP_FILE_HEADER* bf) Stream_Read_UINT16(s, bf->bfReserved1); Stream_Read_UINT16(s, bf->bfReserved2); Stream_Read_UINT32(s, bf->bfOffBits); - return TRUE; + + if (bf->bfSize < sizeof(WINPR_BITMAP_FILE_HEADER)) + { + WLog_ERR(TAG, ""); + return FALSE; + } + + return Stream_CheckAndLogRequiredCapacity(TAG, s, + bf->bfSize - sizeof(WINPR_BITMAP_FILE_HEADER)); } static BOOL writeBitmapInfoHeader(wStream* s, const WINPR_BITMAP_INFO_HEADER* bi) @@ -101,11 +114,12 @@ static BOOL writeBitmapInfoHeader(wStream* s, const WINPR_BITMAP_INFO_HEADER* bi return TRUE; } -static BOOL readBitmapInfoHeader(wStream* s, WINPR_BITMAP_INFO_HEADER* bi) +static BOOL readBitmapInfoHeader(wStream* s, WINPR_BITMAP_INFO_HEADER* bi, size_t* poffset) { if (!s || !bi || (!Stream_CheckAndLogRequiredLength(TAG, s, sizeof(WINPR_BITMAP_INFO_HEADER)))) return FALSE; + const size_t start = Stream_GetPosition(s); Stream_Read_UINT32(s, bi->biSize); Stream_Read_INT32(s, bi->biWidth); Stream_Read_INT32(s, bi->biHeight); @@ -117,7 +131,54 @@ static BOOL readBitmapInfoHeader(wStream* s, WINPR_BITMAP_INFO_HEADER* bi) Stream_Read_INT32(s, bi->biYPelsPerMeter); Stream_Read_UINT32(s, bi->biClrUsed); Stream_Read_UINT32(s, bi->biClrImportant); - return TRUE; + + if ((bi->biBitCount < 1) || (bi->biBitCount > 32)) + { + WLog_WARN(TAG, "invalid biBitCount=%" PRIu32, bi->biBitCount); + return FALSE; + } + + /* https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader */ + size_t offset = 0; + switch (bi->biCompression) + { + case BI_RGB: + if (bi->biBitCount <= 8) + { + DWORD used = bi->biClrUsed; + if (used == 0) + used = (1 << bi->biBitCount) / 8; + offset += sizeof(RGBQUAD) * used; + } + if (bi->biSizeImage == 0) + { + UINT32 stride = ((((bi->biWidth * bi->biBitCount) + 31) & ~31) >> 3); + bi->biSizeImage = abs(bi->biHeight) * stride; + } + break; + case BI_BITFIELDS: + offset += sizeof(DWORD) * 3; // 3 DWORD color masks + break; + default: + WLog_ERR(TAG, "unsupported biCompression %" PRIu32, bi->biCompression); + return FALSE; + } + + if (bi->biSizeImage == 0) + { + WLog_ERR(TAG, "invalid biSizeImage %" PRIuz, bi->biSizeImage); + return FALSE; + } + + const size_t pos = Stream_GetPosition(s) - start; + if (bi->biSize < pos) + { + WLog_ERR(TAG, "invalid biSize %" PRIuz " < (actual) offset %" PRIuz, bi->biSize, pos); + return FALSE; + } + + *poffset = offset; + return Stream_SafeSeek(s, bi->biSize - pos); } BYTE* winpr_bitmap_construct_header(size_t width, size_t height, size_t bpp) @@ -140,19 +201,38 @@ BYTE* winpr_bitmap_construct_header(size_t width, size_t height, size_t bpp) bf.bfType[1] = 'M'; bf.bfReserved1 = 0; bf.bfReserved2 = 0; - bf.bfOffBits = (UINT32)sizeof(WINPR_BITMAP_FILE_HEADER) + sizeof(WINPR_BITMAP_INFO_HEADER); + bi.biSize = (UINT32)sizeof(WINPR_BITMAP_INFO_HEADER); + bf.bfOffBits = (UINT32)sizeof(WINPR_BITMAP_FILE_HEADER) + bi.biSize; bi.biSizeImage = (UINT32)imgSize; bf.bfSize = bf.bfOffBits + bi.biSizeImage; bi.biWidth = (INT32)width; bi.biHeight = -1 * (INT32)height; bi.biPlanes = 1; bi.biBitCount = (UINT16)bpp; - bi.biCompression = 0; + bi.biCompression = BI_RGB; bi.biXPelsPerMeter = (INT32)width; bi.biYPelsPerMeter = (INT32)height; bi.biClrUsed = 0; bi.biClrImportant = 0; - bi.biSize = (UINT32)sizeof(WINPR_BITMAP_INFO_HEADER); + + size_t offset = 0; + switch (bi.biCompression) + { + case BI_RGB: + if (bi.biBitCount <= 8) + { + DWORD used = bi.biClrUsed; + if (used == 0) + used = (1 << bi.biBitCount) / 8; + offset += sizeof(RGBQUAD) * used; + } + break; + case BI_BITFIELDS: + offset += sizeof(DWORD) * 3; // 3 DWORD color masks + break; + default: + return FALSE; + } if (!writeBitmapFileHeader(s, &bf)) goto fail; @@ -160,6 +240,10 @@ BYTE* winpr_bitmap_construct_header(size_t width, size_t height, size_t bpp) if (!writeBitmapInfoHeader(s, &bi)) goto fail; + if (!Stream_EnsureRemainingCapacity(s, offset)) + goto fail; + + Stream_Zero(s, offset); result = Stream_Buffer(s); fail: Stream_Free(s, result == 0); @@ -218,7 +302,7 @@ int winpr_bitmap_write_ex(const char* filename, const BYTE* data, size_t stride, { FILE* fp = NULL; int ret = -1; - const size_t bpp_stride = width * (bpp / 8); + const size_t bpp_stride = ((((width * bpp) + 31) & ~31) >> 3); if (stride == 0) stride = bpp_stride; @@ -287,32 +371,44 @@ static int winpr_image_bitmap_read_buffer(wImage* image, const BYTE* buffer, siz int rc = -1; UINT32 index = 0; BOOL vFlip = 0; - BYTE* pDstData = NULL; - WINPR_BITMAP_FILE_HEADER bf; - WINPR_BITMAP_INFO_HEADER bi; + WINPR_BITMAP_FILE_HEADER bf = { 0 }; + WINPR_BITMAP_INFO_HEADER bi = { 0 }; wStream sbuffer = { 0 }; wStream* s = Stream_StaticConstInit(&sbuffer, buffer, size); if (!s) return -1; - if (!readBitmapFileHeader(s, &bf) || !readBitmapInfoHeader(s, &bi)) + size_t bmpoffset = 0; + if (!readBitmapFileHeader(s, &bf) || !readBitmapInfoHeader(s, &bi, &bmpoffset)) goto fail; if ((bf.bfType[0] != 'B') || (bf.bfType[1] != 'M')) + { + WLog_WARN(TAG, "Invalid bitmap header %c%c", bf.bfType[0], bf.bfType[1]); goto fail; + } image->type = WINPR_IMAGE_BITMAP; - if (Stream_GetPosition(s) > bf.bfOffBits) - goto fail; - if (!Stream_SafeSeek(s, bf.bfOffBits - Stream_GetPosition(s))) + const size_t pos = Stream_GetPosition(s); + const size_t expect = bf.bfOffBits; + + if (pos != expect) + { + WLog_WARN(TAG, "pos=%" PRIuz ", expected %" PRIuz ", offset=" PRIuz, pos, expect, + bmpoffset); goto fail; + } + if (!Stream_CheckAndLogRequiredCapacity(TAG, s, bi.biSizeImage)) goto fail; - if (bi.biWidth < 0) + if (bi.biWidth <= 0) + { + WLog_WARN(TAG, "bi.biWidth=%" PRId32, bi.biWidth); goto fail; + } image->width = (UINT32)bi.biWidth; @@ -327,9 +423,21 @@ static int winpr_image_bitmap_read_buffer(wImage* image, const BYTE* buffer, siz image->height = (UINT32)bi.biHeight; } + if (image->height <= 0) + { + WLog_WARN(TAG, "image->height=%" PRIu32, image->height); + goto fail; + } + image->bitsPerPixel = bi.biBitCount; image->bytesPerPixel = (image->bitsPerPixel / 8); - image->scanline = (bi.biSizeImage / image->height); + image->scanline = ((((bi.biWidth * bi.biBitCount) + 31) & ~31) >> 3); + const size_t bmpsize = 1ull * image->scanline * image->height; + if (bmpsize != bi.biSizeImage) + WLog_WARN(TAG, "bmpsize=%" PRIuz " != bi.biSizeImage=%" PRIu32, bmpsize, bi.biSizeImage); + if (bi.biSizeImage < bmpsize) + goto fail; + image->data = (BYTE*)malloc(bi.biSizeImage); if (!image->data) @@ -339,7 +447,7 @@ static int winpr_image_bitmap_read_buffer(wImage* image, const BYTE* buffer, siz Stream_Read(s, image->data, bi.biSizeImage); else { - pDstData = &(image->data[(image->height - 1) * image->scanline]); + BYTE* pDstData = &(image->data[(image->height - 1ull) * image->scanline]); for (index = 0; index < image->height; index++) { @@ -490,12 +598,12 @@ void* winpr_convert_to_jpeg(const void* data, size_t size, UINT32 width, UINT32 unsigned long outsize = 0; struct jpeg_compress_struct cinfo = { 0 }; - const size_t expect1 = stride * height; + const size_t expect1 = 1ull * stride * height; const size_t bytes = (bpp + 7) / 8; - const size_t expect2 = width * height * bytes; + const size_t expect2 = 1ull * width * height * bytes; if (expect1 != expect2) return NULL; - if (expect1 != size) + if (expect1 > size) return NULL; /* Set up the error handler. */ diff --git a/winpr/libwinpr/utils/image.h b/winpr/libwinpr/utils/image.h deleted file mode 100644 index 8a618e384..000000000 --- a/winpr/libwinpr/utils/image.h +++ /dev/null @@ -1,45 +0,0 @@ -/** - * WinPR: Windows Portable Runtime - * Image Utils - * - * Copyright 2024 Armin Novak - * Copyright 2024 Thincast Technologies GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef WINPR_UTILS_IMAGE_H -#define WINPR_UTILS_IMAGE_H - -#include -#include - -WINPR_LOCAL void* winpr_convert_to_png(const void* data, size_t size, UINT32 width, UINT32 height, - UINT32 stride, UINT32 bpp, UINT32* pSize); -WINPR_LOCAL SSIZE_T winpr_convert_from_png(const char* comp_data, size_t comp_data_bytes, - UINT32* width, UINT32* height, UINT32* bpp, - char** ppdecomp_data); - -WINPR_LOCAL void* winpr_convert_to_webp(const void* data, size_t size, UINT32 width, UINT32 height, - UINT32 stride, UINT32 bpp, UINT32* pSize); -WINPR_LOCAL SSIZE_T winpr_convert_from_webp(const char* comp_data, size_t comp_data_bytes, - UINT32* width, UINT32* height, UINT32* bpp, - char** ppdecomp_data); - -WINPR_LOCAL void* winpr_convert_to_jpeg(const void* data, size_t size, UINT32 width, UINT32 height, - UINT32 stride, UINT32 bpp, UINT32* pSize); -WINPR_LOCAL SSIZE_T winpr_convert_from_jpeg(const char* comp_data, size_t comp_data_bytes, - UINT32* width, UINT32* height, UINT32* bpp, - char** ppdecomp_data); - -#endif diff --git a/winpr/libwinpr/utils/test/TestImage.c b/winpr/libwinpr/utils/test/TestImage.c index 38519d561..8635fa1aa 100644 --- a/winpr/libwinpr/utils/test/TestImage.c +++ b/winpr/libwinpr/utils/test/TestImage.c @@ -175,6 +175,43 @@ static BOOL test_read_write(void) return rc; } +static BOOL test_load_file(const char* name) +{ + BOOL rc = FALSE; + wImage* image = winpr_image_new(); + if (!image || !name) + goto fail; + + const int res = winpr_image_read(image, name); + rc = (res > 0) ? TRUE : FALSE; + +fail: + winpr_image_free(image, TRUE); + return rc; +} + +static BOOL test_load(void) +{ + const char* names[] = { + "rgb.16a.bmp", "rgb.16a.nocolor.bmp", "rgb.16.bmp", "rgb.16.nocolor.bmp", + "rgb.16x.bmp", "rgb.16x.nocolor.bmp", "rgb.24.bmp", "rgb.24.nocolor.bmp", + "rgb.32.bmp", "rgb.32.nocolor.bmp", "rgb.32x.bmp", "rgb.32x.nocolor.bmp", + "rgb.bmp" + }; + + for (size_t x = 0; x < ARRAYSIZE(names); x++) + { + const char* name = names[x]; + char* fname = GetCombinedPath(TEST_SOURCE_PATH, name); + const BOOL res = test_load_file(fname); + free(fname); + if (!res) + return FALSE; + } + + return TRUE; +} + int TestImage(int argc, char* argv[]) { int rc = 0; @@ -183,10 +220,13 @@ int TestImage(int argc, char* argv[]) WINPR_UNUSED(argv); if (!test_equal()) - rc = -1; + rc -= 1; if (!test_read_write()) - rc = -1; + rc -= 2; + + if (!test_load()) + rc -= 4; return rc; } diff --git a/winpr/libwinpr/utils/test/rgb.16.bmp b/winpr/libwinpr/utils/test/rgb.16.bmp new file mode 100644 index 000000000..3e6e1ad4f Binary files /dev/null and b/winpr/libwinpr/utils/test/rgb.16.bmp differ diff --git a/winpr/libwinpr/utils/test/rgb.16.nocolor.bmp b/winpr/libwinpr/utils/test/rgb.16.nocolor.bmp new file mode 100644 index 000000000..70875dd28 Binary files /dev/null and b/winpr/libwinpr/utils/test/rgb.16.nocolor.bmp differ diff --git a/winpr/libwinpr/utils/test/rgb.16a.bmp b/winpr/libwinpr/utils/test/rgb.16a.bmp new file mode 100644 index 000000000..4a7b506eb Binary files /dev/null and b/winpr/libwinpr/utils/test/rgb.16a.bmp differ diff --git a/winpr/libwinpr/utils/test/rgb.16a.nocolor.bmp b/winpr/libwinpr/utils/test/rgb.16a.nocolor.bmp new file mode 100644 index 000000000..ae739dd1d Binary files /dev/null and b/winpr/libwinpr/utils/test/rgb.16a.nocolor.bmp differ diff --git a/winpr/libwinpr/utils/test/rgb.16x.bmp b/winpr/libwinpr/utils/test/rgb.16x.bmp new file mode 100644 index 000000000..5fb5cd807 Binary files /dev/null and b/winpr/libwinpr/utils/test/rgb.16x.bmp differ diff --git a/winpr/libwinpr/utils/test/rgb.16x.nocolor.bmp b/winpr/libwinpr/utils/test/rgb.16x.nocolor.bmp new file mode 100644 index 000000000..7e04ec35b Binary files /dev/null and b/winpr/libwinpr/utils/test/rgb.16x.nocolor.bmp differ diff --git a/winpr/libwinpr/utils/test/rgb.24.bmp b/winpr/libwinpr/utils/test/rgb.24.bmp new file mode 100644 index 000000000..7719f9d2e Binary files /dev/null and b/winpr/libwinpr/utils/test/rgb.24.bmp differ diff --git a/winpr/libwinpr/utils/test/rgb.24.nocolor.bmp b/winpr/libwinpr/utils/test/rgb.24.nocolor.bmp new file mode 100644 index 000000000..ffc30edc7 Binary files /dev/null and b/winpr/libwinpr/utils/test/rgb.24.nocolor.bmp differ diff --git a/winpr/libwinpr/utils/test/rgb.32.nocolor.bmp b/winpr/libwinpr/utils/test/rgb.32.nocolor.bmp new file mode 100644 index 000000000..5217757d0 Binary files /dev/null and b/winpr/libwinpr/utils/test/rgb.32.nocolor.bmp differ diff --git a/winpr/libwinpr/utils/test/rgb.32x.bmp b/winpr/libwinpr/utils/test/rgb.32x.bmp new file mode 100644 index 000000000..41ae40c3e Binary files /dev/null and b/winpr/libwinpr/utils/test/rgb.32x.bmp differ diff --git a/winpr/libwinpr/utils/test/rgb.32x.nocolor.bmp b/winpr/libwinpr/utils/test/rgb.32x.nocolor.bmp new file mode 100644 index 000000000..10abb96f2 Binary files /dev/null and b/winpr/libwinpr/utils/test/rgb.32x.nocolor.bmp differ