[winpr,utils] improve winpr image API

* Add checks for bitmap read functions
* Add more unit tests
* Do not expose internals to WinPR
This commit is contained in:
Armin Novak 2024-02-14 15:34:18 +01:00 committed by akallabeth
parent 7cc4158c5e
commit 894474b161
15 changed files with 498 additions and 172 deletions

View File

@ -22,9 +22,22 @@
#include <errno.h>
#include <winpr/crt.h>
#include <winpr/user.h>
#include <winpr/image.h>
#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,46 +617,57 @@ 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_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);
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, 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_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);
UINT32 altFormatId = ClipboardRegisterFormat(clipboard, mime_text_plain);
ClipboardRegisterSynthesizer(clipboard, CF_UNICODETEXT, altFormatId,
clipboard_synthesize_utf8_string);
}
/**
* UTF8_STRING
*/
formatId = ClipboardRegisterFormat(clipboard, mime_text_plain);
{
UINT32 formatId = ClipboardRegisterFormat(clipboard, mime_text_plain);
if (formatId)
{
ClipboardRegisterSynthesizer(clipboard, formatId, CF_TEXT, clipboard_synthesize_cf_text);
ClipboardRegisterSynthesizer(clipboard, formatId, CF_TEXT,
clipboard_synthesize_cf_text);
ClipboardRegisterSynthesizer(clipboard, formatId, CF_OEMTEXT,
clipboard_synthesize_cf_oemtext);
ClipboardRegisterSynthesizer(clipboard, formatId, CF_UNICODETEXT,
@ -523,15 +675,17 @@ BOOL ClipboardInitSynthesizers(wClipboard* clipboard)
ClipboardRegisterSynthesizer(clipboard, formatId, CF_LOCALE,
clipboard_synthesize_cf_locale);
}
}
/**
* text/plain
*/
formatId = ClipboardRegisterFormat(clipboard, mime_text_plain);
{
UINT32 formatId = ClipboardRegisterFormat(clipboard, mime_text_plain);
if (formatId)
{
ClipboardRegisterSynthesizer(clipboard, formatId, CF_TEXT, clipboard_synthesize_cf_text);
ClipboardRegisterSynthesizer(clipboard, formatId, CF_TEXT,
clipboard_synthesize_cf_text);
ClipboardRegisterSynthesizer(clipboard, formatId, CF_OEMTEXT,
clipboard_synthesize_cf_oemtext);
ClipboardRegisterSynthesizer(clipboard, formatId, CF_UNICODETEXT,
@ -539,65 +693,134 @@ BOOL ClipboardInitSynthesizers(wClipboard* clipboard)
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");
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");
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");
{
UINT32 formatId = ClipboardRegisterFormat(clipboard, "HTML Format");
if (formatId)
{
altFormatId = ClipboardRegisterFormat(clipboard, "text/html");
const UINT32 altFormatId = ClipboardRegisterFormat(clipboard, "text/html");
ClipboardRegisterSynthesizer(clipboard, formatId, altFormatId,
clipboard_synthesize_text_html);
}
}
/**
* text/html
*/
formatId = ClipboardRegisterFormat(clipboard, "text/html");
{
UINT32 formatId = ClipboardRegisterFormat(clipboard, "text/html");
if (formatId)
{
altFormatId = ClipboardRegisterFormat(clipboard, "HTML Format");
const UINT32 altFormatId = ClipboardRegisterFormat(clipboard, "HTML Format");
ClipboardRegisterSynthesizer(clipboard, formatId, altFormatId,
clipboard_synthesize_html_format);
}
}
return TRUE;
}

View File

@ -29,8 +29,6 @@
#include <winpr/image.h>
#include "image.h"
#if defined(WINPR_UTILS_IMAGE_PNG)
#include <png.h>
#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. */

View File

@ -1,45 +0,0 @@
/**
* WinPR: Windows Portable Runtime
* Image Utils
*
* Copyright 2024 Armin Novak <armin.novak@thincast.com>
* 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 <winpr/winpr.h>
#include <winpr/wtypes.h>
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

View File

@ -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;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 MiB