diff --git a/dll/3rdparty/libpng/CMakeLists.txt b/dll/3rdparty/libpng/CMakeLists.txt index 2e293d369c5..cf4b0b4ed42 100644 --- a/dll/3rdparty/libpng/CMakeLists.txt +++ b/dll/3rdparty/libpng/CMakeLists.txt @@ -27,7 +27,7 @@ list(APPEND SOURCE pngwutil.c pngpriv.h) -add_library(libpng MODULE ${SOURCE}) +add_library(libpng SHARED ${SOURCE}) set_module_type(libpng win32dll) target_link_libraries(libpng zlib) add_importlibs(libpng msvcrt kernel32 ntdll) diff --git a/win32ss/user/user32/CMakeLists.txt b/win32ss/user/user32/CMakeLists.txt index 8c938015115..67224595af0 100644 --- a/win32ss/user/user32/CMakeLists.txt +++ b/win32ss/user/user32/CMakeLists.txt @@ -80,7 +80,7 @@ add_library(user32 MODULE ${CMAKE_CURRENT_BINARY_DIR}/user32.def) set_module_type(user32 win32dll UNICODE ENTRYPOINT DllMain 12) -target_link_libraries(user32 user32_wsprintf wine win32ksys ${PSEH_LIB}) +target_link_libraries(user32 libpng user32_wsprintf wine win32ksys ${PSEH_LIB}) add_dependencies(user32 asm) if(MSVC AND (ARCH STREQUAL "i386")) diff --git a/win32ss/user/user32/misc/exticon.c b/win32ss/user/user32/misc/exticon.c index fec6103b2bb..9830a2c124b 100644 --- a/win32ss/user/user32/misc/exticon.c +++ b/win32ss/user/user32/misc/exticon.c @@ -422,7 +422,11 @@ static UINT ICO_ExtractIconExW( sig = USER32_GetResourceTable(peimage, fsizel, &pData); /* NE exe/dll */ +#ifdef __REACTOS__ + if (sig==IMAGE_OS2_SIGNATURE || sig==1) /* .ICO file */ +#else if (sig==IMAGE_OS2_SIGNATURE) +#endif { BYTE *pCIDir = 0; NE_TYPEINFO *pTInfo = (NE_TYPEINFO*)(pData + 2); diff --git a/win32ss/user/user32/windows/cursoricon.c b/win32ss/user/user32/windows/cursoricon.c index a3f8a48602e..ab56cd58c53 100644 --- a/win32ss/user/user32/windows/cursoricon.c +++ b/win32ss/user/user32/windows/cursoricon.c @@ -7,11 +7,374 @@ */ #include +#include "libpng\png.h" WINE_DEFAULT_DEBUG_CHANNEL(cursor); WINE_DECLARE_DEBUG_CHANNEL(icon); //WINE_DECLARE_DEBUG_CHANNEL(resource); +#include "pshpack1.h" +typedef struct { + BYTE bWidth; + BYTE bHeight; + BYTE bColorCount; + BYTE bReserved; + WORD xHotspot; + WORD yHotspot; + DWORD dwDIBSize; + DWORD dwDIBOffset; +} CURSORICONFILEDIRENTRY; + +typedef struct +{ + WORD idReserved; + WORD idType; + WORD idCount; + CURSORICONFILEDIRENTRY idEntries[1]; +} CURSORICONFILEDIR; +#include "poppack.h" + +CHAR* FindTempDirectoryA(void) +{ + static CHAR lpTempPathBuffer[MAX_PATH]; + CHAR *out = lpTempPathBuffer; + DWORD dwRetVal = 0; + FILE * fp; + DWORD dwAttrib; + + /* Gets the temp path env string. Not guaranteed to be valid. */ + dwRetVal = GetTempPathA(MAX_PATH, lpTempPathBuffer); + + if (dwRetVal > MAX_PATH || (dwRetVal == 0)) + { + ERR("Find Temp Directory Failed\n"); + goto exit_error; + } + + dwAttrib = GetFileAttributesA(lpTempPathBuffer); + if (dwAttrib != INVALID_FILE_ATTRIBUTES && + (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)) + TRACE("Temp Subdir OK\n"); + else + goto exit_error; + + if (dwRetVal + sizeof("bmpout.ico1")> MAX_PATH) + { + ERR("Not Enough working room\n"); + goto exit_error; + } + else + { + strcat(lpTempPathBuffer, "bmpout.ico1"); + } + + fp = fopen(lpTempPathBuffer, "wb"); + if (!fp) + { + ERR("Temp Directory Path File Open Failed\n"); + goto exit_error; + } + else + { + fclose(fp); + } + + remove(lpTempPathBuffer); + lpTempPathBuffer[strlen(lpTempPathBuffer) - 1] = 0; + + return out; +exit_error: + return 0; +} + +WCHAR* FindTempDirectoryW(void) +{ + static WCHAR lpTempPathBuffer[MAX_PATH + 1]; + WCHAR *out = lpTempPathBuffer; + DWORD dwRetVal = 0; + FILE * fp; + DWORD dwAttrib; + + /* Gets the temp path env string. Not guaranteed to be valid. */ + dwRetVal = GetTempPathW(MAX_PATH * 2, lpTempPathBuffer); + + if (dwRetVal > MAX_PATH || (dwRetVal == 0)) + { + ERR("Find Temp Directory Failed\n"); + goto exit_error; + } + + dwAttrib = GetFileAttributesW(lpTempPathBuffer); + if (dwAttrib != INVALID_FILE_ATTRIBUTES && + (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)) + TRACE("Temp Subdir OK\n"); + else + goto exit_error; + + if (dwRetVal + sizeof(L"bmpout.ico1")> MAX_PATH) + { + ERR("Not Enough working room\n"); + goto exit_error; + } + else + { + wcscat(lpTempPathBuffer, L"bmpout.ico1"); + } + + fp = _wfopen(lpTempPathBuffer, L"wb"); + if (!fp) + { + ERR("Temp Directory Path File Open Failed\n"); + goto exit_error; + } + else + { + fclose(fp); + } + + _wremove(lpTempPathBuffer); + lpTempPathBuffer[wcslen(lpTempPathBuffer) - 1] = 0; + + return out; +exit_error: + return 0; +} + +/* libpng defines */ +#define png_infopp_NULL (png_infopp)NULL +#define PNG_BYTES_TO_CHECK 4 + +/* libpng helpers */ +typedef struct { + png_bytep buffer; + png_uint_32 bufsize; + png_uint_32 current_pos; +} MEMORY_READER_STATE; + +struct png_wrapper +{ + const char *buffer; + size_t size, pos; +}; + +/* This function will be used for reading png data from array */ +static void read_data_memory(png_structp png_ptr, png_bytep data, png_uint_32 length) +{ + MEMORY_READER_STATE *f = png_get_io_ptr(png_ptr); + if (length > (f->bufsize - f->current_pos)) + png_error(png_ptr, "read error in read_data_memory (loadpng)"); + memcpy(data, f->buffer + f->current_pos, length); + f->current_pos += length; +} + +void PNGtoBMP(_In_ LPBYTE pngbits, _In_ DWORD filesize, _Out_ LPBYTE outbits) +{ + int is_png; + BITMAPINFOHEADER info = { 0 }; + CURSORICONFILEDIR cifd = { 0 }; + int bpp = 0; + int image_size = 0; + FILE * fp; + CHAR lpTempPathBuffer[MAX_PATH + 1]; + MEMORY_READER_STATE memory_reader_state; + png_uint_32 width, height, channels; + int bit_depth, color_type, interlace_type; + png_bytep* row_pointers; + int width1, height1, size, rowbytes, pos, stride; + LPBYTE data; + + if (!pngbits) + return; + + is_png = !png_sig_cmp((png_const_bytep)pngbits, 0, 8); + + TRACE("is_png %d and filesize %d\n", is_png, filesize); + + if (!is_png) + return; + + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) + { + ERR("png_create_read_struct error\n"); + return; + } + + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + ERR("png_create_info_struct error\n"); + png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); + return; + } + + png_infop end_info = png_create_info_struct(png_ptr); + if (!end_info) + { + ERR("png end_info error\n"); + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); + return; + } + + // png_source is array which has png data + memory_reader_state.buffer = (png_bytep)pngbits; + memory_reader_state.bufsize = filesize; + memory_reader_state.current_pos = PNG_BYTES_TO_CHECK; + + // set our own read_function + png_set_read_fn(png_ptr, &memory_reader_state, read_data_memory); + png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK); + + // Read png info + png_read_info(png_ptr, info_ptr); + + png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, \ + &interlace_type, NULL, NULL); + TRACE("width %d, height %d, bit depth %d, color type %d interlace type %d\n", + width, height, bit_depth, color_type, interlace_type); + + channels = png_get_channels(png_ptr, info_ptr); + TRACE("channels is %d\n", channels); + + // row_pointers + row_pointers = png_get_rows(png_ptr, info_ptr); + width1 = png_get_image_width(png_ptr, info_ptr); + height1 = png_get_image_height(png_ptr, info_ptr); + + size = width1 * channels; + + TRACE("size %d, width1 %d, height1 %d\n", + size, width1, height1); + rowbytes = png_get_rowbytes(png_ptr, info_ptr); // same as size above + image_size = height * rowbytes; + + // Read png image data + // Set row pointer which will take pixel value from png file + row_pointers = (png_bytepp)png_malloc(png_ptr, sizeof(png_bytepp) * height); + + for (int i = 0; i < height; i++) + { + row_pointers[i] = png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr)); + } + + // Set row pointer to the png struct + png_set_rows(png_ptr, info_ptr, row_pointers); + + // Read png image data and save in row pointer + // After reading the image, you can deal with the image data with row pointers + png_read_image(png_ptr, row_pointers); + + png_read_end(png_ptr, info_ptr); + + data = NULL; + size += size % 4; // Align + size *= height; + data = malloc(size); + pos = 0; + stride=channels; + + for(int i = height-1; i >= 0; i--) + { + for(int j = 0; j bmiHeader.biBitCount <= 8)) colors = 1 << info->bmiHeader.biBitCount; + /* Account for BI_BITFIELDS in BITMAPINFOHEADER(v1-v3) bmp's. The + * 'max' selection using biSize below will exclude v4 & v5's. */ if (info->bmiHeader.biCompression == BI_BITFIELDS) masks = 3; size = max( info->bmiHeader.biSize, sizeof(BITMAPINFOHEADER) + masks * sizeof(DWORD) ); + /* Test if compression BI_BITFIELDS and bpp either 16 or 32. + * If so, account for the 3 DWORD masks (RGB Order). + * BITMAPCOREHEADER tested above has no 16 or 32 bpp types. + * See table "All of the possible pixel formats in a DIB" + * at https://en.wikipedia.org/wiki/BMP_file_format. */ + if (info->bmiHeader.biSize >= sizeof(BITMAPV4HEADER) && + info->bmiHeader.biCompression == BI_BITFIELDS && + (info->bmiHeader.biBitCount == 16 || info->bmiHeader.biBitCount == 32)) + size += 3 * sizeof(DWORD); // BI_BITFIELDS + return size + colors * ((coloruse == DIB_RGB_COLORS) ? sizeof(RGBQUAD) : sizeof(WORD)); } } @@ -179,6 +558,13 @@ static int bitmap_info_size( const BITMAPINFO * info, WORD coloruse ) static int DIB_GetBitmapInfo( const BITMAPINFOHEADER *header, LONG *width, LONG *height, WORD *bpp, DWORD *compr ) { +#define CR 13 +#define LF 10 +#define EOFM 26 // DOS End Of File Marker +#define HighBitDetect 0x89 // Byte with high bit set to test if not 7-bit +/* wine's definition */ +static const BYTE png_sig_pattern[] = { HighBitDetect, 'P', 'N', 'G', CR, LF, EOFM, LF }; + if (header->biSize == sizeof(BITMAPCOREHEADER)) { const BITMAPCOREHEADER *core = (const BITMAPCOREHEADER *)header; @@ -198,7 +584,20 @@ static int DIB_GetBitmapInfo( const BITMAPINFOHEADER *header, LONG *width, *compr = header->biCompression; return 1; } - ERR("(%d): unknown/wrong size for header\n", header->biSize ); + + int is_png = !png_sig_cmp((png_const_bytep)&header->biSize, 0, 8); + TRACE("is_png is %d\n", is_png); + + if (memcmp(&header->biSize, png_sig_pattern, sizeof(png_sig_pattern)) == 0) + { + ERR("Cannot yet display PNG icons\n"); + /* for PNG format details see https://en.wikipedia.org/wiki/PNG */ + } + else + { + ERR("Unknown/wrong size for header of 0x%x\n", header->biSize ); + } + return -1; } @@ -440,29 +839,6 @@ done: return alpha; } -#include "pshpack1.h" - -typedef struct { - BYTE bWidth; - BYTE bHeight; - BYTE bColorCount; - BYTE bReserved; - WORD xHotspot; - WORD yHotspot; - DWORD dwDIBSize; - DWORD dwDIBOffset; -} CURSORICONFILEDIRENTRY; - -typedef struct -{ - WORD idReserved; - WORD idType; - WORD idCount; - CURSORICONFILEDIRENTRY idEntries[1]; -} CURSORICONFILEDIR; - -#include "poppack.h" - const CURSORICONFILEDIRENTRY* get_best_icon_file_entry( _In_ const CURSORICONFILEDIR* dir, @@ -1085,7 +1461,7 @@ BITMAP_LoadImageW( HBITMAP hbmpOld, hbmpRet = NULL; LONG width, height; WORD bpp; - DWORD compr; + DWORD compr, ResSize = 0; /* Map the bitmap info */ if(fuLoad & LR_LOADFROMFILE) @@ -1123,8 +1499,14 @@ BITMAP_LoadImageW( pbmi = LockResource(hgRsrc); if(!pbmi) return NULL; + ResSize = SizeofResource(hinst, hrsrc); } + if (pbmi->bmiHeader.biCompression == BI_BITFIELDS && + pbmi->bmiHeader.biBitCount == 32 && + pbmi->bmiHeader.biSizeImage + pbmi->bmiHeader.biSize + 12 != ResSize) + WARN("Possibly bad resource size provided\n"); + /* Fix up values */ if(DIB_GetBitmapInfo(&pbmi->bmiHeader, &width, &height, &bpp, &compr) == -1) goto end; @@ -1148,6 +1530,22 @@ BITMAP_LoadImageW( goto end; CopyMemory(pbmiCopy, pbmi, iBMISize); + TRACE("Size Image %d, Size Header %d, ResSize %d\n", + pbmiCopy->bmiHeader.biSizeImage, pbmiCopy->bmiHeader.biSize, ResSize); + + /* Test if this is a GCC windres.exe compiled 32 bpp bitmap using + * BI_BITFIELDS and if so, then a mistake causes it not to include + * the bytes for the bitfields. So, we have to substract out the + * size of the bitfields previously included from bitmap_info_size. + * If this is ever fixed, then this code needs to be removed. */ + if (compr == BI_BITFIELDS && bpp == 32 && + pbmiCopy->bmiHeader.biSizeImage + pbmiCopy->bmiHeader.biSize == ResSize) + { + /* GCC pointer to the image data has 12 less bytes than MSVC */ + pvBits = (char*)pvBits - 12; + ERR("GCC Resource Compiled 32-bpp with error\n"); + } + /* Fix it up, if needed */ if(fuLoad & (LR_LOADTRANSPARENT | LR_LOADMAP3DCOLORS)) { @@ -1342,8 +1740,11 @@ CURSORICON_LoadFromFileW( const CURSORICONFILEDIR *dir; DWORD filesize = 0; LPBYTE bits; + LPBYTE pngbits; HANDLE hCurIcon = NULL; CURSORDATA cursorData; + int is_png; + WCHAR lpTempPathBuffer[MAX_PATH + 1]; TRACE("loading %s\n", debugstr_w( lpszName )); @@ -1377,7 +1778,54 @@ CURSORICON_LoadFromFileW( /* Do the dance */ if(!CURSORICON_GetCursorDataFromBMI(&cursorData, (BITMAPINFO*)(&bits[entry->dwDIBOffset]))) + { + pngbits = &bits[entry->dwDIBOffset]; + + PNGtoBMP(pngbits, filesize, pngbits); + + is_png = !png_sig_cmp((png_const_bytep)(&bits[entry->dwDIBOffset]), 0, 8); + is_png = !png_sig_cmp((png_const_bytep)pngbits, 0, 8); + ERR("is_png is %d and filesize is %d\n", is_png, filesize); + + if (FindTempDirectoryW() == NULL) + ERR("Temp DirectoryW Not Found\n"); + else + { + wcscpy(lpTempPathBuffer, FindTempDirectoryW()); + DPRINTF("Temp File Name is %S\n", lpTempPathBuffer); + } + + bits = map_fileW(lpTempPathBuffer, &filesize ); + if (!bits) + { + ERR("bit is NULL\n"); + return NULL; + } + + dir = (CURSORICONFILEDIR*) bits; + entry = get_best_icon_file_entry(dir, filesize, cxDesired, cyDesired, bIcon, fuLoad); + + if(!entry) + goto end; + + /* Fix dimensions */ + if(!cxDesired) cxDesired = entry->bWidth; + if(!cyDesired) cyDesired = entry->bHeight; + /* A bit of preparation */ + ZeroMemory(&cursorData, sizeof(cursorData)); + + cursorData.rt = (USHORT)((ULONG_PTR)(bIcon ? RT_ICON : RT_CURSOR)); + + if(!CURSORICON_GetCursorDataFromBMI(&cursorData, (BITMAPINFO*)(&bits[entry->dwDIBOffset]))) + { + + ERR("Failing File is \n '%S'.\n", lpszName); goto end; + } + + ERR("Processing Special File:\n '%S'.\n", lpszName); + + } hCurIcon = NtUserxCreateEmptyCurObject(FALSE); if(!hCurIcon) @@ -2436,6 +2884,10 @@ HICON WINAPI CreateIconFromResourceEx( CURSORDATA cursorData; HICON hIcon; BOOL isAnimated; + int is_png; + BYTE outbytes; + PBYTE pbIconBitsOut = & outbytes; + WCHAR lpTempPathBuffer[MAX_PATH + 1]; TRACE("%p, %lu, %lu, %lu, %i, %i, %lu.\n", pbIconBits, cbIconBits, fIcon, dwVersion, cxDesired, cyDesired, uFlags); @@ -2519,11 +2971,62 @@ HICON WINAPI CreateIconFromResourceEx( pbIconBits = (PBYTE)pt; } + is_png = !png_sig_cmp((png_const_bytep)pbIconBits, 0, 8); + TRACE("is_png %d\n", is_png); + if (!CURSORICON_GetCursorDataFromBMI(&cursorData, (BITMAPINFO*)pbIconBits)) { + LPBYTE bits; + const CURSORICONFILEDIRENTRY *entry; + const CURSORICONFILEDIR *dir; + DWORD filesize = 0; + + if (is_png) + PNGtoBMP(pbIconBits, cbIconBits, pbIconBitsOut); + + if (FindTempDirectoryW() == NULL) + ERR("Temp DirectoryW Not Found\n"); + else + { + wcscpy(lpTempPathBuffer, FindTempDirectoryW()); + ERR("Temp File Name is %S\n", lpTempPathBuffer); + } + + bits = map_fileW(lpTempPathBuffer, &filesize ); + if (!bits) + { + ERR("bit is NULL\n"); + return NULL; + } + + dir = (CURSORICONFILEDIR*) bits; + entry = get_best_icon_file_entry(dir, filesize, cxDesired, cyDesired, fIcon, uFlags); + + if(!entry) + goto out; + + /* Fix dimensions */ + if(!cxDesired) cxDesired = entry->bWidth; + if(!cyDesired) cyDesired = entry->bHeight; + /* A bit of preparation */ + ZeroMemory(&cursorData, sizeof(cursorData)); + + cursorData.rt = (USHORT)((ULONG_PTR)(fIcon ? RT_ICON : RT_CURSOR)); + + if(!CURSORICON_GetCursorDataFromBMI(&cursorData, (BITMAPINFO*) + (&bits[entry->dwDIBOffset]))) + { +// ERR("Failing File is \n '%S'.\n", lpszName); + goto end; + } + +// ERR("Processing Special File:\n '%S'.\n", lpszName); + +out: ERR("Couldn't fill the CURSORDATA structure.\n"); if (ResHandle) FreeResource(ResHandle); + if (!is_png) return NULL; } if (ResHandle) @@ -2548,6 +3051,7 @@ HICON WINAPI CreateIconFromResourceEx( if(isAnimated) HeapFree(GetProcessHeap(), 0, cursorData.aspcur); +end: return hIcon; /* Clean up */